diff options
150 files changed, 1408 insertions, 989 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 85e83ff7f2..57833fe42f 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -48,11 +48,22 @@ ProjectSettings *ProjectSettings::get_singleton() { return singleton; } +String ProjectSettings::get_project_data_dir_name() const { + return ".godot"; +} + +String ProjectSettings::get_project_data_path() const { + String project_data_dir_name = get_project_data_dir_name(); + return "res://" + project_data_dir_name; +} + String ProjectSettings::get_resource_path() const { return resource_path; } -const String ProjectSettings::IMPORTED_FILES_PATH("res://.godot/imported"); +String ProjectSettings::get_imported_files_path() const { + return get_project_data_path().plus_file("imported"); +} String ProjectSettings::localize_path(const String &p_path) const { if (resource_path.is_empty() || p_path.begins_with("res://") || p_path.begins_with("user://") || diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 7e93f26f0d..b642051402 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -42,7 +42,6 @@ class ProjectSettings : public Object { public: typedef Map<String, Variant> CustomMap; - static const String IMPORTED_FILES_PATH; enum { //properties that are not for built in values begin from this value, so builtin ones are displayed first @@ -141,7 +140,10 @@ public: bool property_can_revert(const String &p_name); Variant property_get_revert(const String &p_name); + String get_project_data_dir_name() const; + String get_project_data_path() const; String get_resource_path() const; + String get_imported_files_path() const; static ProjectSettings *get_singleton(); diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp index 635e53fa9c..a6b0a708c3 100644 --- a/core/extension/native_extension.cpp +++ b/core/extension/native_extension.cpp @@ -35,7 +35,9 @@ #include "core/object/method_bind.h" #include "core/os/os.h" -const char *NativeExtension::EXTENSION_LIST_CONFIG_FILE = "res://.godot/extension_list.cfg"; +String NativeExtension::get_extension_list_config_file() { + return ProjectSettings::get_singleton()->get_project_data_path().plus_file("extension_list.cfg"); +} class NativeExtensionMethodBind : public MethodBind { GDNativeExtensionClassMethodCall call_func; diff --git a/core/extension/native_extension.h b/core/extension/native_extension.h index 52e869ad4d..f7f235d8fc 100644 --- a/core/extension/native_extension.h +++ b/core/extension/native_extension.h @@ -62,7 +62,7 @@ protected: static void _bind_methods(); public: - static const char *EXTENSION_LIST_CONFIG_FILE; + static String get_extension_list_config_file(); Error open_library(const String &p_path, const String &p_entry_symbol); void close_library(); diff --git a/core/extension/native_extension_manager.cpp b/core/extension/native_extension_manager.cpp index 4eac5249c9..c8755250d5 100644 --- a/core/extension/native_extension_manager.cpp +++ b/core/extension/native_extension_manager.cpp @@ -112,7 +112,7 @@ void NativeExtensionManager::deinitialize_extensions(NativeExtension::Initializa } void NativeExtensionManager::load_extensions() { - FileAccessRef f = FileAccess::open(NativeExtension::EXTENSION_LIST_CONFIG_FILE, FileAccess::READ); + FileAccessRef f = FileAccess::open(NativeExtension::get_extension_list_config_file(), FileAccess::READ); while (f && !f->eof_reached()) { String s = f->get_line().strip_edges(); if (s != String()) { diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index 1e166015b0..cd44c537a8 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -418,7 +418,7 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const St } String ResourceFormatImporter::get_import_base_path(const String &p_for_file) const { - return ProjectSettings::IMPORTED_FILES_PATH.plus_file(p_for_file.get_file() + "-" + p_for_file.md5_text()); + return ProjectSettings::get_singleton()->get_imported_files_path().plus_file(p_for_file.get_file() + "-" + p_for_file.md5_text()); } bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) const { diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index 290a71043c..b7d01712ff 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -29,6 +29,8 @@ /*************************************************************************/ #include "resource_uid.h" + +#include "core/config/project_settings.h" #include "core/crypto/crypto.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" @@ -36,7 +38,9 @@ static constexpr uint32_t char_count = ('z' - 'a'); static constexpr uint32_t base = char_count + ('9' - '0'); -const char *ResourceUID::CACHE_FILE = "res://.godot/uid_cache.bin"; +String ResourceUID::get_cache_file() { + return ProjectSettings::get_singleton()->get_project_data_path().plus_file("uid_cache.bin"); +} String ResourceUID::id_to_text(ID p_id) const { if (p_id < 0) { @@ -135,12 +139,13 @@ void ResourceUID::remove_id(ID p_id) { } Error ResourceUID::save_to_cache() { - if (!FileAccess::exists(CACHE_FILE)) { + String cache_file = get_cache_file(); + if (!FileAccess::exists(cache_file)) { DirAccessRef d = DirAccess::create(DirAccess::ACCESS_RESOURCES); - d->make_dir_recursive(String(CACHE_FILE).get_base_dir()); //ensure base dir exists + d->make_dir_recursive(String(cache_file).get_base_dir()); //ensure base dir exists } - FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::WRITE); + FileAccessRef f = FileAccess::open(cache_file, FileAccess::WRITE); if (!f) { return ERR_CANT_OPEN; } @@ -164,7 +169,7 @@ Error ResourceUID::save_to_cache() { } Error ResourceUID::load_from_cache() { - FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::READ); + FileAccessRef f = FileAccess::open(get_cache_file(), FileAccess::READ); if (!f) { return ERR_CANT_OPEN; } @@ -206,7 +211,7 @@ Error ResourceUID::update_cache() { for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) { if (!E.get().saved_to_cache) { if (f == nullptr) { - f = FileAccess::open(CACHE_FILE, FileAccess::READ_WRITE); //append + f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); //append if (!f) { return ERR_CANT_OPEN; } diff --git a/core/io/resource_uid.h b/core/io/resource_uid.h index b12138425a..2f1bfdf243 100644 --- a/core/io/resource_uid.h +++ b/core/io/resource_uid.h @@ -44,7 +44,7 @@ public: INVALID_ID = -1 }; - static const char *CACHE_FILE; + static String get_cache_file(); private: mutable Ref<Crypto> crypto; diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 8ba46e49eb..d5509c50f1 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -529,7 +529,7 @@ Object *ClassDB::instantiate(const StringName &p_class) { } ERR_FAIL_COND_V_MSG(!ti, nullptr, "Cannot get class '" + String(p_class) + "'."); ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled."); - ERR_FAIL_COND_V(!ti->creation_func, nullptr); + ERR_FAIL_COND_V_MSG(!ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated."); } #ifdef TOOLS_ENABLED if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index a214a238a6..221a8c4f98 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -90,6 +90,17 @@ const char *VariantParser::tk_name[TK_MAX] = { "ERROR" }; +static double stor_fix(const String &p_str) { + if (p_str == "inf") { + return INFINITY; + } else if (p_str == "inf_neg") { + return -INFINITY; + } else if (p_str == "nan") { + return NAN; + } + return -1; +} + Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, String &r_err_str) { bool string_name = false; @@ -469,8 +480,19 @@ Error VariantParser::_parse_construct(Stream *p_stream, Vector<T> &r_construct, if (first && token.type == TK_PARENTHESIS_CLOSE) { break; } else if (token.type != TK_NUMBER) { - r_err_str = "Expected float in constructor"; - return ERR_PARSE_ERROR; + bool valid = false; + if (token.type == TK_IDENTIFIER) { + double real = stor_fix(token.value); + if (real != -1) { + token.type = TK_NUMBER; + token.value = real; + valid = true; + } + } + if (!valid) { + r_err_str = "Expected float in constructor"; + return ERR_PARSE_ERROR; + } } r_construct.push_back(token.value); @@ -507,6 +529,8 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, value = Variant(); } else if (id == "inf") { value = INFINITY; + } else if (id == "inf_neg") { + value = -INFINITY; } else if (id == "nan") { value = NAN; } else if (id == "Vector2") { @@ -1401,11 +1425,19 @@ Error VariantParser::parse(Stream *p_stream, Variant &r_ret, String &r_err_str, ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// -static String rtosfix(double p_value) { +static String rtos_fix(double p_value) { if (p_value == 0.0) { return "0"; //avoid negative zero (-0) being written, which may annoy git, svn, etc. for changes when they don't exist. + } else if (isnan(p_value)) { + return "nan"; + } else if (isinf(p_value)) { + if (p_value > 0) { + return "inf"; + } else { + return "inf_neg"; + } } else { return rtoss(p_value); } @@ -1423,8 +1455,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, itos(p_variant.operator int64_t())); } break; case Variant::FLOAT: { - String s = rtosfix(p_variant.operator double()); - if (s != "inf" && s != "nan") { + String s = rtos_fix(p_variant.operator double()); + if (s != "inf" && s != "inf_neg" && s != "nan") { if (s.find(".") == -1 && s.find("e") == -1) { s += ".0"; } @@ -1439,7 +1471,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::VECTOR2: { Vector2 v = p_variant; - p_store_string_func(p_store_string_ud, "Vector2(" + rtosfix(v.x) + ", " + rtosfix(v.y) + ")"); + p_store_string_func(p_store_string_ud, "Vector2(" + rtos_fix(v.x) + ", " + rtos_fix(v.y) + ")"); } break; case Variant::VECTOR2I: { Vector2i v = p_variant; @@ -1447,7 +1479,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::RECT2: { Rect2 aabb = p_variant; - p_store_string_func(p_store_string_ud, "Rect2(" + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + ")"); + p_store_string_func(p_store_string_ud, "Rect2(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ")"); } break; case Variant::RECT2I: { @@ -1457,7 +1489,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::VECTOR3: { Vector3 v = p_variant; - p_store_string_func(p_store_string_ud, "Vector3(" + rtosfix(v.x) + ", " + rtosfix(v.y) + ", " + rtosfix(v.z) + ")"); + p_store_string_func(p_store_string_ud, "Vector3(" + rtos_fix(v.x) + ", " + rtos_fix(v.y) + ", " + rtos_fix(v.z) + ")"); } break; case Variant::VECTOR3I: { Vector3i v = p_variant; @@ -1465,17 +1497,17 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::PLANE: { Plane p = p_variant; - p_store_string_func(p_store_string_ud, "Plane(" + rtosfix(p.normal.x) + ", " + rtosfix(p.normal.y) + ", " + rtosfix(p.normal.z) + ", " + rtosfix(p.d) + ")"); + p_store_string_func(p_store_string_ud, "Plane(" + rtos_fix(p.normal.x) + ", " + rtos_fix(p.normal.y) + ", " + rtos_fix(p.normal.z) + ", " + rtos_fix(p.d) + ")"); } break; case Variant::AABB: { AABB aabb = p_variant; - p_store_string_func(p_store_string_ud, "AABB(" + rtosfix(aabb.position.x) + ", " + rtosfix(aabb.position.y) + ", " + rtosfix(aabb.position.z) + ", " + rtosfix(aabb.size.x) + ", " + rtosfix(aabb.size.y) + ", " + rtosfix(aabb.size.z) + ")"); + p_store_string_func(p_store_string_ud, "AABB(" + rtos_fix(aabb.position.x) + ", " + rtos_fix(aabb.position.y) + ", " + rtos_fix(aabb.position.z) + ", " + rtos_fix(aabb.size.x) + ", " + rtos_fix(aabb.size.y) + ", " + rtos_fix(aabb.size.z) + ")"); } break; case Variant::QUATERNION: { Quaternion quaternion = p_variant; - p_store_string_func(p_store_string_ud, "Quaternion(" + rtosfix(quaternion.x) + ", " + rtosfix(quaternion.y) + ", " + rtosfix(quaternion.z) + ", " + rtosfix(quaternion.w) + ")"); + p_store_string_func(p_store_string_ud, "Quaternion(" + rtos_fix(quaternion.x) + ", " + rtos_fix(quaternion.y) + ", " + rtos_fix(quaternion.z) + ", " + rtos_fix(quaternion.w) + ")"); } break; case Variant::TRANSFORM2D: { @@ -1486,7 +1518,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i != 0 || j != 0) { s += ", "; } - s += rtosfix(m3.elements[i][j]); + s += rtos_fix(m3.elements[i][j]); } } @@ -1501,7 +1533,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i != 0 || j != 0) { s += ", "; } - s += rtosfix(m3.elements[i][j]); + s += rtos_fix(m3.elements[i][j]); } } @@ -1517,11 +1549,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i != 0 || j != 0) { s += ", "; } - s += rtosfix(m3.elements[i][j]); + s += rtos_fix(m3.elements[i][j]); } } - s = s + ", " + rtosfix(t.origin.x) + ", " + rtosfix(t.origin.y) + ", " + rtosfix(t.origin.z); + s = s + ", " + rtos_fix(t.origin.x) + ", " + rtos_fix(t.origin.y) + ", " + rtos_fix(t.origin.z); p_store_string_func(p_store_string_ud, s + ")"); } break; @@ -1529,7 +1561,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str // misc types case Variant::COLOR: { Color c = p_variant; - p_store_string_func(p_store_string_ud, "Color(" + rtosfix(c.r) + ", " + rtosfix(c.g) + ", " + rtosfix(c.b) + ", " + rtosfix(c.a) + ")"); + p_store_string_func(p_store_string_ud, "Color(" + rtos_fix(c.r) + ", " + rtos_fix(c.g) + ", " + rtos_fix(c.b) + ", " + rtos_fix(c.a) + ")"); } break; case Variant::STRING_NAME: { @@ -1707,7 +1739,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i > 0) { p_store_string_func(p_store_string_ud, ", "); } - p_store_string_func(p_store_string_ud, rtosfix(ptr[i])); + p_store_string_func(p_store_string_ud, rtos_fix(ptr[i])); } p_store_string_func(p_store_string_ud, ")"); @@ -1723,7 +1755,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i > 0) { p_store_string_func(p_store_string_ud, ", "); } - p_store_string_func(p_store_string_ud, rtosfix(ptr[i])); + p_store_string_func(p_store_string_ud, rtos_fix(ptr[i])); } p_store_string_func(p_store_string_ud, ")"); @@ -1759,7 +1791,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i > 0) { p_store_string_func(p_store_string_ud, ", "); } - p_store_string_func(p_store_string_ud, rtosfix(ptr[i].x) + ", " + rtosfix(ptr[i].y)); + p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].x) + ", " + rtos_fix(ptr[i].y)); } p_store_string_func(p_store_string_ud, ")"); @@ -1775,7 +1807,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i > 0) { p_store_string_func(p_store_string_ud, ", "); } - p_store_string_func(p_store_string_ud, rtosfix(ptr[i].x) + ", " + rtosfix(ptr[i].y) + ", " + rtosfix(ptr[i].z)); + p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].x) + ", " + rtos_fix(ptr[i].y) + ", " + rtos_fix(ptr[i].z)); } p_store_string_func(p_store_string_ud, ")"); @@ -1792,7 +1824,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, ", "); } - p_store_string_func(p_store_string_ud, rtosfix(ptr[i].r) + ", " + rtosfix(ptr[i].g) + ", " + rtosfix(ptr[i].b) + ", " + rtosfix(ptr[i].a)); + p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].r) + ", " + rtos_fix(ptr[i].g) + ", " + rtos_fix(ptr[i].b) + ", " + rtos_fix(ptr[i].a)); } p_store_string_func(p_store_string_ud, ")"); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 5d45947a19..4b710d5f12 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -838,7 +838,7 @@ [codeblock] sqrt(9) # Returns 3 [/codeblock] - [b]Note:[/b]Negative values of [code]x[/code] return NaN. If you need negative inputs, use [code]System.Numerics.Complex[/code] in C#. + [b]Note:[/b] Negative values of [code]x[/code] return NaN. If you need negative inputs, use [code]System.Numerics.Complex[/code] in C#. </description> </method> <method name="step_decimals"> @@ -2340,7 +2340,7 @@ hint_string = "%s/%s:Resource" % [TYPE_OBJECT, TYPE_OBJECT] # Array of resources. hint_string = "%s:%s/%s:Resource" % [TYPE_ARRAY, TYPE_OBJECT, TYPE_OBJECT] # Two-dimensional array of resources. [/codeblock] - [b]Note:[/b] the final colon is required to specify for properly detecting built-in types. + [b]Note:[/b] The final colon is required to specify for properly detecting built-in types. </constant> <constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="26" enum="PropertyHint"> </constant> diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml index 5d18f69409..1be6d5a440 100644 --- a/doc/classes/AABB.xml +++ b/doc/classes/AABB.xml @@ -9,7 +9,7 @@ [b]Note:[/b] Unlike [Rect2], [AABB] does not have a variant that uses integer coordinates. </description> <tutorials> - <link title="Math tutorial index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link title="Math documentation index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> <link title="Vector math">https://docs.godotengine.org/en/latest/tutorials/math/vector_math.html</link> <link title="Advanced vector math">https://docs.godotengine.org/en/latest/tutorials/math/vectors_advanced.html</link> </tutorials> diff --git a/doc/classes/AESContext.xml b/doc/classes/AESContext.xml index 847d1226a2..8c390155ee 100644 --- a/doc/classes/AESContext.xml +++ b/doc/classes/AESContext.xml @@ -89,7 +89,7 @@ <return type="PackedByteArray" /> <description> Get the current IV state for this context (IV gets updated when calling [method update]). You normally don't need this function. - Note: This function only makes sense when the context is started with [constant MODE_CBC_ENCRYPT] or [constant MODE_CBC_DECRYPT]. + [b]Note:[/b] This function only makes sense when the context is started with [constant MODE_CBC_ENCRYPT] or [constant MODE_CBC_DECRYPT]. </description> </method> <method name="start"> @@ -106,7 +106,7 @@ <argument index="0" name="src" type="PackedByteArray" /> <description> Run the desired operation for this AES context. Will return a [PackedByteArray] containing the result of encrypting (or decrypting) the given [code]src[/code]. See [method start] for mode of operation. - Note: The size of [code]src[/code] must be a multiple of 16. Apply some padding if needed. + [b]Note:[/b] The size of [code]src[/code] must be a multiple of 16. Apply some padding if needed. </description> </method> </methods> diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index 38e02745d4..d2ecbdde26 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -28,7 +28,7 @@ Animations are just data containers, and must be added to nodes such as an [AnimationPlayer] to be played back. Animation tracks have different types, each with its own set of dedicated methods. Check [enum TrackType] to see available types. </description> <tutorials> - <link title="Animation tutorial index">https://docs.godotengine.org/en/latest/tutorials/animation/index.html</link> + <link title="Animation documentation index">https://docs.godotengine.org/en/latest/tutorials/animation/index.html</link> </tutorials> <methods> <method name="add_track"> diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml index 1ef9d81d02..2fd923df85 100644 --- a/doc/classes/AnimationPlayer.xml +++ b/doc/classes/AnimationPlayer.xml @@ -10,7 +10,7 @@ </description> <tutorials> <link title="2D Sprite animation">https://docs.godotengine.org/en/latest/tutorials/2d/2d_sprite_animation.html</link> - <link title="Animation tutorial index">https://docs.godotengine.org/en/latest/tutorials/animation/index.html</link> + <link title="Animation documentation index">https://docs.godotengine.org/en/latest/tutorials/animation/index.html</link> <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <methods> @@ -188,7 +188,7 @@ </member> <member name="current_animation" type="String" setter="set_current_animation" getter="get_current_animation" default=""""> The name of the currently playing animation. If no animation is playing, the property's value is an empty string. Changing this value does not restart the animation. See [method play] for more information on playing animations. - [b]Note[/b]: while this property appears in the inspector, it's not meant to be edited, and it's not saved in the scene. This property is mainly used to get the currently playing animation, and internally for animation playback tracks. For more information, see [Animation]. + [b]Note:[/b] while this property appears in the inspector, it's not meant to be edited, and it's not saved in the scene. This property is mainly used to get the currently playing animation, and internally for animation playback tracks. For more information, see [Animation]. </member> <member name="current_animation_length" type="float" setter="" getter="get_current_animation_length"> The length (in seconds) of the currently being played animation. diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml index 843dd5a6d1..3d112e258e 100644 --- a/doc/classes/AnimationTree.xml +++ b/doc/classes/AnimationTree.xml @@ -4,7 +4,8 @@ A node to be used for advanced animation transitions in an [AnimationPlayer]. </brief_description> <description> - Note: When linked with an [AnimationPlayer], several properties and methods of the corresponding [AnimationPlayer] will not function as expected. Playback and transitions should be handled using only the [AnimationTree] and its constituent [AnimationNode](s). The [AnimationPlayer] node should be used solely for adding, deleting, and editing animations. + A node to be used for advanced animation transitions in an [AnimationPlayer]. + [b]Note:[/b] When linked with an [AnimationPlayer], several properties and methods of the corresponding [AnimationPlayer] will not function as expected. Playback and transitions should be handled using only the [AnimationTree] and its constituent [AnimationNode](s). The [AnimationPlayer] node should be used solely for adding, deleting, and editing animations. </description> <tutorials> <link title="Using AnimationTree">https://docs.godotengine.org/en/latest/tutorials/animation/animation_tree.html</link> diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 44e27643bb..8898a59036 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -530,7 +530,7 @@ <argument index="0" name="func" type="Callable" /> <description> Sorts the array using a custom method. The custom method receives two arguments (a pair of elements from the array) and must return either [code]true[/code] or [code]false[/code]. For two elements [code]a[/code] and [code]b[/code], if the given method returns [code]true[/code], element [code]b[/code] will be after element [code]a[/code] in the array. - [b]Note:[/b] you cannot randomize the return value as the heapsort algorithm expects a deterministic result. Doing so will result in unexpected behavior. + [b]Note:[/b] You cannot randomize the return value as the heapsort algorithm expects a deterministic result. Doing so will result in unexpected behavior. [codeblocks] [gdscript] class MyCustomSorter: diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index 49ce2588c5..7b77462322 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -47,7 +47,7 @@ [b]Note:[/b] Godot uses clockwise [url=https://learnopengl.com/Advanced-OpenGL/Face-culling]winding order[/url] for front faces of triangle primitive modes. </description> <tutorials> - <link title="Procedural geometry using the ArrayMesh">https://docs.godotengine.org/en/latest/tutorials/content/procedural_geometry/arraymesh.html</link> + <link title="Procedural geometry using the ArrayMesh">https://docs.godotengine.org/en/latest/tutorials/3d/procedural_geometry/arraymesh.html</link> </tutorials> <methods> <method name="add_blend_shape"> diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index 84ed0de42d..818ab50030 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -7,7 +7,7 @@ This provides a default material with a wide variety of rendering features and properties without the need to write shader code. See the tutorial below for details. </description> <tutorials> - <link title="Spatial material">https://docs.godotengine.org/en/latest/tutorials/3d/spatial_material.html</link> + <link title="Standard Material 3D">https://docs.godotengine.org/en/latest/tutorials/3d/standard_material_3d.html</link> </tutorials> <methods> <method name="get_feature" qualifiers="const"> @@ -232,7 +232,7 @@ </member> <member name="metallic_specular" type="float" setter="set_specular" getter="get_specular" default="0.5"> Sets the size of the specular lobe. The specular lobe is the bright spot that is reflected from light sources. - [b]Note:[/b] unlike [member metallic], this is not energy-conserving, so it should be left at [code]0.5[/code] in most cases. See also [member roughness]. + [b]Note:[/b] Unlike [member metallic], this is not energy-conserving, so it should be left at [code]0.5[/code] in most cases. See also [member roughness]. </member> <member name="metallic_texture" type="Texture2D" setter="set_texture" getter="get_texture"> Texture used to specify metallic for an object. This is multiplied by [member metallic]. @@ -352,7 +352,7 @@ </member> <member name="use_point_size" type="bool" setter="set_flag" getter="get_flag" default="false"> If [code]true[/code], render point size can be changed. - [b]Note:[/b] this is only effective for objects whose geometry is point-based rather than triangle-based. See also [member point_size]. + [b]Note:[/b] This is only effective for objects whose geometry is point-based rather than triangle-based. See also [member point_size]. </member> <member name="uv1_offset" type="Vector3" setter="set_uv1_offset" getter="get_uv1_offset" default="Vector3(0, 0, 0)"> How much to offset the [code]UV[/code] coordinates. This amount will be added to [code]UV[/code] in the vertex function. This can be used to offset a texture. diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index 63df5c40b2..7f0d4cbbe3 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -10,7 +10,7 @@ For more information, read the "Matrices and transforms" documentation article. </description> <tutorials> - <link title="Math tutorial index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link title="Math documentation index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> <link title="Matrices and transforms">https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html</link> <link title="Using 3D transforms">https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link> <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/584</link> diff --git a/doc/classes/CharFXTransform.xml b/doc/classes/CharFXTransform.xml index b00031edf6..2d8e7817c1 100644 --- a/doc/classes/CharFXTransform.xml +++ b/doc/classes/CharFXTransform.xml @@ -7,7 +7,7 @@ By setting various properties on this object, you can control how individual characters will be displayed in a [RichTextEffect]. </description> <tutorials> - <link title="BBCode in RichTextLabel">https://docs.godotengine.org/en/latest/tutorials/gui/bbcode_in_richtextlabel.html</link> + <link title="BBCode in RichTextLabel">https://docs.godotengine.org/en/latest/tutorials/ui/bbcode_in_richtextlabel.html</link> <link title="RichTextEffect test project (third-party)">https://github.com/Eoin-ONeill-Yokai/Godot-Rich-Text-Effect-Test-Project</link> </tutorials> <members> diff --git a/doc/classes/ClassDB.xml b/doc/classes/ClassDB.xml index 5f39fc48e0..b4f98c6c5f 100644 --- a/doc/classes/ClassDB.xml +++ b/doc/classes/ClassDB.xml @@ -78,7 +78,7 @@ <argument index="1" name="no_inheritance" type="bool" default="false" /> <description> Returns an array with all the methods of [code]class[/code] or its ancestry if [code]no_inheritance[/code] is [code]false[/code]. Every element of the array is a [Dictionary] with the following keys: [code]args[/code], [code]default_args[/code], [code]flags[/code], [code]id[/code], [code]name[/code], [code]return: (class_name, hint, hint_string, name, type, usage)[/code]. - [b]Note:[/code] In exported release builds the debug info is not available, so the returned dictionaries will contain only method names. + [b]Note:[/b] In exported release builds the debug info is not available, so the returned dictionaries will contain only method names. </description> </method> <method name="class_get_property" qualifiers="const"> diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml index d856990ff8..bd1f3af436 100644 --- a/doc/classes/CodeEdit.xml +++ b/doc/classes/CodeEdit.xml @@ -5,7 +5,7 @@ </brief_description> <description> CodeEdit is a specialised [TextEdit] designed for editing plain text code files. It contains a bunch of features commonly found in code editors such as line numbers, line folding, code completion, indent management and string / comment management. - [b]Note[/b]: By default [CodeEdit] always use left-to-right text direction to correctly display source code. + [b]Note:[/b] By default [CodeEdit] always use left-to-right text direction to correctly display source code. </description> <tutorials> </tutorials> @@ -51,7 +51,7 @@ <argument index="5" name="value" type="Variant" default="0" /> <description> Submits an item to the queue of potential candidates for the autocomplete menu. Call [method update_code_completion_options] to update the list. - [b]Note[/b]: This list will replace all current candidates. + [b]Note:[/b] This list will replace all current candidates. </description> </method> <method name="add_comment_delimiter"> @@ -431,7 +431,7 @@ <argument index="0" name="force" type="bool" /> <description> Submits all completion options added with [method add_code_completion_option]. Will try to force the autoccomplete menu to popup, if [code]force[/code] is [code]true[/code]. - [b]Note[/b]: This will replace all current candidates. + [b]Note:[/b] This will replace all current candidates. </description> </method> </methods> diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index f33016cc6c..fca6a7631a 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -16,7 +16,7 @@ <argument index="0" name="color" type="Color" /> <description> Adds the given color to a list of color presets. The presets are displayed in the color picker and the user will be able to select them. - [b]Note:[/b] the presets list is only for [i]this[/i] color picker. + [b]Note:[/b] The presets list is only for [i]this[/i] color picker. </description> </method> <method name="erase_preset"> diff --git a/doc/classes/ConcavePolygonShape3D.xml b/doc/classes/ConcavePolygonShape3D.xml index ac569a8992..fbbd0b4876 100644 --- a/doc/classes/ConcavePolygonShape3D.xml +++ b/doc/classes/ConcavePolygonShape3D.xml @@ -5,7 +5,7 @@ </brief_description> <description> Concave polygon shape resource, which can be set into a [PhysicsBody3D] or area. This shape is created by feeding a list of triangles. - Note: when used for collision, [ConcavePolygonShape3D] is intended to work with static [PhysicsBody3D] nodes like [StaticBody3D] and will not work with [CharacterBody3D] or [RigidDynamicBody3D] with a mode other than Static. + [b]Note:[/b] When used for collision, [ConcavePolygonShape3D] is intended to work with static [PhysicsBody3D] nodes like [StaticBody3D] and will not work with [CharacterBody3D] or [RigidDynamicBody3D] with a mode other than Static. </description> <tutorials> <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> diff --git a/doc/classes/Container.xml b/doc/classes/Container.xml index e78eb8d259..24e73534d3 100644 --- a/doc/classes/Container.xml +++ b/doc/classes/Container.xml @@ -29,6 +29,11 @@ <member name="mouse_filter" type="int" setter="set_mouse_filter" getter="get_mouse_filter" override="true" enum="Control.MouseFilter" default="1" /> </members> <signals> + <signal name="pre_sort_children"> + <description> + Emitted when children are going to be sorted. + </description> + </signal> <signal name="sort_children"> <description> Emitted when sorting the children is needed. @@ -36,7 +41,10 @@ </signal> </signals> <constants> - <constant name="NOTIFICATION_SORT_CHILDREN" value="50"> + <constant name="NOTIFICATION_PRE_SORT_CHILDREN" value="50"> + Notification just before children are going to be sorted, in case there's something to process beforehand. + </constant> + <constant name="NOTIFICATION_SORT_CHILDREN" value="51"> Notification for when sorting the children, it must be obeyed immediately. </constant> </constants> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 6ec754d3f9..7c8239977f 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -16,7 +16,7 @@ [b]Note:[/b] Theme items are [i]not[/i] [Object] properties. This means you can't access their values using [method Object.get] and [method Object.set]. Instead, use the [code]get_theme_*[/code] and [code]add_theme_*_override[/code] methods provided by this class. </description> <tutorials> - <link title="GUI tutorial index">https://docs.godotengine.org/en/latest/tutorials/ui/index.html</link> + <link title="GUI documentation index">https://docs.godotengine.org/en/latest/tutorials/ui/index.html</link> <link title="Custom drawing in 2D">https://docs.godotengine.org/en/latest/tutorials/2d/custom_drawing_in_2d.html</link> <link title="Control node gallery">https://docs.godotengine.org/en/latest/tutorials/ui/control_node_gallery.html</link> <link title="All GUI Demos">https://github.com/godotengine/godot-demo-projects/tree/master/gui</link> diff --git a/doc/classes/Crypto.xml b/doc/classes/Crypto.xml index a87c8bfab2..04aee347e0 100644 --- a/doc/classes/Crypto.xml +++ b/doc/classes/Crypto.xml @@ -88,7 +88,7 @@ <argument index="1" name="ciphertext" type="PackedByteArray" /> <description> Decrypt the given [code]ciphertext[/code] with the provided private [code]key[/code]. - [b]Note[/b]: The maximum size of accepted ciphertext is limited by the key size. + [b]Note:[/b] The maximum size of accepted ciphertext is limited by the key size. </description> </method> <method name="encrypt"> @@ -97,7 +97,7 @@ <argument index="1" name="plaintext" type="PackedByteArray" /> <description> Encrypt the given [code]plaintext[/code] with the provided public [code]key[/code]. - [b]Note[/b]: The maximum size of accepted plaintext is limited by the key size. + [b]Note:[/b] The maximum size of accepted plaintext is limited by the key size. </description> </method> <method name="generate_random_bytes"> diff --git a/doc/classes/CryptoKey.xml b/doc/classes/CryptoKey.xml index 5665c629a8..111da858ea 100644 --- a/doc/classes/CryptoKey.xml +++ b/doc/classes/CryptoKey.xml @@ -23,7 +23,7 @@ <argument index="1" name="public_only" type="bool" default="false" /> <description> Loads a key from [code]path[/code]. If [code]public_only[/code] is [code]true[/code], only the public key will be loaded. - [b]Note[/b]: [code]path[/code] should be a "*.pub" file if [code]public_only[/code] is [code]true[/code], a "*.key" file otherwise. + [b]Note:[/b] [code]path[/code] should be a "*.pub" file if [code]public_only[/code] is [code]true[/code], a "*.key" file otherwise. </description> </method> <method name="load_from_string"> @@ -40,7 +40,7 @@ <argument index="1" name="public_only" type="bool" default="false" /> <description> Saves a key to the given [code]path[/code]. If [code]public_only[/code] is [code]true[/code], only the public key will be saved. - [b]Note[/b]: [code]path[/code] should be a "*.pub" file if [code]public_only[/code] is [code]true[/code], a "*.key" file otherwise. + [b]Note:[/b] [code]path[/code] should be a "*.pub" file if [code]public_only[/code] is [code]true[/code], a "*.key" file otherwise. </description> </method> <method name="save_to_string"> diff --git a/doc/classes/DTLSServer.xml b/doc/classes/DTLSServer.xml index 16e65eaadf..f98d8813c9 100644 --- a/doc/classes/DTLSServer.xml +++ b/doc/classes/DTLSServer.xml @@ -160,7 +160,7 @@ <argument index="0" name="udp_peer" type="PacketPeerUDP" /> <description> Try to initiate the DTLS handshake with the given [code]udp_peer[/code] which must be already connected (see [method PacketPeerUDP.connect_to_host]). - [b]Note[/b]: You must check that the state of the return PacketPeerUDP is [constant PacketPeerDTLS.STATUS_HANDSHAKING], as it is normal that 50% of the new connections will be invalid due to cookie exchange. + [b]Note:[/b] You must check that the state of the return PacketPeerUDP is [constant PacketPeerDTLS.STATUS_HANDSHAKING], as it is normal that 50% of the new connections will be invalid due to cookie exchange. </description> </method> </methods> diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index adc1eab393..bab1a261ab 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -177,7 +177,7 @@ [b]Note:[/b] When declaring a dictionary with [code]const[/code], the dictionary itself can still be mutated by defining the values of individual keys. Using [code]const[/code] will only prevent assigning the constant with another value after it was initialized. </description> <tutorials> - <link title="GDScript basics: Dictionary">https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/gdscript_basics.html#dictionary</link> + <link title="GDScript basics: Dictionary">https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/gdscript_basics.html#dictionary</link> <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> <link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link> </tutorials> diff --git a/doc/classes/Directory.xml b/doc/classes/Directory.xml index c8fda27989..93f04ff2a2 100644 --- a/doc/classes/Directory.xml +++ b/doc/classes/Directory.xml @@ -54,7 +54,7 @@ [/codeblocks] </description> <tutorials> - <link title="File system">https://docs.godotengine.org/en/latest/getting_started/step_by_step/filesystem.html</link> + <link title="File system">https://docs.godotengine.org/en/latest/tutorials/scripting/filesystem.html</link> </tutorials> <methods> <method name="change_dir"> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 7eff8db59c..01ca23c217 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -556,6 +556,7 @@ <return type="int" enum="DisplayServer.WindowMode" /> <argument index="0" name="window_id" type="int" default="0" /> <description> + Returns the current window's mode. </description> </method> <method name="window_get_position" qualifiers="const"> @@ -666,6 +667,8 @@ <argument index="0" name="mode" type="int" enum="DisplayServer.WindowMode" /> <argument index="1" name="window_id" type="int" default="0" /> <description> + Sets window mode for the given window to [code]mode[/code]. See [enum WindowMode] for possible values and how each mode behaves. + [b]Note:[/b] Setting the window to fullscreen forcibly sets the borderless flag to [code]true[/code], so make sure to set it back to [code]false[/code] when not wanted. </description> </method> <method name="window_set_mouse_passthrough"> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index b8436be76a..43e65f1aa0 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -7,7 +7,7 @@ Plugins are used by the editor to extend functionality. The most common types of plugins are those which edit a given node or resource type, import plugins and export plugins. See also [EditorScript] to add functions to the editor. </description> <tutorials> - <link title="Editor plugins tutorial index">https://docs.godotengine.org/en/latest/tutorials/plugins/editor/index.html</link> + <link title="Editor plugins documentation index">https://docs.godotengine.org/en/latest/tutorials/plugins/editor/index.html</link> </tutorials> <methods> <method name="_apply_changes" qualifiers="virtual"> diff --git a/doc/classes/EditorResourcePreview.xml b/doc/classes/EditorResourcePreview.xml index 4dc46945cf..c780045263 100644 --- a/doc/classes/EditorResourcePreview.xml +++ b/doc/classes/EditorResourcePreview.xml @@ -32,7 +32,7 @@ <argument index="3" name="userdata" type="Variant" /> <description> Queue the [code]resource[/code] being edited for preview. Once the preview is ready, the [code]receiver[/code]'s [code]receiver_func[/code] will be called. The [code]receiver_func[/code] must take the following four arguments: [String] path, [Texture2D] preview, [Texture2D] thumbnail_preview, [Variant] userdata. [code]userdata[/code] can be anything, and will be returned when [code]receiver_func[/code] is called. - [b]Note[/b]: If it was not possible to create the preview the [code]receiver_func[/code] will still be called, but the preview will be null. + [b]Note:[/b] If it was not possible to create the preview the [code]receiver_func[/code] will still be called, but the preview will be null. </description> </method> <method name="queue_resource_preview"> @@ -43,7 +43,7 @@ <argument index="3" name="userdata" type="Variant" /> <description> Queue a resource file located at [code]path[/code] for preview. Once the preview is ready, the [code]receiver[/code]'s [code]receiver_func[/code] will be called. The [code]receiver_func[/code] must take the following four arguments: [String] path, [Texture2D] preview, [Texture2D] thumbnail_preview, [Variant] userdata. [code]userdata[/code] can be anything, and will be returned when [code]receiver_func[/code] is called. - [b]Note[/b]: If it was not possible to create the preview the [code]receiver_func[/code] will still be called, but the preview will be null. + [b]Note:[/b] If it was not possible to create the preview the [code]receiver_func[/code] will still be called, but the preview will be null. </description> </method> <method name="remove_preview_generator"> diff --git a/doc/classes/EditorScenePostImport.xml b/doc/classes/EditorScenePostImport.xml index 241531c35f..8a731a6d1d 100644 --- a/doc/classes/EditorScenePostImport.xml +++ b/doc/classes/EditorScenePostImport.xml @@ -52,7 +52,7 @@ [/codeblocks] </description> <tutorials> - <link title="Importing 3D scenes: Custom script">https://docs.godotengine.org/en/latest/getting_started/workflow/assets/importing_scenes.html#custom-script</link> + <link title="Importing 3D scenes: Custom script">https://docs.godotengine.org/en/latest/tutorials/assets_pipeline/importing_scenes.html#custom-script</link> </tutorials> <methods> <method name="_post_import" qualifiers="virtual"> diff --git a/doc/classes/File.xml b/doc/classes/File.xml index cf08029c72..811aeb8aab 100644 --- a/doc/classes/File.xml +++ b/doc/classes/File.xml @@ -45,7 +45,7 @@ [b]Note:[/b] Files are automatically closed only if the process exits "normally" (such as by clicking the window manager's close button or pressing [b]Alt + F4[/b]). If you stop the project execution by pressing [b]F8[/b] while the project is running, the file won't be closed as the game process will be killed. You can work around this by calling [method flush] at regular intervals. </description> <tutorials> - <link title="File system">https://docs.godotengine.org/en/latest/getting_started/step_by_step/filesystem.html</link> + <link title="File system">https://docs.godotengine.org/en/latest/tutorials/scripting/filesystem.html</link> <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> </tutorials> <methods> diff --git a/doc/classes/FontData.xml b/doc/classes/FontData.xml index a814685388..7b1fc2ab42 100644 --- a/doc/classes/FontData.xml +++ b/doc/classes/FontData.xml @@ -23,7 +23,8 @@ <argument index="0" name="cache_index" type="int" /> <argument index="1" name="size" type="Vector2i" /> <description> - Removes all rendered glyphs information from the cache entry. Note: This function will not remove textures associated with the glyphs, use [method remove_texture] to remove them manually. + Removes all rendered glyphs information from the cache entry. + [b]Note:[/b] This function will not remove textures associated with the glyphs, use [method remove_texture] to remove them manually. </description> </method> <method name="clear_kerning_map"> @@ -46,7 +47,8 @@ <argument index="0" name="cache_index" type="int" /> <argument index="1" name="size" type="Vector2i" /> <description> - Removes all textures from font cache entry. Note: This function will not remove glyphs associated with the texture, use [method remove_glyph] to remove them manually. + Removes all textures from font cache entry. + [b]Note:[/b] This function will not remove glyphs associated with the texture, use [method remove_glyph] to remove them manually. </description> </method> <method name="find_cache" qualifiers="const"> @@ -97,7 +99,8 @@ <argument index="1" name="size" type="int" /> <argument index="2" name="glyph" type="int" /> <description> - Returns glyph advance (offset of the next glyph). Note: advance for glyphs outlines is the same as the base glyph advance and is not saved. + Returns glyph advance (offset of the next glyph). + [b]Note:[/b] Advance for glyphs outlines is the same as the base glyph advance and is not saved. </description> </method> <method name="get_glyph_index" qualifiers="const"> @@ -364,7 +367,8 @@ <argument index="1" name="size" type="Vector2i" /> <argument index="2" name="glyph" type="int" /> <description> - Removes specified rendered glyph information from the cache entry. Note: This function will not remove textures associated with the glyphs, use [method remove_texture] to remove them manually. + Removes specified rendered glyph information from the cache entry. + [b]Note:[/b] This function will not remove textures associated with the glyphs, use [method remove_texture] to remove them manually. </description> </method> <method name="remove_kerning"> @@ -404,7 +408,8 @@ <argument index="1" name="size" type="Vector2i" /> <argument index="2" name="texture_index" type="int" /> <description> - Removes specified texture from font cache entry. Note: This function will not remove glyphs associated with the texture, remove them manually, using [method remove_glyph]. + Removes specified texture from font cache entry. + [b]Note:[/b] This function will not remove glyphs associated with the texture, remove them manually, using [method remove_glyph]. </description> </method> <method name="render_glyph"> @@ -472,7 +477,8 @@ <argument index="2" name="glyph" type="int" /> <argument index="3" name="advance" type="Vector2" /> <description> - Sets glyph advance (offset of the next glyph). Note: advance for glyphs outlines is the same as the base glyph advance and is not saved. + Sets glyph advance (offset of the next glyph). + [b]Note:[/b] Advance for glyphs outlines is the same as the base glyph advance and is not saved. </description> </method> <method name="set_glyph_offset"> diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml index 3f7b20f274..0bed561de3 100644 --- a/doc/classes/GPUParticles3D.xml +++ b/doc/classes/GPUParticles3D.xml @@ -8,7 +8,7 @@ Use the [code]process_material[/code] property to add a [ParticlesMaterial] to configure particle appearance and behavior. Alternatively, you can add a [ShaderMaterial] which will be applied to all particles. </description> <tutorials> - <link title="Controlling thousands of fish with Particles">https://docs.godotengine.org/en/latest/tutorials/3d/vertex_animation/controlling_thousands_of_fish.html</link> + <link title="Controlling thousands of fish with Particles">https://docs.godotengine.org/en/latest/tutorials/performance/vertex_animation/controlling_thousands_of_fish.html</link> <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <methods> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 5d79e22c49..492bddca1f 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -9,7 +9,7 @@ [b]Note:[/b] The maximum image size is 16384×16384 pixels due to graphics hardware limitations. Larger images may fail to import. </description> <tutorials> - <link title="Importing images">https://docs.godotengine.org/en/latest/getting_started/workflow/assets/importing_images.html</link> + <link title="Importing images">https://docs.godotengine.org/en/latest/tutorials/assets_pipeline/importing_images.html</link> </tutorials> <methods> <method name="adjust_bcs"> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index ebbcd2b894..00ead31115 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -7,7 +7,7 @@ A singleton that deals with inputs. This includes key presses, mouse buttons and movement, joypads, and input actions. Actions and their events can be set in the [b]Input Map[/b] tab in the [b]Project > Project Settings[/b], or with the [InputMap] class. </description> <tutorials> - <link title="Inputs tutorial index">https://docs.godotengine.org/en/latest/tutorials/inputs/index.html</link> + <link title="Inputs documentation index">https://docs.godotengine.org/en/latest/tutorials/inputs/index.html</link> <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> </tutorials> @@ -386,6 +386,7 @@ </constant> <constant name="CURSOR_DRAG" value="6" enum="CursorShape"> Drag cursor. Usually displayed when dragging something. + [b]Note:[/b] Windows lacks a dragging cursor, so [constant CURSOR_DRAG] is the same as [constant CURSOR_MOVE] for this platform. </constant> <constant name="CURSOR_CAN_DROP" value="7" enum="CursorShape"> Can drop cursor. Usually displayed when dragging something to indicate that it can be dropped at the current position. diff --git a/doc/classes/JavaScript.xml b/doc/classes/JavaScript.xml index 5865ad734e..2bb2666df4 100644 --- a/doc/classes/JavaScript.xml +++ b/doc/classes/JavaScript.xml @@ -8,7 +8,7 @@ [b]Note:[/b] This singleton can be disabled at build-time to improve security. By default, the JavaScript singleton is enabled. Official export templates also have the JavaScript singleton enabled. See [url=https://docs.godotengine.org/en/latest/development/compiling/compiling_for_web.html]Compiling for the Web[/url] in the documentation for more information. </description> <tutorials> - <link title="Exporting for the Web: Calling JavaScript from script">https://docs.godotengine.org/en/latest/getting_started/workflow/export/exporting_for_web.html#calling-javascript-from-script</link> + <link title="Exporting for the Web: Calling JavaScript from script">https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_web.html#calling-javascript-from-script</link> </tutorials> <methods> <method name="create_callback"> diff --git a/doc/classes/JavaScriptObject.xml b/doc/classes/JavaScriptObject.xml index 5aa54a7d0c..6dd8702d5f 100644 --- a/doc/classes/JavaScriptObject.xml +++ b/doc/classes/JavaScriptObject.xml @@ -31,7 +31,7 @@ # [0, 9, [JavaScriptObject:1180]] print(args) [/codeblock] - Note: Only available in the "HTML5" platform. + [b]Note:[/b] Only available in the HTML5 platform. </description> <tutorials> </tutorials> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index 83e3f5b05a..4ad71d1e80 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -175,7 +175,7 @@ </member> <member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="false"> Allow moving caret, selecting and removing the individual composite character components. - Note: [kbd]Backspace[/kbd] is always removing individual composite character components. + [b]Note:[/b] [kbd]Backspace[/kbd] is always removing individual composite character components. </member> <member name="clear_button_enabled" type="bool" setter="set_clear_button_enabled" getter="is_clear_button_enabled" default="false"> If [code]true[/code], the [LineEdit] will show a clear button if [code]text[/code] is not empty, which can be used to clear the text quickly. diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml index 7b4a53a810..7890bbcc33 100644 --- a/doc/classes/MultiMesh.xml +++ b/doc/classes/MultiMesh.xml @@ -10,8 +10,8 @@ Since instances may have any behavior, the AABB used for visibility must be provided by the user. </description> <tutorials> - <link title="Animating thousands of fish with MultiMeshInstance">https://docs.godotengine.org/en/latest/tutorials/3d/vertex_animation/animating_thousands_of_fish.html</link> - <link title="Optimization using MultiMeshes">https://docs.godotengine.org/en/latest/tutorials/optimization/using_multimesh.html</link> + <link title="Animating thousands of fish with MultiMeshInstance">https://docs.godotengine.org/en/latest/tutorials/performance/vertex_animation/animating_thousands_of_fish.html</link> + <link title="Optimization using MultiMeshes">https://docs.godotengine.org/en/latest/tutorials/performance/using_multimesh.html</link> </tutorials> <methods> <method name="get_aabb" qualifiers="const"> diff --git a/doc/classes/MultiMeshInstance3D.xml b/doc/classes/MultiMeshInstance3D.xml index 7bf05d2d34..158579e952 100644 --- a/doc/classes/MultiMeshInstance3D.xml +++ b/doc/classes/MultiMeshInstance3D.xml @@ -8,9 +8,9 @@ This is useful to optimize the rendering of a high amount of instances of a given mesh (for example trees in a forest or grass strands). </description> <tutorials> - <link title="Animating thousands of fish with MultiMeshInstance">https://docs.godotengine.org/en/latest/tutorials/3d/vertex_animation/animating_thousands_of_fish.html</link> + <link title="Animating thousands of fish with MultiMeshInstance">https://docs.godotengine.org/en/latest/tutorials/performance/vertex_animation/animating_thousands_of_fish.html</link> <link title="Using MultiMeshInstance">https://docs.godotengine.org/en/latest/tutorials/3d/using_multi_mesh_instance.html</link> - <link title="Optimization using MultiMeshes">https://docs.godotengine.org/en/latest/tutorials/optimization/using_multimesh.html</link> + <link title="Optimization using MultiMeshes">https://docs.godotengine.org/en/latest/tutorials/performance/using_multimesh.html</link> </tutorials> <members> <member name="multimesh" type="MultiMesh" setter="set_multimesh" getter="get_multimesh"> diff --git a/doc/classes/MultiplayerReplicator.xml b/doc/classes/MultiplayerReplicator.xml index e0c309ef39..0f97cc1d0a 100644 --- a/doc/classes/MultiplayerReplicator.xml +++ b/doc/classes/MultiplayerReplicator.xml @@ -95,7 +95,7 @@ <argument index="1" name="peer_id" type="int" default="0" /> <description> Manually request a sync for all the instances of the scene identified by [code]scene_id[/code]. This function will trigger the default sync behaviour, or call your send custom send callable if specified in [method sync_config]. - Note: The default implementation only allow syncing from server to clients. + [b]Note:[/b] The default implementation only allow syncing from server to clients. </description> </method> <method name="sync_config"> diff --git a/doc/classes/Mutex.xml b/doc/classes/Mutex.xml index eb9bec1104..a840cb2ec7 100644 --- a/doc/classes/Mutex.xml +++ b/doc/classes/Mutex.xml @@ -7,7 +7,7 @@ A synchronization mutex (mutual exclusion). This is used to synchronize multiple [Thread]s, and is equivalent to a binary [Semaphore]. It guarantees that only one thread can ever acquire the lock at a time. A mutex can be used to protect a critical section; however, be careful to avoid deadlocks. </description> <tutorials> - <link title="Using multiple threads">https://docs.godotengine.org/en/latest/tutorials/threads/using_multiple_threads.html</link> + <link title="Using multiple threads">https://docs.godotengine.org/en/latest/tutorials/performance/using_multiple_threads.html</link> </tutorials> <methods> <method name="lock"> diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index 1740093eb2..971d3839f2 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -8,7 +8,7 @@ Maps are made up of regions, which are made of navigation polygons. Together, they define the navigable areas in the 2D world. For two regions to be connected to each other, they must share a similar edge. An edge is considered connected to another if both of its two vertices are at a distance less than [code]edge_connection_margin[/code] to the respective other edge's vertex. You may assign navigation layers to regions with [method NavigationServer2D.region_set_layers], which then can be checked upon when requesting a path with [method NavigationServer2D.map_get_path]. This allows allowing or forbidding some areas to 2D objects. To use the collision avoidance system, you may use agents. You can set an agent's target velocity, then the servers will emit a callback with a modified velocity. - [b]Note:[/b] the collision avoidance system ignores regions. Using the modified velocity as-is might lead to pushing and agent outside of a navigable area. This is a limitation of the collision avoidance system, any more complex situation may require the use of the physics engine. + [b]Note:[/b] The collision avoidance system ignores regions. Using the modified velocity as-is might lead to pushing and agent outside of a navigable area. This is a limitation of the collision avoidance system, any more complex situation may require the use of the physics engine. This server keeps tracks of any call and executes them during the sync phase. This means that you can request any change to the map, using any thread, without worrying. </description> <tutorials> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index 3e10657838..16eba8bba8 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -8,7 +8,7 @@ Maps are made up of regions, which are made of navigation meshes. Together, they define the navigable areas in the 3D world. For two regions to be connected to each other, they must share a similar edge. An edge is considered connected to another if both of its two vertices are at a distance less than [code]edge_connection_margin[/code] to the respective other edge's vertex. You may assign navigation layers to regions with [method NavigationServer3D.region_set_layers], which then can be checked upon when requesting a path with [method NavigationServer3D.map_get_path]. This allows allowing or forbidding some areas to 3D objects. To use the collision avoidance system, you may use agents. You can set an agent's target velocity, then the servers will emit a callback with a modified velocity. - [b]Note:[/b] the collision avoidance system ignores regions. Using the modified velocity as-is might lead to pushing and agent outside of a navigable area. This is a limitation of the collision avoidance system, any more complex situation may require the use of the physics engine. + [b]Note:[/b] The collision avoidance system ignores regions. Using the modified velocity as-is might lead to pushing and agent outside of a navigable area. This is a limitation of the collision avoidance system, any more complex situation may require the use of the physics engine. This server keeps tracks of any call and executes them during the sync phase. This means that you can request any change to the map, using any thread, without worrying. </description> <tutorials> @@ -234,7 +234,7 @@ <description> Process the collision avoidance agents. The result of this process is needed by the physics server, so this must be called in the main thread. - Note: This function is not thread safe. + [b]Note:[/b] This function is not thread safe. </description> </method> <method name="region_bake_navmesh" qualifiers="const"> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index b4edcf49f4..11c42fbd4a 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -17,7 +17,7 @@ [b]Networking with nodes:[/b] After connecting to a server (or making one, see [ENetMultiplayerPeer]), it is possible to use the built-in RPC (remote procedure call) system to communicate over the network. By calling [method rpc] with a method name, it will be called locally and in all connected peers (peers = clients and the server that accepts connections). To identify which node receives the RPC call, Godot will use its [NodePath] (make sure node names are the same on all peers). Also, take a look at the high-level networking tutorial and corresponding demos. </description> <tutorials> - <link title="Scenes and nodes">https://docs.godotengine.org/en/latest/getting_started/step_by_step/scenes_and_nodes.html</link> + <link title="Nodes and scenes">https://docs.godotengine.org/en/latest/getting_started/step_by_step/nodes_and_scenes.htmltml</link> <link title="All Demos">https://github.com/godotengine/godot-demo-projects/</link> </tutorials> <methods> diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index ed045f8390..5a84364b92 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -29,7 +29,7 @@ [b]Note:[/b] Unlike references to a [RefCounted], references to an Object stored in a variable can become invalid without warning. Therefore, it's recommended to use [RefCounted] for data classes instead of [Object]. </description> <tutorials> - <link title="When and how to avoid using nodes for everything">https://docs.godotengine.org/en/latest/getting_started/workflow/best_practices/node_alternatives.html</link> + <link title="When and how to avoid using nodes for everything">https://docs.godotengine.org/en/latest/tutorials/best_practices/node_alternatives.html</link> </tutorials> <methods> <method name="_get" qualifiers="virtual"> diff --git a/doc/classes/PacketPeerUDP.xml b/doc/classes/PacketPeerUDP.xml index 9b77859b50..31640eb3d8 100644 --- a/doc/classes/PacketPeerUDP.xml +++ b/doc/classes/PacketPeerUDP.xml @@ -74,7 +74,7 @@ <description> Joins the multicast group specified by [code]multicast_address[/code] using the interface identified by [code]interface_name[/code]. You can join the same multicast group with multiple interfaces. Use [method IP.get_local_interfaces] to know which are available. - Note: Some Android devices might require the [code]CHANGE_WIFI_MULTICAST_STATE[/code] permission for multicast to work. + [b]Note:[/b] Some Android devices might require the [code]CHANGE_WIFI_MULTICAST_STATE[/code] permission for multicast to work. </description> </method> <method name="leave_multicast_group"> @@ -90,7 +90,7 @@ <argument index="0" name="enabled" type="bool" /> <description> Enable or disable sending of broadcast packets (e.g. [code]set_dest_address("255.255.255.255", 4343)[/code]. This option is disabled by default. - Note: Some Android devices might require the [code]CHANGE_WIFI_MULTICAST_STATE[/code] permission and this option to be enabled to receive broadcast packets too. + [b]Note:[/b] Some Android devices might require the [code]CHANGE_WIFI_MULTICAST_STATE[/code] permission and this option to be enabled to receive broadcast packets too. </description> </method> <method name="set_dest_address"> @@ -99,7 +99,7 @@ <argument index="1" name="port" type="int" /> <description> Sets the destination address and port for sending packets and variables. A hostname will be resolved using DNS if needed. - Note: [method set_broadcast_enabled] must be enabled before sending packets to a broadcast address (e.g. [code]255.255.255.255[/code]). + [b]Note:[/b] [method set_broadcast_enabled] must be enabled before sending packets to a broadcast address (e.g. [code]255.255.255.255[/code]). </description> </method> <method name="wait"> diff --git a/doc/classes/Plane.xml b/doc/classes/Plane.xml index bc9b3cafb5..78e1e81752 100644 --- a/doc/classes/Plane.xml +++ b/doc/classes/Plane.xml @@ -7,7 +7,7 @@ Plane represents a normalized plane equation. Basically, "normal" is the normal of the plane (a,b,c normalized), and "d" is the distance from the origin to the plane (in the direction of "normal"). "Over" or "Above" the plane is considered the side of the plane towards where the normal is pointing. </description> <tutorials> - <link title="Math tutorial index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link title="Math documentation index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> </tutorials> <methods> <method name="Plane" qualifiers="constructor"> diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml index ce5d48cfa4..592c074a77 100644 --- a/doc/classes/RayCast2D.xml +++ b/doc/classes/RayCast2D.xml @@ -70,7 +70,7 @@ <return type="Vector2" /> <description> Returns the collision point at which the ray intersects the closest object. - [b]Note:[/b] this point is in the [b]global[/b] coordinate system. + [b]Note:[/b] This point is in the [b]global[/b] coordinate system. </description> </method> <method name="is_colliding" qualifiers="const"> diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml index e585224818..a6bb81b589 100644 --- a/doc/classes/Rect2.xml +++ b/doc/classes/Rect2.xml @@ -9,7 +9,7 @@ The 3D counterpart to [Rect2] is [AABB]. </description> <tutorials> - <link title="Math tutorial index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link title="Math documentation index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> <link title="Vector math">https://docs.godotengine.org/en/latest/tutorials/math/vector_math.html</link> <link title="Advanced vector math">https://docs.godotengine.org/en/latest/tutorials/math/vectors_advanced.html</link> </tutorials> diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml index 2f6f4de66d..0ba013cade 100644 --- a/doc/classes/Rect2i.xml +++ b/doc/classes/Rect2i.xml @@ -8,7 +8,7 @@ It uses integer coordinates. If you need floating-point coordinates, use [Rect2] instead. </description> <tutorials> - <link title="Math tutorial index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link title="Math documentation index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> <link title="Vector math">https://docs.godotengine.org/en/latest/tutorials/math/vector_math.html</link> </tutorials> <methods> diff --git a/doc/classes/RefCounted.xml b/doc/classes/RefCounted.xml index 5f18ccc14d..d965d2ea10 100644 --- a/doc/classes/RefCounted.xml +++ b/doc/classes/RefCounted.xml @@ -10,7 +10,7 @@ [b]Note:[/b] In C#, references will not be freed instantly after they are no longer in use. Instead, garbage collection will run periodically and will free references that are no longer in use. This means that unused references will linger on for a while before being removed. </description> <tutorials> - <link title="When and how to avoid using nodes for everything">https://docs.godotengine.org/en/latest/getting_started/workflow/best_practices/node_alternatives.html</link> + <link title="When and how to avoid using nodes for everything">https://docs.godotengine.org/en/latest/tutorials/best_practices/node_alternatives.html</link> </tutorials> <methods> <method name="init_ref"> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index d40ad80d68..c79ed2a8b8 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -15,7 +15,7 @@ In 2D, all visible objects are some form of canvas item. In order to be visible, a canvas item needs to be the child of a canvas attached to a viewport, or it needs to be the child of another canvas item that is eventually attached to the canvas. </description> <tutorials> - <link title="Optimization using Servers">https://docs.godotengine.org/en/latest/tutorials/optimization/using_servers.html</link> + <link title="Optimization using Servers">https://docs.godotengine.org/en/latest/tutorials/performance/using_servers.html</link> </tutorials> <methods> <method name="bake_render_uv2"> @@ -2351,7 +2351,8 @@ <argument index="0" name="particles" type="RID" /> <argument index="1" name="material" type="RID" /> <description> - Sets the material for processing the particles. Note: this is not the material used to draw the materials. Equivalent to [member GPUParticles3D.process_material]. + Sets the material for processing the particles. + [b]Note:[/b] This is not the material used to draw the materials. Equivalent to [member GPUParticles3D.process_material]. </description> </method> <method name="particles_set_randomness_ratio"> diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml index 45b68f342c..327183893b 100644 --- a/doc/classes/Resource.xml +++ b/doc/classes/Resource.xml @@ -8,8 +8,8 @@ [b]Note:[/b] In C#, resources will not be freed instantly after they are no longer in use. Instead, garbage collection will run periodically and will free resources that are no longer in use. This means that unused resources will linger on for a while before being removed. </description> <tutorials> - <link title="Resources">https://docs.godotengine.org/en/latest/getting_started/step_by_step/resources.html</link> - <link title="When and how to avoid using nodes for everything">https://docs.godotengine.org/en/latest/getting_started/workflow/best_practices/node_alternatives.html</link> + <link title="Resources">https://docs.godotengine.org/en/latest/tutorials/scripting/resources.html</link> + <link title="When and how to avoid using nodes for everything">https://docs.godotengine.org/en/latest/tutorials/best_practices/node_alternatives.html</link> </tutorials> <methods> <method name="duplicate" qualifiers="const"> diff --git a/doc/classes/RichTextEffect.xml b/doc/classes/RichTextEffect.xml index cf4b4f4a48..62323722f7 100644 --- a/doc/classes/RichTextEffect.xml +++ b/doc/classes/RichTextEffect.xml @@ -19,7 +19,7 @@ [b]Note:[/b] As soon as a [RichTextLabel] contains at least one [RichTextEffect], it will continuously process the effect unless the project is paused. This may impact battery life negatively. </description> <tutorials> - <link title="BBCode in RichTextLabel">https://docs.godotengine.org/en/latest/tutorials/gui/bbcode_in_richtextlabel.html</link> + <link title="BBCode in RichTextLabel">https://docs.godotengine.org/en/latest/tutorials/ui/bbcode_in_richtextlabel.html</link> <link title="RichTextEffect test project (third-party)">https://github.com/Eoin-ONeill-Yokai/Godot-Rich-Text-Effect-Test-Project</link> </tutorials> <methods> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 50db1dc122..a2e1c3d2c2 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -11,7 +11,7 @@ [b]Note:[/b] Unlike [Label], RichTextLabel doesn't have a [i]property[/i] to horizontally align text to the center. Instead, enable [member bbcode_enabled] and surround the text in a [code][center][/code] tag as follows: [code][center]Example[/center][/code]. There is currently no built-in way to vertically align text either, but this can be emulated by relying on anchors/containers and the [member fit_content_height] property. </description> <tutorials> - <link title="BBCode in RichTextLabel">https://docs.godotengine.org/en/latest/tutorials/gui/bbcode_in_richtextlabel.html</link> + <link title="BBCode in RichTextLabel">https://docs.godotengine.org/en/latest/tutorials/ui/bbcode_in_richtextlabel.html</link> <link title="GUI Rich Text/BBcode Demo">https://godotengine.org/asset-library/asset/132</link> <link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link> </tutorials> diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index 8d7427611a..68f2d9a8d8 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -9,8 +9,8 @@ [SceneTree] is the default [MainLoop] implementation used by scenes, and is thus in charge of the game loop. </description> <tutorials> - <link title="SceneTree">https://docs.godotengine.org/en/latest/getting_started/step_by_step/scene_tree.html</link> - <link title="Multiple resolutions">https://docs.godotengine.org/en/latest/tutorials/viewports/multiple_resolutions.html</link> + <link title="SceneTree">https://docs.godotengine.org/en/latest/tutorials/scripting/scene_tree.html</link> + <link title="Multiple resolutions">https://docs.godotengine.org/en/latest/tutorials/rendering/multiple_resolutions.html</link> </tutorials> <methods> <method name="call_group" qualifiers="vararg"> diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml index b7a4f448b0..7c59e87848 100644 --- a/doc/classes/Script.xml +++ b/doc/classes/Script.xml @@ -8,7 +8,7 @@ The [code]new[/code] method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - <link title="Scripting">https://docs.godotengine.org/en/latest/getting_started/step_by_step/scripting.html</link> + <link title="Scripting documentation index">https://docs.godotengine.org/en/latest/tutorials/scripting/index.html</link> </tutorials> <methods> <method name="can_instantiate" qualifiers="const"> diff --git a/doc/classes/Semaphore.xml b/doc/classes/Semaphore.xml index 2f3fa021d4..9ff9cc0c87 100644 --- a/doc/classes/Semaphore.xml +++ b/doc/classes/Semaphore.xml @@ -7,7 +7,7 @@ A synchronization semaphore which can be used to synchronize multiple [Thread]s. Initialized to zero on creation. Be careful to avoid deadlocks. For a binary version, see [Mutex]. </description> <tutorials> - <link title="Using multiple threads">https://docs.godotengine.org/en/latest/tutorials/threads/using_multiple_threads.html</link> + <link title="Using multiple threads">https://docs.godotengine.org/en/latest/tutorials/performance/using_multiple_threads.html</link> </tutorials> <methods> <method name="post"> diff --git a/doc/classes/Shader.xml b/doc/classes/Shader.xml index bc5457b8ce..14c5ba9247 100644 --- a/doc/classes/Shader.xml +++ b/doc/classes/Shader.xml @@ -7,8 +7,7 @@ This class allows you to define a custom shader program that can be used by a [ShaderMaterial]. Shaders allow you to write your own custom behavior for rendering objects or updating particle information. For a detailed explanation and usage, please see the tutorials linked below. </description> <tutorials> - <link title="Shading tutorial index">https://docs.godotengine.org/en/latest/tutorials/shading/index.html</link> - <link title="What are shaders?">https://docs.godotengine.org/en/latest/tutorials/shading/your_first_shader/what_are_shaders.html</link> + <link title="Shaders documentation index">https://docs.godotengine.org/en/latest/tutorials/shaders/index.html</link> </tutorials> <methods> <method name="get_default_texture_param" qualifiers="const"> diff --git a/doc/classes/ShaderMaterial.xml b/doc/classes/ShaderMaterial.xml index 13f2e2fe5f..d5fc3fd210 100644 --- a/doc/classes/ShaderMaterial.xml +++ b/doc/classes/ShaderMaterial.xml @@ -7,7 +7,7 @@ A material that uses a custom [Shader] program to render either items to screen or process particles. You can create multiple materials for the same shader but configure different values for the uniforms defined in the shader. </description> <tutorials> - <link title="Shading tutorial index">https://docs.godotengine.org/en/latest/tutorials/shading/index.html</link> + <link title="Shaders documentation index">https://docs.godotengine.org/en/latest/tutorials/shaders/index.html</link> </tutorials> <methods> <method name="get_shader_param" qualifiers="const"> @@ -36,7 +36,8 @@ <argument index="0" name="param" type="StringName" /> <argument index="1" name="value" type="Variant" /> <description> - Changes the value set for this material of a uniform in the shader. [b]Note:[/b] [code]param[/code] must match the name of the uniform in the code exactly. + Changes the value set for this material of a uniform in the shader. + [b]Note:[/b] [code]param[/code] must match the name of the uniform in the code exactly. </description> </method> </methods> diff --git a/doc/classes/SkeletonModification2DCCDIK.xml b/doc/classes/SkeletonModification2DCCDIK.xml index ab9a482609..6cbc24f100 100644 --- a/doc/classes/SkeletonModification2DCCDIK.xml +++ b/doc/classes/SkeletonModification2DCCDIK.xml @@ -4,7 +4,7 @@ A modification that uses CCDIK to manipulate a series of bones to reach a target in 2D. </brief_description> <description> - This [SkeletonModification2D] uses an algorithm called [b]C[/b]yclic [b]C[/b]oordinate [b]D[/b]escent [b]I[/b]nverse [b]K[/b]inematics, or CCDIK, to manipulate a chain of bones in a [Skeleton2D] so it reaches a defined target. + This [SkeletonModification2D] uses an algorithm called Cyclic Coordinate Descent Inverse Kinematics, or CCDIK, to manipulate a chain of bones in a [Skeleton2D] so it reaches a defined target. CCDIK works by rotating a set of bones, typically called a "bone chain", on a single axis. Each bone is rotated to face the target from the tip (by default), which over a chain of bones allow it to rotate properly to reach the target. Because the bones only rotate on a single axis, CCDIK [i]can[/i] look more robotic than other IK solvers. [b]Note:[/b] The CCDIK modifier has [code]ccdik_joints[/code], which are the data objects that hold the data for each joint in the CCDIK chain. This is different from a bone! CCDIK joints hold the data needed for each bone in the bone chain used by CCDIK. CCDIK also fully supports angle constraints, allowing for more control over how a solution is met. diff --git a/doc/classes/SkeletonModification2DFABRIK.xml b/doc/classes/SkeletonModification2DFABRIK.xml index 16c22a45d3..82b99d20bd 100644 --- a/doc/classes/SkeletonModification2DFABRIK.xml +++ b/doc/classes/SkeletonModification2DFABRIK.xml @@ -4,7 +4,7 @@ A modification that uses FABRIK to manipulate a series of [Bone2D] nodes to reach a target. </brief_description> <description> - This [SkeletonModification2D] uses an algorithm called [b]F[/b]orward [b]A[/b]nd [b]B[/b]ackward [b]R[/b]eaching [b]I[/b]nverse [b]K[/b]inematics, or FABRIK, to rotate a bone chain so that it reaches a target. + This [SkeletonModification2D] uses an algorithm called Forward And Backward Reaching Inverse Kinematics, or FABRIK, to rotate a bone chain so that it reaches a target. FABRIK works by knowing the positions and lengths of a series of bones, typically called a "bone chain". It first starts by running a forward pass, which places the final bone at the target's position. Then all other bones are moved towards the tip bone, so they stay at the defined bone length away. Then a backwards pass is performed, where the root/first bone in the FABRIK chain is placed back at the origin. then all other bones are moved so they stay at the defined bone length away. This positions the bone chain so that it reaches the target when possible, but all of the bones stay the correct length away from each other. Because of how FABRIK works, it often gives more natural results than those seen in [SkeletonModification2DCCDIK]. FABRIK also supports angle constraints, which are fully taken into account when solving. [b]Note:[/b] The FABRIK modifier has [code]fabrik_joints[/code], which are the data objects that hold the data for each joint in the FABRIK chain. This is different from [Bone2D] nodes! FABRIK joints hold the data needed for each [Bone2D] in the bone chain used by FABRIK. diff --git a/doc/classes/SkeletonModification3DCCDIK.xml b/doc/classes/SkeletonModification3DCCDIK.xml index ef3200a07a..55777ab0f4 100644 --- a/doc/classes/SkeletonModification3DCCDIK.xml +++ b/doc/classes/SkeletonModification3DCCDIK.xml @@ -4,7 +4,7 @@ A modification that uses CCDIK to manipulate a series of bones to reach a target. </brief_description> <description> - This [SkeletonModification3D] uses an algorithm called [b]C[/b]yclic [b]C[/b]oordinate [b]D[/b]escent [b]I[/b]nverse [b]K[/b]inematics, or CCDIK, to maniuplate a chain of bones in a Skeleton so it reaches a defined target. + This [SkeletonModification3D] uses an algorithm called Cyclic Coordinate Descent Inverse Kinematics, or CCDIK, to maniuplate a chain of bones in a Skeleton so it reaches a defined target. CCDIK works by rotating a set of bones, typically called a "bone chain", on a single axis. Each bone is rotated to face the target from the tip (by default), which over a chain of bones allow it to rotate properly to reach the target. Because the bones only rotate on a single axis, CCDIK [i]can[/i] look more robotic than other IK solvers. [b]Note:[/b] The CCDIK modifier has [code]ccdik_joints[/code], which are the data objects that hold the data for each joint in the CCDIK chain. This is different from a bone! CCDIK joints hold the data needed for each bone in the bone chain used by CCDIK. CCDIK also fully supports angle constraints, allowing for more control over how a solution is met. diff --git a/doc/classes/SkeletonModification3DFABRIK.xml b/doc/classes/SkeletonModification3DFABRIK.xml index 4c4e01e9d1..1c69ad7b3f 100644 --- a/doc/classes/SkeletonModification3DFABRIK.xml +++ b/doc/classes/SkeletonModification3DFABRIK.xml @@ -4,7 +4,7 @@ A modification that uses FABRIK to manipulate a series of bones to reach a target. </brief_description> <description> - This [SkeletonModification3D] uses an algorithm called [b]F[/b]orward [b]A[/b]nd [b]B[/b]ackward [b]R[/b]eaching [b]I[/b]nverse [b]K[/b]inematics, or FABRIK, to rotate a bone chain so that it reaches a target. + This [SkeletonModification3D] uses an algorithm called Forward And Backward Reaching Inverse Kinematics, or FABRIK, to rotate a bone chain so that it reaches a target. FABRIK works by knowing the positions and lengths of a series of bones, typically called a "bone chain". It first starts by running a forward pass, which places the final bone at the target's position. Then all other bones are moved towards the tip bone, so they stay at the defined bone length away. Then a backwards pass is performed, where the root/first bone in the FABRIK chain is placed back at the origin. then all other bones are moved so they stay at the defined bone length away. This positions the bone chain so that it reaches the target when possible, but all of the bones stay the correct length away from each other. Because of how FABRIK works, it often gives more natural results than those seen in [SkeletonModification3DCCDIK], though FABRIK currently does not support joint constraints. [b]Note:[/b] The FABRIK modifier has [code]fabrik_joints[/code], which are the data objects that hold the data for each joint in the FABRIK chain. This is different from a bone! FABRIK joints hold the data needed for each bone in the bone chain used by FABRIK. @@ -67,7 +67,7 @@ <argument index="0" name="joint_idx" type="int" /> <description> Returns a boolean indiciating whether the FABRIK joint uses the target's [Basis] for its rotation. - [b]Note:[/b] this option is only available for the final bone in the FABRIK chain, with this setting being ignored for all other bones. + [b]Note:[/b] This option is only available for the final bone in the FABRIK chain, with this setting being ignored for all other bones. </description> </method> <method name="get_fabrik_joint_use_tip_node" qualifiers="const"> @@ -132,7 +132,7 @@ <argument index="1" name="use_target_basis" type="bool" /> <description> Sets whether the FABRIK joint at [code]joint_idx[/code] uses the target's [Basis] for its rotation. - [b]Note:[/b] this option is only available for the final bone in the FABRIK chain, with this setting being ignored for all other bones. + [b]Note:[/b] This option is only available for the final bone in the FABRIK chain, with this setting being ignored for all other bones. </description> </method> <method name="set_fabrik_joint_use_tip_node"> diff --git a/doc/classes/SkeletonModification3DStackHolder.xml b/doc/classes/SkeletonModification3DStackHolder.xml index 138f9818ab..eeaa509ed7 100644 --- a/doc/classes/SkeletonModification3DStackHolder.xml +++ b/doc/classes/SkeletonModification3DStackHolder.xml @@ -5,7 +5,7 @@ </brief_description> <description> This [SkeletonModification3D] holds a reference to a [SkeletonModificationStack3D], allowing you to use multiple modification stacks on a single [Skeleton3D]. - [b]Note[/b]: The modifications in the held [SkeletonModificationStack3D] will only be executed if their execution mode matches the execution mode of the SkeletonModification3DStackHolder. + [b]Note:[/b] The modifications in the held [SkeletonModificationStack3D] will only be executed if their execution mode matches the execution mode of the SkeletonModification3DStackHolder. </description> <tutorials> </tutorials> diff --git a/doc/classes/StandardMaterial3D.xml b/doc/classes/StandardMaterial3D.xml index 8a36a734f1..43ba95e345 100644 --- a/doc/classes/StandardMaterial3D.xml +++ b/doc/classes/StandardMaterial3D.xml @@ -5,5 +5,6 @@ <description> </description> <tutorials> + <link title="Standard Material 3D">https://docs.godotengine.org/en/latest/tutorials/3d/standard_material_3d.html</link> </tutorials> </class> diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml index 0622626846..805f056289 100644 --- a/doc/classes/StreamPeer.xml +++ b/doc/classes/StreamPeer.xml @@ -173,7 +173,7 @@ <argument index="0" name="value" type="String" /> <description> Puts a zero-terminated ASCII string into the stream prepended by a 32-bit unsigned integer representing its size. - Note: To put an ASCII string without prepending its size, you can use [method put_data]: + [b]Note:[/b] To put an ASCII string without prepending its size, you can use [method put_data]: [codeblocks] [gdscript] put_data("Hello world".to_ascii()) @@ -217,7 +217,7 @@ <argument index="0" name="value" type="String" /> <description> Puts a zero-terminated UTF-8 string into the stream prepended by a 32 bits unsigned integer representing its size. - Note: To put an UTF-8 string without prepending its size, you can use [method put_data]: + [b]Note:[/b] To put an UTF-8 string without prepending its size, you can use [method put_data]: [codeblocks] [gdscript] put_data("Hello world".to_utf8()) diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 0991788483..10ce03c4b2 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -7,7 +7,7 @@ This is the built-in string class (and the one used by GDScript). It supports Unicode and provides all necessary means for string handling. Strings are reference-counted and use a copy-on-write approach, so passing them around is cheap in resources. </description> <tutorials> - <link title="GDScript format strings">https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/gdscript_format_string.html</link> + <link title="GDScript format strings">https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/gdscript_format_string.html</link> </tutorials> <methods> <method name="String" qualifiers="constructor"> diff --git a/doc/classes/SubViewport.xml b/doc/classes/SubViewport.xml index 28866699f6..c2a76b587f 100644 --- a/doc/classes/SubViewport.xml +++ b/doc/classes/SubViewport.xml @@ -6,8 +6,8 @@ <description> </description> <tutorials> + <link title="Using Viewports">https://docs.godotengine.org/en/latest/tutorials/rendering/viewports.html</link> <link title="Viewport and canvas transforms">https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link> - <link title="Viewports tutorial index">https://docs.godotengine.org/en/latest/tutorials/viewports/index.html</link> <link title="GUI in 3D Demo">https://godotengine.org/asset-library/asset/127</link> <link title="3D in 2D Demo">https://godotengine.org/asset-library/asset/128</link> <link title="2D in 3D Demo">https://godotengine.org/asset-library/asset/129</link> diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 6a38c1a117..0e39381991 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -599,7 +599,7 @@ <argument index="3" name="to_column" type="int" /> <description> Removes text between the given positions. - [b]Note:[/b]This does not adjust the caret or selection, which as a result it can end up in an invalid position. + [b]Note:[/b] This does not adjust the caret or selection, which as a result it can end up in an invalid position. </description> </method> <method name="search" qualifiers="const"> @@ -902,7 +902,7 @@ </member> <member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="false"> Allow moving caret, selecting and removing the individual composite character components. - Note: [kbd]Backspace[/kbd] is always removing individual composite character components. + [b]Note:[/b] [kbd]Backspace[/kbd] is always removing individual composite character components. </member> <member name="caret_move_on_right_click" type="bool" setter="set_move_caret_on_right_click_enabled" getter="is_move_caret_on_right_click_enabled" default="true"> If [code]true[/code], a right-click moves the caret at the mouse position before displaying the context menu. diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 18ace85465..b10c7bcc96 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -21,8 +21,8 @@ <argument index="1" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> Creates new buffer for complex text layout, with the given [code]direction[/code] and [code]orientation[/code]. To free the resulting buffer, use [method free_rid] method. - Note: Direction is ignored if server does not support [code]FEATURE_BIDI_LAYOUT[/code] feature. - Note: Orientation is ignored if server does not support [code]FEATURE_VERTICAL_LAYOUT[/code] feature. + [b]Note:[/b] Direction is ignored if server does not support [code]FEATURE_BIDI_LAYOUT[/code] feature. + [b]Note:[/b] Orientation is ignored if server does not support [code]FEATURE_VERTICAL_LAYOUT[/code] feature. </description> </method> <method name="draw_hex_code_box" qualifiers="const"> @@ -41,7 +41,8 @@ <argument index="0" name="font_rid" type="RID" /> <argument index="1" name="size" type="Vector2i" /> <description> - Removes all rendered glyphs information from the cache entry. Note: This function will not remove textures associated with the glyphs, use [method font_remove_texture] to remove them manually. + Removes all rendered glyphs information from the cache entry. + [b]Note:[/b] This function will not remove textures associated with the glyphs, use [method font_remove_texture] to remove them manually. </description> </method> <method name="font_clear_kerning_map"> @@ -64,7 +65,8 @@ <argument index="0" name="font_rid" type="RID" /> <argument index="1" name="size" type="Vector2i" /> <description> - Removes all textures from font cache entry. Note: This function will not remove glyphs associated with the texture, use [method font_remove_glyph] to remove them manually. + Removes all textures from font cache entry. + [b]Note:[/b] This function will not remove glyphs associated with the texture, use [method font_remove_glyph] to remove them manually. </description> </method> <method name="font_draw_glyph" qualifiers="const"> @@ -77,7 +79,7 @@ <argument index="5" name="color" type="Color" default="Color(1, 1, 1, 1)" /> <description> Draws single glyph into a canvas item at the position, using [code]font_rid[/code] at the size [code]size[/code]. - Note: Glyph index is specific to the font, use glyphs indices returned by [method shaped_text_get_glyphs] or [method font_get_glyph_index]. + [b]Note:[/b] Glyph index is specific to the font, use glyphs indices returned by [method shaped_text_get_glyphs] or [method font_get_glyph_index]. </description> </method> <method name="font_draw_glyph_outline" qualifiers="const"> @@ -91,7 +93,7 @@ <argument index="6" name="color" type="Color" default="Color(1, 1, 1, 1)" /> <description> Draws single glyph outline of size [code]outline_size[/code] into a canvas item at the position, using [code]font_rid[/code] at the size [code]size[/code]. - Note: Glyph index is specific to the font, use glyphs indices returned by [method shaped_text_get_glyphs] or [method font_get_glyph_index]. + [b]Note:[/b] Glyph index is specific to the font, use glyphs indices returned by [method shaped_text_get_glyphs] or [method font_get_glyph_index]. </description> </method> <method name="font_get_ascent" qualifiers="const"> @@ -129,7 +131,8 @@ <argument index="1" name="size" type="int" /> <argument index="2" name="glyph" type="int" /> <description> - Returns glyph advance (offset of the next glyph). Note: advance for glyphs outlines is the same as the base glyph advance and is not saved. + Returns glyph advance (offset of the next glyph). + [b]Note:[/b] Advance for glyphs outlines is the same as the base glyph advance and is not saved. </description> </method> <method name="font_get_glyph_contours" qualifiers="const"> @@ -404,7 +407,8 @@ <argument index="1" name="size" type="Vector2i" /> <argument index="2" name="glyph" type="int" /> <description> - Removes specified rendered glyph information from the cache entry. Note: This function will not remove textures associated with the glyphs, use [method font_remove_texture] to remove them manually. + Removes specified rendered glyph information from the cache entry. + [b]Note:[/b] This function will not remove textures associated with the glyphs, use [method font_remove_texture] to remove them manually. </description> </method> <method name="font_remove_kerning"> @@ -446,7 +450,8 @@ <argument index="1" name="size" type="Vector2i" /> <argument index="2" name="texture_index" type="int" /> <description> - Removes specified texture from font cache entry. Note: This function will not remove glyphs associated with the texture, remove them manually, using [method font_remove_glyph]. + Removes specified texture from font cache entry. + [b]Note:[/b] This function will not remove glyphs associated with the texture, remove them manually, using [method font_remove_glyph]. </description> </method> <method name="font_render_glyph"> @@ -523,7 +528,7 @@ <argument index="0" name="oversampling" type="float" /> <description> Sets oversampling factor, shared by all font in the TextServer. - Note: This value can be automaticaly changed by display server. + [b]Note:[/b] This value can be automaticaly changed by display server. </description> </method> <method name="font_set_glyph_advance"> @@ -533,7 +538,8 @@ <argument index="2" name="glyph" type="int" /> <argument index="3" name="advance" type="Vector2" /> <description> - Sets glyph advance (offset of the next glyph). Note: advance for glyphs outlines is the same as the base glyph advance and is not saved. + Sets glyph advance (offset of the next glyph). + [b]Note:[/b] Advance for glyphs outlines is the same as the base glyph advance and is not saved. </description> </method> <method name="font_set_glyph_offset"> @@ -797,7 +803,7 @@ <argument index="0" name="filename" type="String" /> <description> Loads optional TextServer database (e.g. ICU break iterators and dictionaries). - Note: This function should be called before any other TextServer functions used, otherwise it won't have any effect. + [b]Note:[/b] This function should be called before any other TextServer functions used, otherwise it won't have any effect. </description> </method> <method name="name_to_tag" qualifiers="const"> @@ -827,7 +833,7 @@ <argument index="0" name="filename" type="String" /> <description> Saves optional TextServer database (e.g. ICU break iterators and dictionaries) to the file. - Note: This function is used by during project export, to include TextServer database. + [b]Note:[/b] This function is used by during project export, to include TextServer database. </description> </method> <method name="shaped_text_add_object"> @@ -900,7 +906,7 @@ <argument index="0" name="shaped" type="RID" /> <description> Returns the text ascent (number of pixels above the baseline for horizontal layout or to the left of baseline for vertical). - Note: overall ascent can be higher than font ascent, if some glyphs are displaced from the baseline. + [b]Note:[/b] Overall ascent can be higher than font ascent, if some glyphs are displaced from the baseline. </description> </method> <method name="shaped_text_get_carets" qualifiers="const"> @@ -916,7 +922,7 @@ <argument index="0" name="shaped" type="RID" /> <description> Returns the text descent (number of pixels below the baseline for horizontal layout or to the right of baseline for vertical). - Note: overall descent can be higher than font descent, if some glyphs are displaced from the baseline. + [b]Note:[/b] Overall descent can be higher than font descent, if some glyphs are displaced from the baseline. </description> </method> <method name="shaped_text_get_direction" qualifiers="const"> @@ -1032,7 +1038,7 @@ <argument index="0" name="shaped" type="RID" /> <description> Returns [code]true[/code] if text buffer is configured to display hexadecimal codes in place of invalid characters. - Note: If set to [code]false[/code], nothing is displayed in place of invalid characters. + [b]Note:[/b] If set to [code]false[/code], nothing is displayed in place of invalid characters. </description> </method> <method name="shaped_text_get_range" qualifiers="const"> @@ -1167,7 +1173,7 @@ <argument index="1" name="direction" type="int" enum="TextServer.Direction" default="0" /> <description> Sets desired text direction. If set to [code]TEXT_DIRECTION_AUTO[/code], direction will be detected based on the buffer contents and current locale. - Note: Direction is ignored if server does not support [code]FEATURE_BIDI_LAYOUT[/code] feature. + [b]Note:[/b] Direction is ignored if server does not support [code]FEATURE_BIDI_LAYOUT[/code] feature. </description> </method> <method name="shaped_text_set_orientation"> @@ -1176,7 +1182,7 @@ <argument index="1" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> Sets desired text orientation. - Note: Orientation is ignored if server does not support [code]FEATURE_VERTICAL_LAYOUT[/code] feature. + [b]Note:[/b] Orientation is ignored if server does not support [code]FEATURE_VERTICAL_LAYOUT[/code] feature. </description> </method> <method name="shaped_text_set_preserve_control"> @@ -1200,7 +1206,7 @@ <argument index="0" name="shaped" type="RID" /> <description> Shapes buffer if it's not shaped. Returns [code]true[/code] if the string is shaped successfully. - Note: It is not necessary to call this function manually, buffer will be shaped automatically as soon as any of its output data is requested. + [b]Note:[/b] It is not necessary to call this function manually, buffer will be shaped automatically as soon as any of its output data is requested. </description> </method> <method name="shaped_text_sort_logical"> diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index 29571463d2..99382d5463 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -21,8 +21,8 @@ <argument index="1" name="orientation" type="int" enum="TextServer.Orientation" /> <description> Creates new buffer for complex text layout, with the given [code]direction[/code] and [code]orientation[/code]. To free the resulting buffer, use [method _free] method. - Note: Direction is ignored if server does not support [code]FEATURE_BIDI_LAYOUT[/code] feature. - Note: Orientation is ignored if server does not support [code]FEATURE_VERTICAL_LAYOUT[/code] feature. + [b]Note:[/b] Direction is ignored if server does not support [code]FEATURE_BIDI_LAYOUT[/code] feature. + [b]Note:[/b] Orientation is ignored if server does not support [code]FEATURE_VERTICAL_LAYOUT[/code] feature. </description> </method> <method name="_draw_hex_code_box" qualifiers="virtual const"> @@ -41,7 +41,8 @@ <argument index="0" name="font_rid" type="RID" /> <argument index="1" name="size" type="Vector2i" /> <description> - Removes all rendered glyphs information from the cache entry. Note: This function will not remove textures associated with the glyphs, use [method _font_remove_texture] to remove them manually. + Removes all rendered glyphs information from the cache entry. + [b]Note:[/b] This function will not remove textures associated with the glyphs, use [method _font_remove_texture] to remove them manually. </description> </method> <method name="_font_clear_kerning_map" qualifiers="virtual"> @@ -64,7 +65,8 @@ <argument index="0" name="font_rid" type="RID" /> <argument index="1" name="size" type="Vector2i" /> <description> - Removes all textures from font cache entry. Note: This function will not remove glyphs associated with the texture, use [method _font_remove_glyph] to remove them manually. + Removes all textures from font cache entry. + [b]Note:[/b] This function will not remove glyphs associated with the texture, use [method _font_remove_glyph] to remove them manually. </description> </method> <method name="_font_draw_glyph" qualifiers="virtual const"> @@ -77,7 +79,7 @@ <argument index="5" name="color" type="Color" /> <description> Draws single glyph into a canvas item at the position, using [code]font_rid[/code] at the size [code]size[/code]. - Note: Glyph index is specific to the font, use glyphs indices returned by [method _shaped_text_get_glyphs] or [method _font_get_glyph_index]. + [b]Note:[/b] Glyph index is specific to the font, use glyphs indices returned by [method _shaped_text_get_glyphs] or [method _font_get_glyph_index]. </description> </method> <method name="_font_draw_glyph_outline" qualifiers="virtual const"> @@ -91,7 +93,7 @@ <argument index="6" name="color" type="Color" /> <description> Draws single glyph outline of size [code]outline_size[/code] into a canvas item at the position, using [code]font_rid[/code] at the size [code]size[/code]. - Note: Glyph index is specific to the font, use glyphs indices returned by [method _shaped_text_get_glyphs] or [method _font_get_glyph_index]. + [b]Note:[/b] Glyph index is specific to the font, use glyphs indices returned by [method _shaped_text_get_glyphs] or [method _font_get_glyph_index]. </description> </method> <method name="_font_get_ascent" qualifiers="virtual const"> @@ -129,7 +131,8 @@ <argument index="1" name="size" type="int" /> <argument index="2" name="glyph" type="int" /> <description> - Returns glyph advance (offset of the next glyph). Note: advance for glyphs outlines is the same as the base glyph advance and is not saved. + Returns glyph advance (offset of the next glyph). + [b]Note:[/b] Advance for glyphs outlines is the same as the base glyph advance and is not saved. </description> </method> <method name="_font_get_glyph_contours" qualifiers="virtual const"> @@ -404,7 +407,8 @@ <argument index="1" name="size" type="Vector2i" /> <argument index="2" name="glyph" type="int" /> <description> - Removes specified rendered glyph information from the cache entry. Note: This function will not remove textures associated with the glyphs, use [method _font_remove_texture] to remove them manually. + Removes specified rendered glyph information from the cache entry. + [b]Note:[/b] This function will not remove textures associated with the glyphs, use [method _font_remove_texture] to remove them manually. </description> </method> <method name="_font_remove_kerning" qualifiers="virtual"> @@ -446,7 +450,8 @@ <argument index="1" name="size" type="Vector2i" /> <argument index="2" name="texture_index" type="int" /> <description> - Removes specified texture from font cache entry. Note: This function will not remove glyphs associated with the texture, remove them manually, using [method _font_remove_glyph]. + Removes specified texture from font cache entry. + [b]Note:[/b] This function will not remove glyphs associated with the texture, remove them manually, using [method _font_remove_glyph]. </description> </method> <method name="_font_render_glyph" qualifiers="virtual"> @@ -531,7 +536,7 @@ <argument index="0" name="oversampling" type="float" /> <description> Sets oversampling factor, shared by all font in the TextServer. - Note: This value can be automaticaly changed by display server. + [b]Note:[/b] This value can be automaticaly changed by display server. </description> </method> <method name="_font_set_glyph_advance" qualifiers="virtual"> @@ -541,7 +546,8 @@ <argument index="2" name="glyph" type="int" /> <argument index="3" name="advance" type="Vector2" /> <description> - Sets glyph advance (offset of the next glyph). Note: advance for glyphs outlines is the same as the base glyph advance and is not saved. + Sets glyph advance (offset of the next glyph). + [b]Note:[/b] Advance for glyphs outlines is the same as the base glyph advance and is not saved. </description> </method> <method name="_font_set_glyph_offset" qualifiers="virtual"> @@ -804,7 +810,7 @@ <argument index="0" name="filename" type="String" /> <description> Loads optional TextServer database (e.g. ICU break iterators and dictionaries). - Note: This function should be called before any other TextServer functions used, otherwise it won't have any effect. + [b]Note:[/b] This function should be called before any other TextServer functions used, otherwise it won't have any effect. </description> </method> <method name="_name_to_tag" qualifiers="virtual const"> @@ -834,7 +840,7 @@ <argument index="0" name="filename" type="String" /> <description> Saves optional TextServer database (e.g. ICU break iterators and dictionaries) to the file. - Note: This function is used by during project export, to include TextServer database. + [b]Note:[/b] This function is used by during project export, to include TextServer database. </description> </method> <method name="_shaped_text_add_object" qualifiers="virtual"> @@ -906,7 +912,7 @@ <argument index="0" name="shaped" type="RID" /> <description> Returns the text ascent (number of pixels above the baseline for horizontal layout or to the left of baseline for vertical). - Note: overall ascent can be higher than font ascent, if some glyphs are displaced from the baseline. + [b]Note:[/b] Overall ascent can be higher than font ascent, if some glyphs are displaced from the baseline. </description> </method> <method name="_shaped_text_get_carets" qualifiers="virtual const"> @@ -923,7 +929,7 @@ <argument index="0" name="shaped" type="RID" /> <description> Returns the text descent (number of pixels below the baseline for horizontal layout or to the right of baseline for vertical). - Note: overall descent can be higher than font descent, if some glyphs are displaced from the baseline. + [b]Note:[/b] Overall descent can be higher than font descent, if some glyphs are displaced from the baseline. </description> </method> <method name="_shaped_text_get_direction" qualifiers="virtual const"> @@ -1041,7 +1047,7 @@ <argument index="0" name="shaped" type="RID" /> <description> Returns [code]true[/code] if text buffer is configured to display hexadecimal codes in place of invalid characters. - Note: If set to [code]false[/code], nothing is displayed in place of invalid characters. + [b]Note:[/b] If set to [code]false[/code], nothing is displayed in place of invalid characters. </description> </method> <method name="_shaped_text_get_range" qualifiers="virtual const"> @@ -1176,7 +1182,7 @@ <argument index="1" name="direction" type="int" enum="TextServer.Direction" /> <description> Sets desired text direction. If set to [code]TEXT_DIRECTION_AUTO[/code], direction will be detected based on the buffer contents and current locale. - Note: Direction is ignored if server does not support [code]FEATURE_BIDI_LAYOUT[/code] feature. + [b]Note:[/b] Direction is ignored if server does not support [code]FEATURE_BIDI_LAYOUT[/code] feature. </description> </method> <method name="_shaped_text_set_orientation" qualifiers="virtual"> @@ -1185,7 +1191,7 @@ <argument index="1" name="orientation" type="int" enum="TextServer.Orientation" /> <description> Sets desired text orientation. - Note: Orientation is ignored if server does not support [code]FEATURE_VERTICAL_LAYOUT[/code] feature. + [b]Note:[/b] Orientation is ignored if server does not support [code]FEATURE_VERTICAL_LAYOUT[/code] feature. </description> </method> <method name="_shaped_text_set_preserve_control" qualifiers="virtual"> @@ -1209,7 +1215,7 @@ <argument index="0" name="shaped" type="RID" /> <description> Shapes buffer if it's not shaped. Returns [code]true[/code] if the string is shaped successfully. - Note: It is not necessary to call this function manually, buffer will be shaped automatically as soon as any of its output data is requested. + [b]Note:[/b] It is not necessary to call this function manually, buffer will be shaped automatically as soon as any of its output data is requested. </description> </method> <method name="_shaped_text_sort_logical" qualifiers="virtual"> diff --git a/doc/classes/TextServerManager.xml b/doc/classes/TextServerManager.xml index aa2177c3b1..d6a1cde945 100644 --- a/doc/classes/TextServerManager.xml +++ b/doc/classes/TextServerManager.xml @@ -5,7 +5,7 @@ </brief_description> <description> [TextServerManager] is the API backend for loading, enumeration and switching [TextServer]s. - Note: Switching text server at runtime is possible, but will invalidate all fonts and text buffers. Make sure to unload all controls, fonts, and themes before doing so. + [b]Note:[/b] Switching text server at runtime is possible, but will invalidate all fonts and text buffers. Make sure to unload all controls, fonts, and themes before doing so. </description> <tutorials> </tutorials> diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml index 2f5648dda9..52a419ce0f 100644 --- a/doc/classes/Theme.xml +++ b/doc/classes/Theme.xml @@ -8,7 +8,7 @@ Theme resources can alternatively be loaded by writing them in a [code].theme[/code] file, see the documentation for more information. </description> <tutorials> - <link title="GUI skinning">https://docs.godotengine.org/en/latest/tutorials/gui/gui_skinning.html</link> + <link title="GUI skinning">https://docs.godotengine.org/en/latest/tutorials/ui/gui_skinning.html</link> </tutorials> <methods> <method name="clear"> @@ -497,7 +497,7 @@ Marks [code]theme_type[/code] as being a variation of [code]base_type[/code]. This adds [code]theme_type[/code] as a suggested option for [member Control.theme_type_variation] on a [Control] that is of the [code]base_type[/code] class. Variations can also be nested, i.e. [code]base_type[/code] can be another variation. If a chain of variations ends with a [code]base_type[/code] matching a class of a [Control], the whole chain is going to be suggested as options. - Note: Suggestions only show up if this [Theme] is set as the project default theme. See [member ProjectSettings.gui/theme/custom]. + [b]Note:[/b] Suggestions only show up if this [Theme] is set as the project default theme. See [member ProjectSettings.gui/theme/custom]. </description> </method> </methods> diff --git a/doc/classes/Thread.xml b/doc/classes/Thread.xml index ae5c0761b1..d5069dd8f3 100644 --- a/doc/classes/Thread.xml +++ b/doc/classes/Thread.xml @@ -8,8 +8,8 @@ [b]Note:[/b] Breakpoints won't break on code if it's running in a thread. This is a current limitation of the GDScript debugger. </description> <tutorials> - <link title="Using multiple threads">https://docs.godotengine.org/en/latest/tutorials/threads/using_multiple_threads.html</link> - <link title="Thread-safe APIs">https://docs.godotengine.org/en/latest/tutorials/threads/thread_safe_apis.html</link> + <link title="Using multiple threads">https://docs.godotengine.org/en/latest/tutorials/performance/using_multiple_threads.html</link> + <link title="Thread-safe APIs">https://docs.godotengine.org/en/latest/tutorials/performance/thread_safe_apis.html</link> <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> </tutorials> <methods> diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index b91f159160..a6ea5738d5 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -22,7 +22,7 @@ <argument index="0" name="time_sec" type="float" default="-1" /> <description> Starts the timer. Sets [code]wait_time[/code] to [code]time_sec[/code] if [code]time_sec > 0[/code]. This also resets the remaining time to [code]wait_time[/code]. - [b]Note:[/b] this method will not resume a paused timer. See [member paused]. + [b]Note:[/b] This method will not resume a paused timer. See [member paused]. </description> </method> <method name="stop"> diff --git a/doc/classes/TouchScreenButton.xml b/doc/classes/TouchScreenButton.xml index e75a41a936..f1becb6906 100644 --- a/doc/classes/TouchScreenButton.xml +++ b/doc/classes/TouchScreenButton.xml @@ -30,7 +30,7 @@ </member> <member name="passby_press" type="bool" setter="set_passby_press" getter="is_passby_press_enabled" default="false"> If [code]true[/code], the [signal pressed] and [signal released] signals are emitted whenever a pressed finger goes in and out of the button, even if the pressure started outside the active area of the button. - [b]Note:[/b] this is a "pass-by" (not "bypass") press mode. + [b]Note:[/b] This is a "pass-by" (not "bypass") press mode. </member> <member name="pressed" type="Texture2D" setter="set_texture_pressed" getter="get_texture_pressed"> The button's texture for the pressed state. diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index 4fd788467d..d64752a00f 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -8,7 +8,7 @@ For more information, read the "Matrices and transforms" documentation article. </description> <tutorials> - <link title="Math tutorial index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link title="Math documentation index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> <link title="Matrices and transforms">https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html</link> <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/584</link> <link title="2.5D Demo">https://godotengine.org/asset-library/asset/583</link> diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index 337e7d2693..63a7746328 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -8,7 +8,7 @@ For more information, read the "Matrices and transforms" documentation article. </description> <tutorials> - <link title="Math tutorial index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link title="Math documentation index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> <link title="Matrices and transforms">https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html</link> <link title="Using 3D transforms">https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link> <link title="Matrix Transform Demo">https://godotengine.org/asset-library/asset/584</link> diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index b61d5bea0e..4035fb0ad2 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -9,7 +9,7 @@ [b]Note:[/b] In a boolean context, a Vector2 will evaluate to [code]false[/code] if it's equal to [code]Vector2(0, 0)[/code]. Otherwise, a Vector2 will always evaluate to [code]true[/code]. </description> <tutorials> - <link title="Math tutorial index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link title="Math documentation index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> <link title="Vector math">https://docs.godotengine.org/en/latest/tutorials/math/vector_math.html</link> <link title="Advanced vector math">https://docs.godotengine.org/en/latest/tutorials/math/vectors_advanced.html</link> <link title="3Blue1Brown Essence of Linear Algebra">https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab</link> diff --git a/doc/classes/Vector2i.xml b/doc/classes/Vector2i.xml index 2e69d6efdf..52fd8c6780 100644 --- a/doc/classes/Vector2i.xml +++ b/doc/classes/Vector2i.xml @@ -9,7 +9,7 @@ [b]Note:[/b] In a boolean context, a Vector2i will evaluate to [code]false[/code] if it's equal to [code]Vector2i(0, 0)[/code]. Otherwise, a Vector2i will always evaluate to [code]true[/code]. </description> <tutorials> - <link title="Math tutorial index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link title="Math documentation index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> <link title="Vector math">https://docs.godotengine.org/en/latest/tutorials/math/vector_math.html</link> <link title="3Blue1Brown Essence of Linear Algebra">https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab</link> </tutorials> diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index 9f2ea7b2a0..267a0d2e9e 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -9,7 +9,7 @@ [b]Note:[/b] In a boolean context, a Vector3 will evaluate to [code]false[/code] if it's equal to [code]Vector3(0, 0, 0)[/code]. Otherwise, a Vector3 will always evaluate to [code]true[/code]. </description> <tutorials> - <link title="Math tutorial index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link title="Math documentation index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> <link title="Vector math">https://docs.godotengine.org/en/latest/tutorials/math/vector_math.html</link> <link title="Advanced vector math">https://docs.godotengine.org/en/latest/tutorials/math/vectors_advanced.html</link> <link title="3Blue1Brown Essence of Linear Algebra">https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab</link> diff --git a/doc/classes/Vector3i.xml b/doc/classes/Vector3i.xml index bc3712ba3e..2a7ee1ffb8 100644 --- a/doc/classes/Vector3i.xml +++ b/doc/classes/Vector3i.xml @@ -9,7 +9,7 @@ [b]Note:[/b] In a boolean context, a Vector3i will evaluate to [code]false[/code] if it's equal to [code]Vector3i(0, 0, 0)[/code]. Otherwise, a Vector3i will always evaluate to [code]true[/code]. </description> <tutorials> - <link title="Math tutorial index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link title="Math documentation index">https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> <link title="Vector math">https://docs.godotengine.org/en/latest/tutorials/math/vector_math.html</link> <link title="3Blue1Brown Essence of Linear Algebra">https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab</link> </tutorials> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 06a7177bfc..cdb9e7632b 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -12,8 +12,8 @@ Finally, viewports can also behave as render targets, in which case they will not be visible unless the associated texture is used to draw. </description> <tutorials> + <link title="Using Viewports">https://docs.godotengine.org/en/latest/tutorials/rendering/viewports.html</link> <link title="Viewport and canvas transforms">https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link> - <link title="Viewports tutorial index">https://docs.godotengine.org/en/latest/tutorials/viewports/index.html</link> <link title="GUI in 3D Demo">https://godotengine.org/asset-library/asset/127</link> <link title="3D in 2D Demo">https://godotengine.org/asset-library/asset/128</link> <link title="2D in 3D Demo">https://godotengine.org/asset-library/asset/129</link> diff --git a/doc/classes/VisualShaderNode.xml b/doc/classes/VisualShaderNode.xml index 2cff70b4f1..b752e94490 100644 --- a/doc/classes/VisualShaderNode.xml +++ b/doc/classes/VisualShaderNode.xml @@ -7,7 +7,7 @@ Visual shader graphs consist of various nodes. Each node in the graph is a separate object and they are represented as a rectangular boxes with title and a set of properties. Each node has also connection ports that allow to connect it to another nodes and control the flow of the shader. </description> <tutorials> - <link title="VisualShaders">https://docs.godotengine.org/en/latest/tutorials/shading/visual_shaders.html</link> + <link title="VisualShaders">https://docs.godotengine.org/en/latest/tutorials/shaders/visual_shaders.html</link> </tutorials> <methods> <method name="clear_default_input_values"> diff --git a/doc/classes/VisualShaderNodeInput.xml b/doc/classes/VisualShaderNodeInput.xml index 68f64ad98e..46d7dd6322 100644 --- a/doc/classes/VisualShaderNodeInput.xml +++ b/doc/classes/VisualShaderNodeInput.xml @@ -7,7 +7,7 @@ Gives access to input variables (built-ins) available for the shader. See the shading reference for the list of available built-ins for each shader type (check [code]Tutorials[/code] section for link). </description> <tutorials> - <link title="Shading reference index">https://docs.godotengine.org/en/stable/tutorials/shading/shading_reference/index.html</link> + <link title="Shading reference index">https://docs.godotengine.org/en/latest/tutorials/shaders/shader_reference/index.html</link> </tutorials> <methods> <method name="get_input_real_name" qualifiers="const"> diff --git a/doc/classes/VoxelGI.xml b/doc/classes/VoxelGI.xml index eceb651d5e..f6470782ee 100644 --- a/doc/classes/VoxelGI.xml +++ b/doc/classes/VoxelGI.xml @@ -9,7 +9,7 @@ [b]Note:[/b] Meshes should have sufficiently thick walls to avoid light leaks (avoid one-sided walls). For interior levels, enclose your level geometry in a sufficiently large box and bridge the loops to close the mesh. </description> <tutorials> - <link title="GI probes">https://docs.godotengine.org/en/latest/tutorials/3d/voxel_gi.html</link> + <link title="GI probes">https://docs.godotengine.org/en/latest/tutorials/3d/gi_probes.html</link> <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <methods> diff --git a/doc/classes/XRCamera3D.xml b/doc/classes/XRCamera3D.xml index 682a797b5e..31f05ca06c 100644 --- a/doc/classes/XRCamera3D.xml +++ b/doc/classes/XRCamera3D.xml @@ -8,6 +8,6 @@ The position and orientation of this node is automatically updated by the XR Server to represent the location of the HMD if such tracking is available and can thus be used by game logic. Note that, in contrast to the XR Controller, the render thread has access to the most up-to-date tracking data of the HMD and the location of the XRCamera3D can lag a few milliseconds behind what is used for rendering as a result. </description> <tutorials> - <link title="VR tutorial index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> + <link title="VR documentation index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> </tutorials> </class> diff --git a/doc/classes/XRController3D.xml b/doc/classes/XRController3D.xml index 1a05a7b651..35edf5c2b2 100644 --- a/doc/classes/XRController3D.xml +++ b/doc/classes/XRController3D.xml @@ -9,7 +9,7 @@ The position of the controller node is automatically updated by the [XRServer]. This makes this node ideal to add child nodes to visualize the controller. </description> <tutorials> - <link title="VR tutorial index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> + <link title="VR documentation index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> </tutorials> <methods> <method name="get_controller_name" qualifiers="const"> diff --git a/doc/classes/XRInterface.xml b/doc/classes/XRInterface.xml index 2a740ab1e8..ffc2bc138d 100644 --- a/doc/classes/XRInterface.xml +++ b/doc/classes/XRInterface.xml @@ -8,7 +8,7 @@ Interfaces should be written in such a way that simply enabling them will give us a working setup. You can query the available interfaces through [XRServer]. </description> <tutorials> - <link title="VR tutorial index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> + <link title="VR documentation index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> </tutorials> <methods> <method name="get_camera_feed_id"> diff --git a/doc/classes/XROrigin3D.xml b/doc/classes/XROrigin3D.xml index cdf319093c..0d8acfeb1b 100644 --- a/doc/classes/XROrigin3D.xml +++ b/doc/classes/XROrigin3D.xml @@ -10,7 +10,7 @@ For example, if your character is driving a car, the XROrigin3D node should be a child node of this car. Or, if you're implementing a teleport system to move your character, you should change the position of this node. </description> <tutorials> - <link title="VR tutorial index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> + <link title="VR documentation index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> </tutorials> <members> <member name="world_scale" type="float" setter="set_world_scale" getter="get_world_scale" default="1.0"> diff --git a/doc/classes/XRPositionalTracker.xml b/doc/classes/XRPositionalTracker.xml index 8cc7c872b6..d231bfde74 100644 --- a/doc/classes/XRPositionalTracker.xml +++ b/doc/classes/XRPositionalTracker.xml @@ -9,7 +9,7 @@ The [XRController3D] and [XRAnchor3D] both consume objects of this type and should be used in your project. The positional trackers are just under-the-hood objects that make this all work. These are mostly exposed so that GDNative-based interfaces can interact with them. </description> <tutorials> - <link title="VR tutorial index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> + <link title="VR documentation index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> </tutorials> <methods> <method name="get_joy_id" qualifiers="const"> diff --git a/doc/classes/XRServer.xml b/doc/classes/XRServer.xml index 85170804cc..0929094fd1 100644 --- a/doc/classes/XRServer.xml +++ b/doc/classes/XRServer.xml @@ -7,7 +7,7 @@ The AR/VR server is the heart of our Advanced and Virtual Reality solution and handles all the processing. </description> <tutorials> - <link title="VR tutorial index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> + <link title="VR documentation index">https://docs.godotengine.org/en/latest/tutorials/vr/index.html</link> </tutorials> <methods> <method name="add_interface"> diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py index a23324fd24..1ec9627c2c 100755 --- a/doc/tools/makerst.py +++ b/doc/tools/makerst.py @@ -871,7 +871,7 @@ def rstize_text(text, state): # type: (str, State) -> str inside_url = True url_has_name = False elif cmd == "/url": - tag_text = ("" if url_has_name else url_link) + " <" + url_link + ">`_" + tag_text = ("" if url_has_name else url_link) + " <" + url_link + ">`__" tag_depth -= 1 escape_post = True inside_url = False @@ -1053,6 +1053,11 @@ def make_method_signature( ret_type = method_def.return_type.to_rst(state) ref_type = "method" + # FIXME: Need to add proper support for operator methods, but generating a unique + # and valid ref for them is not trivial. + if method_def.name.startswith("operator "): + make_ref = False + out = "" if make_ref: @@ -1102,6 +1107,7 @@ def make_footer(): # type: () -> str ".. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n" ".. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n" ".. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n" + ".. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n" ".. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n" ) # fmt: on @@ -1126,9 +1132,9 @@ def make_link(url, title): # type: (str, str) -> str # External link, for example: # `http://enet.bespin.org/usergroup0.html` if title != "": - return "`" + title + " <" + url + ">`_" + return "`" + title + " <" + url + ">`__" else: - return "`" + url + " <" + url + ">`_" + return "`" + url + " <" + url + ">`__" if __name__ == "__main__": diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 24c9b69542..3b2f078120 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -274,7 +274,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); } - bool using_audio_client_3 = !p_capture; // IID_IAudioClient3 is only used for adjustable output latency (not input) + using_audio_client_3 = !p_capture; // IID_IAudioClient3 is only used for adjustable output latency (not input) if (using_audio_client_3) { hr = device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); if (hr != S_OK) { @@ -378,6 +378,13 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c // Due to WASAPI Shared Mode we have no control of the buffer size buffer_frames = max_frames; + + int64_t latency = 0; + audio_output.audio_client->GetStreamLatency(&latency); + // WASAPI REFERENCE_TIME units are 100 nanoseconds per unit + // https://docs.microsoft.com/en-us/windows/win32/directshow/reference-time + // Convert REFTIME to seconds as godot uses for latency + real_latency = (float)latency / (float)REFTIMES_PER_SEC; } else { IAudioClient3 *device_audio_client_3 = (IAudioClient3 *)p_device->audio_client; @@ -411,6 +418,11 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c hr = device_audio_client_3->InitializeSharedAudioStream(0, period_frames, pwfex, nullptr); ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + "."); + uint32_t output_latency_in_frames; + WAVEFORMATEX *current_pwfex; + device_audio_client_3->GetCurrentSharedModeEnginePeriod(¤t_pwfex, &output_latency_in_frames); + real_latency = (float)output_latency_in_frames / (float)current_pwfex->nSamplesPerSec; + CoTaskMemFree(current_pwfex); } if (p_capture) { @@ -518,6 +530,10 @@ int AudioDriverWASAPI::get_mix_rate() const { return mix_rate; } +float AudioDriverWASAPI::get_latency() { + return real_latency; +} + AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); } diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index a19cd3da09..6df9b4d2ee 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -73,6 +73,8 @@ class AudioDriverWASAPI : public AudioDriver { int mix_rate = 0; int buffer_frames = 0; int target_latency_ms = 0; + float real_latency = 0.0; + bool using_audio_client_3 = false; bool thread_exited = false; mutable bool exit_thread = false; @@ -99,6 +101,7 @@ public: virtual Error init(); virtual void start(); virtual int get_mix_rate() const; + virtual float get_latency(); virtual SpeakerMode get_speaker_mode() const; virtual Array get_device_list(); virtual String get_device(); diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index a88adf3634..2010ee01db 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -1046,17 +1046,19 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & return err; } } - if (FileAccess::exists(ResourceUID::CACHE_FILE)) { - Vector<uint8_t> array = FileAccess::get_file_as_array(ResourceUID::CACHE_FILE); - err = p_func(p_udata, ResourceUID::CACHE_FILE, array, idx, total, enc_in_filters, enc_ex_filters, key); + String resource_cache_file = ResourceUID::get_cache_file(); + if (FileAccess::exists(resource_cache_file)) { + Vector<uint8_t> array = FileAccess::get_file_as_array(resource_cache_file); + err = p_func(p_udata, resource_cache_file, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } } - if (FileAccess::exists(NativeExtension::EXTENSION_LIST_CONFIG_FILE)) { - Vector<uint8_t> array = FileAccess::get_file_as_array(NativeExtension::EXTENSION_LIST_CONFIG_FILE); - err = p_func(p_udata, NativeExtension::EXTENSION_LIST_CONFIG_FILE, array, idx, total, enc_in_filters, enc_ex_filters, key); + String extension_list_config_file = NativeExtension::get_extension_list_config_file(); + if (FileAccess::exists(extension_list_config_file)) { + Vector<uint8_t> array = FileAccess::get_file_as_array(extension_list_config_file); + err = p_func(p_udata, extension_list_config_file, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 8956983646..675234959a 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -588,7 +588,7 @@ void EditorFileDialog::_item_list_item_rmb_selected(int p_item, const Vector2 &p continue; } Dictionary item_meta = item_list->get_item_metadata(i); - if (String(item_meta["path"]).begins_with("res://.godot")) { + if (String(item_meta["path"]).begins_with(ProjectSettings::get_singleton()->get_project_data_path())) { allow_delete = false; break; } diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 0882b525d7..953989aadd 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -2163,6 +2163,10 @@ Error EditorFileSystem::_resource_import(const String &p_path) { } bool EditorFileSystem::_should_skip_directory(const String &p_path) { + if (p_path == ProjectSettings::get_singleton()->get_project_data_path()) { + return true; + } + if (FileAccess::exists(p_path.plus_file("project.godot"))) { // skip if another project inside this return true; @@ -2228,7 +2232,7 @@ void EditorFileSystem::move_group_file(const String &p_path, const String &p_new } ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate) { - if (!p_path.is_resource_file() || p_path.begins_with("res://.godot")) { + if (!p_path.is_resource_file() || p_path.begins_with(ProjectSettings::get_singleton()->get_project_data_path())) { //saved externally (configuration file) or internal file, do not assign an ID. return ResourceUID::INVALID_ID; } @@ -2286,17 +2290,18 @@ bool EditorFileSystem::_scan_extensions() { } } + String extension_list_config_file = NativeExtension::get_extension_list_config_file(); if (extensions.size()) { if (extensions_added.size() || extensions_removed.size()) { //extensions were added or removed - FileAccessRef f = FileAccess::open(NativeExtension::EXTENSION_LIST_CONFIG_FILE, FileAccess::WRITE); + FileAccessRef f = FileAccess::open(extension_list_config_file, FileAccess::WRITE); for (const String &E : extensions) { f->store_line(E); } } } else { - if (loaded_extensions.size() || FileAccess::exists(NativeExtension::EXTENSION_LIST_CONFIG_FILE)) { //extensions were removed + if (loaded_extensions.size() || FileAccess::exists(extension_list_config_file)) { //extensions were removed DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - da->remove(NativeExtension::EXTENSION_LIST_CONFIG_FILE); + da->remove(extension_list_config_file); } } diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp index c9817190dd..71f13c0c2f 100644 --- a/editor/editor_paths.cpp +++ b/editor/editor_paths.cpp @@ -87,6 +87,8 @@ void EditorPaths::_bind_methods() { EditorPaths::EditorPaths() { singleton = this; + project_data_dir = ProjectSettings::get_singleton()->get_project_data_path(); + // Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir. String exe_path = OS::get_singleton()->get_executable_path().get_base_dir(); { @@ -183,7 +185,7 @@ EditorPaths::EditorPaths() { } } - // Validate or create project-specific editor data dir (`res://.godot`), + // Validate or create project-specific editor data dir, // including shader cache subdir. if (Main::is_project_manager() || Main::is_cmdline_tool()) { @@ -205,8 +207,9 @@ EditorPaths::EditorPaths() { dir_res->make_dir("editor"); } // Imported assets dir. - if (!dir_res->dir_exists(ProjectSettings::IMPORTED_FILES_PATH)) { - dir_res->make_dir(ProjectSettings::IMPORTED_FILES_PATH); + String imported_files_path = ProjectSettings::get_singleton()->get_imported_files_path(); + if (!dir_res->dir_exists(imported_files_path)) { + dir_res->make_dir(imported_files_path); } } } diff --git a/editor/editor_paths.h b/editor/editor_paths.h index 2c156b7c96..cf94ed797a 100644 --- a/editor/editor_paths.h +++ b/editor/editor_paths.h @@ -41,7 +41,7 @@ class EditorPaths : public Object { String data_dir; // Editor data (templates, shader cache, etc.). String config_dir; // Editor config (settings, profiles, themes, etc.). String cache_dir; // Editor cache (thumbnails, tmp generated files). - String project_data_dir = "res://.godot"; // Project-specific data (metadata, shader cache, etc.). + String project_data_dir; // Project-specific data (metadata, shader cache, etc.). bool self_contained = false; // Self-contained means everything goes to `editor_data` dir. String self_contained_file; // Self-contained file with configuration. diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 380b269675..283496c0f1 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -233,8 +233,9 @@ void FindInFiles::_scan_dir(String path, PackedStringArray &out_folders) { break; } - // Ignore special dirs (such as .git and .import) - if (file == "." || file == ".." || file.begins_with(".")) { + // Ignore special dirs (such as .git and project data directory) + String project_data_dir_name = ProjectSettings::get_singleton()->get_project_data_dir_name(); + if (file.begins_with(".") || file == project_data_dir_name) { continue; } if (dir->current_is_hidden()) { diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index bc026146ef..3fbd315aec 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -131,6 +131,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na return false; } + // ID and size related properties. if (tiles.size() == 1) { const Vector2i &coords = tiles.front()->get().tile; const int &alternative = tiles.front()->get().alternative; @@ -160,36 +161,6 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i); emit_signal(SNAME("changed"), "size_in_atlas"); return true; - } else if (p_name == "animation_columns") { - bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(coords), p_value, tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords); - ERR_FAIL_COND_V(!has_room_for_tile, false); - tile_set_atlas_source->set_tile_animation_columns(coords, p_value); - emit_signal(SNAME("changed"), "animation_columns"); - return true; - } else if (p_name == "animation_separation") { - bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(coords), tile_set_atlas_source->get_tile_animation_columns(coords), p_value, tile_set_atlas_source->get_tile_animation_frames_count(coords), coords); - ERR_FAIL_COND_V(!has_room_for_tile, false); - tile_set_atlas_source->set_tile_animation_separation(coords, p_value); - emit_signal(SNAME("changed"), "animation_separation"); - return true; - } else if (p_name == "animation_speed") { - tile_set_atlas_source->set_tile_animation_speed(coords, p_value); - emit_signal(SNAME("changed"), "animation_speed"); - return true; - } else if (p_name == "animation_frames_count") { - bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(coords), tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), p_value, coords); - ERR_FAIL_COND_V(!has_room_for_tile, false); - tile_set_atlas_source->set_tile_animation_frames_count(coords, p_value); - notify_property_list_changed(); - emit_signal(SNAME("changed"), "animation_separation"); - return true; - } else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) { - int frame = components[0].trim_prefix("animation_frame_").to_int(); - ERR_FAIL_INDEX_V(frame, tile_set_atlas_source->get_tile_animation_frames_count(coords), false); - if (components[1] == "duration") { - tile_set_atlas_source->set_tile_animation_frame_duration(coords, frame, p_value); - return true; - } } } else if (alternative > 0) { if (p_name == "alternative_id") { @@ -213,6 +184,74 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na } } + // Animation. + // Check if all tiles have an alternative_id of 0. + bool all_alternatve_id_zero = true; + for (TileSelection tile : tiles) { + if (tile.alternative != 0) { + all_alternatve_id_zero = false; + break; + } + } + + if (all_alternatve_id_zero) { + Vector<String> components = String(p_name).split("/", true, 2); + if (p_name == "animation_columns") { + for (TileSelection tile : tiles) { + bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), p_value, tile_set_atlas_source->get_tile_animation_separation(tile.tile), tile_set_atlas_source->get_tile_animation_frames_count(tile.tile), tile.tile); + if (!has_room_for_tile) { + ERR_PRINT("No room for tile"); + } else { + tile_set_atlas_source->set_tile_animation_columns(tile.tile, p_value); + } + } + emit_signal(SNAME("changed"), "animation_columns"); + return true; + } else if (p_name == "animation_separation") { + for (TileSelection tile : tiles) { + bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), tile_set_atlas_source->get_tile_animation_columns(tile.tile), p_value, tile_set_atlas_source->get_tile_animation_frames_count(tile.tile), tile.tile); + if (!has_room_for_tile) { + ERR_PRINT("No room for tile"); + } else { + tile_set_atlas_source->set_tile_animation_separation(tile.tile, p_value); + } + } + emit_signal(SNAME("changed"), "animation_separation"); + return true; + } else if (p_name == "animation_speed") { + for (TileSelection tile : tiles) { + tile_set_atlas_source->set_tile_animation_speed(tile.tile, p_value); + } + emit_signal(SNAME("changed"), "animation_speed"); + return true; + } else if (p_name == "animation_frames_count") { + for (TileSelection tile : tiles) { + bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), tile_set_atlas_source->get_tile_animation_columns(tile.tile), tile_set_atlas_source->get_tile_animation_separation(tile.tile), p_value, tile.tile); + if (!has_room_for_tile) { + ERR_PRINT("No room for tile"); + } else { + tile_set_atlas_source->set_tile_animation_frames_count(tile.tile, p_value); + } + } + notify_property_list_changed(); + emit_signal(SNAME("changed"), "animation_separation"); + return true; + } else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) { + for (TileSelection tile : tiles) { + int frame = components[0].trim_prefix("animation_frame_").to_int(); + if (frame < 0 || frame >= tile_set_atlas_source->get_tile_animation_frames_count(tile.tile)) { + ERR_PRINT(vformat("No tile animation frame with index %d", frame)); + } else { + if (components[1] == "duration") { + tile_set_atlas_source->set_tile_animation_frame_duration(tile.tile, frame, p_value); + return true; + } + } + } + } + } + + // Other properties. bool any_valid = false; for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) { const Vector2i &coords = E->get().tile; @@ -238,6 +277,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na return false; } + // ID and size related properties.s if (tiles.size() == 1) { const Vector2i &coords = tiles.front()->get().tile; const int &alternative = tiles.front()->get().alternative; @@ -250,27 +290,6 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na } else if (p_name == "size_in_atlas") { r_ret = tile_set_atlas_source->get_tile_size_in_atlas(coords); return true; - } else if (p_name == "animation_columns") { - r_ret = tile_set_atlas_source->get_tile_animation_columns(coords); - return true; - } else if (p_name == "animation_separation") { - r_ret = tile_set_atlas_source->get_tile_animation_separation(coords); - return true; - } else if (p_name == "animation_speed") { - r_ret = tile_set_atlas_source->get_tile_animation_speed(coords); - return true; - } else if (p_name == "animation_frames_count") { - r_ret = tile_set_atlas_source->get_tile_animation_frames_count(coords); - return true; - } else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) { - int frame = components[0].trim_prefix("animation_frame_").to_int(); - if (components[1] == "duration") { - if (frame < 0 || frame >= tile_set_atlas_source->get_tile_animation_frames_count(coords)) { - return false; - } - r_ret = tile_set_atlas_source->get_tile_animation_frame_duration(coords, frame); - return true; - } } } else if (alternative > 0) { if (p_name == "alternative_id") { @@ -280,6 +299,44 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na } } + // Animation. + // Check if all tiles have an alternative_id of 0. + bool all_alternatve_id_zero = true; + for (TileSelection tile : tiles) { + if (tile.alternative != 0) { + all_alternatve_id_zero = false; + break; + } + } + + if (all_alternatve_id_zero) { + const Vector2i &coords = tiles.front()->get().tile; + + Vector<String> components = String(p_name).split("/", true, 2); + if (p_name == "animation_columns") { + r_ret = tile_set_atlas_source->get_tile_animation_columns(coords); + return true; + } else if (p_name == "animation_separation") { + r_ret = tile_set_atlas_source->get_tile_animation_separation(coords); + return true; + } else if (p_name == "animation_speed") { + r_ret = tile_set_atlas_source->get_tile_animation_speed(coords); + return true; + } else if (p_name == "animation_frames_count") { + r_ret = tile_set_atlas_source->get_tile_animation_frames_count(coords); + return true; + } else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) { + int frame = components[0].trim_prefix("animation_frame_").to_int(); + if (components[1] == "duration") { + if (frame < 0 || frame >= tile_set_atlas_source->get_tile_animation_frames_count(coords)) { + return false; + } + r_ret = tile_set_atlas_source->get_tile_animation_frame_duration(coords, frame); + return true; + } + } + } + for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) { // Return the first tile with a property matching the name. // Note: It's a little bit annoying, but the behavior is the same the one in MultiNodeEdit. @@ -304,29 +361,42 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro return; } + // ID and size related properties. if (tiles.size() == 1) { if (tiles.front()->get().alternative == 0) { p_list->push_back(PropertyInfo(Variant::VECTOR2I, "atlas_coords", PROPERTY_HINT_NONE, "")); p_list->push_back(PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "")); - - // Animation. - p_list->push_back(PropertyInfo(Variant::NIL, "Animation", PROPERTY_HINT_NONE, "animation_", PROPERTY_USAGE_GROUP)); - p_list->push_back(PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "")); - p_list->push_back(PropertyInfo(Variant::VECTOR2I, "animation_separation", PROPERTY_HINT_NONE, "")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "")); - p_list->push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Frames,animation_frame_")); - if (tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile) == 1) { - p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_frame_0/duration", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); - } else { - for (int i = 0; i < tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile); i++) { - p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "")); - } - } } else { p_list->push_back(PropertyInfo(Variant::INT, "alternative_id", PROPERTY_HINT_NONE, "")); } } + // Animation. + // Check if all tiles have an alternative_id of 0. + bool all_alternatve_id_zero = true; + for (TileSelection tile : tiles) { + if (tile.alternative != 0) { + all_alternatve_id_zero = false; + break; + } + } + + if (all_alternatve_id_zero) { + p_list->push_back(PropertyInfo(Variant::NIL, "Animation", PROPERTY_HINT_NONE, "animation_", PROPERTY_USAGE_GROUP)); + p_list->push_back(PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "")); + p_list->push_back(PropertyInfo(Variant::VECTOR2I, "animation_separation", PROPERTY_HINT_NONE, "")); + p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "")); + p_list->push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Frames,animation_frame_")); + // Not optimal, but returns value for the first tile. This is similar to what MultiNodeEdit does. + if (tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile) == 1) { + p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_frame_0/duration", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); + } else { + for (int i = 0; i < tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile); i++) { + p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "")); + } + } + } + // Get the list of properties common to all tiles (similar to what's done in MultiNodeEdit). struct PropertyId { int occurence_id = 0; diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 81554c9550..e8fd3070c2 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -2123,8 +2123,8 @@ void ProjectManager::_run_project_confirm() { const String &selected = selected_list[i].project_key; String path = EditorSettings::get_singleton()->get("projects/" + selected); - // `.substr(6)` on `IMPORTED_FILES_PATH` strips away the leading "res://". - if (!DirAccess::exists(path.plus_file(ProjectSettings::IMPORTED_FILES_PATH.substr(6)))) { + // `.substr(6)` on `ProjectSettings::get_singleton()->get_imported_files_path()` strips away the leading "res://". + if (!DirAccess::exists(path.plus_file(ProjectSettings::get_singleton()->get_imported_files_path().substr(6)))) { run_error_diag->set_text(TTR("Can't run project: Assets need to be imported.\nPlease edit the project to trigger the initial import.")); run_error_diag->popup_centered(); continue; diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml index 00469ab44c..fcdf282a7d 100644 --- a/modules/enet/doc_classes/ENetConnection.xml +++ b/modules/enet/doc_classes/ENetConnection.xml @@ -51,7 +51,7 @@ <argument index="3" name="data" type="int" default="0" /> <description> Initiates a connection to a foreign [code]address[/code] using the specified [code]port[/code] and allocting the requested [code]channels[/code]. Optional [code]data[/code] can be passed during connection in the form of a 32 bit integer. - Note: You must call either [method create_host] or [method create_host_bound] before calling this method. + [b]Note:[/b] You must call either [method create_host] or [method create_host_bound] before calling this method. </description> </method> <method name="create_host"> @@ -121,7 +121,7 @@ <return type="Array" /> <description> Returns the list of peers associated with this host. - Note: This list might include some peers that are not fully connected or are still being disconnected. + [b]Note:[/b] This list might include some peers that are not fully connected or are still being disconnected. </description> </method> <method name="pop_statistic"> @@ -136,7 +136,7 @@ <argument index="0" name="refuse" type="bool" /> <description> Configures the DTLS server to automatically drop new connections. - Note: This method is only relevant after calling [method dtls_server_setup]. + [b]Note:[/b] This method is only relevant after calling [method dtls_server_setup]. </description> </method> <method name="service"> diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml index f9659c092a..22136c3944 100644 --- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml +++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml @@ -18,7 +18,7 @@ <argument index="1" name="host" type="ENetConnection" /> <description> Add a new remote peer with the given [code]peer_id[/code] connected to the given [code]host[/code]. - Note: The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state. + [b]Note:[/b] The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state. </description> </method> <method name="close_connection"> diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index 3654870b09..6b3bd714b9 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -7,8 +7,8 @@ A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as XRInterfaceGDNative. The library must be compiled for each platform and architecture that the project will run on. </description> <tutorials> - <link title="GDNative C example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link> - <link title="GDNative C++ example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-cpp-example.html</link> + <link title="GDNative C example">https://docs.godotengine.org/en/latest/tutorials/scripting/gdnative/gdnative_c_example.html</link> + <link title="GDNative C++ example">https://docs.godotengine.org/en/latest/tutorials/scripting/gdnative/gdnative_cpp_example.html</link> </tutorials> <methods> <method name="get_current_dependencies" qualifiers="const"> diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml index 9d34e89f02..221374a7a4 100644 --- a/modules/gdnative/doc_classes/NativeScript.xml +++ b/modules/gdnative/doc_classes/NativeScript.xml @@ -38,7 +38,7 @@ <return type="Variant" /> <description> Constructs a new object of the base type with a script of this type already attached. - [i]Note[/i]: Any arguments passed to this function will be ignored and not passed to the native constructor function. This will change with in a future API extension. + [b]Note:[/b] Any arguments passed to this function will be ignored and not passed to the native constructor function. This will change with in a future API extension. </description> </method> </methods> diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index d45202bd40..0a448ed88c 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -8,7 +8,7 @@ [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - <link title="GDScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link> + <link title="GDScript documentation index">https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/index.html</link> </tutorials> <methods> <method name="get_as_byte_code" qualifiers="const"> diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index 45a6f991bf..abd860a55f 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -8,7 +8,7 @@ See also [GodotSharp]. </description> <tutorials> - <link title="C# tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link> + <link title="C# documentation index">https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/index.html</link> </tutorials> <methods> <method name="new" qualifiers="vararg"> diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs index 0f50c90531..1d7bfaf0a4 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs @@ -121,6 +121,7 @@ namespace GodotTools.IdeMessaging this.messageHandler = messageHandler; this.logger = logger; + // TODO: Need to fetch the project data dir name from ProjectSettings instead of defaulting to ".godot" string projectMetadataDir = Path.Combine(godotProjectDir, ".godot", "mono", "metadata"); MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName); diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 375ad413c4..24bd1ed492 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -122,7 +122,7 @@ public: private: _GodotSharpDirs() { - res_data_dir = "res://.godot/mono"; + res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().plus_file("mono"); res_metadata_dir = res_data_dir.plus_file("metadata"); res_assemblies_base_dir = res_data_dir.plus_file("assemblies"); res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config()); diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py index b15479797c..e8990c43c8 100644 --- a/modules/visual_script/config.py +++ b/modules/visual_script/config.py @@ -17,6 +17,7 @@ def get_doc_classes(): "VisualScriptConstant", "VisualScriptConstructor", "VisualScriptCustomNode", + "VisualScriptCustomNodes", "VisualScriptDeconstruct", "VisualScriptEditor", "VisualScriptEmitSignal", diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index 372d46bc10..be6bf00e50 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -9,7 +9,7 @@ You are most likely to use this class via the Visual Script editor or when writing plugins for it. </description> <tutorials> - <link title="VisualScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link> + <link title="VisualScript documentation index">https://docs.godotengine.org/en/latest/tutorials/scripting/visual_script/index.html</link> </tutorials> <methods> <method name="add_custom_signal"> diff --git a/doc/classes/VisualScriptCustomNodes.xml b/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml index 1681da7653..1681da7653 100644 --- a/doc/classes/VisualScriptCustomNodes.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml index f6f360503f..618fe14137 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -26,7 +26,8 @@ <method name="close"> <return type="void" /> <description> - Close the peer connection and all data channels associated with it. Note, you cannot reuse this object for a new connection unless you call [method initialize]. + Close the peer connection and all data channels associated with it. + [b]Note:[/b] You cannot reuse this object for a new connection unless you call [method initialize]. </description> </method> <method name="create_data_channel"> diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index ab7ef6c4d0..2f455c32fd 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -4,7 +4,7 @@ A class representing a specific WebSocket connection. </brief_description> <description> - This class represent a specific WebSocket connection, you can do lower level operations with it. + This class represents a specific WebSocket connection, allowing you to do lower level operations with it. You can choose to write to the socket in binary or text mode, and you can recognize the mode used for writing by the other peer. </description> <tutorials> diff --git a/platform/javascript/.eslintrc.libs.js b/platform/javascript/.eslintrc.libs.js index 81b1b8c864..8e579fd462 100644 --- a/platform/javascript/.eslintrc.libs.js +++ b/platform/javascript/.eslintrc.libs.js @@ -15,6 +15,7 @@ module.exports = { "IDBFS": true, "GodotOS": true, "GodotConfig": true, + "GodotEventListeners": true, "GodotRuntime": true, "GodotFS": true, "IDHandler": true, diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index 62a8660ae4..fa9e6eed15 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -20,6 +20,7 @@ sys_env.AddJSLibraries( "js/libs/library_godot_fetch.js", "js/libs/library_godot_os.js", "js/libs/library_godot_runtime.js", + "js/libs/library_godot_input.js", ] ) diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp index 45a2cd595a..df4c790755 100644 --- a/platform/javascript/api/javascript_tools_editor_plugin.cpp +++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp @@ -131,9 +131,10 @@ void JavaScriptToolsEditorPlugin::_zip_recursive(String p_path, String p_base_pa } dir->list_dir_begin(); String cur = dir->get_next(); + String project_data_dir_name = ProjectSettings::get_singleton()->get_project_data_dir_name(); while (!cur.is_empty()) { String cs = p_path.plus_file(cur); - if (cur == "." || cur == ".." || cur == ".import") { + if (cur == "." || cur == ".." || cur == project_data_dir_name) { // Skip } else if (dir->current_is_dir()) { String path = cs.replace_first(p_base_path, "") + "/"; diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp index 124b4ee1c8..c2eb826db9 100644 --- a/platform/javascript/display_server_javascript.cpp +++ b/platform/javascript/display_server_javascript.cpp @@ -62,26 +62,13 @@ bool DisplayServerJavaScript::check_size_force_redraw() { return godot_js_display_size_update() != 0; } -Point2 DisplayServerJavaScript::compute_position_in_canvas(int p_x, int p_y) { - int point[2]; - godot_js_display_compute_position(p_x, p_y, point, point + 1); - return Point2(point[0], point[1]); -} - -EM_BOOL DisplayServerJavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) { +void DisplayServerJavaScript::fullscreen_change_callback(int p_fullscreen) { DisplayServerJavaScript *display = get_singleton(); - // Empty ID is canvas. - String target_id = String::utf8(p_event->id); - if (target_id.is_empty() || target_id == String::utf8(&(display->canvas_id[1]))) { - // This event property is the only reliable data on - // browser fullscreen state. - if (p_event->isFullscreen) { - display->window_mode = WINDOW_MODE_FULLSCREEN; - } else { - display->window_mode = WINDOW_MODE_WINDOWED; - } + if (p_fullscreen) { + display->window_mode = WINDOW_MODE_FULLSCREEN; + } else { + display->window_mode = WINDOW_MODE_WINDOWED; } - return false; } // Drag and drop callback. @@ -118,88 +105,51 @@ void DisplayServerJavaScript::request_quit_callback() { // Keys -template <typename T> -void DisplayServerJavaScript::dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) { - godot_event->set_shift_pressed(emscripten_event_ptr->shiftKey); - godot_event->set_alt_pressed(emscripten_event_ptr->altKey); - godot_event->set_ctrl_pressed(emscripten_event_ptr->ctrlKey); - godot_event->set_meta_pressed(emscripten_event_ptr->metaKey); +void DisplayServerJavaScript::dom2godot_mod(Ref<InputEventWithModifiers> ev, int p_mod) { + ev->set_shift_pressed(p_mod & 1); + ev->set_alt_pressed(p_mod & 2); + ev->set_ctrl_pressed(p_mod & 4); + ev->set_meta_pressed(p_mod & 8); } -Ref<InputEventKey> DisplayServerJavaScript::setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) { +void DisplayServerJavaScript::key_callback(int p_pressed, int p_repeat, int p_modifiers) { + DisplayServerJavaScript *ds = get_singleton(); + JSKeyEvent &key_event = ds->key_event; + // Resume audio context after input in case autoplay was denied. + OS_JavaScript::get_singleton()->resume_audio(); + Ref<InputEventKey> ev; ev.instantiate(); - ev->set_echo(emscripten_event->repeat); - dom2godot_mod(emscripten_event, ev); - ev->set_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, false)); - ev->set_physical_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, true)); - - String unicode = String::utf8(emscripten_event->key); - // Check if empty or multi-character (e.g. `CapsLock`). - if (unicode.length() != 1) { - // Might be empty as well, but better than nonsense. - unicode = String::utf8(emscripten_event->charValue); - } + ev->set_echo(p_repeat); + ev->set_keycode(dom_code2godot_scancode(key_event.code, key_event.key, false)); + ev->set_physical_keycode(dom_code2godot_scancode(key_event.code, key_event.key, true)); + ev->set_pressed(p_pressed); + dom2godot_mod(ev, p_modifiers); + + String unicode = String::utf8(key_event.key); if (unicode.length() == 1) { ev->set_unicode(unicode[0]); } - - return ev; -} - -EM_BOOL DisplayServerJavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *display = get_singleton(); - Ref<InputEventKey> ev = setup_key_event(p_event); - ev->set_pressed(true); - if (ev->get_unicode() == 0 && keycode_has_unicode(ev->get_keycode())) { - // Defer to keypress event for legacy unicode retrieval. - display->deferred_key_event = ev; - // Do not suppress keypress event. - return false; - } Input::get_singleton()->parse_input_event(ev); // Make sure to flush all events so we can call restricted APIs inside the event. Input::get_singleton()->flush_buffered_events(); - - return true; -} - -EM_BOOL DisplayServerJavaScript::keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *display = get_singleton(); - display->deferred_key_event->set_unicode(p_event->charCode); - Input::get_singleton()->parse_input_event(display->deferred_key_event); - - // Make sure to flush all events so we can call restricted APIs inside the event. - Input::get_singleton()->flush_buffered_events(); - - return true; -} - -EM_BOOL DisplayServerJavaScript::keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - Ref<InputEventKey> ev = setup_key_event(p_event); - ev->set_pressed(false); - Input::get_singleton()->parse_input_event(ev); - - // Make sure to flush all events so we can call restricted APIs inside the event. - Input::get_singleton()->flush_buffered_events(); - - return ev->get_keycode() != KEY_UNKNOWN && ev->get_keycode() != (Key)0; } // Mouse -EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *display = get_singleton(); +int DisplayServerJavaScript::mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers) { + DisplayServerJavaScript *ds = get_singleton(); Ref<InputEventMouseButton> ev; ev.instantiate(); - ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN); - ev->set_position(compute_position_in_canvas(p_event->clientX, p_event->clientY)); + ev->set_pressed(p_pressed); + ev->set_position(Point2(p_x, p_y)); ev->set_global_position(ev->get_position()); - dom2godot_mod(p_event, ev); + ev->set_pressed(p_pressed); + dom2godot_mod(ev, p_modifiers); - switch (p_event->button) { + switch (p_button) { case DOM_BUTTON_LEFT: ev->set_button_index(MOUSE_BUTTON_LEFT); break; @@ -219,34 +169,30 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E return false; } - if (ev->is_pressed()) { - double diff = emscripten_get_now() - display->last_click_ms; + if (p_pressed) { + uint64_t diff = (OS::get_singleton()->get_ticks_usec() / 1000) - ds->last_click_ms; - if (ev->get_button_index() == display->last_click_button_index) { - if (diff < 400 && Point2(display->last_click_pos).distance_to(ev->get_position()) < 5) { - display->last_click_ms = 0; - display->last_click_pos = Point2(-100, -100); - display->last_click_button_index = -1; + if (ev->get_button_index() == ds->last_click_button_index) { + if (diff < 400 && Point2(ds->last_click_pos).distance_to(ev->get_position()) < 5) { + ds->last_click_ms = 0; + ds->last_click_pos = Point2(-100, -100); + ds->last_click_button_index = -1; ev->set_double_click(true); } } else { - display->last_click_button_index = ev->get_button_index(); + ds->last_click_button_index = ev->get_button_index(); } if (!ev->is_double_click()) { - display->last_click_ms += diff; - display->last_click_pos = ev->get_position(); + ds->last_click_ms += diff; + ds->last_click_pos = ev->get_position(); } } - Input *input = Input::get_singleton(); - int mask = input->get_mouse_button_mask(); - MouseButton button_flag = MouseButton(1 << (ev->get_button_index() - 1)); + int mask = Input::get_singleton()->get_mouse_button_mask(); + int button_flag = 1 << (ev->get_button_index() - 1); if (ev->is_pressed()) { - // Since the event is consumed, focus manually. The containing iframe, - // if exists, may not have focus yet, so focus even if already focused. - focus_canvas(); mask |= button_flag; } else if (mask & button_flag) { mask &= ~button_flag; @@ -256,7 +202,9 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E } ev->set_button_mask(mask); - input->parse_input_event(ev); + Input::get_singleton()->parse_input_event(ev); + // Resume audio context after input in case autoplay was denied. + OS_JavaScript::get_singleton()->resume_audio(); // Make sure to flush all events so we can call restricted APIs inside the event. Input::get_singleton()->flush_buffered_events(); @@ -266,31 +214,26 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E return true; } -EM_BOOL DisplayServerJavaScript::mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *ds = get_singleton(); - Input *input = Input::get_singleton(); - int input_mask = input->get_mouse_button_mask(); - Point2 pos = compute_position_in_canvas(p_event->clientX, p_event->clientY); +void DisplayServerJavaScript::mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) { + int input_mask = Input::get_singleton()->get_mouse_button_mask(); // For motion outside the canvas, only read mouse movement if dragging // started inside the canvas; imitating desktop app behaviour. - if (!ds->cursor_inside_canvas && !input_mask) - return false; + if (!get_singleton()->cursor_inside_canvas && !input_mask) + return; Ref<InputEventMouseMotion> ev; ev.instantiate(); - dom2godot_mod(p_event, ev); + dom2godot_mod(ev, p_modifiers); ev->set_button_mask(input_mask); - ev->set_position(pos); + ev->set_position(Point2(p_x, p_y)); ev->set_global_position(ev->get_position()); - ev->set_relative(Vector2(p_event->movementX, p_event->movementY)); - input->set_mouse_position(ev->get_position()); - ev->set_speed(input->get_last_mouse_speed()); + ev->set_relative(Vector2(p_rel_x, p_rel_y)); + Input::get_singleton()->set_mouse_position(ev->get_position()); + ev->set_speed(Input::get_singleton()->get_last_mouse_speed()); - input->parse_input_event(ev); - // Don't suppress mouseover/-leave events. - return false; + Input::get_singleton()->parse_input_event(ev); } // Cursor @@ -430,17 +373,15 @@ void DisplayServerJavaScript::mouse_set_mode(MouseMode p_mode) { if (p_mode == MOUSE_MODE_VISIBLE) { godot_js_display_cursor_set_visible(1); - emscripten_exit_pointerlock(); + godot_js_display_cursor_lock_set(0); } else if (p_mode == MOUSE_MODE_HIDDEN) { godot_js_display_cursor_set_visible(0); - emscripten_exit_pointerlock(); + godot_js_display_cursor_lock_set(0); } else if (p_mode == MOUSE_MODE_CAPTURED) { godot_js_display_cursor_set_visible(1); - EMSCRIPTEN_RESULT result = emscripten_request_pointerlock(canvas_id, false); - ERR_FAIL_COND_MSG(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED, "MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback."); - ERR_FAIL_COND_MSG(result != EMSCRIPTEN_RESULT_SUCCESS, "MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback."); + godot_js_display_cursor_lock_set(1); } } @@ -449,18 +390,17 @@ DisplayServer::MouseMode DisplayServerJavaScript::mouse_get_mode() const { return MOUSE_MODE_HIDDEN; } - EmscriptenPointerlockChangeEvent ev; - emscripten_get_pointerlock_status(&ev); - return (ev.isActive && String::utf8(ev.id) == String::utf8(&canvas_id[1])) ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE; + if (godot_js_display_cursor_is_locked()) { + return MOUSE_MODE_CAPTURED; + } + return MOUSE_MODE_VISIBLE; } // Wheel -EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data) { - ERR_FAIL_COND_V(p_event_type != EMSCRIPTEN_EVENT_WHEEL, false); - DisplayServerJavaScript *ds = get_singleton(); - if (!is_canvas_focused()) { - if (ds->cursor_inside_canvas) { - focus_canvas(); +int DisplayServerJavaScript::mouse_wheel_callback(double p_delta_x, double p_delta_y) { + if (!godot_js_display_canvas_is_focused()) { + if (get_singleton()->cursor_inside_canvas) { + godot_js_display_canvas_focus(); } else { return false; } @@ -477,16 +417,17 @@ EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const Emscript ev->set_ctrl_pressed(input->is_key_pressed(KEY_CTRL)); ev->set_meta_pressed(input->is_key_pressed(KEY_META)); - if (p_event->deltaY < 0) + if (p_delta_y < 0) { ev->set_button_index(MOUSE_BUTTON_WHEEL_UP); - else if (p_event->deltaY > 0) + } else if (p_delta_y > 0) { ev->set_button_index(MOUSE_BUTTON_WHEEL_DOWN); - else if (p_event->deltaX > 0) + } else if (p_delta_x > 0) { ev->set_button_index(MOUSE_BUTTON_WHEEL_LEFT); - else if (p_event->deltaX < 0) + } else if (p_delta_x < 0) { ev->set_button_index(MOUSE_BUTTON_WHEEL_RIGHT); - else + } else { return false; + } // Different browsers give wildly different delta values, and we can't // interpret deltaMode, so use default value for wheel events' factor. @@ -494,7 +435,7 @@ EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const Emscript int button_flag = 1 << (ev->get_button_index() - 1); ev->set_pressed(true); - ev->set_button_mask(MouseButton(input->get_mouse_button_mask() | button_flag)); + ev->set_button_mask(input->get_mouse_button_mask() | button_flag); input->parse_input_event(ev); Ref<InputEventMouseButton> release = ev->duplicate(); @@ -506,52 +447,43 @@ EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const Emscript } // Touch -EM_BOOL DisplayServerJavaScript::touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *display = get_singleton(); - Ref<InputEventScreenTouch> ev; - int lowest_id_index = -1; - for (int i = 0; i < p_event->numTouches; ++i) { - const EmscriptenTouchPoint &touch = p_event->touches[i]; - if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier) - lowest_id_index = i; - if (!touch.isChanged) - continue; - ev.instantiate(); - ev->set_index(touch.identifier); - ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY)); - display->touches[i] = ev->get_position(); - ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART); +void DisplayServerJavaScript::touch_callback(int p_type, int p_count) { + DisplayServerJavaScript *ds = get_singleton(); - Input::get_singleton()->parse_input_event(ev); - } + const JSTouchEvent &touch_event = ds->touch_event; + for (int i = 0; i < p_count; i++) { + Point2 point(touch_event.coords[i * 2], touch_event.coords[i * 2 + 1]); + if (p_type == 2) { + // touchmove + Ref<InputEventScreenDrag> ev; + ev.instantiate(); + ev->set_index(touch_event.identifier[i]); + ev->set_position(point); + + Point2 &prev = ds->touches[i]; + ev->set_relative(ev->get_position() - prev); + prev = ev->get_position(); + + Input::get_singleton()->parse_input_event(ev); + } else { + // touchstart/touchend + Ref<InputEventScreenTouch> ev; - // Make sure to flush all events so we can call restricted APIs inside the event. - Input::get_singleton()->flush_buffered_events(); + // Resume audio context after input in case autoplay was denied. + OS_JavaScript::get_singleton()->resume_audio(); - // Resume audio context after input in case autoplay was denied. - return true; -} + ev.instantiate(); + ev->set_index(touch_event.identifier[i]); + ev->set_position(point); + ev->set_pressed(p_type == 0); + ds->touches[i] = point; -EM_BOOL DisplayServerJavaScript::touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *display = get_singleton(); - Ref<InputEventScreenDrag> ev; - int lowest_id_index = -1; - for (int i = 0; i < p_event->numTouches; ++i) { - const EmscriptenTouchPoint &touch = p_event->touches[i]; - if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier) - lowest_id_index = i; - if (!touch.isChanged) - continue; - ev.instantiate(); - ev->set_index(touch.identifier); - ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY)); - Point2 &prev = display->touches[i]; - ev->set_relative(ev->get_position() - prev); - prev = ev->get_position(); - - Input::get_singleton()->parse_input_event(ev); + Input::get_singleton()->parse_input_event(ev); + + // Make sure to flush all events so we can call restricted APIs inside the event. + Input::get_singleton()->flush_buffered_events(); + } } - return true; } bool DisplayServerJavaScript::screen_is_touchscreen(int p_screen) const { @@ -595,10 +527,8 @@ void DisplayServerJavaScript::virtual_keyboard_hide() { godot_js_display_vk_hide(); } -// Window blur callback -EM_BOOL DisplayServerJavaScript::blur_callback(int p_event_type, const EmscriptenFocusEvent *p_event, void *p_user_data) { +void DisplayServerJavaScript::window_blur_callback() { Input::get_singleton()->release_pressed_events(); - return false; } // Gamepad @@ -613,14 +543,14 @@ void DisplayServerJavaScript::gamepad_callback(int p_index, int p_connected, con void DisplayServerJavaScript::process_joypads() { Input *input = Input::get_singleton(); - int32_t pads = godot_js_display_gamepad_sample_count(); + int32_t pads = godot_js_input_gamepad_sample_count(); int32_t s_btns_num = 0; int32_t s_axes_num = 0; int32_t s_standard = 0; float s_btns[16]; float s_axes[10]; for (int idx = 0; idx < pads; idx++) { - int err = godot_js_display_gamepad_sample_get(idx, s_btns, &s_btns_num, s_axes, &s_axes_num, &s_standard); + int err = godot_js_input_gamepad_sample_get(idx, s_btns, &s_btns_num, s_axes, &s_axes_num, &s_standard); if (err) { continue; } @@ -718,11 +648,6 @@ void DisplayServerJavaScript::set_icon(const Ref<Image> &p_icon) { } void DisplayServerJavaScript::_dispatch_input_event(const Ref<InputEvent> &p_event) { - OS_JavaScript *os = OS_JavaScript::get_singleton(); - - // Resume audio context after input in case autoplay was denied. - os->resume_audio(); - Callable cb = get_singleton()->input_event_callback; if (!cb.is_null()) { Variant ev = p_event; @@ -788,44 +713,24 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive video_driver_index = p_video_driver; #endif - EMSCRIPTEN_RESULT result; -#define EM_CHECK(ev) \ - if (result != EMSCRIPTEN_RESULT_SUCCESS) \ - ERR_PRINT("Error while setting " #ev " callback: Code " + itos(result)); -#define SET_EM_CALLBACK(target, ev, cb) \ - result = emscripten_set_##ev##_callback(target, nullptr, true, &cb); \ - EM_CHECK(ev) -#define SET_EM_WINDOW_CALLBACK(ev, cb) \ - result = emscripten_set_##ev##_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, &cb); \ - EM_CHECK(ev) - // These callbacks from Emscripten's html5.h suffice to access most - // JavaScript APIs. - SET_EM_CALLBACK(canvas_id, mousedown, mouse_button_callback) - SET_EM_WINDOW_CALLBACK(mousemove, mousemove_callback) - SET_EM_WINDOW_CALLBACK(mouseup, mouse_button_callback) - SET_EM_WINDOW_CALLBACK(blur, blur_callback) - SET_EM_CALLBACK(canvas_id, wheel, wheel_callback) - SET_EM_CALLBACK(canvas_id, touchstart, touch_press_callback) - SET_EM_CALLBACK(canvas_id, touchmove, touchmove_callback) - SET_EM_CALLBACK(canvas_id, touchend, touch_press_callback) - SET_EM_CALLBACK(canvas_id, touchcancel, touch_press_callback) - SET_EM_CALLBACK(canvas_id, keydown, keydown_callback) - SET_EM_CALLBACK(canvas_id, keypress, keypress_callback) - SET_EM_CALLBACK(canvas_id, keyup, keyup_callback) - SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, fullscreenchange, fullscreen_change_callback) -#undef SET_EM_CALLBACK -#undef EM_CHECK - - // For APIs that are not (sufficiently) exposed, a - // library is used below (implemented in library_godot_display.js). + // JS Input interface (js/libs/library_godot_input.js) + godot_js_input_mouse_button_cb(&DisplayServerJavaScript::mouse_button_callback); + godot_js_input_mouse_move_cb(&DisplayServerJavaScript::mouse_move_callback); + godot_js_input_mouse_wheel_cb(&DisplayServerJavaScript::mouse_wheel_callback); + godot_js_input_touch_cb(&DisplayServerJavaScript::touch_callback, touch_event.identifier, touch_event.coords); + godot_js_input_key_cb(&DisplayServerJavaScript::key_callback, key_event.code, key_event.key); + godot_js_input_paste_cb(update_clipboard_callback); + godot_js_input_drop_files_cb(drop_files_js_callback); + godot_js_input_gamepad_cb(&DisplayServerJavaScript::gamepad_callback); + + // JS Display interface (js/libs/library_godot_display.js) + godot_js_display_fullscreen_cb(&DisplayServerJavaScript::fullscreen_change_callback); + godot_js_display_window_blur_cb(&window_blur_callback); godot_js_display_notification_cb(&send_window_event_callback, WINDOW_EVENT_MOUSE_ENTER, WINDOW_EVENT_MOUSE_EXIT, WINDOW_EVENT_FOCUS_IN, WINDOW_EVENT_FOCUS_OUT); - godot_js_display_paste_cb(update_clipboard_callback); - godot_js_display_drop_files_cb(drop_files_js_callback); - godot_js_display_gamepad_cb(&DisplayServerJavaScript::gamepad_callback); godot_js_display_vk_cb(&vk_input_text_callback); Input::get_singleton()->set_event_dispatch_function(_dispatch_input_event); @@ -1048,7 +953,7 @@ bool DisplayServerJavaScript::can_any_window_draw() const { void DisplayServerJavaScript::process_events() { Input::get_singleton()->flush_buffered_events(); - if (godot_js_display_gamepad_sample() == OK) { + if (godot_js_input_gamepad_sample() == OK) { process_joypads(); } } diff --git a/platform/javascript/display_server_javascript.h b/platform/javascript/display_server_javascript.h index 1863ddefeb..1aa600c421 100644 --- a/platform/javascript/display_server_javascript.h +++ b/platform/javascript/display_server_javascript.h @@ -38,6 +38,19 @@ class DisplayServerJavaScript : public DisplayServer { private: + struct JSTouchEvent { + uint32_t identifier[32] = { 0 }; + double coords[64] = { 0 }; + }; + JSTouchEvent touch_event; + + struct JSKeyEvent { + char code[32] = { 0 }; + char key[32] = { 0 }; + uint8_t modifiers[4] = { 0 }; + }; + JSKeyEvent key_event; + WindowMode window_mode = WINDOW_MODE_WINDOWED; ObjectID window_attached_instance_id = {}; @@ -47,46 +60,31 @@ private: Callable drop_files_callback; String clipboard; - Ref<InputEventKey> deferred_key_event; Point2 touches[32]; char canvas_id[256] = { 0 }; bool cursor_inside_canvas = true; CursorShape cursor_shape = CURSOR_ARROW; Point2i last_click_pos = Point2(-100, -100); // TODO check this again. - double last_click_ms = 0; + uint64_t last_click_ms = 0; int last_click_button_index = -1; bool swap_cancel_ok = false; // utilities - static Point2 compute_position_in_canvas(int p_x, int p_y); static void focus_canvas(); static bool is_canvas_focused(); - template <typename T> - static void dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event); - static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscripten_event); + static void dom2godot_mod(Ref<InputEventWithModifiers> ev, int p_mod); static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape); // events - static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data); - - static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); - static EM_BOOL keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); - static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); - + static void fullscreen_change_callback(int p_fullscreen); + static int mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers); + static void mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers); + static int mouse_wheel_callback(double p_delta_x, double p_delta_y); + static void touch_callback(int p_type, int p_count); + static void key_callback(int p_pressed, int p_repeat, int p_modifiers); static void vk_input_text_callback(const char *p_text, int p_cursor); - - static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data); - static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data); - - static EM_BOOL wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data); - - static EM_BOOL touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); - static EM_BOOL touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); - - static EM_BOOL blur_callback(int p_event_type, const EmscriptenFocusEvent *p_event, void *p_user_data); - static void gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid); void process_joypads(); @@ -96,6 +94,7 @@ private: static void _dispatch_input_event(const Ref<InputEvent> &p_event); static void request_quit_callback(); + static void window_blur_callback(); static void update_clipboard_callback(const char *p_text); static void send_window_event_callback(int p_notification); static void drop_files_js_callback(char **p_filev, int p_filec); diff --git a/platform/javascript/godot_js.h b/platform/javascript/godot_js.h index d332af2c31..8051b270e6 100644 --- a/platform/javascript/godot_js.h +++ b/platform/javascript/godot_js.h @@ -50,12 +50,28 @@ extern int godot_js_os_execute(const char *p_json); extern void godot_js_os_shell_open(const char *p_uri); extern int godot_js_os_hw_concurrency_get(); +// Input +extern void godot_js_input_mouse_button_cb(int (*p_callback)(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers)); +extern void godot_js_input_mouse_move_cb(void (*p_callback)(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers)); +extern void godot_js_input_mouse_wheel_cb(int (*p_callback)(double p_delta_x, double p_delta_y)); +extern void godot_js_input_touch_cb(void (*p_callback)(int p_type, int p_count), uint32_t *r_identifiers, double *r_coords); +extern void godot_js_input_key_cb(void (*p_callback)(int p_type, int p_repeat, int p_modifiers), char r_code[32], char r_key[32]); + +// Input gamepad +extern void godot_js_input_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid)); +extern int godot_js_input_gamepad_sample(); +extern int godot_js_input_gamepad_sample_count(); +extern int godot_js_input_gamepad_sample_get(int p_idx, float r_btns[16], int32_t *r_btns_num, float r_axes[10], int32_t *r_axes_num, int32_t *r_standard); +extern void godot_js_input_paste_cb(void (*p_callback)(const char *p_text)); +extern void godot_js_input_drop_files_cb(void (*p_callback)(char **p_filev, int p_filec)); + // Display extern int godot_js_display_screen_dpi_get(); extern double godot_js_display_pixel_ratio_get(); extern void godot_js_display_alert(const char *p_text); extern int godot_js_display_touchscreen_is_available(); extern int godot_js_display_is_swap_ok_cancel(); +extern void godot_js_display_setup_canvas(int p_width, int p_height, int p_fullscreen, int p_hidpi); // Display canvas extern void godot_js_display_canvas_focus(); @@ -68,7 +84,6 @@ extern void godot_js_display_window_size_get(int32_t *p_x, int32_t *p_y); extern void godot_js_display_screen_size_get(int32_t *p_x, int32_t *p_y); extern int godot_js_display_fullscreen_request(); extern int godot_js_display_fullscreen_exit(); -extern void godot_js_display_compute_position(int p_x, int p_y, int32_t *r_x, int32_t *r_y); extern void godot_js_display_window_title_set(const char *p_text); extern void godot_js_display_window_icon_set(const uint8_t *p_ptr, int p_len); extern int godot_js_display_has_webgl(int p_version); @@ -82,18 +97,13 @@ extern void godot_js_display_cursor_set_shape(const char *p_cursor); extern int godot_js_display_cursor_is_hidden(); extern void godot_js_display_cursor_set_custom_shape(const char *p_shape, const uint8_t *p_ptr, int p_len, int p_hotspot_x, int p_hotspot_y); extern void godot_js_display_cursor_set_visible(int p_visible); - -// Display gamepad -extern void godot_js_display_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid)); -extern int godot_js_display_gamepad_sample(); -extern int godot_js_display_gamepad_sample_count(); -extern int godot_js_display_gamepad_sample_get(int p_idx, float r_btns[16], int32_t *r_btns_num, float r_axes[10], int32_t *r_axes_num, int32_t *r_standard); +extern void godot_js_display_cursor_lock_set(int p_lock); +extern int godot_js_display_cursor_is_locked(); // Display listeners +extern void godot_js_display_fullscreen_cb(void (*p_callback)(int p_fullscreen)); +extern void godot_js_display_window_blur_cb(void (*p_callback)()); extern void godot_js_display_notification_cb(void (*p_callback)(int p_notification), int p_enter, int p_exit, int p_in, int p_out); -extern void godot_js_display_paste_cb(void (*p_callback)(const char *p_text)); -extern void godot_js_display_drop_files_cb(void (*p_callback)(char **p_filev, int p_filec)); -extern void godot_js_display_setup_canvas(int p_width, int p_height, int p_fullscreen, int p_hidpi); // Display Virtual Keyboard extern int godot_js_display_vk_available(); diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js index affae90370..2689f1c22c 100644 --- a/platform/javascript/js/libs/library_godot_display.js +++ b/platform/javascript/js/libs/library_godot_display.js @@ -28,224 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -/* - * Display Server listeners. - * Keeps track of registered event listeners so it can remove them on shutdown. - */ -const GodotDisplayListeners = { - $GodotDisplayListeners__deps: ['$GodotOS'], - $GodotDisplayListeners__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayListeners.clear(); resolve(); });', - $GodotDisplayListeners: { - handlers: [], - - has: function (target, event, method, capture) { - return GodotDisplayListeners.handlers.findIndex(function (e) { - return e.target === target && e.event === event && e.method === method && e.capture === capture; - }) !== -1; - }, - - add: function (target, event, method, capture) { - if (GodotDisplayListeners.has(target, event, method, capture)) { - return; - } - function Handler(p_target, p_event, p_method, p_capture) { - this.target = p_target; - this.event = p_event; - this.method = p_method; - this.capture = p_capture; - } - GodotDisplayListeners.handlers.push(new Handler(target, event, method, capture)); - target.addEventListener(event, method, capture); - }, - - clear: function () { - GodotDisplayListeners.handlers.forEach(function (h) { - h.target.removeEventListener(h.event, h.method, h.capture); - }); - GodotDisplayListeners.handlers.length = 0; - }, - }, -}; -mergeInto(LibraryManager.library, GodotDisplayListeners); - -/* - * Drag and drop handler. - * This is pretty big, but basically detect dropped files on GodotConfig.canvas, - * process them one by one (recursively for directories), and copies them to - * the temporary FS path '/tmp/drop-[random]/' so it can be emitted as a godot - * event (that requires a string array of paths). - * - * NOTE: The temporary files are removed after the callback. This means that - * deferred callbacks won't be able to access the files. - */ -const GodotDisplayDragDrop = { - $GodotDisplayDragDrop__deps: ['$FS', '$GodotFS'], - $GodotDisplayDragDrop: { - promises: [], - pending_files: [], - - add_entry: function (entry) { - if (entry.isDirectory) { - GodotDisplayDragDrop.add_dir(entry); - } else if (entry.isFile) { - GodotDisplayDragDrop.add_file(entry); - } else { - GodotRuntime.error('Unrecognized entry...', entry); - } - }, - - add_dir: function (entry) { - GodotDisplayDragDrop.promises.push(new Promise(function (resolve, reject) { - const reader = entry.createReader(); - reader.readEntries(function (entries) { - for (let i = 0; i < entries.length; i++) { - GodotDisplayDragDrop.add_entry(entries[i]); - } - resolve(); - }); - })); - }, - - add_file: function (entry) { - GodotDisplayDragDrop.promises.push(new Promise(function (resolve, reject) { - entry.file(function (file) { - const reader = new FileReader(); - reader.onload = function () { - const f = { - 'path': file.relativePath || file.webkitRelativePath, - 'name': file.name, - 'type': file.type, - 'size': file.size, - 'data': reader.result, - }; - if (!f['path']) { - f['path'] = f['name']; - } - GodotDisplayDragDrop.pending_files.push(f); - resolve(); - }; - reader.onerror = function () { - GodotRuntime.print('Error reading file'); - reject(); - }; - reader.readAsArrayBuffer(file); - }, function (err) { - GodotRuntime.print('Error!'); - reject(); - }); - })); - }, - - process: function (resolve, reject) { - if (GodotDisplayDragDrop.promises.length === 0) { - resolve(); - return; - } - GodotDisplayDragDrop.promises.pop().then(function () { - setTimeout(function () { - GodotDisplayDragDrop.process(resolve, reject); - }, 0); - }); - }, - - _process_event: function (ev, callback) { - ev.preventDefault(); - if (ev.dataTransfer.items) { - // Use DataTransferItemList interface to access the file(s) - for (let i = 0; i < ev.dataTransfer.items.length; i++) { - const item = ev.dataTransfer.items[i]; - let entry = null; - if ('getAsEntry' in item) { - entry = item.getAsEntry(); - } else if ('webkitGetAsEntry' in item) { - entry = item.webkitGetAsEntry(); - } - if (entry) { - GodotDisplayDragDrop.add_entry(entry); - } - } - } else { - GodotRuntime.error('File upload not supported'); - } - new Promise(GodotDisplayDragDrop.process).then(function () { - const DROP = `/tmp/drop-${parseInt(Math.random() * (1 << 30), 10)}/`; - const drops = []; - const files = []; - FS.mkdir(DROP); - GodotDisplayDragDrop.pending_files.forEach((elem) => { - const path = elem['path']; - GodotFS.copy_to_fs(DROP + path, elem['data']); - let idx = path.indexOf('/'); - if (idx === -1) { - // Root file - drops.push(DROP + path); - } else { - // Subdir - const sub = path.substr(0, idx); - idx = sub.indexOf('/'); - if (idx < 0 && drops.indexOf(DROP + sub) === -1) { - drops.push(DROP + sub); - } - } - files.push(DROP + path); - }); - GodotDisplayDragDrop.promises = []; - GodotDisplayDragDrop.pending_files = []; - callback(drops); - if (GodotConfig.persistent_drops) { - // Delay removal at exit. - GodotOS.atexit(function (resolve, reject) { - GodotDisplayDragDrop.remove_drop(files, DROP); - resolve(); - }); - } else { - GodotDisplayDragDrop.remove_drop(files, DROP); - } - }); - }, - - remove_drop: function (files, drop_path) { - const dirs = [drop_path.substr(0, drop_path.length - 1)]; - // Remove temporary files - files.forEach(function (file) { - FS.unlink(file); - let dir = file.replace(drop_path, ''); - let idx = dir.lastIndexOf('/'); - while (idx > 0) { - dir = dir.substr(0, idx); - if (dirs.indexOf(drop_path + dir) === -1) { - dirs.push(drop_path + dir); - } - idx = dir.lastIndexOf('/'); - } - }); - // Remove dirs. - dirs.sort(function (a, b) { - const al = (a.match(/\//g) || []).length; - const bl = (b.match(/\//g) || []).length; - if (al > bl) { - return -1; - } else if (al < bl) { - return 1; - } - return 0; - }).forEach(function (dir) { - FS.rmdir(dir); - }); - }, - - handler: function (callback) { - return function (ev) { - GodotDisplayDragDrop._process_event(ev, callback); - }; - }, - }, -}; -mergeInto(LibraryManager.library, GodotDisplayDragDrop); - const GodotDisplayVK = { - $GodotDisplayVK__deps: ['$GodotRuntime', '$GodotConfig', '$GodotDisplayListeners'], + $GodotDisplayVK__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners'], $GodotDisplayVK__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayVK.clear(); resolve(); });', $GodotDisplayVK: { textinput: null, @@ -271,12 +56,12 @@ const GodotDisplayVK = { elem.style.outline = 'none'; elem.readonly = true; elem.disabled = true; - GodotDisplayListeners.add(elem, 'input', function (evt) { + GodotEventListeners.add(elem, 'input', function (evt) { const c_str = GodotRuntime.allocString(elem.value); input_cb(c_str, elem.selectionEnd); GodotRuntime.free(c_str); }, false); - GodotDisplayListeners.add(elem, 'blur', function (evt) { + GodotEventListeners.add(elem, 'blur', function (evt) { elem.style.display = 'none'; elem.readonly = true; elem.disabled = true; @@ -376,136 +161,23 @@ const GodotDisplayCursor = { delete GodotDisplayCursor.cursors[key]; }); }, - }, -}; -mergeInto(LibraryManager.library, GodotDisplayCursor); - -/* - * Display Gamepad API helper. - */ -const GodotDisplayGamepads = { - $GodotDisplayGamepads__deps: ['$GodotRuntime', '$GodotDisplayListeners'], - $GodotDisplayGamepads: { - samples: [], - - get_pads: function () { - try { - // Will throw in iframe when permission is denied. - // Will throw/warn in the future for insecure contexts. - // See https://github.com/w3c/gamepad/pull/120 - const pads = navigator.getGamepads(); - if (pads) { - return pads; - } - return []; - } catch (e) { - return []; - } - }, - - get_samples: function () { - return GodotDisplayGamepads.samples; - }, - - get_sample: function (index) { - const samples = GodotDisplayGamepads.samples; - return index < samples.length ? samples[index] : null; - }, - - sample: function () { - const pads = GodotDisplayGamepads.get_pads(); - const samples = []; - for (let i = 0; i < pads.length; i++) { - const pad = pads[i]; - if (!pad) { - samples.push(null); - continue; - } - const s = { - standard: pad.mapping === 'standard', - buttons: [], - axes: [], - connected: pad.connected, - }; - for (let b = 0; b < pad.buttons.length; b++) { - s.buttons.push(pad.buttons[b].value); - } - for (let a = 0; a < pad.axes.length; a++) { - s.axes.push(pad.axes[a]); - } - samples.push(s); + lockPointer: function () { + const canvas = GodotConfig.canvas; + if (canvas.requestPointerLock) { + canvas.requestPointerLock(); } - GodotDisplayGamepads.samples = samples; }, - - init: function (onchange) { - GodotDisplayListeners.samples = []; - function add(pad) { - const guid = GodotDisplayGamepads.get_guid(pad); - const c_id = GodotRuntime.allocString(pad.id); - const c_guid = GodotRuntime.allocString(guid); - onchange(pad.index, 1, c_id, c_guid); - GodotRuntime.free(c_id); - GodotRuntime.free(c_guid); + releasePointer: function () { + if (document.exitPointerLock) { + document.exitPointerLock(); } - const pads = GodotDisplayGamepads.get_pads(); - for (let i = 0; i < pads.length; i++) { - // Might be reserved space. - if (pads[i]) { - add(pads[i]); - } - } - GodotDisplayListeners.add(window, 'gamepadconnected', function (evt) { - add(evt.gamepad); - }, false); - GodotDisplayListeners.add(window, 'gamepaddisconnected', function (evt) { - onchange(evt.gamepad.index, 0); - }, false); }, - - get_guid: function (pad) { - if (pad.mapping) { - return pad.mapping; - } - const ua = navigator.userAgent; - let os = 'Unknown'; - if (ua.indexOf('Android') >= 0) { - os = 'Android'; - } else if (ua.indexOf('Linux') >= 0) { - os = 'Linux'; - } else if (ua.indexOf('iPhone') >= 0) { - os = 'iOS'; - } else if (ua.indexOf('Macintosh') >= 0) { - // Updated iPads will fall into this category. - os = 'MacOSX'; - } else if (ua.indexOf('Windows') >= 0) { - os = 'Windows'; - } - - const id = pad.id; - // Chrom* style: NAME (Vendor: xxxx Product: xxxx) - const exp1 = /vendor: ([0-9a-f]{4}) product: ([0-9a-f]{4})/i; - // Firefox/Safari style (safari may remove leading zeores) - const exp2 = /^([0-9a-f]+)-([0-9a-f]+)-/i; - let vendor = ''; - let product = ''; - if (exp1.test(id)) { - const match = exp1.exec(id); - vendor = match[1].padStart(4, '0'); - product = match[2].padStart(4, '0'); - } else if (exp2.test(id)) { - const match = exp2.exec(id); - vendor = match[1].padStart(4, '0'); - product = match[2].padStart(4, '0'); - } - if (!vendor || !product) { - return `${os}Unknown`; - } - return os + vendor + product; + isPointerLocked: function () { + return document.pointerLockElement === GodotConfig.canvas; }, }, }; -mergeInto(LibraryManager.library, GodotDisplayGamepads); +mergeInto(LibraryManager.library, GodotDisplayCursor); const GodotDisplayScreen = { $GodotDisplayScreen__deps: ['$GodotConfig', '$GodotOS', '$GL', 'emscripten_webgl_get_current_context'], @@ -622,7 +294,7 @@ mergeInto(LibraryManager.library, GodotDisplayScreen); * Exposes all the functions needed by DisplayServer implementation. */ const GodotDisplay = { - $GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotDisplayListeners', '$GodotDisplayDragDrop', '$GodotDisplayGamepads', '$GodotDisplayScreen', '$GodotDisplayVK'], + $GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotEventListeners', '$GodotDisplayScreen', '$GodotDisplayVK'], $GodotDisplay: { window_icon: '', findDPI: function () { @@ -710,15 +382,6 @@ const GodotDisplay = { GodotRuntime.setHeapValue(p_height, GodotConfig.canvas.height, 'i32'); }, - godot_js_display_compute_position: function (x, y, r_x, r_y) { - const canvas = GodotConfig.canvas; - const rect = canvas.getBoundingClientRect(); - const rw = canvas.width / rect.width; - const rh = canvas.height / rect.height; - GodotRuntime.setHeapValue(r_x, (x - rect.x) * rw, 'i32'); - GodotRuntime.setHeapValue(r_y, (y - rect.y) * rh, 'i32'); - }, - godot_js_display_has_webgl__sig: 'ii', godot_js_display_has_webgl: function (p_version) { if (p_version !== 1 && p_version !== 2) { @@ -859,60 +522,64 @@ const GodotDisplay = { } }, + godot_js_display_cursor_lock_set__sig: 'vi', + godot_js_display_cursor_lock_set: function (p_lock) { + if (p_lock) { + GodotDisplayCursor.lockPointer(); + } else { + GodotDisplayCursor.releasePointer(); + } + }, + + godot_js_display_cursor_is_locked__sig: 'i', + godot_js_display_cursor_is_locked: function () { + return GodotDisplayCursor.isPointerLocked() ? 1 : 0; + }, + /* * Listeners */ - godot_js_display_notification_cb__sig: 'viiiii', - godot_js_display_notification_cb: function (callback, p_enter, p_exit, p_in, p_out) { + godot_js_display_fullscreen_cb__sig: 'vi', + godot_js_display_fullscreen_cb: function (callback) { const canvas = GodotConfig.canvas; const func = GodotRuntime.get_func(callback); - const notif = [p_enter, p_exit, p_in, p_out]; - ['mouseover', 'mouseleave', 'focus', 'blur'].forEach(function (evt_name, idx) { - GodotDisplayListeners.add(canvas, evt_name, function () { - func(notif[idx]); - }, true); - }); + function change_cb(evt) { + if (evt.target === canvas) { + func(GodotDisplayScreen.isFullscreen()); + } + } + GodotEventListeners.add(document, 'fullscreenchange', change_cb, false); + GodotEventListeners.add(document, 'mozfullscreenchange', change_cb, false); + GodotEventListeners.add(document, 'webkitfullscreenchange', change_cb, false); }, - godot_js_display_paste_cb__sig: 'vi', - godot_js_display_paste_cb: function (callback) { + godot_js_display_window_blur_cb__sig: 'vi', + godot_js_display_window_blur_cb: function (callback) { const func = GodotRuntime.get_func(callback); - GodotDisplayListeners.add(window, 'paste', function (evt) { - const text = evt.clipboardData.getData('text'); - const ptr = GodotRuntime.allocString(text); - func(ptr); - GodotRuntime.free(ptr); + GodotEventListeners.add(window, 'blur', function () { + func(); }, false); }, - godot_js_display_drop_files_cb__sig: 'vi', - godot_js_display_drop_files_cb: function (callback) { - const func = GodotRuntime.get_func(callback); - const dropFiles = function (files) { - const args = files || []; - if (!args.length) { - return; - } - const argc = args.length; - const argv = GodotRuntime.allocStringArray(args); - func(argv, argc); - GodotRuntime.freeStringArray(argv, argc); - }; + godot_js_display_notification_cb__sig: 'viiiii', + godot_js_display_notification_cb: function (callback, p_enter, p_exit, p_in, p_out) { const canvas = GodotConfig.canvas; - GodotDisplayListeners.add(canvas, 'dragover', function (ev) { - // Prevent default behavior (which would try to open the file(s)) - ev.preventDefault(); - }, false); - GodotDisplayListeners.add(canvas, 'drop', GodotDisplayDragDrop.handler(dropFiles)); + const func = GodotRuntime.get_func(callback); + const notif = [p_enter, p_exit, p_in, p_out]; + ['mouseover', 'mouseleave', 'focus', 'blur'].forEach(function (evt_name, idx) { + GodotEventListeners.add(canvas, evt_name, function () { + func(notif[idx]); + }, true); + }); }, godot_js_display_setup_canvas__sig: 'viiii', godot_js_display_setup_canvas: function (p_width, p_height, p_fullscreen, p_hidpi) { const canvas = GodotConfig.canvas; - GodotDisplayListeners.add(canvas, 'contextmenu', function (ev) { + GodotEventListeners.add(canvas, 'contextmenu', function (ev) { ev.preventDefault(); }, false); - GodotDisplayListeners.add(canvas, 'webglcontextlost', function (ev) { + GodotEventListeners.add(canvas, 'webglcontextlost', function (ev) { alert('WebGL context lost, please reload the page'); // eslint-disable-line no-alert ev.preventDefault(); }, false); @@ -965,49 +632,6 @@ const GodotDisplay = { GodotDisplayVK.init(input_cb); } }, - - /* - * Gamepads - */ - godot_js_display_gamepad_cb__sig: 'vi', - godot_js_display_gamepad_cb: function (change_cb) { - const onchange = GodotRuntime.get_func(change_cb); - GodotDisplayGamepads.init(onchange); - }, - - godot_js_display_gamepad_sample_count__sig: 'i', - godot_js_display_gamepad_sample_count: function () { - return GodotDisplayGamepads.get_samples().length; - }, - - godot_js_display_gamepad_sample__sig: 'i', - godot_js_display_gamepad_sample: function () { - GodotDisplayGamepads.sample(); - return 0; - }, - - godot_js_display_gamepad_sample_get__sig: 'iiiiiii', - godot_js_display_gamepad_sample_get: function (p_index, r_btns, r_btns_num, r_axes, r_axes_num, r_standard) { - const sample = GodotDisplayGamepads.get_sample(p_index); - if (!sample || !sample.connected) { - return 1; - } - const btns = sample.buttons; - const btns_len = btns.length < 16 ? btns.length : 16; - for (let i = 0; i < btns_len; i++) { - GodotRuntime.setHeapValue(r_btns + (i << 2), btns[i], 'float'); - } - GodotRuntime.setHeapValue(r_btns_num, btns_len, 'i32'); - const axes = sample.axes; - const axes_len = axes.length < 10 ? axes.length : 10; - for (let i = 0; i < axes_len; i++) { - GodotRuntime.setHeapValue(r_axes + (i << 2), axes[i], 'float'); - } - GodotRuntime.setHeapValue(r_axes_num, axes_len, 'i32'); - const is_standard = sample.standard ? 1 : 0; - GodotRuntime.setHeapValue(r_standard, is_standard, 'i32'); - return 0; - }, }; autoAddDeps(GodotDisplay, '$GodotDisplay'); diff --git a/platform/javascript/js/libs/library_godot_input.js b/platform/javascript/js/libs/library_godot_input.js new file mode 100644 index 0000000000..587c320f35 --- /dev/null +++ b/platform/javascript/js/libs/library_godot_input.js @@ -0,0 +1,526 @@ +/*************************************************************************/ +/* library_godot_input.js */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +/* + * Gamepad API helper. + */ +const GodotInputGamepads = { + $GodotInputGamepads__deps: ['$GodotRuntime', '$GodotEventListeners'], + $GodotInputGamepads: { + samples: [], + + get_pads: function () { + try { + // Will throw in iframe when permission is denied. + // Will throw/warn in the future for insecure contexts. + // See https://github.com/w3c/gamepad/pull/120 + const pads = navigator.getGamepads(); + if (pads) { + return pads; + } + return []; + } catch (e) { + return []; + } + }, + + get_samples: function () { + return GodotInputGamepads.samples; + }, + + get_sample: function (index) { + const samples = GodotInputGamepads.samples; + return index < samples.length ? samples[index] : null; + }, + + sample: function () { + const pads = GodotInputGamepads.get_pads(); + const samples = []; + for (let i = 0; i < pads.length; i++) { + const pad = pads[i]; + if (!pad) { + samples.push(null); + continue; + } + const s = { + standard: pad.mapping === 'standard', + buttons: [], + axes: [], + connected: pad.connected, + }; + for (let b = 0; b < pad.buttons.length; b++) { + s.buttons.push(pad.buttons[b].value); + } + for (let a = 0; a < pad.axes.length; a++) { + s.axes.push(pad.axes[a]); + } + samples.push(s); + } + GodotInputGamepads.samples = samples; + }, + + init: function (onchange) { + GodotEventListeners.samples = []; + function add(pad) { + const guid = GodotInputGamepads.get_guid(pad); + const c_id = GodotRuntime.allocString(pad.id); + const c_guid = GodotRuntime.allocString(guid); + onchange(pad.index, 1, c_id, c_guid); + GodotRuntime.free(c_id); + GodotRuntime.free(c_guid); + } + const pads = GodotInputGamepads.get_pads(); + for (let i = 0; i < pads.length; i++) { + // Might be reserved space. + if (pads[i]) { + add(pads[i]); + } + } + GodotEventListeners.add(window, 'gamepadconnected', function (evt) { + add(evt.gamepad); + }, false); + GodotEventListeners.add(window, 'gamepaddisconnected', function (evt) { + onchange(evt.gamepad.index, 0); + }, false); + }, + + get_guid: function (pad) { + if (pad.mapping) { + return pad.mapping; + } + const ua = navigator.userAgent; + let os = 'Unknown'; + if (ua.indexOf('Android') >= 0) { + os = 'Android'; + } else if (ua.indexOf('Linux') >= 0) { + os = 'Linux'; + } else if (ua.indexOf('iPhone') >= 0) { + os = 'iOS'; + } else if (ua.indexOf('Macintosh') >= 0) { + // Updated iPads will fall into this category. + os = 'MacOSX'; + } else if (ua.indexOf('Windows') >= 0) { + os = 'Windows'; + } + + const id = pad.id; + // Chrom* style: NAME (Vendor: xxxx Product: xxxx) + const exp1 = /vendor: ([0-9a-f]{4}) product: ([0-9a-f]{4})/i; + // Firefox/Safari style (safari may remove leading zeores) + const exp2 = /^([0-9a-f]+)-([0-9a-f]+)-/i; + let vendor = ''; + let product = ''; + if (exp1.test(id)) { + const match = exp1.exec(id); + vendor = match[1].padStart(4, '0'); + product = match[2].padStart(4, '0'); + } else if (exp2.test(id)) { + const match = exp2.exec(id); + vendor = match[1].padStart(4, '0'); + product = match[2].padStart(4, '0'); + } + if (!vendor || !product) { + return `${os}Unknown`; + } + return os + vendor + product; + }, + }, +}; +mergeInto(LibraryManager.library, GodotInputGamepads); + +/* + * Drag and drop helper. + * This is pretty big, but basically detect dropped files on GodotConfig.canvas, + * process them one by one (recursively for directories), and copies them to + * the temporary FS path '/tmp/drop-[random]/' so it can be emitted as a godot + * event (that requires a string array of paths). + * + * NOTE: The temporary files are removed after the callback. This means that + * deferred callbacks won't be able to access the files. + */ +const GodotInputDragDrop = { + $GodotInputDragDrop__deps: ['$FS', '$GodotFS'], + $GodotInputDragDrop: { + promises: [], + pending_files: [], + + add_entry: function (entry) { + if (entry.isDirectory) { + GodotInputDragDrop.add_dir(entry); + } else if (entry.isFile) { + GodotInputDragDrop.add_file(entry); + } else { + GodotRuntime.error('Unrecognized entry...', entry); + } + }, + + add_dir: function (entry) { + GodotInputDragDrop.promises.push(new Promise(function (resolve, reject) { + const reader = entry.createReader(); + reader.readEntries(function (entries) { + for (let i = 0; i < entries.length; i++) { + GodotInputDragDrop.add_entry(entries[i]); + } + resolve(); + }); + })); + }, + + add_file: function (entry) { + GodotInputDragDrop.promises.push(new Promise(function (resolve, reject) { + entry.file(function (file) { + const reader = new FileReader(); + reader.onload = function () { + const f = { + 'path': file.relativePath || file.webkitRelativePath, + 'name': file.name, + 'type': file.type, + 'size': file.size, + 'data': reader.result, + }; + if (!f['path']) { + f['path'] = f['name']; + } + GodotInputDragDrop.pending_files.push(f); + resolve(); + }; + reader.onerror = function () { + GodotRuntime.print('Error reading file'); + reject(); + }; + reader.readAsArrayBuffer(file); + }, function (err) { + GodotRuntime.print('Error!'); + reject(); + }); + })); + }, + + process: function (resolve, reject) { + if (GodotInputDragDrop.promises.length === 0) { + resolve(); + return; + } + GodotInputDragDrop.promises.pop().then(function () { + setTimeout(function () { + GodotInputDragDrop.process(resolve, reject); + }, 0); + }); + }, + + _process_event: function (ev, callback) { + ev.preventDefault(); + if (ev.dataTransfer.items) { + // Use DataTransferItemList interface to access the file(s) + for (let i = 0; i < ev.dataTransfer.items.length; i++) { + const item = ev.dataTransfer.items[i]; + let entry = null; + if ('getAsEntry' in item) { + entry = item.getAsEntry(); + } else if ('webkitGetAsEntry' in item) { + entry = item.webkitGetAsEntry(); + } + if (entry) { + GodotInputDragDrop.add_entry(entry); + } + } + } else { + GodotRuntime.error('File upload not supported'); + } + new Promise(GodotInputDragDrop.process).then(function () { + const DROP = `/tmp/drop-${parseInt(Math.random() * (1 << 30), 10)}/`; + const drops = []; + const files = []; + FS.mkdir(DROP); + GodotInputDragDrop.pending_files.forEach((elem) => { + const path = elem['path']; + GodotFS.copy_to_fs(DROP + path, elem['data']); + let idx = path.indexOf('/'); + if (idx === -1) { + // Root file + drops.push(DROP + path); + } else { + // Subdir + const sub = path.substr(0, idx); + idx = sub.indexOf('/'); + if (idx < 0 && drops.indexOf(DROP + sub) === -1) { + drops.push(DROP + sub); + } + } + files.push(DROP + path); + }); + GodotInputDragDrop.promises = []; + GodotInputDragDrop.pending_files = []; + callback(drops); + if (GodotConfig.persistent_drops) { + // Delay removal at exit. + GodotOS.atexit(function (resolve, reject) { + GodotInputDragDrop.remove_drop(files, DROP); + resolve(); + }); + } else { + GodotInputDragDrop.remove_drop(files, DROP); + } + }); + }, + + remove_drop: function (files, drop_path) { + const dirs = [drop_path.substr(0, drop_path.length - 1)]; + // Remove temporary files + files.forEach(function (file) { + FS.unlink(file); + let dir = file.replace(drop_path, ''); + let idx = dir.lastIndexOf('/'); + while (idx > 0) { + dir = dir.substr(0, idx); + if (dirs.indexOf(drop_path + dir) === -1) { + dirs.push(drop_path + dir); + } + idx = dir.lastIndexOf('/'); + } + }); + // Remove dirs. + dirs.sort(function (a, b) { + const al = (a.match(/\//g) || []).length; + const bl = (b.match(/\//g) || []).length; + if (al > bl) { + return -1; + } else if (al < bl) { + return 1; + } + return 0; + }).forEach(function (dir) { + FS.rmdir(dir); + }); + }, + + handler: function (callback) { + return function (ev) { + GodotInputDragDrop._process_event(ev, callback); + }; + }, + }, +}; +mergeInto(LibraryManager.library, GodotInputDragDrop); + +/* + * Godot exposed input functions. + */ +const GodotInput = { + $GodotInput__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners', '$GodotInputGamepads', '$GodotInputDragDrop'], + $GodotInput: { + getModifiers: function (evt) { + return (evt.shiftKey + 0) + ((evt.altKey + 0) << 1) + ((evt.ctrlKey + 0) << 2) + ((evt.metaKey + 0) << 3); + }, + computePosition: function (evt, rect) { + const canvas = GodotConfig.canvas; + const rw = canvas.width / rect.width; + const rh = canvas.height / rect.height; + const x = (evt.clientX - rect.x) * rw; + const y = (evt.clientY - rect.y) * rh; + return [x, y]; + }, + }, + + /* + * Mouse API + */ + godot_js_input_mouse_move_cb__sig: 'vi', + godot_js_input_mouse_move_cb: function (callback) { + const func = GodotRuntime.get_func(callback); + const canvas = GodotConfig.canvas; + function move_cb(evt) { + const rect = canvas.getBoundingClientRect(); + const pos = GodotInput.computePosition(evt, rect); + // Scale movement + const rw = canvas.width / rect.width; + const rh = canvas.height / rect.height; + const rel_pos_x = evt.movementX * rw; + const rel_pos_y = evt.movementY * rh; + const modifiers = GodotInput.getModifiers(evt); + func(pos[0], pos[1], rel_pos_x, rel_pos_y, modifiers); + } + GodotEventListeners.add(window, 'mousemove', move_cb, false); + }, + + godot_js_input_mouse_wheel_cb__sig: 'vi', + godot_js_input_mouse_wheel_cb: function (callback) { + const func = GodotRuntime.get_func(callback); + function wheel_cb(evt) { + if (func(evt['deltaX'] || 0, evt['deltaY'] || 0)) { + evt.preventDefault(); + } + } + GodotEventListeners.add(GodotConfig.canvas, 'wheel', wheel_cb, false); + }, + + godot_js_input_mouse_button_cb__sig: 'vi', + godot_js_input_mouse_button_cb: function (callback) { + const func = GodotRuntime.get_func(callback); + const canvas = GodotConfig.canvas; + function button_cb(p_pressed, evt) { + const rect = canvas.getBoundingClientRect(); + const pos = GodotInput.computePosition(evt, rect); + const modifiers = GodotInput.getModifiers(evt); + if (func(p_pressed, evt.button, pos[0], pos[1], modifiers)) { + evt.preventDefault(); + } + } + GodotEventListeners.add(canvas, 'mousedown', button_cb.bind(null, 1), false); + GodotEventListeners.add(window, 'mouseup', button_cb.bind(null, 0), false); + }, + + /* + * Touch API + */ + godot_js_input_touch_cb__sig: 'viii', + godot_js_input_touch_cb: function (callback, ids, coords) { + const func = GodotRuntime.get_func(callback); + const canvas = GodotConfig.canvas; + function touch_cb(type, evt) { + const rect = canvas.getBoundingClientRect(); + const touches = evt.changedTouches; + for (let i = 0; i < touches.length; i++) { + const touch = touches[i]; + const pos = GodotInput.computePosition(touch, rect); + GodotRuntime.setHeapValue(coords + (i * 2), pos[0], 'double'); + GodotRuntime.setHeapValue(coords + (i * 2 + 8), pos[1], 'double'); + GodotRuntime.setHeapValue(ids + i, touch.identifier, 'i32'); + } + func(type, touches.length); + if (evt.cancelable) { + evt.preventDefault(); + } + } + GodotEventListeners.add(canvas, 'touchstart', touch_cb.bind(null, 0), false); + GodotEventListeners.add(canvas, 'touchend', touch_cb.bind(null, 1), false); + GodotEventListeners.add(canvas, 'touchcancel', touch_cb.bind(null, 1), false); + GodotEventListeners.add(canvas, 'touchmove', touch_cb.bind(null, 2), false); + }, + + /* + * Key API + */ + godot_js_input_key_cb__sig: 'viii', + godot_js_input_key_cb: function (callback, code, key) { + const func = GodotRuntime.get_func(callback); + function key_cb(pressed, evt) { + const modifiers = GodotInput.getModifiers(evt); + GodotRuntime.stringToHeap(evt.code, code, 32); + GodotRuntime.stringToHeap(evt.key, key, 32); + func(pressed, evt.repeat, modifiers); + evt.preventDefault(); + } + GodotEventListeners.add(GodotConfig.canvas, 'keydown', key_cb.bind(null, 1), false); + GodotEventListeners.add(GodotConfig.canvas, 'keyup', key_cb.bind(null, 0), false); + }, + + /* + * Gamepad API + */ + godot_js_input_gamepad_cb__sig: 'vi', + godot_js_input_gamepad_cb: function (change_cb) { + const onchange = GodotRuntime.get_func(change_cb); + GodotInputGamepads.init(onchange); + }, + + godot_js_input_gamepad_sample_count__sig: 'i', + godot_js_input_gamepad_sample_count: function () { + return GodotInputGamepads.get_samples().length; + }, + + godot_js_input_gamepad_sample__sig: 'i', + godot_js_input_gamepad_sample: function () { + GodotInputGamepads.sample(); + return 0; + }, + + godot_js_input_gamepad_sample_get__sig: 'iiiiiii', + godot_js_input_gamepad_sample_get: function (p_index, r_btns, r_btns_num, r_axes, r_axes_num, r_standard) { + const sample = GodotInputGamepads.get_sample(p_index); + if (!sample || !sample.connected) { + return 1; + } + const btns = sample.buttons; + const btns_len = btns.length < 16 ? btns.length : 16; + for (let i = 0; i < btns_len; i++) { + GodotRuntime.setHeapValue(r_btns + (i << 2), btns[i], 'float'); + } + GodotRuntime.setHeapValue(r_btns_num, btns_len, 'i32'); + const axes = sample.axes; + const axes_len = axes.length < 10 ? axes.length : 10; + for (let i = 0; i < axes_len; i++) { + GodotRuntime.setHeapValue(r_axes + (i << 2), axes[i], 'float'); + } + GodotRuntime.setHeapValue(r_axes_num, axes_len, 'i32'); + const is_standard = sample.standard ? 1 : 0; + GodotRuntime.setHeapValue(r_standard, is_standard, 'i32'); + return 0; + }, + + /* + * Drag/Drop API + */ + godot_js_input_drop_files_cb__sig: 'vi', + godot_js_input_drop_files_cb: function (callback) { + const func = GodotRuntime.get_func(callback); + const dropFiles = function (files) { + const args = files || []; + if (!args.length) { + return; + } + const argc = args.length; + const argv = GodotRuntime.allocStringArray(args); + func(argv, argc); + GodotRuntime.freeStringArray(argv, argc); + }; + const canvas = GodotConfig.canvas; + GodotEventListeners.add(canvas, 'dragover', function (ev) { + // Prevent default behavior (which would try to open the file(s)) + ev.preventDefault(); + }, false); + GodotEventListeners.add(canvas, 'drop', GodotInputDragDrop.handler(dropFiles)); + }, + + /* Paste API */ + godot_js_input_paste_cb__sig: 'vi', + godot_js_input_paste_cb: function (callback) { + const func = GodotRuntime.get_func(callback); + GodotEventListeners.add(window, 'paste', function (evt) { + const text = evt.clipboardData.getData('text'); + const ptr = GodotRuntime.allocString(text); + func(ptr); + GodotRuntime.free(ptr); + }, false); + }, +}; + +autoAddDeps(GodotInput, '$GodotInput'); +mergeInto(LibraryManager.library, GodotInput); diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js index 99e7ee8b5f..c552e99415 100644 --- a/platform/javascript/js/libs/library_godot_os.js +++ b/platform/javascript/js/libs/library_godot_os.js @@ -328,3 +328,43 @@ const GodotOS = { autoAddDeps(GodotOS, '$GodotOS'); mergeInto(LibraryManager.library, GodotOS); + +/* + * Godot event listeners. + * Keeps track of registered event listeners so it can remove them on shutdown. + */ +const GodotEventListeners = { + $GodotEventListeners__deps: ['$GodotOS'], + $GodotEventListeners__postset: 'GodotOS.atexit(function(resolve, reject) { GodotEventListeners.clear(); resolve(); });', + $GodotEventListeners: { + handlers: [], + + has: function (target, event, method, capture) { + return GodotEventListeners.handlers.findIndex(function (e) { + return e.target === target && e.event === event && e.method === method && e.capture === capture; + }) !== -1; + }, + + add: function (target, event, method, capture) { + if (GodotEventListeners.has(target, event, method, capture)) { + return; + } + function Handler(p_target, p_event, p_method, p_capture) { + this.target = p_target; + this.event = p_event; + this.method = p_method; + this.capture = p_capture; + } + GodotEventListeners.handlers.push(new Handler(target, event, method, capture)); + target.addEventListener(event, method, capture); + }, + + clear: function () { + GodotEventListeners.handlers.forEach(function (h) { + h.target.removeEventListener(h.event, h.method, h.capture); + }); + GodotEventListeners.handlers.length = 0; + }, + }, +}; +mergeInto(LibraryManager.library, GodotEventListeners); diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 8a946d8136..905c4142a8 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1208,7 +1208,7 @@ void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) { IDC_CROSS, IDC_WAIT, IDC_APPSTARTING, - IDC_ARROW, + IDC_SIZEALL, IDC_ARROW, IDC_NO, IDC_SIZENS, diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index b816f12bc3..da54903871 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -433,7 +433,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { return; } if (k->is_action("ui_end", true)) { - code_completion_current_selected = MIN(code_completion_options.size() - 1, code_completion_current_selected + code_completion_max_lines); + code_completion_current_selected = code_completion_options.size() - 1; update(); accept_event(); return; @@ -2937,6 +2937,7 @@ void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) { return; } + lines_edited_changed += p_to_line - p_from_line; lines_edited_from = (lines_edited_from == -1) ? MIN(p_from_line, p_to_line) : MIN(lines_edited_from, MIN(p_from_line, p_to_line)); lines_edited_to = (lines_edited_to == -1) ? MAX(p_from_line, p_to_line) : MAX(lines_edited_from, MAX(p_from_line, p_to_line)); } @@ -2963,7 +2964,6 @@ void CodeEdit::_text_changed() { } lc = get_line_count(); - int line_change_size = (lines_edited_to - lines_edited_from); List<int> breakpoints; breakpointed_lines.get_key_list(&breakpoints); for (const int &line : breakpoints) { @@ -2974,8 +2974,8 @@ void CodeEdit::_text_changed() { breakpointed_lines.erase(line); emit_signal(SNAME("breakpoint_toggled"), line); - int next_line = line + line_change_size; - if (next_line < lc && is_line_breakpointed(next_line)) { + int next_line = line + lines_edited_changed; + if (next_line > -1 && next_line < lc && is_line_breakpointed(next_line)) { emit_signal(SNAME("breakpoint_toggled"), next_line); breakpointed_lines[next_line] = true; continue; @@ -2984,6 +2984,7 @@ void CodeEdit::_text_changed() { lines_edited_from = -1; lines_edited_to = -1; + lines_edited_changed = 0; } CodeEdit::CodeEdit() { diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index d8eccb7d32..f0d971dd35 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -240,6 +240,7 @@ private: int line_spacing = 1; /* Callbacks */ + int lines_edited_changed = 0; int lines_edited_from = -1; int lines_edited_to = -1; diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index 11941529cd..a1bd82f6f7 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -87,6 +87,9 @@ void Container::_sort_children() { return; } + notification(NOTIFICATION_PRE_SORT_CHILDREN); + emit_signal(SceneStringNames::get_singleton()->pre_sort_children); + notification(NOTIFICATION_SORT_CHILDREN); emit_signal(SceneStringNames::get_singleton()->sort_children); pending_sort = false; @@ -174,7 +177,10 @@ void Container::_bind_methods() { ClassDB::bind_method(D_METHOD("queue_sort"), &Container::queue_sort); ClassDB::bind_method(D_METHOD("fit_child_in_rect", "child", "rect"), &Container::fit_child_in_rect); + BIND_CONSTANT(NOTIFICATION_PRE_SORT_CHILDREN); BIND_CONSTANT(NOTIFICATION_SORT_CHILDREN); + + ADD_SIGNAL(MethodInfo("pre_sort_children")); ADD_SIGNAL(MethodInfo("sort_children")); } diff --git a/scene/gui/container.h b/scene/gui/container.h index bce3085f0c..f3ae948556 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -51,7 +51,8 @@ protected: public: enum { - NOTIFICATION_SORT_CHILDREN = 50 + NOTIFICATION_PRE_SORT_CHILDREN = 50, + NOTIFICATION_SORT_CHILDREN = 51, }; void fit_child_in_rect(Control *p_child, const Rect2 &p_rect); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 57bcbb7c2d..6370ea9c95 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2399,6 +2399,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { } String TextEdit::get_tooltip(const Point2 &p_pos) const { + Object *tooltip_obj = ObjectDB::get_instance(tooltip_obj_id); if (!tooltip_obj) { return Control::get_tooltip(p_pos); } @@ -2421,7 +2422,8 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const { } void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata) { - tooltip_obj = p_obj; + ERR_FAIL_NULL(p_obj); + tooltip_obj_id = p_obj->get_instance_id(); tooltip_func = p_function; tooltip_ud = p_udata; } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 07e585847a..3230de776f 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -330,7 +330,7 @@ private: int _get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column) const; /* Tooltip. */ - Object *tooltip_obj = nullptr; + ObjectID tooltip_obj_id; StringName tooltip_func; Variant tooltip_ud; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index b3b743370b..e88bb3b952 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -57,17 +57,17 @@ #include "servers/audio_server.h" void ViewportTexture::setup_local_to_scene() { + Node *local_scene = get_local_scene(); + if (!local_scene) { + return; + } + if (vp) { vp->viewport_textures.erase(this); } vp = nullptr; - Node *local_scene = get_local_scene(); - if (!local_scene) { - return; - } - Node *vpn = local_scene->get_node(path); ERR_FAIL_COND_MSG(!vpn, "ViewportTexture: Path to node is invalid."); diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp index fa95ab0e79..b291724c4c 100644 --- a/scene/resources/canvas_item_material.cpp +++ b/scene/resources/canvas_item_material.cpp @@ -135,7 +135,7 @@ void CanvasItemMaterial::_update_shader() { code += " particle_frame = mod(particle_frame, particle_total_frames);\n"; code += " }"; code += " UV /= vec2(h_frames, v_frames);\n"; - code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n"; + code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n"; code += "}\n"; } diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 66f04f0292..abb3381c4e 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -811,7 +811,7 @@ void BaseMaterial3D::_update_shader() { code += " particle_frame = mod(particle_frame, particle_total_frames);\n"; code += " }"; code += " UV /= vec2(h_frames, v_frames);\n"; - code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n"; + code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n"; } break; case BILLBOARD_MAX: break; // Internal value, skip. diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index c8d3ea5e37..60cda637ca 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -388,7 +388,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map editable_instances.push_back(p_owner->get_path_to(p_node)); // Node is the root of an editable instance. is_editable_instance = true; - } else if (p_node->get_owner() && p_node->get_owner() != p_owner && p_owner->is_editable_instance(p_node->get_owner())) { + } else if (p_node->get_owner() && p_owner->is_ancestor_of(p_node->get_owner()) && p_owner->is_editable_instance(p_node->get_owner())) { // Node is part of an editable instance. is_editable_instance = true; } diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index b283749ffa..29e5dd6056 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -73,6 +73,7 @@ SceneStringNames::SceneStringNames() { focus_entered = StaticCString::create("focus_entered"); focus_exited = StaticCString::create("focus_exited"); + pre_sort_children = StaticCString::create("pre_sort_children"); sort_children = StaticCString::create("sort_children"); body_shape_entered = StaticCString::create("body_shape_entered"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 2923351eab..5e3195952e 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -89,6 +89,7 @@ public: StringName focus_entered; StringName focus_exited; + StringName pre_sort_children; StringName sort_children; StringName finished; diff --git a/tests/test_code_edit.h b/tests/test_code_edit.h index 0e31a976bf..62235ed0ae 100644 --- a/tests/test_code_edit.h +++ b/tests/test_code_edit.h @@ -284,6 +284,26 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") { CHECK_FALSE(code_edit->is_line_breakpointed(1)); ERR_PRINT_ON; SIGNAL_CHECK("breakpoint_toggled", args); + + /* Backspace above breakpointed line moves it. */ + ((Array)args[0])[0] = 2; + + code_edit->set_text("\n\n"); + code_edit->set_line_as_breakpoint(2, true); + CHECK(code_edit->is_line_breakpointed(2)); + SIGNAL_CHECK("breakpoint_toggled", args); + + code_edit->set_caret_line(1); + + Array arg2; + arg2.push_back(1); + args.push_back(arg2); + SEND_GUI_ACTION(code_edit, "ui_text_backspace"); + ERR_PRINT_OFF; + CHECK_FALSE(code_edit->is_line_breakpointed(2)); + ERR_PRINT_ON; + CHECK(code_edit->is_line_breakpointed(1)); + SIGNAL_CHECK("breakpoint_toggled", args); } SUBCASE("[CodeEdit] breakpoints and delete") { @@ -312,6 +332,26 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") { CHECK_FALSE(code_edit->is_line_breakpointed(1)); ERR_PRINT_ON; SIGNAL_CHECK("breakpoint_toggled", args); + + /* Delete above breakpointed line moves it. */ + ((Array)args[0])[0] = 2; + + code_edit->set_text("\n\n"); + code_edit->set_line_as_breakpoint(2, true); + CHECK(code_edit->is_line_breakpointed(2)); + SIGNAL_CHECK("breakpoint_toggled", args); + + code_edit->set_caret_line(0); + + Array arg2; + arg2.push_back(1); + args.push_back(arg2); + SEND_GUI_ACTION(code_edit, "ui_text_delete"); + ERR_PRINT_OFF; + CHECK_FALSE(code_edit->is_line_breakpointed(2)); + ERR_PRINT_ON; + CHECK(code_edit->is_line_breakpointed(1)); + SIGNAL_CHECK("breakpoint_toggled", args); } SUBCASE("[CodeEdit] breakpoints and delete selection") { @@ -330,6 +370,41 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") { MessageQueue::get_singleton()->flush(); CHECK_FALSE(code_edit->is_line_breakpointed(0)); SIGNAL_CHECK("breakpoint_toggled", args); + + /* Should handle breakpoint move when deleting selection by adding less text then removed. */ + ((Array)args[0])[0] = 9; + + code_edit->set_text("\n\n\n\n\n\n\n\n\n"); + code_edit->set_line_as_breakpoint(9, true); + CHECK(code_edit->is_line_breakpointed(9)); + SIGNAL_CHECK("breakpoint_toggled", args); + + code_edit->select(0, 0, 6, 0); + + Array arg2; + arg2.push_back(4); + args.push_back(arg2); + SEND_GUI_ACTION(code_edit, "ui_text_newline"); + ERR_PRINT_OFF; + CHECK_FALSE(code_edit->is_line_breakpointed(9)); + ERR_PRINT_ON; + CHECK(code_edit->is_line_breakpointed(4)); + SIGNAL_CHECK("breakpoint_toggled", args); + + /* Should handle breakpoint move when deleting selection by adding more text then removed. */ + ((Array)args[0])[0] = 9; + ((Array)args[1])[0] = 14; + + code_edit->insert_text_at_caret("\n\n\n\n\n"); + MessageQueue::get_singleton()->flush(); + SIGNAL_DISCARD("breakpoint_toggled") + CHECK(code_edit->is_line_breakpointed(9)); + + code_edit->select(0, 0, 6, 0); + code_edit->insert_text_at_caret("\n\n\n\n\n\n\n\n\n\n\n"); + MessageQueue::get_singleton()->flush(); + CHECK(code_edit->is_line_breakpointed(14)); + SIGNAL_CHECK("breakpoint_toggled", args); } SUBCASE("[CodeEdit] breakpoints and undo") { diff --git a/thirdparty/README.md b/thirdparty/README.md index 23380b6413..e7ceea6268 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -28,7 +28,7 @@ Files extracted from upstream source: - `src/*` apart from CMakeLists.txt and premake4.lua files - `LICENSE.txt`, and `VERSION` as `VERSION.txt` -Includes a warning fix which should be upstreamed soon (see patch in `patches`). +Includes some patches in the `patches` folder which have been sent upstream. ## certs diff --git a/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportWin32.cpp b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportWin32.cpp index 922e449cce..5862264a67 100644 --- a/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportWin32.cpp +++ b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportWin32.cpp @@ -82,6 +82,11 @@ typedef BOOL(WINAPI* Pfn_GetLogicalProcessorInformation)(PSYSTEM_LOGICAL_PROCESS void getProcessorInformation(btProcessorInfo* procInfo) { memset(procInfo, 0, sizeof(*procInfo)); +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ + !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + // Can't dlopen libraries on UWP. + return; +#else Pfn_GetLogicalProcessorInformation getLogicalProcInfo = (Pfn_GetLogicalProcessorInformation)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "GetLogicalProcessorInformation"); if (getLogicalProcInfo == NULL) @@ -160,6 +165,7 @@ void getProcessorInformation(btProcessorInfo* procInfo) } } free(buf); +#endif } ///btThreadSupportWin32 helps to initialize/shutdown libspe2, start/stop SPU tasks and communication diff --git a/thirdparty/bullet/patches/fix-win32-scheduler-uwp.patch b/thirdparty/bullet/patches/fix-win32-scheduler-uwp.patch new file mode 100644 index 0000000000..c65db49388 --- /dev/null +++ b/thirdparty/bullet/patches/fix-win32-scheduler-uwp.patch @@ -0,0 +1,24 @@ +diff --git a/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportWin32.cpp b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportWin32.cpp +index 922e449cce..5862264a67 100644 +--- a/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportWin32.cpp ++++ b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportWin32.cpp +@@ -82,6 +82,11 @@ typedef BOOL(WINAPI* Pfn_GetLogicalProcessorInformation)(PSYSTEM_LOGICAL_PROCESS + void getProcessorInformation(btProcessorInfo* procInfo) + { + memset(procInfo, 0, sizeof(*procInfo)); ++#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ ++ !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) ++ // Can't dlopen libraries on UWP. ++ return; ++#else + Pfn_GetLogicalProcessorInformation getLogicalProcInfo = + (Pfn_GetLogicalProcessorInformation)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "GetLogicalProcessorInformation"); + if (getLogicalProcInfo == NULL) +@@ -160,6 +165,7 @@ void getProcessorInformation(btProcessorInfo* procInfo) + } + } + free(buf); ++#endif + } + + ///btThreadSupportWin32 helps to initialize/shutdown libspe2, start/stop SPU tasks and communication |