diff options
86 files changed, 544 insertions, 282 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 56e9057a2a..933b9891cc 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1304,16 +1304,8 @@ ProjectSettings::ProjectSettings() { } extensions.push_back("gdshader"); - GLOBAL_DEF("editor/run/main_run_args", ""); - GLOBAL_DEF(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "editor/script/templates_search_path", PROPERTY_HINT_DIR), "res://script_templates"); - - // For correct doc generation. - GLOBAL_DEF("editor/naming/default_signal_callback_name", "_on_{node_name}_{signal_name}"); - GLOBAL_DEF("editor/naming/default_signal_callback_to_self_name", "_on_{signal_name}"); - _add_builtin_input_map(); // Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum. diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index e26ead6d8c..79b0ebc641 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -1052,6 +1052,7 @@ void GDExtensionAPIDump::generate_extension_json_file(const String &p_path) { String text = json->stringify(api, "\t", false) + "\n"; Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_MSG(fa.is_null(), vformat("Cannot open file '%s' for writing.", p_path)); fa->store_string(text); } diff --git a/core/extension/make_interface_dumper.py b/core/extension/make_interface_dumper.py index a8af0e9ff6..a604112d13 100644 --- a/core/extension/make_interface_dumper.py +++ b/core/extension/make_interface_dumper.py @@ -27,6 +27,7 @@ class GDExtensionInterfaceDump { public: static void generate_gdextension_interface_file(const String &p_path) { Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_MSG(fa.is_null(), vformat("Cannot open file '%s' for writing.", p_path)); CharString cs(gdextension_interface_dump); fa->store_buffer((const uint8_t *)cs.ptr(), cs.length()); }; diff --git a/doc/Makefile b/doc/Makefile index c8bf32d6e2..cc2ebf9881 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,28 +1,28 @@ BASEDIR = . -CLASSES = $(BASEDIR)/classes/ $(BASEDIR)/../modules/ +CLASSES = "$(BASEDIR)/classes/" "$(BASEDIR)/../modules/" OUTPUTDIR = $(BASEDIR)/_build TOOLSDIR = $(BASEDIR)/tools -JSDIR = $(BASEDIR)/../platform/web +JSDIR = "$(BASEDIR)/../platform/web" LANGARG ?= en LANGCMD = -l $(LANGARG) .ONESHELL: clean: - rm -rf $(OUTPUTDIR) + rm -rf "$(OUTPUTDIR)" doxygen: - rm -rf $(OUTPUTDIR)/doxygen - mkdir -p $(OUTPUTDIR)/doxygen + rm -rf "$(OUTPUTDIR)/doxygen" + mkdir -p "$(OUTPUTDIR)/doxygen" doxygen Doxyfile rst: - rm -rf $(OUTPUTDIR)/rst - mkdir -p $(OUTPUTDIR)/rst - python3 $(TOOLSDIR)/make_rst.py -o $(OUTPUTDIR)/rst $(LANGCMD) $(CLASSES) + rm -rf "$(OUTPUTDIR)/rst" + mkdir -p "$(OUTPUTDIR)/rst" + python3 "$(TOOLSDIR)/make_rst.py" -o "$(OUTPUTDIR)/rst" "$(LANGCMD)" $(CLASSES) rstjs: - rm -rf $(OUTPUTDIR)/rstjs - mkdir -p $(OUTPUTDIR)/rstjs - npm --prefix $(JSDIR) ci - npm --prefix $(JSDIR) run docs -- --destination $(OUTPUTDIR)/rstjs/html5_shell_classref.rst + rm -rf "$(OUTPUTDIR)/rstjs" + mkdir -p "$(OUTPUTDIR)/rstjs" + npm --prefix "$(JSDIR)" ci + npm --prefix "$(JSDIR)" run docs -- --destination "$(OUTPUTDIR)/rstjs/html5_shell_classref.rst" diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml index 2c5337eea3..b4bab2de67 100644 --- a/doc/classes/AABB.xml +++ b/doc/classes/AABB.xml @@ -188,7 +188,7 @@ <param index="0" name="from" type="Vector3" /> <param index="1" name="dir" type="Vector3" /> <description> - Returns [code]true[/code] if the given ray intersects with this [AABB]. Ray length is infinite. + Returns the point of intersection of the given ray with this [AABB] or [code]null[/code] if there is no intersection. Ray length is infinite. </description> </method> <method name="intersects_segment" qualifiers="const"> @@ -196,7 +196,7 @@ <param index="0" name="from" type="Vector3" /> <param index="1" name="to" type="Vector3" /> <description> - Returns [code]true[/code] if the [AABB] intersects the line segment between [param from] and [param to]. + Returns the point of intersection between [param from] and [param to] with this [AABB] or [code]null[/code] if there is no intersection. </description> </method> <method name="is_equal_approx" qualifiers="const"> diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index c4fec5a729..86f8f357f8 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -41,7 +41,6 @@ [b]Note:[/b] Concatenating with the [code]+=[/code] operator will create a new array, which has a cost. If you want to append another array to an existing array, [method append_array] is more efficient. [b]Note:[/b] Arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. [b]Note:[/b] Erasing elements while iterating over arrays is [b]not[/b] supported and will result in unpredictable behavior. - [b]Note:[/b] When declaring an array with [code]const[/code], the array itself can still be mutated by defining the values at individual indices or pushing/removing elements. Using [code]const[/code] will only prevent assigning the constant with another value after it was initialized. </description> <tutorials> </tutorials> diff --git a/doc/classes/DTLSServer.xml b/doc/classes/DTLSServer.xml index ae1ba10ec7..3e91780095 100644 --- a/doc/classes/DTLSServer.xml +++ b/doc/classes/DTLSServer.xml @@ -23,8 +23,8 @@ func _process(delta): while server.is_connection_available(): - var peer : PacketPeerUDP = server.take_connection() - var dtls_peer : PacketPeerDTLS = dtls.take_connection(peer) + var peer: PacketPeerUDP = server.take_connection() + var dtls_peer: PacketPeerDTLS = dtls.take_connection(peer) if dtls_peer.get_status() != PacketPeerDTLS.STATUS_HANDSHAKING: continue # It is normal that 50% of the connections fails due to cookie exchange. print("Peer connected!") diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index a5a50b4c6a..d31fd75a04 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -135,7 +135,6 @@ [/csharp] [/codeblocks] [b]Note:[/b] Erasing elements while iterating over dictionaries is [b]not[/b] supported and will result in unpredictable behavior. - [b]Note:[/b] When declaring a dictionary with [code]const[/code], the dictionary becomes read-only. A read-only Dictionary's entries cannot be overridden at run-time. This does [i]not[/i] affect nested [Array] and [Dictionary] values. </description> <tutorials> <link title="GDScript basics: Dictionary">$DOCS_URL/tutorials/scripting/gdscript/gdscript_basics.html#dictionary</link> diff --git a/doc/classes/DirAccess.xml b/doc/classes/DirAccess.xml index 27f2eb7f2f..8e1528f1c6 100644 --- a/doc/classes/DirAccess.xml +++ b/doc/classes/DirAccess.xml @@ -290,7 +290,7 @@ </methods> <members> <member name="include_hidden" type="bool" setter="set_include_hidden" getter="get_include_hidden"> - If [code]true[/code], hidden files are included when the navigating directory. + If [code]true[/code], hidden files are included when navigating the directory. Affects [method list_dir_begin], [method get_directories] and [method get_files]. </member> <member name="include_navigational" type="bool" setter="set_include_navigational" getter="get_include_navigational"> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 31b9703b01..18c11ec68d 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -190,7 +190,7 @@ <description> Adds a new checkable item with text [param label] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. - An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using boolean OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). + An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. [b]Supported system menu IDs:[/b] @@ -213,7 +213,7 @@ <description> Adds a new checkable item with text [param label] and icon [param icon] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. - An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using boolean OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). + An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. [b]Supported system menu IDs:[/b] @@ -236,7 +236,7 @@ <description> Adds a new item with text [param label] and icon [param icon] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. - An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using boolean OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). + An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. [b]Supported system menu IDs:[/b] @@ -259,7 +259,7 @@ <description> Adds a new radio-checkable item with text [param label] and icon [param icon] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. - An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using boolean OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). + An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). [b]Note:[/b] Radio-checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually. See [method global_menu_set_item_checked] for more info on how to control it. [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. @@ -282,7 +282,7 @@ <description> Adds a new item with text [param label] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. - An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using boolean OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). + An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. [b]Supported system menu IDs:[/b] @@ -307,7 +307,7 @@ Adds a new item with text [param label] to the global menu with ID [param menu_root]. Contrarily to normal binary items, multistate items can have more than two states, as defined by [param max_states]. Each press or activate of the item will increase the state by one. The default value is defined by [param default_state]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. - An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using boolean OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). + An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). [b]Note:[/b] By default, there's no indication of the current item state, it should be changed manually. [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. @@ -330,7 +330,7 @@ <description> Adds a new radio-checkable item with text [param label] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. - An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using boolean OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). + An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). [b]Note:[/b] Radio-checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually. See [method global_menu_set_item_checked] for more info on how to control it. [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. @@ -507,7 +507,7 @@ <param index="0" name="menu_root" type="String" /> <param index="1" name="idx" type="int" /> <description> - Returns the tooltip associated with the specified index index [param idx]. + Returns the tooltip associated with the specified index [param idx]. [b]Note:[/b] This method is implemented on macOS. </description> </method> @@ -565,7 +565,7 @@ <param index="1" name="idx" type="int" /> <param index="2" name="keycode" type="int" enum="Key" /> <description> - Sets the accelerator of the item at index [param idx]. [param keycode] can be a single [enum Key], or a combination of [enum KeyModifierMask]s and [enum Key]s using boolean OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). + Sets the accelerator of the item at index [param idx]. [param keycode] can be a single [enum Key], or a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). [b]Note:[/b] This method is implemented on macOS. </description> </method> @@ -576,7 +576,7 @@ <param index="2" name="callback" type="Callable" /> <description> Sets the callback of the item at index [param idx]. Callback is emitted when an item is pressed. - [b]Note:[/b] The [param callback] Callable needs to accept exactly one Variant parameter, the parameter passed to the Callable will be the value passed to the tag parameter when the menu item was created. + [b]Note:[/b] The [param callback] Callable needs to accept exactly one Variant parameter, the parameter passed to the Callable will be the value passed to the [code]tag[/code] parameter when the menu item was created. [b]Note:[/b] This method is implemented on macOS. </description> </method> @@ -638,7 +638,7 @@ <param index="2" name="key_callback" type="Callable" /> <description> Sets the callback of the item at index [param idx]. Callback is emitted when its accelerator is activated. - [b]Note:[/b] The [param key_callback] Callable needs to accept exactly one Variant parameter, the parameter passed to the Callable will be the value passed to the tag parameter when the menu item was created. + [b]Note:[/b] The [param key_callback] Callable needs to accept exactly one Variant parameter, the parameter passed to the Callable will be the value passed to the [code]tag[/code] parameter when the menu item was created. [b]Note:[/b] This method is implemented on macOS. </description> </method> diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml index 7f8808acc7..b0032564cf 100644 --- a/doc/classes/EditorFileDialog.xml +++ b/doc/classes/EditorFileDialog.xml @@ -67,7 +67,7 @@ The view format in which the [EditorFileDialog] displays resources to the user. </member> <member name="file_mode" type="int" setter="set_file_mode" getter="get_file_mode" enum="EditorFileDialog.FileMode" default="4"> - The dialog's open or save mode, which affects the selection behavior. See [enum FileMode] + The dialog's open or save mode, which affects the selection behavior. See [enum FileMode]. </member> <member name="filters" type="PackedStringArray" setter="set_filters" getter="get_filters" default="PackedStringArray()"> The available file type filters. For example, this shows only [code].png[/code] and [code].gd[/code] files: [code]set_filters(PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"]))[/code]. Multiple file types can also be specified in a single filter. [code]"*.png, *.jpg, *.jpeg ; Supported Images"[/code] will show both PNG and JPEG files when selected. diff --git a/doc/classes/EditorResourceConversionPlugin.xml b/doc/classes/EditorResourceConversionPlugin.xml index 6bdfbbe40c..7d125d731c 100644 --- a/doc/classes/EditorResourceConversionPlugin.xml +++ b/doc/classes/EditorResourceConversionPlugin.xml @@ -10,13 +10,13 @@ [gdscript] extends EditorResourceConversionPlugin - func _handles(resource : Resource): + func _handles(resource: Resource): return resource is ImageTexture func _converts_to(): return "PortableCompressedTexture2D" - func _convert(itex : Resource): + func _convert(itex: Resource): var ptex = PortableCompressedTexture2D.new() ptex.create_from_image(itex.get_image(), PortableCompressedTexture2D.COMPRESSION_MODE_LOSSLESS) return ptex diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 86b84849f8..b2ccaa3e60 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -87,7 +87,7 @@ <method name="get_changed_settings" qualifiers="const"> <return type="PackedStringArray" /> <description> - Gets an array of the settings which have been changed since the last save. Note that internally [code]changed_settings[/code] is cleared after a successful save, so generally the most appropriate place to use this method is when processing [constant NOTIFICATION_EDITOR_SETTINGS_CHANGED] + Gets an array of the settings which have been changed since the last save. Note that internally [code]changed_settings[/code] is cleared after a successful save, so generally the most appropriate place to use this method is when processing [constant NOTIFICATION_EDITOR_SETTINGS_CHANGED]. </description> </method> <method name="get_favorites" qualifiers="const"> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 7ee1f2dfd4..580aefbcc8 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -277,7 +277,7 @@ [b]Note:[/b] SSR is only supported in the Forward+ rendering method, not Mobile or Compatibility. </member> <member name="ssr_fade_in" type="float" setter="set_ssr_fade_in" getter="get_ssr_fade_in" default="0.15"> - The fade-in distance for screen-space reflections. Affects the area from the reflected material to the screen-space reflection). Only positive values are valid (negative values will be clamped to [code]0.0[/code]). + The fade-in distance for screen-space reflections. Affects the area from the reflected material to the screen-space reflection. Only positive values are valid (negative values will be clamped to [code]0.0[/code]). </member> <member name="ssr_fade_out" type="float" setter="set_ssr_fade_out" getter="get_ssr_fade_out" default="2.0"> The fade-out distance for screen-space reflections. Affects the area from the screen-space reflection to the "global" reflection. Only positive values are valid (negative values will be clamped to [code]0.0[/code]). diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml index 2f1c68c322..4b46f00821 100644 --- a/doc/classes/Font.xml +++ b/doc/classes/Font.xml @@ -271,6 +271,17 @@ <description> Returns list of supported [url=https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg]variation coordinates[/url], each coordinate is returned as [code]tag: Vector3i(min_value,max_value,default_value)[/code]. Font variations allow for continuous change of glyph characteristics along some given design axis, such as weight, width or slant. + To print available variation axes of a variable font: + [codeblock] + var fv = FontVariation.new() + fv.set_base_font = load("res://RobotoFlex.ttf") + var variation_list = fv.get_supported_variation_list() + for tag in variation_list: + var name = TextServerManager.get_primary_interface().tag_to_name(tag) + var values = variation_list[tag] + print("variation axis: %s (%d)\n\tmin, max, default: %s" % [name, tag, values]) + [/codeblock] + [b]Note:[/b] To set and get variation coordinates of a [FontVariation], use [member FontVariation.variation_opentype]. </description> </method> <method name="get_underline_position" qualifiers="const"> diff --git a/doc/classes/FontVariation.xml b/doc/classes/FontVariation.xml index 11286e46a4..abe282db7e 100644 --- a/doc/classes/FontVariation.xml +++ b/doc/classes/FontVariation.xml @@ -22,6 +22,13 @@ GetNode("Label").AddThemeFontSizeOverride("font_size", 64); [/csharp] [/codeblocks] + To set the coordinate of multiple variation axes: + [codeblock] + var fv = FontVariation.new(); + var ts = TextServerManager.get_primary_interface() + fv.base_font = load("res://BarlowCondensed-Regular.ttf") + fv.variation_opentype = { ts.name_to_tag("wght"): 900, ts.name_to_tag("custom_hght"): 900 } + [/codeblock] </description> <tutorials> </tutorials> @@ -40,7 +47,7 @@ Base font used to create a variation. If not set, default [Theme] font is used. </member> <member name="fallbacks" type="Font[]" setter="set_fallbacks" getter="get_fallbacks" default="[]"> - Array of fallback [Font]s. If not set [member base_font] fallback are ussed. + Array of fallback [Font]s to use as a substitute if a glyph is not found in this [FontVariation]. If not set, [member base_font]'s fallbacks are used instead. </member> <member name="opentype_features" type="Dictionary" setter="set_opentype_features" getter="get_opentype_features" default="{}"> A set of OpenType feature tags. More info: [url=https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags]OpenType feature tags[/url]. @@ -66,6 +73,8 @@ </member> <member name="variation_opentype" type="Dictionary" setter="set_variation_opentype" getter="get_variation_opentype" default="{}"> Font OpenType variation coordinates. More info: [url=https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg]OpenType variation tags[/url]. + [b]Note:[/b] This [Dictionary] uses OpenType tags as keys. Variation axes can be identified both by tags([code]int[/code]) and names ([code]string[/code]). Some axes might be accessible by multiple names. For example, [code]wght[/code] refers to the same axis as [code]weight[/code]. Tags on the other hand are unique. To convert between names and tags, use [method TextServer.name_to_tag] and [method TextServer.tag_to_name]. + [b]Note:[/b] To get available variation axes of a font, use [method Font.get_supported_variation_list]. </member> <member name="variation_transform" type="Transform2D" setter="set_variation_transform" getter="get_variation_transform" default="Transform2D(1, 0, 0, 1, 0, 0)"> 2D transform, applied to the font outlines, can be used for slanting, flipping and rotating glyphs. diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index bf8567751e..0cc3281236 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -31,8 +31,8 @@ Below is a sample code to help get started: [codeblock] func _is_in_input_hotzone(in_node, in_port, mouse_position): - var port_size : Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical")) - var port_pos : Vector2 = in_node.get_position() + in_node.get_connection_input_position(in_port) - port_size / 2 + var port_size: Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical")) + var port_pos: Vector2 = in_node.get_position() + in_node.get_connection_input_position(in_port) - port_size / 2 var rect = Rect2(port_pos, port_size) return rect.has_point(mouse_position) @@ -49,8 +49,8 @@ Below is a sample code to help get started: [codeblock] func _is_in_output_hotzone(in_node, in_port, mouse_position): - var port_size : Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical")) - var port_pos : Vector2 = in_node.get_position() + in_node.get_connection_output_position(in_port) - port_size / 2 + var port_size: Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical")) + var port_pos: Vector2 = in_node.get_position() + in_node.get_connection_output_position(in_port) - port_size / 2 var rect = Rect2(port_pos, port_size) return rect.has_point(mouse_position) diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml index 8c0e8dc3c3..f97fc78b23 100644 --- a/doc/classes/GraphNode.xml +++ b/doc/classes/GraphNode.xml @@ -342,7 +342,7 @@ <theme_item name="title_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)"> Color of the title text. </theme_item> - <theme_item name="close_h_offset" data_type="constant" type="int" default="22"> + <theme_item name="close_h_offset" data_type="constant" type="int" default="12"> </theme_item> <theme_item name="close_offset" data_type="constant" type="int" default="22"> The vertical offset of the close button. diff --git a/doc/classes/ImageTexture.xml b/doc/classes/ImageTexture.xml index 03d1947475..7248861cf7 100644 --- a/doc/classes/ImageTexture.xml +++ b/doc/classes/ImageTexture.xml @@ -20,7 +20,7 @@ [b]Note:[/b] The image can be retrieved from an imported texture using the [method Texture2D.get_image] method, which returns a copy of the image: [codeblock] var texture = load("res://icon.svg") - var image : Image = texture.get_image() + var image: Image = texture.get_image() [/codeblock] An [ImageTexture] is not meant to be operated from within the editor interface directly, and is mostly useful for rendering images on screen dynamically via code. If you need to generate images procedurally from within the editor, consider saving and importing images as custom texture resources implementing a new [EditorImportPlugin]. [b]Note:[/b] The maximum texture size is 16384×16384 pixels due to graphics hardware limitations. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index bc43f228a7..d904d20335 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -188,6 +188,7 @@ GetTree().CreateTween().BindNode(this); [/csharp] [/codeblocks] + The Tween will start automatically on the next process frame or physics frame (depending on [enum Tween.TweenProcessMode]). </description> </method> <method name="duplicate" qualifiers="const"> diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml index d046d52ed1..50886d9606 100644 --- a/doc/classes/ParticleProcessMaterial.xml +++ b/doc/classes/ParticleProcessMaterial.xml @@ -285,11 +285,11 @@ Enables and disables Turbulence for the particle system. </member> <member name="turbulence_influence_max" type="float" setter="set_param_max" getter="get_param_max" default="0.1"> - Minimum turbulence influence on each particle. - The actual amount of turbulence influence on each particle is calculated as a random value between [member turbulence_influence_min] and [member turbulence_influence_max] and multiplied by the amount of turbulence influence from [member turbulence_influence_over_life]. + Maximum turbulence influence on each particle. + The actual amount of turbulence influence on each particle is calculated as a random value between [member turbulence_influence_min] and [member turbulence_influence_max] and multiplied by the amount of turbulence influence from [member turbulence_influence_over_life]. </member> <member name="turbulence_influence_min" type="float" setter="set_param_min" getter="get_param_min" default="0.1"> - Maximum turbulence influence on each particle. + Minimum turbulence influence on each particle. The actual amount of turbulence influence on each particle is calculated as a random value between [member turbulence_influence_min] and [member turbulence_influence_max] and multiplied by the amount of turbulence influence from [member turbulence_influence_over_life]. </member> <member name="turbulence_influence_over_life" type="Texture2D" setter="set_param_texture" getter="get_param_texture"> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index 97cc05d1a1..1439321b88 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -192,7 +192,7 @@ <return type="int" enum="Key" /> <param index="0" name="index" type="int" /> <description> - Returns the accelerator of the item at the given [param index]. An accelerator is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The return value is an integer which is generally a combination of [enum KeyModifierMask]s and [enum Key]s using boolean OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). If no accelerator is defined for the specified [param index], [method get_item_accelerator] returns [code]0[/code] (corresponding to [constant @GlobalScope.KEY_NONE]). + Returns the accelerator of the item at the given [param index]. An accelerator is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The return value is an integer which is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). If no accelerator is defined for the specified [param index], [method get_item_accelerator] returns [code]0[/code] (corresponding to [constant @GlobalScope.KEY_NONE]). </description> </method> <method name="get_item_icon" qualifiers="const"> @@ -345,7 +345,7 @@ <param index="0" name="index" type="int" /> <param index="1" name="accel" type="int" enum="Key" /> <description> - Sets the accelerator of the item at the given [param index]. An accelerator is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. [param accel] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using boolean OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). + Sets the accelerator of the item at the given [param index]. An accelerator is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. [param accel] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]). </description> </method> <method name="set_item_as_checkable"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index b321a1f821..27bec07cba 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -753,6 +753,9 @@ <member name="editor/naming/node_name_num_separator" type="int" setter="" getter="" default="0"> What to use to separate node name from number. This is mostly an editor setting. </member> + <member name="editor/naming/scene_name_casing" type="int" setter="" getter="" default="2"> + When generating file names from scene root node, set the type of casing in this project. This is mostly an editor setting. + </member> <member name="editor/run/main_run_args" type="String" setter="" getter="" default=""""> The command-line arguments to append to Godot's own command line when running the project. This doesn't affect the editor itself. It is possible to make another executable run Godot by using the [code]%command%[/code] placeholder. The placeholder will be replaced with Godot's own command line. Program-specific arguments should be placed [i]before[/i] the placeholder, whereas Godot-specific arguments should be placed [i]after[/i] the placeholder. diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index 6adb37a3f7..0466d6f281 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -88,7 +88,7 @@ <method name="create_tween"> <return type="Tween" /> <description> - Creates and returns a new [Tween]. + Creates and returns a new [Tween]. The Tween will start automatically on the next process frame or physics frame (depending on [enum Tween.TweenProcessMode]). </description> </method> <method name="get_first_node_in_group"> diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml index 7fc01ea353..4e038c6802 100644 --- a/doc/classes/Theme.xml +++ b/doc/classes/Theme.xml @@ -240,7 +240,7 @@ <param index="2" name="theme_type" type="StringName" /> <description> Returns the theme property of [param data_type] defined by [param name] and [param theme_type], if it exists. - Returns the engine fallback icon value if the property doesn't exist (see [ThemeDB]). Use [method has_theme_item] to check for existence. + Returns the engine fallback value if the property doesn't exist (see [ThemeDB]). Use [method has_theme_item] to check for existence. [b]Note:[/b] This method is analogous to calling the corresponding data type specific method, but can be used for more generalized logic. </description> </method> diff --git a/doc/classes/Thread.xml b/doc/classes/Thread.xml index 846dae0bae..d2304e0046 100644 --- a/doc/classes/Thread.xml +++ b/doc/classes/Thread.xml @@ -16,13 +16,13 @@ <method name="get_id" qualifiers="const"> <return type="String" /> <description> - Returns the current [Thread]'s ID, uniquely identifying it among all threads. If the [Thread] is not running this returns an empty string. + Returns the current [Thread]'s ID, uniquely identifying it among all threads. If the [Thread] has not started running or if [method wait_to_finish] has been called, this returns an empty string. </description> </method> <method name="is_alive" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if this [Thread] is currently running. This is useful for determining if [method wait_to_finish] can be called without blocking the calling thread. + Returns [code]true[/code] if this [Thread] is currently running the provided function. This is useful for determining if [method wait_to_finish] can be called without blocking the calling thread. To check if a [Thread] is joinable, use [method is_started]. </description> </method> diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index f3ed90a015..7bb9bf85ef 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -120,7 +120,7 @@ <return type="bool" /> <param index="0" name="xform" type="Transform2D" /> <description> - Returns [code]true[/code] if this transform and [code]transform[/code] are approximately equal, by calling [code]is_equal_approx[/code] on each component. + Returns [code]true[/code] if this transform and [param xform] are approximately equal, by calling [code]is_equal_approx[/code] on each component. </description> </method> <method name="is_finite" qualifiers="const"> @@ -133,7 +133,7 @@ <return type="Transform2D" /> <param index="0" name="target" type="Vector2" default="Vector2(0, 0)" /> <description> - Returns a copy of the transform rotated such that it's rotation on the X-axis points towards the [param target] position. + Returns a copy of the transform rotated such that the rotated X-axis points towards the [param target] position. Operations take place in global space. </description> </method> diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index 90c10e3664..e0485c7756 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -80,7 +80,7 @@ <return type="bool" /> <param index="0" name="xform" type="Transform3D" /> <description> - Returns [code]true[/code] if this transform and [code]transform[/code] are approximately equal, by calling [code]is_equal_approx[/code] on each component. + Returns [code]true[/code] if this transform and [param xform] are approximately equal, by calling [code]is_equal_approx[/code] on each component. </description> </method> <method name="is_finite" qualifiers="const"> diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index 704430a1c2..37a58722a4 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -89,8 +89,8 @@ [/codeblocks] Some [Tweener]s use transitions and eases. The first accepts a [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [url=https://easings.net/]easings.net[/url] for some examples). The second accepts an [enum EaseType] constant, and controls where the [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [constant EASE_IN_OUT], and use the one that looks best. [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/tween_cheatsheet.png]Tween easing and transition types cheatsheet[/url] - [b]Note:[/b] All [Tween]s will automatically start by default. To prevent a [Tween] from autostarting, you can call [method stop] immediately after it is created. - [b]Note:[/b] [Tween]s are processing after all of nodes in the current frame, i.e. after [method Node._process] or [method Node._physics_process] (depending on [enum TweenProcessMode]). + [b]Note:[/b] Tweens are not designed to be re-used and trying to do so results in an undefined behavior. Create a new Tween for each animation and every time you replay an animation from start. Keep in mind that Tweens start immediately, so only create a Tween when you want to start animating. + [b]Note:[/b] Tweens are processing after all of nodes in the current frame, i.e. after [method Node._process] or [method Node._physics_process] (depending on [enum TweenProcessMode]). </description> <tutorials> </tutorials> @@ -129,7 +129,6 @@ <description> Processes the [Tween] by the given [param delta] value, in seconds. This is mostly useful for manual control when the [Tween] is paused. It can also be used to end the [Tween] animation immediately, by setting [param delta] longer than the whole duration of the [Tween] animation. Returns [code]true[/code] if the [Tween] still has [Tweener]s that haven't finished. - [b]Note:[/b] The [Tween] will become invalid in the next processing frame after its animation finishes. Calling [method stop] after performing [method custom_step] instead keeps and resets the [Tween]. </description> </method> <method name="get_total_elapsed_time" qualifiers="const"> @@ -432,7 +431,6 @@ <signal name="finished"> <description> Emitted when the [Tween] has finished all tweening. Never emitted when the [Tween] is set to infinite looping (see [method set_loops]). - [b]Note:[/b] The [Tween] is removed (invalidated) in the next processing frame after this signal is emitted. Calling [method stop] inside the signal callback will prevent the [Tween] from being removed. </description> </signal> <signal name="loop_finished"> diff --git a/doc/classes/UDPServer.xml b/doc/classes/UDPServer.xml index 8151ecb625..60d1986bee 100644 --- a/doc/classes/UDPServer.xml +++ b/doc/classes/UDPServer.xml @@ -22,7 +22,7 @@ func _process(delta): server.poll() # Important! if server.is_connection_available(): - var peer : PacketPeerUDP = server.take_connection() + var peer: PacketPeerUDP = server.take_connection() var packet = peer.get_packet() print("Accepted peer: %s:%s" % [peer.get_packet_ip(), peer.get_packet_port()]) print("Received data: %s" % [packet.get_string_from_utf8()]) diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 92050eaa34..b8f731809a 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -582,7 +582,7 @@ The window's title. If the [Window] is non-embedded, title styles set in [Theme] will have no effect. </member> <member name="transient" type="bool" setter="set_transient" getter="is_transient" default="false"> - If [code]true[/code], the [Window] is transient, i.e. it's considered a child of another [Window]. Transient window is will be destroyed with its transient parent and will return focus to their parent when closed. The transient window is displayed on top of a non-exclusive full-screen parent window. Transient windows can't enter full-screen mode. + If [code]true[/code], the [Window] is transient, i.e. it's considered a child of another [Window]. The transient window will be destroyed with its transient parent and will return focus to their parent when closed. The transient window is displayed on top of a non-exclusive full-screen parent window. Transient windows can't enter full-screen mode. Note that behavior might be different depending on the platform. </member> <member name="transparent" type="bool" setter="set_flag" getter="get_flag" default="false"> @@ -764,7 +764,7 @@ The content can be expanded horizontally. Scaling vertically will result in keeping the height ratio and then black bars on top and bottom sides. </constant> <constant name="CONTENT_SCALE_ASPECT_EXPAND" value="4" enum="ContentScaleAspect"> - The content's aspect will be preserved. If the target size has different aspect from the base one, the content will stay in the to-left corner and add an extra visible area in the stretched space. + The content's aspect will be preserved. If the target size has different aspect from the base one, the content will stay in the top-left corner and add an extra visible area in the stretched space. </constant> <constant name="LAYOUT_DIRECTION_INHERITED" value="0" enum="LayoutDirection"> Automatic layout direction, determined from the parent window layout direction. diff --git a/doc/classes/XRInterface.xml b/doc/classes/XRInterface.xml index cdada35970..96a6f5decd 100644 --- a/doc/classes/XRInterface.xml +++ b/doc/classes/XRInterface.xml @@ -116,9 +116,9 @@ [b]Note:[/b] Not all runtimes support all environment blend modes, so it is important to check this at startup. For example: [codeblock] func _ready(): - var xr_interface : XRInterface = XRServer.find_interface("OpenXR") + var xr_interface: XRInterface = XRServer.find_interface("OpenXR") if xr_interface and xr_interface.is_initialized(): - var vp : Viewport = get_viewport() + var vp: Viewport = get_viewport() vp.use_xr = true var acceptable_modes = [ XRInterface.XR_ENV_BLEND_MODE_OPAQUE, XRInterface.XR_ENV_BLEND_MODE_ADDITIVE ] var modes = xr_interface.get_supported_environment_blend_modes() diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index ce66943328..dae722186c 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -832,6 +832,8 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { tex->is_render_target = false; tex->is_proxy = true; tex->proxies.clear(); + tex->canvas_texture = nullptr; + tex->tex_id = 0; proxy_to->proxies.push_back(p_texture); } diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index ed7638414c..ece5537ff3 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -257,7 +257,14 @@ void EditorAudioBus::update_bus() { } void EditorAudioBus::_name_changed(const String &p_new_name) { + if (updating_bus) { + return; + } + updating_bus = true; + track_name->release_focus(); + if (p_new_name == AudioServer::get_singleton()->get_bus_name(get_index())) { + updating_bus = false; return; } @@ -280,12 +287,15 @@ void EditorAudioBus::_name_changed(const String &p_new_name) { attempts++; attempt = p_new_name + " " + itos(attempts); } - updating_bus = true; EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); StringName current = AudioServer::get_singleton()->get_bus_name(get_index()); + ur->create_action(TTR("Rename Audio Bus")); + ur->add_do_method(buses, "_set_renaming_buses", true); + ur->add_undo_method(buses, "_set_renaming_buses", true); + ur->add_do_method(AudioServer::get_singleton(), "set_bus_name", get_index(), attempt); ur->add_undo_method(AudioServer::get_singleton(), "set_bus_name", get_index(), current); @@ -301,11 +311,12 @@ void EditorAudioBus::_name_changed(const String &p_new_name) { ur->add_do_method(buses, "_update_sends"); ur->add_undo_method(buses, "_update_sends"); + + ur->add_do_method(buses, "_set_renaming_buses", false); + ur->add_undo_method(buses, "_set_renaming_buses", false); ur->commit_action(); updating_bus = false; - - track_name->release_focus(); } void EditorAudioBus::_volume_changed(float p_normalized) { @@ -995,12 +1006,31 @@ void EditorAudioBusDrop::_bind_methods() { EditorAudioBusDrop::EditorAudioBusDrop() { } +void EditorAudioBuses::_set_renaming_buses(bool p_renaming) { + renaming_buses = p_renaming; +} + void EditorAudioBuses::_update_buses() { - while (bus_hb->get_child_count() > 0) { - memdelete(bus_hb->get_child(0)); + if (renaming_buses) { + // This case will be handled more gracefully, no need to trigger a full rebuild. + // This is possibly a mistake in the AudioServer, which fires bus_layout_changed + // on a rename. This may not be intended, but no way to tell at the moment. + return; } - drop_end = nullptr; + for (int i = bus_hb->get_child_count() - 1; i >= 0; i--) { + EditorAudioBus *audio_bus = Object::cast_to<EditorAudioBus>(bus_hb->get_child(i)); + if (audio_bus) { + bus_hb->remove_child(audio_bus); + audio_bus->queue_free(); + } + } + + if (drop_end) { + bus_hb->remove_child(drop_end); + drop_end->queue_free(); + drop_end = nullptr; + } for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { bool is_master = (i == 0); @@ -1033,6 +1063,7 @@ void EditorAudioBuses::_notification(int p_what) { case NOTIFICATION_DRAG_END: { if (drop_end) { + bus_hb->remove_child(drop_end); drop_end->queue_free(); drop_end = nullptr; } @@ -1259,6 +1290,7 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) { } void EditorAudioBuses::_bind_methods() { + ClassDB::bind_method("_set_renaming_buses", &EditorAudioBuses::_set_renaming_buses); ClassDB::bind_method("_update_buses", &EditorAudioBuses::_update_buses); ClassDB::bind_method("_update_bus", &EditorAudioBuses::_update_bus); ClassDB::bind_method("_update_sends", &EditorAudioBuses::_update_sends); diff --git a/editor/editor_audio_buses.h b/editor/editor_audio_buses.h index 8a7f9b6456..9ff5afac66 100644 --- a/editor/editor_audio_buses.h +++ b/editor/editor_audio_buses.h @@ -172,6 +172,9 @@ class EditorAudioBuses : public VBoxContainer { Timer *save_timer = nullptr; String edited_path; + bool renaming_buses = false; + void _set_renaming_buses(bool p_renaming); + void _add_bus(); void _update_buses(); void _update_bus(int p_index); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 6fcf092834..8040ba5f1c 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -151,6 +151,51 @@ EditorNode *EditorNode::singleton = nullptr; // The metadata key used to store and retrieve the version text to copy to the clipboard. static const String META_TEXT_TO_COPY = "text_to_copy"; +class AcceptDialogAutoReparent : public AcceptDialog { + GDCLASS(AcceptDialogAutoReparent, AcceptDialog); + +protected: + void _notification(int p_what) { + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + if (!is_visible()) { + Node *p = get_parent(); + if (p) { + p->remove_child(this); + } + } + } + } + +public: + void attach_and_popup_centered() { + EditorNode *ed = EditorNode::get_singleton(); + if (ed && !is_inside_tree()) { + Window *w = ed->get_window(); + while (w && w->get_exclusive_child()) { + w = w->get_exclusive_child(); + } + if (w && w != this) { + w->add_child(this); + popup_centered(); + } + } + } + + void attach_and_popup_centered_ratio(float p_ratio = 0.8) { + EditorNode *ed = EditorNode::get_singleton(); + if (ed && !is_inside_tree()) { + Window *w = ed->get_window(); + while (w && w->get_exclusive_child()) { + w = w->get_exclusive_child(); + } + if (w && w != this) { + w->add_child(this); + popup_centered_ratio(p_ratio); + } + } + } +}; + void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames) { ERR_FAIL_COND_MSG(p_full_paths.size() != r_filenames.size(), vformat("disambiguate_filenames requires two string vectors of same length (%d != %d).", p_full_paths.size(), r_filenames.size())); @@ -615,6 +660,21 @@ void EditorNode::_notification(int p_what) { if (progress_dialog) { progress_dialog->queue_free(); } + if (load_error_dialog) { + load_error_dialog->queue_free(); + } + if (execute_output_dialog) { + execute_output_dialog->queue_free(); + } + if (warning) { + warning->queue_free(); + } + if (accept) { + accept->queue_free(); + } + if (save_accept) { + save_accept->queue_free(); + } editor_data.save_editor_external_data(); FileAccess::set_file_close_fail_notify_callback(nullptr); log->deinit(); // Do not get messages anymore. @@ -1233,6 +1293,12 @@ void EditorNode::edit_resource(const Ref<Resource> &p_resource) { void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path) { editor_data.apply_changes_in_editors(); + + if (saving_resources_in_path.has(p_resource)) { + return; + } + saving_resources_in_path.insert(p_resource); + int flg = 0; if (EDITOR_GET("filesystem/on_save/compress_binary_resources")) { flg |= ResourceSaver::FLAG_COMPRESS; @@ -1247,10 +1313,16 @@ void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const St } else { show_accept(TTR("Error saving resource!"), TTR("OK")); } + + saving_resources_in_path.erase(p_resource); return; } ((Resource *)p_resource.ptr())->set_path(path); + saving_resources_in_path.erase(p_resource); + + _resource_saved(p_resource, path); + emit_signal(SNAME("resource_saved"), p_resource); editor_data.notify_resource_saved(p_resource); } @@ -4266,9 +4338,11 @@ void EditorNode::add_io_error(const String &p_error) { void EditorNode::_load_error_notify(void *p_ud, const String &p_text) { EditorNode *en = static_cast<EditorNode *>(p_ud); - en->load_errors->add_image(en->gui_base->get_theme_icon(SNAME("Error"), SNAME("EditorIcons"))); - en->load_errors->add_text(p_text + "\n"); - en->load_error_dialog->popup_centered_ratio(0.5); + if (en && en->load_error_dialog) { + en->load_errors->add_image(en->gui_base->get_theme_icon(SNAME("Error"), SNAME("EditorIcons"))); + en->load_errors->add_text(p_text + "\n"); + en->load_error_dialog->attach_and_popup_centered_ratio(0.5); + } } bool EditorNode::_find_scene_in_use(Node *p_node, const String &p_path) const { @@ -4639,23 +4713,27 @@ Error EditorNode::export_preset(const String &p_preset, const String &p_path, bo void EditorNode::show_accept(const String &p_text, const String &p_title) { current_menu_option = -1; - accept->set_ok_button_text(p_title); - accept->set_text(p_text); - accept->popup_centered(); + if (accept) { + accept->set_ok_button_text(p_title); + accept->set_text(p_text); + accept->attach_and_popup_centered(); + } } void EditorNode::show_save_accept(const String &p_text, const String &p_title) { current_menu_option = -1; - save_accept->set_ok_button_text(p_title); - save_accept->set_text(p_text); - save_accept->popup_centered(); + if (save_accept) { + save_accept->set_ok_button_text(p_title); + save_accept->set_text(p_text); + save_accept->attach_and_popup_centered(); + } } void EditorNode::show_warning(const String &p_text, const String &p_title) { - if (warning->is_inside_tree()) { + if (warning) { warning->set_text(p_text); warning->set_title(p_title); - warning->popup_centered(); + warning->attach_and_popup_centered(); } else { WARN_PRINT(p_title + " " + p_text); } @@ -4795,6 +4873,7 @@ void EditorNode::_dock_select_input(const Ref<InputEvent> &p_input) { dock_slot[nrect]->add_child(dock); dock_popup_selected_idx = nrect; dock_slot[nrect]->set_current_tab(dock_slot[nrect]->get_tab_count() - 1); + dock_slot[nrect]->set_tab_title(dock_slot[nrect]->get_tab_count() - 1, TTRGET(dock->get_name())); dock_slot[nrect]->show(); dock_select->queue_redraw(); @@ -5152,6 +5231,7 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String } dock_slot[i]->add_child(node); dock_slot[i]->move_child(node, 0); + dock_slot[i]->set_tab_title(0, TTRGET(node->get_name())); dock_slot[i]->show(); } } @@ -6441,6 +6521,11 @@ void EditorNode::_renderer_selected(int p_which) { } void EditorNode::_resource_saved(Ref<Resource> p_resource, const String &p_path) { + if (singleton->saving_resources_in_path.has(p_resource)) { + // This is going to be handled by save_resource_in_path when the time is right. + return; + } + if (EditorFileSystem::get_singleton()) { EditorFileSystem::get_singleton()->update_file(p_path); } @@ -6492,7 +6577,6 @@ void EditorNode::_feature_profile_changed() { } void EditorNode::_bind_methods() { - GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/scene_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"), SCENE_NAME_CASING_SNAKE_CASE); ClassDB::bind_method("edit_current", &EditorNode::edit_current); ClassDB::bind_method("edit_node", &EditorNode::edit_node); @@ -6549,11 +6633,13 @@ static void _execute_thread(void *p_ud) { } int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok, bool p_close_on_errors) { - execute_output_dialog->set_title(p_title); - execute_output_dialog->get_ok_button()->set_disabled(true); - execute_outputs->clear(); - execute_outputs->set_scroll_follow(true); - execute_output_dialog->popup_centered_ratio(); + if (execute_output_dialog) { + execute_output_dialog->set_title(p_title); + execute_output_dialog->get_ok_button()->set_disabled(true); + execute_outputs->clear(); + execute_outputs->set_scroll_follow(true); + execute_output_dialog->attach_and_popup_centered_ratio(); + } ExecuteThreadArgs eta; eta.path = p_path; @@ -6571,6 +6657,7 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p String to_add = eta.output.substr(prev_len, eta.output.length()); prev_len = eta.output.length(); execute_outputs->add_text(to_add); + DisplayServer::get_singleton()->process_events(); // Get rid of pending events. Main::iteration(); } } @@ -6580,14 +6667,16 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p eta.execute_output_thread.wait_to_finish(); execute_outputs->add_text("\nExit Code: " + itos(eta.exitcode)); - if (p_close_on_errors && eta.exitcode != 0) { - execute_output_dialog->hide(); - } - if (p_close_on_ok && eta.exitcode == 0) { - execute_output_dialog->hide(); - } + if (execute_output_dialog) { + if (p_close_on_errors && eta.exitcode != 0) { + execute_output_dialog->hide(); + } + if (p_close_on_ok && eta.exitcode == 0) { + execute_output_dialog->hide(); + } - execute_output_dialog->get_ok_button()->set_disabled(false); + execute_output_dialog->get_ok_button()->set_disabled(false); + } return eta.exitcode; } @@ -7158,12 +7247,10 @@ EditorNode::EditorNode() { prev_scene->set_position(Point2(3, 24)); prev_scene->hide(); - accept = memnew(AcceptDialog); - gui_base->add_child(accept); + accept = memnew(AcceptDialogAutoReparent); accept->connect("confirmed", callable_mp(this, &EditorNode::_menu_confirm_current)); - save_accept = memnew(AcceptDialog); - gui_base->add_child(save_accept); + save_accept = memnew(AcceptDialogAutoReparent); save_accept->connect("confirmed", callable_mp(this, &EditorNode::_menu_option).bind((int)MenuOptions::FILE_SAVE_AS_SCENE)); project_export = memnew(ProjectExportDialog); @@ -7208,9 +7295,8 @@ EditorNode::EditorNode() { gui_base->add_child(fbx_importer_manager); #endif - warning = memnew(AcceptDialog); + warning = memnew(AcceptDialogAutoReparent); warning->add_button(TTR("Copy Text"), true, "copy"); - gui_base->add_child(warning); warning->connect("custom_action", callable_mp(this, &EditorNode::_copy_warning)); ED_SHORTCUT("editor/next_tab", TTR("Next Scene Tab"), KeyModifierMask::CMD_OR_CTRL + Key::TAB); @@ -7978,17 +8064,15 @@ EditorNode::EditorNode() { set_process_shortcut_input(true); load_errors = memnew(RichTextLabel); - load_error_dialog = memnew(AcceptDialog); + load_error_dialog = memnew(AcceptDialogAutoReparent); load_error_dialog->add_child(load_errors); load_error_dialog->set_title(TTR("Load Errors")); - gui_base->add_child(load_error_dialog); execute_outputs = memnew(RichTextLabel); execute_outputs->set_selection_enabled(true); - execute_output_dialog = memnew(AcceptDialog); + execute_output_dialog = memnew(AcceptDialogAutoReparent); execute_output_dialog->add_child(execute_outputs); execute_output_dialog->set_title(""); - gui_base->add_child(execute_output_dialog); EditorFileSystem::get_singleton()->connect("sources_changed", callable_mp(this, &EditorNode::_sources_changed)); EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorNode::_fs_changed)); diff --git a/editor/editor_node.h b/editor/editor_node.h index 8ad5969249..66a3bf5be2 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -45,6 +45,7 @@ typedef void (*EditorPluginInitializeCallback)(); typedef bool (*EditorBuildCallback)(); class AcceptDialog; +class AcceptDialogAutoReparent; class AudioStreamPreviewGenerator; class BackgroundProgress; class CenterContainer; @@ -370,10 +371,10 @@ private: PluginConfigDialog *plugin_config_dialog = nullptr; RichTextLabel *load_errors = nullptr; - AcceptDialog *load_error_dialog = nullptr; + AcceptDialogAutoReparent *load_error_dialog = nullptr; RichTextLabel *execute_outputs = nullptr; - AcceptDialog *execute_output_dialog = nullptr; + AcceptDialogAutoReparent *execute_output_dialog = nullptr; Ref<Theme> theme; @@ -388,10 +389,10 @@ private: ConfirmationDialog *import_confirmation = nullptr; ConfirmationDialog *pick_main_scene = nullptr; Button *select_current_scene_button = nullptr; - AcceptDialog *accept = nullptr; - AcceptDialog *save_accept = nullptr; + AcceptDialogAutoReparent *accept = nullptr; + AcceptDialogAutoReparent *save_accept = nullptr; EditorAbout *about = nullptr; - AcceptDialog *warning = nullptr; + AcceptDialogAutoReparent *warning = nullptr; int overridden_default_layout = -1; Ref<ConfigFile> default_layout; @@ -485,6 +486,7 @@ private: Object *current = nullptr; Ref<Resource> saving_resource; + HashSet<Ref<Resource>> saving_resources_in_path; uint64_t update_spinner_step_msec = 0; uint64_t update_spinner_step_frame = 0; diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 7b01ae69bf..fb2110dea3 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -137,6 +137,7 @@ Vector<Ref<Texture2D>> EditorInterface::make_mesh_previews(const Vector<Ref<Mesh RS::get_singleton()->instance_set_transform(light_instance2, xform * Transform3D().looking_at(Vector3(+1, -1, -2), Vector3(0, 1, 0))); ep.step(TTR("Thumbnail..."), i); + DisplayServer::get_singleton()->process_events(); Main::iteration(); Main::iteration(); Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture); diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 86ffbccefd..a83db21f20 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -399,8 +399,12 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { file_system_dock->navigate_to_path(edited_resource->get_path()); // Ensure that the FileSystem dock is visible. - TabContainer *tab_container = (TabContainer *)file_system_dock->get_parent_control(); - tab_container->set_current_tab(tab_container->get_tab_idx_from_control(file_system_dock)); + if (file_system_dock->get_window() == get_tree()->get_root()) { + TabContainer *tab_container = (TabContainer *)file_system_dock->get_parent_control(); + tab_container->set_current_tab(tab_container->get_tab_idx_from_control(file_system_dock)); + } else { + file_system_dock->get_window()->grab_focus(); + } } break; default: { diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 2ab15c1c2c..3e6b0ee07f 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -231,11 +231,11 @@ static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1, return style; } -#ifdef MODULE_SVG_ENABLED // See also `generate_icon()` in `scene/resources/default_theme.cpp`. static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float p_saturation, const HashMap<Color, Color> &p_convert_colors = HashMap<Color, Color>()) { Ref<Image> img = memnew(Image); +#ifdef MODULE_SVG_ENABLED // Upsample icon generation only if the editor scale isn't an integer multiplier. // Generating upsampled icons is slower, and the benefit is hardly visible // with integer editor scales. @@ -246,13 +246,16 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float if (p_saturation != 1.0) { img->adjust_bcs(1.0, 1.0, p_saturation); } +#else + // If the SVG module is disabled, we can't really display the UI well, but at least we won't crash. + // 16 pixels is used as it's the most common base size for Godot icons. + img = Image::create_empty(16 * p_scale, 16 * p_scale, false, Image::FORMAT_RGBA8); +#endif return ImageTexture::create_from_image(img); } -#endif void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, float p_icon_saturation, int p_thumb_size, bool p_only_thumbs = false) { -#ifdef MODULE_SVG_ENABLED // Before we register the icons, we adjust their colors and saturation. // Most icons follow the standard rules for color conversion to follow the editor // theme's polarity (dark/light). We also adjust the saturation for most icons, @@ -379,9 +382,6 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, f p_theme->set_icon(editor_icons_names[index], SNAME("EditorIcons"), icon); } } -#else - WARN_PRINT("SVG support disabled, editor icons won't be rendered."); -#endif } Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { @@ -618,6 +618,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { regenerate_thumb_icons = !Math::is_equal_approx(prev_thumb_size, thumb_size); } +#ifndef MODULE_SVG_ENABLED + WARN_PRINT("SVG support disabled, editor icons won't be rendered."); +#endif + if (keep_old_icons) { for (int i = 0; i < editor_icons_count; i++) { theme->set_icon(editor_icons_names[i], SNAME("EditorIcons"), p_theme->get_icon(editor_icons_names[i], SNAME("EditorIcons"))); diff --git a/editor/editor_toaster.cpp b/editor/editor_toaster.cpp index 73b645f351..10c3e963af 100644 --- a/editor/editor_toaster.cpp +++ b/editor/editor_toaster.cpp @@ -90,10 +90,10 @@ void EditorToaster::_notification(int p_what) { } // Hide element if it is not visible anymore. - if (modulate_fade.a <= 0 && element.key->is_visible()) { + if (modulate_fade.a <= 0.0 && element.key->is_visible()) { element.key->hide(); needs_update = true; - } else if (modulate_fade.a >= 0 && !element.key->is_visible()) { + } else if (modulate_fade.a > 0.0 && !element.key->is_visible()) { element.key->show(); needs_update = true; } diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index e9ea0012be..a7ac95bd5b 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -943,8 +943,8 @@ void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) { } void ProjectExportDialog::_open_export_template_manager() { - EditorNode::get_singleton()->open_export_template_manager(); hide(); + EditorNode::get_singleton()->open_export_template_manager(); } void ProjectExportDialog::_validate_export_path(const String &p_path) { diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index fcefbb7d06..235e34d9b5 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -648,7 +648,7 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() { if (!tile_data_editors.has("texture_origin")) { TileDataTextureOriginEditor *tile_data_texture_origin_editor = memnew(TileDataTextureOriginEditor); tile_data_texture_origin_editor->hide(); - tile_data_texture_origin_editor->setup_property_editor(Variant::VECTOR2, "texture_origin"); + tile_data_texture_origin_editor->setup_property_editor(Variant::VECTOR2I, "texture_origin"); tile_data_texture_origin_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::queue_redraw)); tile_data_texture_origin_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::queue_redraw)); tile_data_editors["texture_origin"] = tile_data_texture_origin_editor; diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 78522dfa73..1682d07e13 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -145,7 +145,10 @@ void TilesEditorPlugin::_tile_map_changed() { void TilesEditorPlugin::_update_editors() { // If tile_map is not edited, we change the edited only if we are not editing a tile_set. - tileset_editor->edit(tile_set); + if (tile_set.is_valid()) { + tileset_editor->edit(tile_set); + } + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (tile_map) { tilemap_editor->edit(tile_map); @@ -158,6 +161,7 @@ void TilesEditorPlugin::_update_editors() { // Make sure the tile set editor is visible if we have one assigned. tileset_editor_button->set_visible(is_visible && tile_set.is_valid()); + tilemap_editor_button->set_visible(is_visible && tile_map); // Update visibility of bottom panel buttons. if (tileset_editor_button->is_pressed() && !tile_set.is_valid()) { diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp index 9695a7042d..4d76fdc997 100644 --- a/editor/progress_dialog.cpp +++ b/editor/progress_dialog.cpp @@ -221,9 +221,7 @@ bool ProgressDialog::task_step(const String &p_task, const String &p_state, int t.state->set_text(p_state); last_progress_tick = OS::get_singleton()->get_ticks_usec(); - if (cancel_hb->is_visible()) { - DisplayServer::get_singleton()->process_events(); - } + DisplayServer::get_singleton()->process_events(); #ifndef ANDROID_ENABLED Main::iteration(); // this will not work on a lot of platforms, so it's only meant for the editor diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 3c6f5c0955..6aa403de17 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -70,9 +70,9 @@ public: RegEx reg_json_print = RegEx("\\bJSON\\b\\.print\\("); RegEx reg_export = RegEx("export\\(([a-zA-Z0-9_]+)\\)[ ]+var[ ]+([a-zA-Z0-9_]+)"); RegEx reg_export_advanced = RegEx("export\\(([^)^\n]+)\\)[ ]+var[ ]+([a-zA-Z0-9_]+)([^\n]+)"); - RegEx reg_setget_setget = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+)setget[ \t]+([a-zA-Z0-9_]+)[ \t]*,[ \t]*([a-zA-Z0-9_]+)"); - RegEx reg_setget_set = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+)setget[ \t]+([a-zA-Z0-9_]+)[ \t]*[,]*[^a-z^A-Z^0-9^_]*$"); - RegEx reg_setget_get = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+)setget[ \t]+,[ \t]*([a-zA-Z0-9_]+)[ \t]*$"); + RegEx reg_setget_setget = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+?)[ \t]*setget[ \t]+([a-zA-Z0-9_]+)[ \t]*,[ \t]*([a-zA-Z0-9_]+)"); + RegEx reg_setget_set = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+?)[ \t]*setget[ \t]+([a-zA-Z0-9_]+)[ \t]*[,]*[^\n]*$"); + RegEx reg_setget_get = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+?)[ \t]*setget[ \t]+,[ \t]*([a-zA-Z0-9_]+)[ \t]*$"); RegEx reg_join = RegEx("([\\(\\)a-zA-Z0-9_]+)\\.join\\(([^\n^\\)]+)\\)"); RegEx reg_image_lock = RegEx("([a-zA-Z0-9_\\.]+)\\.lock\\(\\)"); RegEx reg_image_unlock = RegEx("([a-zA-Z0-9_\\.]+)\\.unlock\\(\\)"); @@ -828,10 +828,10 @@ bool ProjectConverter3To4::test_conversion(RegExContainer ®_container) { valid = valid && test_conversion_with_regex("\n\nmaster func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\nmastersync func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_gdscript_builtin("var size : Vector2 = Vector2() setget set_function , get_function", "var size : Vector2 = Vector2() : get = get_function, set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); - valid = valid && test_conversion_gdscript_builtin("var size : Vector2 = Vector2() setget set_function , ", "var size : Vector2 = Vector2() : set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); - valid = valid && test_conversion_gdscript_builtin("var size : Vector2 = Vector2() setget set_function", "var size : Vector2 = Vector2() : set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); - valid = valid && test_conversion_gdscript_builtin("var size : Vector2 = Vector2() setget , get_function", "var size : Vector2 = Vector2() : get = get_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget set_function, get_function", "var size: Vector2 = Vector2(): get = get_function, set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget set_function, ", "var size: Vector2 = Vector2(): set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget set_function", "var size: Vector2 = Vector2(): set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget , get_function", "var size: Vector2 = Vector2(): get = get_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid && test_conversion_gdscript_builtin("get_node(@", "get_node(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp index 061baaff7e..d9138128cd 100644 --- a/editor/register_editor_types.cpp +++ b/editor/register_editor_types.cpp @@ -212,6 +212,15 @@ void register_editor_types() { EditorPlugins::add_by_type<Skeleton2DEditorPlugin>(); EditorPlugins::add_by_type<Sprite2DEditorPlugin>(); EditorPlugins::add_by_type<TilesEditorPlugin>(); + + // For correct doc generation. + GLOBAL_DEF("editor/run/main_run_args", ""); + + GLOBAL_DEF(PropertyInfo(Variant::STRING, "editor/script/templates_search_path", PROPERTY_HINT_DIR), "res://script_templates"); + + GLOBAL_DEF("editor/naming/default_signal_callback_name", "_on_{node_name}_{signal_name}"); + GLOBAL_DEF("editor/naming/default_signal_callback_to_self_name", "_on_{signal_name}"); + GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/scene_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"), EditorNode::SCENE_NAME_CASING_SNAKE_CASE); } void unregister_editor_types() { diff --git a/main/main.cpp b/main/main.cpp index b15588e700..f4d2dbef52 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -108,6 +108,10 @@ #include "modules/modules_enabled.gen.h" // For mono. +#if defined(MODULE_MONO_ENABLED) && defined(TOOLS_ENABLED) +#include "modules/mono/editor/bindings_generator.h" +#endif + /* Static members */ // Singletons @@ -312,7 +316,6 @@ void finalize_navigation_server() { void initialize_theme_db() { theme_db = memnew(ThemeDB); - theme_db->initialize_theme(); } void finalize_theme_db() { @@ -532,6 +535,7 @@ Error Main::test_setup() { // Theme needs modules to be initialized so that sub-resources can be loaded. initialize_theme_db(); + theme_db->initialize_theme(); register_scene_singletons(); ERR_FAIL_COND_V(TextServerManager::get_singleton()->get_interface_count() == 0, ERR_CANT_CREATE); @@ -2314,6 +2318,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { register_platform_apis(); // Theme needs modules to be initialized so that sub-resources can be loaded. + // Default theme is initialized later, after ScriptServer is ready. initialize_theme_db(); register_scene_singletons(); @@ -2341,8 +2346,18 @@ Error Main::setup2(Thread::ID p_main_tid_override) { // This loads global classes, so it must happen before custom loaders and savers are registered ScriptServer::init_languages(); + theme_db->initialize_theme(); audio_server->load_default_bus_layout(); +#if defined(MODULE_MONO_ENABLED) && defined(TOOLS_ENABLED) + // Hacky to have it here, but we don't have good facility yet to let modules + // register command line options to call at the right time. This needs to happen + // after init'ing the ScriptServer, but also after init'ing the ThemeDB, + // for the C# docs generation in the bindings. + List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); + BindingsGenerator::handle_cmdline_args(cmdline_args); +#endif + if (use_debug_profiler && EngineDebugger::is_active()) { // Start the "scripts" profiler, used in local debugging. // We could add more, and make the CLI arg require a comma-separated list of profilers. diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 045505b720..effc0aab96 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -285,7 +285,7 @@ @export var string = "" @export var int_number = 5 @export var float_number: float = 5 - @export var image : Image + @export var image: Image [/codeblock] </description> </annotation> @@ -309,7 +309,7 @@ Export a [Color] property without allowing its transparency ([member Color.a]) to be edited. See also [constant PROPERTY_HINT_COLOR_NO_ALPHA]. [codeblock] - @export_color_no_alpha var dye_color : Color + @export_color_no_alpha var dye_color: Color [/codeblock] </description> </annotation> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index b6caefbdb5..1a1d021dbc 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1010,12 +1010,14 @@ void GDScript::_bind_methods() { } void GDScript::set_path(const String &p_path, bool p_take_over) { - String old_path = path; if (is_root_script()) { Script::set_path(p_path, p_take_over); } - this->path = p_path; + + String old_path = path; + path = p_path; GDScriptCache::move_script(old_path, p_path); + for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) { kv.value->set_path(p_path, p_take_over); } diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd index 62ac1c3108..900155569c 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd @@ -1,7 +1,7 @@ enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 } enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 } -func enum_func(e : MyEnum) -> void: +func enum_func(e: MyEnum) -> void: print(e) func test(): diff --git a/modules/gdscript/tests/scripts/analyzer/errors/native_freed_instance.gd b/modules/gdscript/tests/scripts/analyzer/errors/native_freed_instance.gd index dd2708b21d..63c080e583 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/native_freed_instance.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/native_freed_instance.gd @@ -4,4 +4,4 @@ func test(): x.free() var ok = x - var bad : Node = x + var bad: Node = x diff --git a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd index 4e75ded96a..c84a4ad8af 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd @@ -2,5 +2,5 @@ enum LocalNamed { VALUE_A, VALUE_B, VALUE_C = 42 } func test(): const P = preload("../features/enum_from_outer.gd") - var x : LocalNamed + var x: LocalNamed x = P.Named.VALUE_A diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd index f1be6aaa0c..35f506aabd 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd @@ -1,7 +1,7 @@ -var _prop : int +var _prop: int # Getter function has wrong return type. -var prop : String: +var prop: String: get = get_prop func get_prop(): diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd index dd190157a1..eedeea915d 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd @@ -1,10 +1,10 @@ -var _prop : int +var _prop: int # Setter function has wrong argument type. -var prop : String: +var prop: String: set = set_prop -func set_prop(value : int): +func set_prop(value: int): _prop = value func test(): diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd index 7f2b29222a..90b00fbe13 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd @@ -1,7 +1,7 @@ -var _prop : int +var _prop: int # Inline getter returns int instead of String. -var prop : String: +var prop: String: get: return _prop diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd index 0ce239dbbd..5747b85fc7 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd @@ -1,7 +1,7 @@ -var _prop : int +var _prop: int # Inline setter assigns String to int. -var prop : String: +var prop: String: set(value): _prop = value diff --git a/modules/gdscript/tests/scripts/analyzer/errors/script_freed_instance.gd b/modules/gdscript/tests/scripts/analyzer/errors/script_freed_instance.gd index 758fbaccc9..b0cfdea75d 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/script_freed_instance.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/script_freed_instance.gd @@ -7,4 +7,4 @@ func test(): x.free() var ok = x - var bad : A = x + var bad: A = x diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd index 3076e7069f..8e6bb22a12 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd @@ -1,7 +1,7 @@ enum Enum {V1, V2} func test(): - var enumAsDict : Dictionary = Enum.duplicate() + var enumAsDict: Dictionary = Enum.duplicate() var enumAsVariant = Enum.duplicate() print(Enum.has("V1")) print(enumAsDict.has("V1")) diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd index 8a4e89d0d6..3a1863a1f4 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd @@ -5,19 +5,19 @@ enum MyEnum { V0, V1, V2 } class InnerClass: enum MyEnum { V0, V2, V1 } - func inner_inner_no_class(e : MyEnum) -> MyEnum: + func inner_inner_no_class(e: MyEnum) -> MyEnum: print(e) return e - func inner_inner_class(e : InnerClass.MyEnum) -> InnerClass.MyEnum: + func inner_inner_class(e: InnerClass.MyEnum) -> InnerClass.MyEnum: print(e) return e - func inner_inner_class_class(e : EnumFunctionTypecheckOuterClass.InnerClass.MyEnum) -> EnumFunctionTypecheckOuterClass.InnerClass.MyEnum: + func inner_inner_class_class(e: EnumFunctionTypecheckOuterClass.InnerClass.MyEnum) -> EnumFunctionTypecheckOuterClass.InnerClass.MyEnum: print(e) return e - func inner_outer(e : EnumFunctionTypecheckOuterClass.MyEnum) -> EnumFunctionTypecheckOuterClass.MyEnum: + func inner_outer(e: EnumFunctionTypecheckOuterClass.MyEnum) -> EnumFunctionTypecheckOuterClass.MyEnum: print(e) return e @@ -59,19 +59,19 @@ class InnerClass: print() -func outer_outer_no_class(e : MyEnum) -> MyEnum: +func outer_outer_no_class(e: MyEnum) -> MyEnum: print(e) return e -func outer_outer_class(e : EnumFunctionTypecheckOuterClass.MyEnum) -> EnumFunctionTypecheckOuterClass.MyEnum: +func outer_outer_class(e: EnumFunctionTypecheckOuterClass.MyEnum) -> EnumFunctionTypecheckOuterClass.MyEnum: print(e) return e -func outer_inner_class(e : InnerClass.MyEnum) -> InnerClass.MyEnum: +func outer_inner_class(e: InnerClass.MyEnum) -> InnerClass.MyEnum: print(e) return e -func outer_inner_class_class(e : EnumFunctionTypecheckOuterClass.InnerClass.MyEnum) -> EnumFunctionTypecheckOuterClass.InnerClass.MyEnum: +func outer_inner_class_class(e: EnumFunctionTypecheckOuterClass.InnerClass.MyEnum) -> EnumFunctionTypecheckOuterClass.InnerClass.MyEnum: print(e) return e diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd index 6a0a1e1969..0e4fd59267 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd @@ -1,9 +1,9 @@ -func print_enum(e : TileSet.TileShape) -> TileSet.TileShape: +func print_enum(e: TileSet.TileShape) -> TileSet.TileShape: print(e) return e func test(): - var v : TileSet.TileShape + var v: TileSet.TileShape v = TileSet.TILE_SHAPE_SQUARE v = print_enum(v) v = print_enum(TileSet.TILE_SHAPE_SQUARE) diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd index b05ae82048..1c4b19d8e0 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd @@ -7,9 +7,9 @@ class InnerClass: static func test_inner_from_inner(): print("Inner - Inner") - var e1 : MyEnum - var e2 : InnerClass.MyEnum - var e3 : EnumTypecheckOuterClass.InnerClass.MyEnum + var e1: MyEnum + var e2: InnerClass.MyEnum + var e3: EnumTypecheckOuterClass.InnerClass.MyEnum print("Self ", e1, e2, e3) e1 = MyEnum.V1 @@ -36,7 +36,7 @@ class InnerClass: static func test_outer_from_inner(): print("Inner - Outer") - var e : EnumTypecheckOuterClass.MyEnum + var e: EnumTypecheckOuterClass.MyEnum e = EnumTypecheckOuterClass.MyEnum.V1 print("Outer.MyEnum ", e) @@ -45,8 +45,8 @@ class InnerClass: func test_outer_from_outer(): print("Outer - Outer") - var e1 : MyEnum - var e2 : EnumTypecheckOuterClass.MyEnum + var e1: MyEnum + var e2: EnumTypecheckOuterClass.MyEnum print("Self ", e1, e2) e1 = MyEnum.V1 @@ -63,8 +63,8 @@ func test_outer_from_outer(): func test_inner_from_outer(): print("Outer - Inner") - var e1 : InnerClass.MyEnum - var e2 : EnumTypecheckOuterClass.InnerClass.MyEnum + var e1: InnerClass.MyEnum + var e2: EnumTypecheckOuterClass.InnerClass.MyEnum print("Inner ", e1, e2) e1 = InnerClass.MyEnum.V1 diff --git a/modules/gdscript/tests/scripts/analyzer/features/getter_return_type.gd b/modules/gdscript/tests/scripts/analyzer/features/getter_return_type.gd index 38bb7f6e9c..5b0e5c0bf0 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/getter_return_type.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/getter_return_type.gd @@ -1,4 +1,4 @@ -var Value:int = 8 : +var Value:int = 8: get: return Value set(v): diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd b/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd index 23eb011b23..474bb49a3b 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd @@ -6,21 +6,21 @@ var prop1: prop1 = value # Typed inline property -var prop2 : int: +var prop2: int: get: return prop2 set(value): prop2 = value # Typed inline property with default value -var prop3 : int = 1: +var prop3: int = 1: get: return prop3 set(value): prop3 = value # Typed inline property with backing variable -var _prop4 : int = 2 +var _prop4: int = 2 var prop4: int: get: return _prop4 diff --git a/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd index beabf3d2e5..5fbc91be5e 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd @@ -1,4 +1,4 @@ -const preloaded : GDScript = preload("gdscript_to_preload.notest.gd") +const preloaded: GDScript = preload("gdscript_to_preload.notest.gd") func test(): var preloaded_instance: preloaded = preloaded.new() diff --git a/modules/gdscript/tests/scripts/parser/features/static_typing.gd b/modules/gdscript/tests/scripts/parser/features/static_typing.gd index d42632c82d..0157ca2128 100644 --- a/modules/gdscript/tests/scripts/parser/features/static_typing.gd +++ b/modules/gdscript/tests/scripts/parser/features/static_typing.gd @@ -1,3 +1,5 @@ +# Do not fix code style here! + func test(): # The following lines are equivalent: var _integer: int = 1 diff --git a/modules/gdscript/tests/scripts/parser/features/static_typing.out b/modules/gdscript/tests/scripts/parser/features/static_typing.out index 92ce7bc0e0..207d90fef1 100644 --- a/modules/gdscript/tests/scripts/parser/features/static_typing.out +++ b/modules/gdscript/tests/scripts/parser/features/static_typing.out @@ -1,21 +1,21 @@ GDTEST_OK >> WARNING ->> Line: 9 +>> Line: 11 >> UNUSED_LOCAL_CONSTANT >> The local constant '_INTEGER' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER' >> WARNING ->> Line: 10 +>> Line: 12 >> UNUSED_LOCAL_CONSTANT >> The local constant '_INTEGER_REDUNDANT_TYPED' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_TYPED' >> WARNING ->> Line: 11 +>> Line: 13 >> UNUSED_LOCAL_CONSTANT >> The local constant '_INTEGER_REDUNDANT_TYPED2' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_TYPED2' >> WARNING ->> Line: 12 +>> Line: 14 >> UNUSED_LOCAL_CONSTANT >> The local constant '_INTEGER_REDUNDANT_INFERRED' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_INFERRED' >> WARNING ->> Line: 13 +>> Line: 15 >> UNUSED_LOCAL_CONSTANT >> The local constant '_INTEGER_REDUNDANT_INFERRED2' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_INFERRED2' diff --git a/modules/gdscript/tests/scripts/runtime/features/default_set_beforehand.gd b/modules/gdscript/tests/scripts/runtime/features/default_set_beforehand.gd index 03278e453f..0851d939dc 100644 --- a/modules/gdscript/tests/scripts/runtime/features/default_set_beforehand.gd +++ b/modules/gdscript/tests/scripts/runtime/features/default_set_beforehand.gd @@ -1,8 +1,8 @@ extends Node @onready var later_inferred := [1] -@onready var later_static : Array -@onready var later_static_with_init : Array = [1] +@onready var later_static: Array +@onready var later_static_with_init: Array = [1] @onready var later_untyped = [1] func test(): diff --git a/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd b/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd index 3eb02816ed..14c93eb159 100644 --- a/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd +++ b/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd @@ -1,5 +1,5 @@ #GDTEST_OK -var prop : int = 0: +var prop: int = 0: get: return prop set(value): diff --git a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd index efa8270526..20b024f09e 100644 --- a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd +++ b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd @@ -4,6 +4,6 @@ func test(): @warning_ignore("narrowing_conversion") var foo: int = 0.0 print(typeof(foo) == TYPE_INT) - var dict : Dictionary = {"a":0.0} + var dict: Dictionary = {"a": 0.0} foo = dict.get("a") print(typeof(foo) == TYPE_INT) diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml index f3b3e61501..b9223bfdfa 100644 --- a/modules/gltf/doc_classes/GLTFNode.xml +++ b/modules/gltf/doc_classes/GLTFNode.xml @@ -5,6 +5,7 @@ </brief_description> <description> Represents a GLTF node. GLTF nodes may have names, transforms, children (other GLTF nodes), and more specialized properties (represented by their own classes). + GLTF nodes generally exist inside of [GLTFState] which represents all data of a GLTF file. Most of GLTFNode's properties are indices of other data in the GLTF file. You can extend a GLTF node with additional properties by using [method get_additional_data] and [method set_additional_data]. </description> <tutorials> <link title="GLTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link> @@ -30,28 +31,40 @@ </methods> <members> <member name="camera" type="int" setter="set_camera" getter="get_camera" default="-1"> + If this GLTF node is a camera, the index of the [GLTFCamera] in the [GLTFState] that describes the camera's properties. If -1, this node is not a camera. </member> <member name="children" type="PackedInt32Array" setter="set_children" getter="get_children" default="PackedInt32Array()"> + The indices of the children nodes in the [GLTFState]. If this GLTF node has no children, this will be an empty array. </member> <member name="height" type="int" setter="set_height" getter="get_height" default="-1"> + How deep into the node hierarchy this node is. A root node will have a height of 0, its children will have a height of 1, and so on. If -1, the height has not been calculated. </member> <member name="light" type="int" setter="set_light" getter="get_light" default="-1"> + If this GLTF node is a light, the index of the [GLTFLight] in the [GLTFState] that describes the light's properties. If -1, this node is not a light. </member> <member name="mesh" type="int" setter="set_mesh" getter="get_mesh" default="-1"> + If this GLTF node is a mesh, the index of the [GLTFMesh] in the [GLTFState] that describes the mesh's properties. If -1, this node is not a mesh. </member> <member name="parent" type="int" setter="set_parent" getter="get_parent" default="-1"> + The index of the parent node in the [GLTFState]. If -1, this node is a root node. </member> <member name="position" type="Vector3" setter="set_position" getter="get_position" default="Vector3(0, 0, 0)"> + The position of the GLTF node relative to its parent. </member> <member name="rotation" type="Quaternion" setter="set_rotation" getter="get_rotation" default="Quaternion(0, 0, 0, 1)"> + The rotation of the GLTF node relative to its parent. </member> <member name="scale" type="Vector3" setter="set_scale" getter="get_scale" default="Vector3(1, 1, 1)"> + The scale of the GLTF node relative to its parent. </member> <member name="skeleton" type="int" setter="set_skeleton" getter="get_skeleton" default="-1"> + If this GLTF node has a skeleton, the index of the [GLTFSkeleton] in the [GLTFState] that describes the skeleton's properties. If -1, this node does not have a skeleton. </member> <member name="skin" type="int" setter="set_skin" getter="get_skin" default="-1"> + If this GLTF node has a skin, the index of the [GLTFSkin] in the [GLTFState] that describes the skin's properties. If -1, this node does not have a skin. </member> <member name="xform" type="Transform3D" setter="set_xform" getter="get_xform" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)"> + The transform of the GLTF node relative to its parent. This property is usually unused since the position, rotation, and scale properties are preferred. </member> </members> </class> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 33b92f37c8..7a46fb8dfc 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -1,8 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFState" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> + Represents all data of a GLTF file. </brief_description> <description> + Contains all nodes and resources of a GLTF file. This is used by [GLTFDocument] as data storage, which allows [GLTFDocument] and all [GLTFDocumentExtension] classes to remain stateless. + GLTFState can be populated by [GLTFDocument] reading a file or by converting a Godot scene. Then the data can either be used to create a Godot scene or save to a GLTF file. The code that converts to/from a Godot scene can be intercepted at arbitrary points by [GLTFDocumentExtension] classes. This allows for custom data to be stored in the GLTF file or for custom data to be converted to/from Godot nodes. </description> <tutorials> </tutorials> @@ -32,17 +35,20 @@ <return type="AnimationPlayer" /> <param index="0" name="idx" type="int" /> <description> + Returns the [AnimationPlayer] node with the given index. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to GLTF animations. </description> </method> <method name="get_animation_players_count"> <return type="int" /> <param index="0" name="idx" type="int" /> <description> + Returns the number of [AnimationPlayer] nodes in this [GLTFState]. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to GLTF animations. </description> </method> <method name="get_animations"> <return type="GLTFAnimation[]" /> <description> + Returns an array of all [GLTFAnimation]s in the GLTF file. When importing, these will be generated as animations in an [AnimationPlayer] node. When exporting, these will be generated from Godot [AnimationPlayer] nodes. </description> </method> <method name="get_buffer_views"> @@ -53,6 +59,7 @@ <method name="get_cameras"> <return type="GLTFCamera[]" /> <description> + Returns an array of all [GLTFCamera]s in the GLTF file. These are the cameras that the [member GLTFNode.camera] index refers to. </description> </method> <method name="get_handle_binary_image"> @@ -68,6 +75,7 @@ <method name="get_lights"> <return type="GLTFLight[]" /> <description> + Returns an array of all [GLTFLight]s in the GLTF file. These are the lights that the [member GLTFNode.light] index refers to. </description> </method> <method name="get_materials"> @@ -78,27 +86,32 @@ <method name="get_meshes"> <return type="GLTFMesh[]" /> <description> + Returns an array of all [GLTFMesh]es in the GLTF file. These are the meshes that the [member GLTFNode.mesh] index refers to. </description> </method> <method name="get_nodes"> <return type="GLTFNode[]" /> <description> + Returns an array of all [GLTFNode]s in the GLTF file. These are the nodes that [member GLTFNode.children] and [member root_nodes] refer to. This includes nodes that may not be generated in the Godot scene, or nodes that may generate multiple Godot scene nodes. </description> </method> <method name="get_scene_node"> <return type="Node" /> <param index="0" name="idx" type="int" /> <description> + Returns the Godot scene node that corresponds to the same index as the [GLTFNode] it was generated from. Not every [GLTFNode] will have a scene node generated, and not every generated scene node will have a corresponding [GLTFNode]. </description> </method> <method name="get_skeletons"> <return type="GLTFSkeleton[]" /> <description> + Returns an array of all [GLTFSkeleton]s in the GLTF file. These are the skeletons that the [member GLTFNode.skeleton] index refers to. </description> </method> <method name="get_skins"> <return type="GLTFSkin[]" /> <description> + Returns an array of all [GLTFSkin]s in the GLTF file. These are the skins that the [member GLTFNode.skin] index refers to. </description> </method> <method name="get_texture_samplers"> @@ -115,11 +128,13 @@ <method name="get_unique_animation_names"> <return type="String[]" /> <description> + Returns an array of unique animation names. This is only used during the import process. </description> </method> <method name="get_unique_names"> <return type="String[]" /> <description> + Returns an array of unique node names. This is used in both the import process and export process. </description> </method> <method name="set_accessors"> @@ -141,6 +156,7 @@ <return type="void" /> <param index="0" name="animations" type="GLTFAnimation[]" /> <description> + Sets the [GLTFAnimation]s in the state. When importing, these will be generated as animations in an [AnimationPlayer] node. When exporting, these will be generated from Godot [AnimationPlayer] nodes. </description> </method> <method name="set_buffer_views"> @@ -153,6 +169,7 @@ <return type="void" /> <param index="0" name="cameras" type="GLTFCamera[]" /> <description> + Sets the [GLTFCamera]s in the state. These are the cameras that the [member GLTFNode.camera] index refers to. </description> </method> <method name="set_handle_binary_image"> @@ -171,6 +188,7 @@ <return type="void" /> <param index="0" name="lights" type="GLTFLight[]" /> <description> + Sets the [GLTFLight]s in the state. These are the lights that the [member GLTFNode.light] index refers to. </description> </method> <method name="set_materials"> @@ -183,24 +201,28 @@ <return type="void" /> <param index="0" name="meshes" type="GLTFMesh[]" /> <description> + Sets the [GLTFMesh]es in the state. These are the meshes that the [member GLTFNode.mesh] index refers to. </description> </method> <method name="set_nodes"> <return type="void" /> <param index="0" name="nodes" type="GLTFNode[]" /> <description> + Sets the [GLTFNode]s in the state. These are the nodes that [member GLTFNode.children] and [member root_nodes] refer to. Some of the nodes set here may not be generated in the Godot scene, or may generate multiple Godot scene nodes. </description> </method> <method name="set_skeletons"> <return type="void" /> <param index="0" name="skeletons" type="GLTFSkeleton[]" /> <description> + Sets the [GLTFSkeleton]s in the state. These are the skeletons that the [member GLTFNode.skeleton] index refers to. </description> </method> <method name="set_skins"> <return type="void" /> <param index="0" name="skins" type="GLTFSkin[]" /> <description> + Sets the [GLTFSkin]s in the state. These are the skins that the [member GLTFNode.skin] index refers to. </description> </method> <method name="set_texture_samplers"> @@ -220,12 +242,14 @@ <return type="void" /> <param index="0" name="unique_animation_names" type="String[]" /> <description> + Sets the unique animation names in the state. This is only used during the import process. </description> </method> <method name="set_unique_names"> <return type="void" /> <param index="0" name="unique_names" type="String[]" /> <description> + Sets the unique node names in the state. This is used in both the import process and export process. </description> </method> </methods> @@ -245,8 +269,10 @@ <member name="minor_version" type="int" setter="set_minor_version" getter="get_minor_version" default="0"> </member> <member name="root_nodes" type="PackedInt32Array" setter="set_root_nodes" getter="get_root_nodes" default="PackedInt32Array()"> + The root nodes of the GLTF file. Typically, a GLTF file will only have one scene, and therefore one root node. However, a GLTF file may have multiple scenes and therefore multiple root nodes, which will be generated as siblings of each other and as children of the root node of the generated Godot scene. </member> <member name="scene_name" type="String" setter="set_scene_name" getter="get_scene_name" default=""""> + The name of the scene. When importing, if not specified, this will be the file name. When exporting, if specified, the scene name will be saved to the GLTF file. </member> <member name="use_named_skin_binds" type="bool" setter="set_use_named_skin_binds" getter="get_use_named_skin_binds" default="false"> </member> diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp index 388c3ec740..2af716b867 100644 --- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp +++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp @@ -70,7 +70,7 @@ Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFState> p_sta } queue.pop_front(); } - while (!queue.is_empty()) { + while (!delete_queue.is_empty()) { List<Node *>::Element *E = delete_queue.front(); Node *node = E->get(); memdelete(node); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 872e803b9c..aad8ab9a57 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -42,7 +42,6 @@ #ifdef TOOLS_ENABLED #include "core/os/keyboard.h" -#include "editor/bindings_generator.h" #include "editor/editor_internal_calls.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" @@ -102,13 +101,6 @@ void CSharpLanguage::init() { } #endif -#if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED) - // Generate the bindings here, before loading assemblies. The Godot assemblies - // may be missing if the glue wasn't generated yet in order to build them. - List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); - BindingsGenerator::handle_cmdline_args(cmdline_args); -#endif - GLOBAL_DEF("dotnet/project/assembly_name", ""); #ifdef TOOLS_ENABLED GLOBAL_DEF("dotnet/project/solution_directory", ""); @@ -589,7 +581,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() _recursion_flag_ = false; }; - if (!gdmono->is_runtime_initialized()) { + if (!gdmono || !gdmono->is_runtime_initialized()) { return Vector<StackInfo>(); } @@ -679,6 +671,7 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft #ifdef GD_MONO_HOT_RELOAD bool CSharpLanguage::is_assembly_reloading_needed() { + ERR_FAIL_NULL_V(gdmono, false); if (!gdmono->is_runtime_initialized()) { return false; } @@ -713,6 +706,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() { } void CSharpLanguage::reload_assemblies(bool p_soft_reload) { + ERR_FAIL_NULL(gdmono); if (!gdmono->is_runtime_initialized()) { return; } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index c267d32f5a..f50803af95 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -35,6 +35,7 @@ namespace GodotTools private PopupMenu _menuPopup; private AcceptDialog _errorDialog; + private ConfirmationDialog _confirmCreateSlnDialog; private Button _bottomPanelBtn; private Button _toolBarBuildButton; @@ -99,7 +100,7 @@ namespace GodotTools pr.Step("Done".TTR()); // Here, after all calls to progress_task_step - CallDeferred(nameof(_RemoveCreateSlnMenuOption)); + CallDeferred(nameof(_ShowDotnetFeatures)); } else { @@ -110,9 +111,8 @@ namespace GodotTools } } - private void _RemoveCreateSlnMenuOption() + private void _ShowDotnetFeatures() { - _menuPopup.RemoveItem(_menuPopup.GetItemIndex((int)MenuOptions.CreateSln)); _bottomPanelBtn.Show(); _toolBarBuildButton.Show(); } @@ -122,8 +122,17 @@ namespace GodotTools switch ((MenuOptions)id) { case MenuOptions.CreateSln: - CreateProjectSolution(); + { + if (File.Exists(GodotSharpDirs.ProjectSlnPath) || File.Exists(GodotSharpDirs.ProjectCsProjPath)) + { + ShowConfirmCreateSlnDialog(); + } + else + { + CreateProjectSolution(); + } break; + } case MenuOptions.SetupGodotNugetFallbackFolder: { try @@ -169,6 +178,13 @@ namespace GodotTools _errorDialog.PopupCentered(); } + public void ShowConfirmCreateSlnDialog() + { + _confirmCreateSlnDialog.Title = "C# solution already exists. This will override the existing C# project file, any manual changes will be lost.".TTR(); + _confirmCreateSlnDialog.DialogText = "Create C# solution".TTR(); + _confirmCreateSlnDialog.PopupCentered(); + } + private static string _vsCodePath = string.Empty; private static readonly string[] VsCodeNames = @@ -420,6 +436,10 @@ namespace GodotTools _errorDialog = new AcceptDialog(); editorBaseControl.AddChild(_errorDialog); + _confirmCreateSlnDialog = new ConfirmationDialog(); + _confirmCreateSlnDialog.Confirmed += () => CreateProjectSolution(); + editorBaseControl.AddChild(_confirmCreateSlnDialog); + MSBuildPanel = new MSBuildPanel(); MSBuildPanel.Ready += () => MSBuildPanel.BuildOutputView.BuildStateChanged += BuildStateChanged; @@ -453,8 +473,8 @@ namespace GodotTools { _bottomPanelBtn.Hide(); _toolBarBuildButton.Hide(); - _menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln); } + _menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln); _menuPopup.IdPressed += _MenuOptionPressed; diff --git a/modules/openxr/doc_classes/OpenXRAction.xml b/modules/openxr/doc_classes/OpenXRAction.xml index d53648723a..5e96f8fc67 100644 --- a/modules/openxr/doc_classes/OpenXRAction.xml +++ b/modules/openxr/doc_classes/OpenXRAction.xml @@ -30,7 +30,7 @@ This action provides a float value between [code]0.0[/code] and [code]1.0[/code] for any analog input such as triggers. </constant> <constant name="OPENXR_ACTION_VECTOR2" value="2" enum="ActionType"> - This action provides a vector2 value and can be bound to embedded trackpads and joysticks + This action provides a [Vector2] value and can be bound to embedded trackpads and joysticks. </constant> <constant name="OPENXR_ACTION_POSE" value="3" enum="ActionType"> </constant> diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 22652daa24..50aea3da2e 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -334,6 +334,8 @@ _FORCE_INLINE_ bool is_connected_to_prev(char32_t p_chr, char32_t p_pchr) { /*************************************************************************/ +bool TextServerAdvanced::icu_data_loaded = false; + bool TextServerAdvanced::_has_feature(Feature p_feature) const { switch (p_feature) { case FEATURE_SIMPLE_LAYOUT: @@ -1384,7 +1386,9 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f FT_Select_Size(fd->face, best_match); } else { FT_Set_Pixel_Sizes(fd->face, 0, double(fd->size.x * fd->oversampling)); - fd->scale = ((double)fd->size.x * fd->oversampling) / (double)fd->face->size->metrics.y_ppem; + if (fd->face->size->metrics.y_ppem != 0) { + fd->scale = ((double)fd->size.x * fd->oversampling) / (double)fd->face->size->metrics.y_ppem; + } } fd->hb_handle = hb_ft_font_create(fd->face, nullptr); @@ -6599,5 +6603,6 @@ TextServerAdvanced::~TextServerAdvanced() { uset_close(allowed); allowed = nullptr; } - u_cleanup(); + + std::atexit(u_cleanup); } diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 02244a294e..f092fa8cca 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -158,7 +158,7 @@ class TextServerAdvanced : public TextServerExtension { // ICU support data. - bool icu_data_loaded = false; + static bool icu_data_loaded; mutable USet *allowed = nullptr; mutable USpoofChecker *sc_spoof = nullptr; mutable USpoofChecker *sc_conf = nullptr; diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 240ae8310a..d67ae6b45b 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -825,7 +825,9 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f FT_Select_Size(fd->face, best_match); } else { FT_Set_Pixel_Sizes(fd->face, 0, Math::round(fd->size.x * fd->oversampling)); - fd->scale = ((double)fd->size.x * fd->oversampling) / (double)fd->face->size->metrics.y_ppem; + if (fd->face->size->metrics.y_ppem != 0) { + fd->scale = ((double)fd->size.x * fd->oversampling) / (double)fd->face->size->metrics.y_ppem; + } } fd->ascent = (fd->face->size->metrics.ascender / 64.0) / fd->oversampling * fd->scale; diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index af4ba1255b..e07e0e1149 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -676,16 +676,19 @@ void DisplayServerAndroid::cursor_set_custom_image(const Ref<Resource> &p_cursor void DisplayServerAndroid::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { #if defined(VULKAN_ENABLED) - context_vulkan->set_vsync_mode(p_window, p_vsync_mode); + if (context_vulkan) { + context_vulkan->set_vsync_mode(p_window, p_vsync_mode); + } #endif } DisplayServer::VSyncMode DisplayServerAndroid::window_get_vsync_mode(WindowID p_window) const { #if defined(VULKAN_ENABLED) - return context_vulkan->get_vsync_mode(p_window); -#else - return DisplayServer::VSYNC_ENABLED; + if (context_vulkan) { + return context_vulkan->get_vsync_mode(p_window); + } #endif + return DisplayServer::VSYNC_ENABLED; } void DisplayServerAndroid::reset_swap_buffers_flag() { diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index 54bb34ef73..88c3d2cc14 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -677,40 +677,45 @@ Vector<String> OS_LinuxBSD::get_system_font_path_for_text(const String &p_font_n } Vector<String> ret; - FcPattern *pattern = FcPatternCreate(); - if (pattern) { - FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8 *>(p_font_name.utf8().get_data())); - FcPatternAddInteger(pattern, FC_WEIGHT, _weight_to_fc(p_weight)); - FcPatternAddInteger(pattern, FC_WIDTH, _stretch_to_fc(p_stretch)); - FcPatternAddInteger(pattern, FC_SLANT, p_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN); - - FcCharSet *char_set = FcCharSetCreate(); - for (int i = 0; i < p_text.size(); i++) { - FcCharSetAddChar(char_set, p_text[i]); - } - FcPatternAddCharSet(pattern, FC_CHARSET, char_set); - - FcLangSet *lang_set = FcLangSetCreate(); - FcLangSetAdd(lang_set, reinterpret_cast<const FcChar8 *>(p_locale.utf8().get_data())); - FcPatternAddLangSet(pattern, FC_LANG, lang_set); - - FcConfigSubstitute(0, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - - FcResult result; - FcPattern *match = FcFontMatch(0, pattern, &result); - if (match) { - char *file_name = nullptr; - if (FcPatternGetString(match, FC_FILE, 0, reinterpret_cast<FcChar8 **>(&file_name)) == FcResultMatch) { - if (file_name) { - ret.push_back(String::utf8(file_name)); + static const char *allowed_formats[] = { "TrueType", "CFF" }; + for (size_t i = 0; i < sizeof(allowed_formats) / sizeof(const char *); i++) { + FcPattern *pattern = FcPatternCreate(); + if (pattern) { + FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); + FcPatternAddString(pattern, FC_FONTFORMAT, reinterpret_cast<const FcChar8 *>(allowed_formats[i])); + FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8 *>(p_font_name.utf8().get_data())); + FcPatternAddInteger(pattern, FC_WEIGHT, _weight_to_fc(p_weight)); + FcPatternAddInteger(pattern, FC_WIDTH, _stretch_to_fc(p_stretch)); + FcPatternAddInteger(pattern, FC_SLANT, p_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN); + + FcCharSet *char_set = FcCharSetCreate(); + for (int j = 0; j < p_text.size(); j++) { + FcCharSetAddChar(char_set, p_text[j]); + } + FcPatternAddCharSet(pattern, FC_CHARSET, char_set); + + FcLangSet *lang_set = FcLangSetCreate(); + FcLangSetAdd(lang_set, reinterpret_cast<const FcChar8 *>(p_locale.utf8().get_data())); + FcPatternAddLangSet(pattern, FC_LANG, lang_set); + + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + FcResult result; + FcPattern *match = FcFontMatch(0, pattern, &result); + if (match) { + char *file_name = nullptr; + if (FcPatternGetString(match, FC_FILE, 0, reinterpret_cast<FcChar8 **>(&file_name)) == FcResultMatch) { + if (file_name) { + ret.push_back(String::utf8(file_name)); + } } + FcPatternDestroy(match); } - FcPatternDestroy(match); + FcPatternDestroy(pattern); + FcCharSetDestroy(char_set); + FcLangSetDestroy(lang_set); } - FcPatternDestroy(pattern); - FcCharSetDestroy(char_set); - FcLangSetDestroy(lang_set); } return ret; @@ -725,47 +730,51 @@ String OS_LinuxBSD::get_system_font_path(const String &p_font_name, int p_weight ERR_FAIL_V_MSG(String(), "Unable to load fontconfig, system font support is disabled."); } - String ret; - FcPattern *pattern = FcPatternCreate(); - if (pattern) { - bool allow_substitutes = (p_font_name.to_lower() == "sans-serif") || (p_font_name.to_lower() == "serif") || (p_font_name.to_lower() == "monospace") || (p_font_name.to_lower() == "cursive") || (p_font_name.to_lower() == "fantasy"); - - FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); - FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8 *>(p_font_name.utf8().get_data())); - FcPatternAddInteger(pattern, FC_WEIGHT, _weight_to_fc(p_weight)); - FcPatternAddInteger(pattern, FC_WIDTH, _stretch_to_fc(p_stretch)); - FcPatternAddInteger(pattern, FC_SLANT, p_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN); - - FcConfigSubstitute(0, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - - FcResult result; - FcPattern *match = FcFontMatch(0, pattern, &result); - if (match) { - if (!allow_substitutes) { - char *family_name = nullptr; - if (FcPatternGetString(match, FC_FAMILY, 0, reinterpret_cast<FcChar8 **>(&family_name)) == FcResultMatch) { - if (family_name && String::utf8(family_name).to_lower() != p_font_name.to_lower()) { + static const char *allowed_formats[] = { "TrueType", "CFF" }; + for (size_t i = 0; i < sizeof(allowed_formats) / sizeof(const char *); i++) { + FcPattern *pattern = FcPatternCreate(); + if (pattern) { + bool allow_substitutes = (p_font_name.to_lower() == "sans-serif") || (p_font_name.to_lower() == "serif") || (p_font_name.to_lower() == "monospace") || (p_font_name.to_lower() == "cursive") || (p_font_name.to_lower() == "fantasy"); + + FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); + FcPatternAddString(pattern, FC_FONTFORMAT, reinterpret_cast<const FcChar8 *>(allowed_formats[i])); + FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8 *>(p_font_name.utf8().get_data())); + FcPatternAddInteger(pattern, FC_WEIGHT, _weight_to_fc(p_weight)); + FcPatternAddInteger(pattern, FC_WIDTH, _stretch_to_fc(p_stretch)); + FcPatternAddInteger(pattern, FC_SLANT, p_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN); + + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + FcResult result; + FcPattern *match = FcFontMatch(0, pattern, &result); + if (match) { + if (!allow_substitutes) { + char *family_name = nullptr; + if (FcPatternGetString(match, FC_FAMILY, 0, reinterpret_cast<FcChar8 **>(&family_name)) == FcResultMatch) { + if (family_name && String::utf8(family_name).to_lower() != p_font_name.to_lower()) { + FcPatternDestroy(match); + FcPatternDestroy(pattern); + continue; + } + } + } + char *file_name = nullptr; + if (FcPatternGetString(match, FC_FILE, 0, reinterpret_cast<FcChar8 **>(&file_name)) == FcResultMatch) { + if (file_name) { + String ret = String::utf8(file_name); FcPatternDestroy(match); FcPatternDestroy(pattern); - - return String(); + return ret; } } + FcPatternDestroy(match); } - char *file_name = nullptr; - if (FcPatternGetString(match, FC_FILE, 0, reinterpret_cast<FcChar8 **>(&file_name)) == FcResultMatch) { - if (file_name) { - ret = String::utf8(file_name); - } - } - - FcPatternDestroy(match); + FcPatternDestroy(pattern); } - FcPatternDestroy(pattern); } - return ret; + return String(); #else ERR_FAIL_V_MSG(String(), "Godot was compiled without fontconfig, system font support is disabled."); #endif diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index dff2f536a8..d1f1115aad 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -5258,6 +5258,9 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode } #ifdef XKB_ENABLED xkb_loaded = (initialize_xkbcommon(dylibloader_verbose) == 0); + if (!xkb_context_new || !xkb_compose_table_new_from_locale || !xkb_compose_table_unref || !xkb_context_unref || !xkb_compose_state_feed || !xkb_compose_state_unref || !xkb_compose_state_new || !xkb_compose_state_get_status || !xkb_compose_state_get_utf8 || !xkb_keysym_to_utf32 || !xkb_keysym_to_upper) { + xkb_loaded = false; + } #endif if (initialize_xext(dylibloader_verbose) != 0) { r_error = ERR_UNAVAILABLE; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 44478d45cc..ec1fbb7e28 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2375,7 +2375,7 @@ int RichTextLabel::_find_list(Item *p_item, Vector<int> &r_index, Vector<ItemLis int index = 1; if (frame != nullptr) { - for (int i = list->line + 1; i <= prev_item->line; i++) { + for (int i = list->line + 1; i <= prev_item->line && i < (int)frame->lines.size(); i++) { if (_find_list_item(frame->lines[i].from) == list) { index++; } diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 59e3d307c6..9fb4ed458f 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -1365,8 +1365,11 @@ void Window::_window_input(const Ref<InputEvent> &p_ev) { emit_signal(SceneStringNames::get_singleton()->window_input, p_ev); - push_input(p_ev); - if (!is_input_handled()) { + if (is_inside_tree()) { + push_input(p_ev); + } + + if (!is_input_handled() && is_inside_tree()) { push_unhandled_input(p_ev); } } diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 7a865691d9..2e1ba96d11 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -86,6 +86,10 @@ static Ref<ImageTexture> generate_icon(int p_index) { ImageLoaderSVG img_loader; Error err = img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap<Color, Color>()); ERR_FAIL_COND_V_MSG(err != OK, Ref<ImageTexture>(), "Failed generating icon, unsupported or invalid SVG data in default theme."); +#else + // If the SVG module is disabled, we can't really display the UI well, but at least we won't crash. + // 16 pixels is used as it's the most common base size for Godot icons. + img = Image::create_empty(16 * scale, 16 * scale, false, Image::FORMAT_RGBA8); #endif return ImageTexture::create_from_image(img); @@ -720,7 +724,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("title_offset", "GraphNode", 26 * scale); theme->set_constant("title_h_offset", "GraphNode", 0); theme->set_constant("close_offset", "GraphNode", 22 * scale); - theme->set_constant("close_h_offset", "GraphNode", 22 * scale); + theme->set_constant("close_h_offset", "GraphNode", 12 * scale); theme->set_constant("port_offset", "GraphNode", 0); // Tree diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 50b324a9fd..a1346661e1 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1193,6 +1193,9 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { prev_tex->proxies.erase(p_texture); } + // Copy canvas_texture so it doesn't leak. + CanvasTexture *canvas_texture = tex->canvas_texture; + *tex = *proxy_to; tex->proxy_to = p_proxy_to; @@ -1200,6 +1203,7 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { tex->is_proxy = true; tex->proxies.clear(); proxy_to->proxies.push_back(p_texture); + tex->canvas_texture = canvas_texture; tex->rd_view.format_override = tex->rd_format; tex->rd_texture = RD::get_singleton()->texture_create_shared(tex->rd_view, proxy_to->rd_texture); |