summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/actions/godot-deps/action.yml2
-rw-r--r--.gitignore1
-rw-r--r--doc/classes/Animation.xml4
-rw-r--r--doc/classes/Basis.xml10
-rw-r--r--doc/classes/NativeMenu.xml11
-rw-r--r--doc/classes/PhysicalSkyMaterial.xml1
-rw-r--r--doc/classes/Skeleton3D.xml7
-rw-r--r--doc/classes/SkeletonModifier3D.xml6
-rw-r--r--doc/classes/TileMap.xml4
-rw-r--r--doc/classes/TileMapLayer.xml7
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp2
-rw-r--r--editor/animation_track_editor.cpp75
-rw-r--r--editor/animation_track_editor.h3
-rw-r--r--editor/connections_dialog.cpp9
-rw-r--r--editor/editor_file_system.cpp165
-rw-r--r--editor/editor_file_system.h1
-rw-r--r--editor/editor_inspector.cpp13
-rw-r--r--editor/editor_inspector.h1
-rw-r--r--editor/editor_node.cpp22
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_properties_array_dict.cpp20
-rw-r--r--editor/editor_properties_array_dict.h11
-rw-r--r--editor/editor_resource_preview.cpp4
-rw-r--r--editor/editor_resource_preview.h3
-rw-r--r--editor/gui/editor_scene_tabs.cpp2
-rw-r--r--editor/gui/scene_tree_editor.cpp24
-rw-r--r--editor/gui/scene_tree_editor.h1
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp14
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp4
-rw-r--r--editor/project_settings_editor.cpp12
-rw-r--r--editor/property_selector.cpp17
-rw-r--r--editor/scene_tree_dock.cpp41
-rw-r--r--editor/scene_tree_dock.h3
-rw-r--r--misc/extension_api_validation/4.2-stable.expected6
-rw-r--r--modules/gdscript/gdscript_parser.cpp2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs2
-rw-r--r--modules/mono/glue/runtime_interop.cpp6
-rw-r--r--modules/navigation/nav_map.cpp2
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.cpp7
-rw-r--r--platform/android/api/jni_singleton.h17
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.cpp3
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp17
-rw-r--r--platform/macos/display_server_macos.mm91
-rw-r--r--platform/macos/godot_menu_delegate.mm9
-rw-r--r--platform/macos/native_menu_macos.h5
-rw-r--r--platform/macos/native_menu_macos.mm49
-rw-r--r--platform/web/display_server_web.cpp11
-rw-r--r--platform/windows/display_server_windows.cpp27
-rw-r--r--platform/windows/native_menu_windows.cpp45
-rw-r--r--platform/windows/native_menu_windows.h2
-rw-r--r--scene/2d/tile_map_layer.cpp48
-rw-r--r--scene/2d/tile_map_layer.h9
-rw-r--r--scene/3d/bone_attachment_3d.cpp10
-rw-r--r--scene/3d/physics/physical_bone_3d.cpp10
-rw-r--r--scene/3d/skeleton_3d.cpp22
-rw-r--r--scene/3d/skeleton_3d.h4
-rw-r--r--scene/3d/skeleton_ik_3d.cpp10
-rw-r--r--scene/3d/skeleton_modifier_3d.cpp2
-rw-r--r--scene/animation/animation_mixer.cpp13
-rw-r--r--scene/animation/animation_player.cpp9
-rw-r--r--scene/gui/box_container.cpp4
-rw-r--r--scene/gui/container.cpp18
-rw-r--r--scene/gui/container.h8
-rw-r--r--scene/gui/file_dialog.cpp4
-rw-r--r--scene/gui/graph_element.cpp4
-rw-r--r--scene/gui/graph_node.cpp8
-rw-r--r--scene/gui/margin_container.cpp4
-rw-r--r--scene/gui/panel_container.cpp2
-rw-r--r--scene/gui/scroll_container.cpp2
-rw-r--r--scene/gui/tab_container.cpp16
-rw-r--r--scene/gui/text_edit.cpp13
-rw-r--r--scene/main/viewport.cpp4
-rw-r--r--scene/main/window.cpp8
-rw-r--r--scene/resources/animation.compat.inc6
-rw-r--r--scene/resources/animation.cpp22
-rw-r--r--scene/resources/animation.h4
-rw-r--r--scene/resources/visual_shader.cpp4
-rw-r--r--scene/resources/visual_shader_nodes.cpp2
-rw-r--r--servers/display/native_menu.cpp7
-rw-r--r--servers/display/native_menu.h2
-rw-r--r--servers/display_server.cpp24
-rw-r--r--servers/display_server.h24
-rw-r--r--tests/scene/test_code_edit.h39
-rw-r--r--tests/servers/test_navigation_server_3d.h6
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/embree/common/sys/sysinfo.cpp2
-rw-r--r--thirdparty/embree/patches/mingw-no-cpuidex.patch13
-rw-r--r--thirdparty/glslang/glslang/Include/InfoSink.h2
-rw-r--r--thirdparty/glslang/patches/disable-absolute-paths-for-apple-compat.patch (renamed from thirdparty/glslang/glslang/patches/disable-absolute-paths-for-apple-compat.patch)11
-rw-r--r--thirdparty/thorvg/inc/config.h2
-rw-r--r--thirdparty/thorvg/inc/thorvg.h48
-rw-r--r--thirdparty/thorvg/src/common/tvgCompressor.cpp15
-rw-r--r--thirdparty/thorvg/src/common/tvgCompressor.h1
-rw-r--r--thirdparty/thorvg/src/common/tvgMath.cpp63
-rw-r--r--thirdparty/thorvg/src/common/tvgMath.h81
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp22
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp24
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp4
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp2
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h8
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp1
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp7
-rw-r--r--thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp197
-rw-r--r--thirdparty/thorvg/src/renderer/tvgPaint.cpp10
-rw-r--r--thirdparty/thorvg/src/renderer/tvgRender.cpp4
-rw-r--r--thirdparty/thorvg/src/renderer/tvgRender.h2
-rw-r--r--thirdparty/thorvg/src/renderer/tvgShape.cpp35
-rw-r--r--thirdparty/thorvg/src/renderer/tvgShape.h63
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh2
112 files changed, 1094 insertions, 719 deletions
diff --git a/.github/actions/godot-deps/action.yml b/.github/actions/godot-deps/action.yml
index 07a364cd79..607666ec5f 100644
--- a/.github/actions/godot-deps/action.yml
+++ b/.github/actions/godot-deps/action.yml
@@ -3,7 +3,7 @@ description: Setup Python, install the pip version of SCons.
inputs:
python-version:
description: The Python version to use.
- default: "3.x"
+ default: "3.12.3"
python-arch:
description: The Python architecture.
default: "x64"
diff --git a/.gitignore b/.gitignore
index 3946cc96e5..32a43b8c63 100644
--- a/.gitignore
+++ b/.gitignore
@@ -159,6 +159,7 @@ gmon.out
# Kate
*.kate-swp
+.kateproject.build
# Kdevelop
*.kdev4
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index 26ed881502..3b7a6e66fe 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -360,9 +360,12 @@
<param index="1" name="time" type="float" />
<param index="2" name="find_mode" type="int" enum="Animation.FindMode" default="0" />
<param index="3" name="limit" type="bool" default="false" />
+ <param index="4" name="backward" type="bool" default="false" />
<description>
Finds the key index by time in a given track. Optionally, only find it if the approx/exact time is given.
If [param limit] is [code]true[/code], it does not return keys outside the animation range.
+ If [param backward] is [code]true[/code], the direction is reversed in methods that rely on one directional processing.
+ For example, in case [param find_mode] is [constant FIND_MODE_NEAREST], if there is no key in the current position just after seeked, the first key found is retrieved by searching before the position, but if [param backward] is [code]true[/code], the first key found is retrieved after the position.
</description>
</method>
<method name="track_get_interpolation_loop_wrap" qualifiers="const">
@@ -583,6 +586,7 @@
<param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated value at the given time (in seconds). The [param track_idx] must be the index of a value track.
+ A [param backward] mainly affects the direction of key retrieval of the track with [constant UPDATE_DISCRETE] converted by [constant AnimationMixer.ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS] to match the result with [method track_find_key].
</description>
</method>
<method name="value_track_set_update_mode">
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index 338d9523fa..41bda1033d 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -444,12 +444,14 @@
Transforms (multiplies) the [param right] vector by this basis, returning a [Vector3].
[codeblocks]
[gdscript]
- var my_basis = Basis(Vector3(1, 1, 1), Vector3(1, 1, 1), Vector3(0, 2, 5))
- print(my_basis * Vector3(1, 2, 3)) # Prints (7, 3, 16)
+ # Basis that swaps the X/Z axes and doubles the scale.
+ var my_basis = Basis(Vector3(0, 2, 0), Vector3(2, 0, 0), Vector3(0, 0, 2))
+ print(my_basis * Vector3(1, 2, 3)) # Prints (4, 2, 6)
[/gdscript]
[csharp]
- var myBasis = new Basis(new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(0, 2, 5));
- GD.Print(my_basis * new Vector3(1, 2, 3)); // Prints (7, 3, 16)
+ // Basis that swaps the X/Z axes and doubles the scale.
+ var myBasis = new Basis(new Vector3(0, 2, 0), new Vector3(2, 0, 0), new Vector3(0, 0, 2));
+ GD.Print(myBasis * new Vector3(1, 2, 3)); // Prints (4, 2, 6)
[/csharp]
[/codeblocks]
</description>
diff --git a/doc/classes/NativeMenu.xml b/doc/classes/NativeMenu.xml
index 475874dee7..2b9e414106 100644
--- a/doc/classes/NativeMenu.xml
+++ b/doc/classes/NativeMenu.xml
@@ -473,6 +473,14 @@
[b]Note:[/b] This method is implemented on macOS and Windows.
</description>
</method>
+ <method name="is_opened" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="rid" type="RID" />
+ <description>
+ Returns [code]true[/code] if the menu is currently opened.
+ [b]Note:[/b] This method is implemented only on macOS.
+ </description>
+ </method>
<method name="is_system_menu" qualifiers="const">
<return type="bool" />
<param index="0" name="rid" type="RID" />
@@ -699,6 +707,7 @@
<param index="1" name="callback" type="Callable" />
<description>
Registers callable to emit when the menu is about to show.
+ [b]Note:[/b] The OS can simulate menu opening to track menu item changes and global shortcuts, in which case the corresponding close callback is not triggered. Use [method is_opened] to check if the menu is currently opened.
[b]Note:[/b] This method is implemented only on macOS.
</description>
</method>
@@ -707,7 +716,7 @@
<param index="0" name="rid" type="RID" />
<param index="1" name="callback" type="Callable" />
<description>
- Registers callable to emit when the menu is about to closed.
+ Registers callable to emit after the menu is closed.
[b]Note:[/b] This method is implemented only on macOS.
</description>
</method>
diff --git a/doc/classes/PhysicalSkyMaterial.xml b/doc/classes/PhysicalSkyMaterial.xml
index 13f9d93e66..460134e5ba 100644
--- a/doc/classes/PhysicalSkyMaterial.xml
+++ b/doc/classes/PhysicalSkyMaterial.xml
@@ -6,7 +6,6 @@
<description>
The [PhysicalSkyMaterial] uses the Preetham analytic daylight model to draw a sky based on physical properties. This results in a substantially more realistic sky than the [ProceduralSkyMaterial], but it is slightly slower and less flexible.
The [PhysicalSkyMaterial] only supports one sun. The color, energy, and direction of the sun are taken from the first [DirectionalLight3D] in the scene tree.
- As it is based on a daylight model, the sky fades to black as the sunset ends. If you want a full day/night cycle, you will have to add a night sky by converting this to a [ShaderMaterial] and adding a night sky directly into the resulting shader.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index 610550d8bd..1167b70c8d 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -155,6 +155,13 @@
Returns the rest transform for a bone [param bone_idx].
</description>
</method>
+ <method name="get_concatenated_bone_names" qualifiers="const">
+ <return type="StringName" />
+ <description>
+ Returns all bone names concatenated with commas ([code],[/code]) as a single [StringName].
+ It is useful to set it as a hint for the enum property.
+ </description>
+ </method>
<method name="get_parentless_bones" qualifiers="const">
<return type="PackedInt32Array" />
<description>
diff --git a/doc/classes/SkeletonModifier3D.xml b/doc/classes/SkeletonModifier3D.xml
index c0b1b6fd53..620eed9b70 100644
--- a/doc/classes/SkeletonModifier3D.xml
+++ b/doc/classes/SkeletonModifier3D.xml
@@ -18,6 +18,12 @@
[method _process_modification] must not apply [member influence] to bone poses because the [Skeleton3D] automatically applies influence to all bone poses set by the modifier.
</description>
</method>
+ <method name="get_skeleton" qualifiers="const">
+ <return type="Skeleton3D" />
+ <description>
+ Get parent [Skeleton3D] node if found.
+ </description>
+ </method>
</methods>
<members>
<member name="active" type="bool" setter="set_active" getter="is_active" default="true">
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 687c7194cd..3f70810a7f 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -486,8 +486,8 @@
Show or hide the TileMap's navigation meshes. If set to [constant VISIBILITY_MODE_DEFAULT], this depends on the show navigation debug settings.
</member>
<member name="rendering_quadrant_size" type="int" setter="set_rendering_quadrant_size" getter="get_rendering_quadrant_size" default="16">
- The TileMap's quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quandrant size groups together [code]16 * 16 = 256[/code] tiles.
- The quadrant size does not apply on Y-sorted layers, as tiles are be grouped by Y position instead in that case.
+ The TileMap's quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quadrant size groups together [code]16 * 16 = 256[/code] tiles.
+ The quadrant size does not apply on Y-sorted layers, as tiles are grouped by Y position instead in that case.
[b]Note:[/b] As quadrants are created according to the map's coordinate system, the quadrant's "square shape" might not look like square in the TileMap's local coordinate system.
</member>
<member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset">
diff --git a/doc/classes/TileMapLayer.xml b/doc/classes/TileMapLayer.xml
index 1bff6d911b..b9acef2095 100644
--- a/doc/classes/TileMapLayer.xml
+++ b/doc/classes/TileMapLayer.xml
@@ -264,8 +264,8 @@
Show or hide the [TileMapLayer]'s navigation meshes. If set to [constant DEBUG_VISIBILITY_MODE_DEFAULT], this depends on the show navigation debug settings.
</member>
<member name="rendering_quadrant_size" type="int" setter="set_rendering_quadrant_size" getter="get_rendering_quadrant_size" default="16">
- The [TileMapLayer]'s quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quandrant size groups together [code]16 * 16 = 256[/code] tiles.
- The quadrant size does not apply on a Y-sorted [TileMapLayer], as tiles are be grouped by Y position instead in that case.
+ The [TileMapLayer]'s quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quadrant size groups together [code]16 * 16 = 256[/code] tiles.
+ The quadrant size does not apply on a Y-sorted [TileMapLayer], as tiles are grouped by Y position instead in that case.
[b]Note:[/b] As quadrants are created according to the map's coordinate system, the quadrant's "square shape" might not look like square in the [TileMapLayer]'s local coordinate system.
</member>
<member name="tile_map_data" type="PackedByteArray" setter="set_tile_map_data_from_array" getter="get_tile_map_data_as_array" default="PackedByteArray()">
@@ -277,6 +277,9 @@
<member name="use_kinematic_bodies" type="bool" setter="set_use_kinematic_bodies" getter="is_using_kinematic_bodies" default="false">
If [code]true[/code], this [TileMapLayer] collision shapes will be instantiated as kinematic bodies. This can be needed for moving [TileMapLayer] nodes (i.e. moving platforms).
</member>
+ <member name="x_draw_order_reversed" type="bool" setter="set_x_draw_order_reversed" getter="is_x_draw_order_reversed" default="false">
+ If [member CanvasItem.y_sort_enabled] is enabled, setting this to [code]true[/code] will reverse the order the tiles are drawn on the X-axis.
+ </member>
<member name="y_sort_origin" type="int" setter="set_y_sort_origin" getter="get_y_sort_origin" default="0">
This Y-sort origin value is added to each tile's Y-sort origin value. This allows, for example, to fake a different height level. This can be useful for top-down view games.
</member>
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 6cbdf5e935..6a51ce6b70 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -3654,7 +3654,7 @@ void RasterizerSceneGLES3::_render_uv2(const PagedArray<RenderGeometryInstance *
glDrawBuffers(draw_buffers.size(), draw_buffers.ptr());
glClearColor(0.0, 0.0, 0.0, 0.0);
- RasterizerGLES3::clear_depth(1.0);
+ RasterizerGLES3::clear_depth(0.0);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
uint64_t base_spec_constant = 0;
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 2d497a281f..58dd659e86 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -57,6 +57,9 @@
#include "scene/main/window.h"
#include "servers/audio/audio_stream.h"
+constexpr double FPS_DECIMAL = 1;
+constexpr double SECOND_DECIMAL = 0.0001;
+
void AnimationTrackKeyEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj);
ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationTrackKeyEdit::_key_ofs_changed);
@@ -1322,7 +1325,7 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) {
return;
}
- p_new_len = MAX(0.0001, p_new_len);
+ p_new_len = MAX(SECOND_DECIMAL, p_new_len);
if (use_fps && animation->get_step() > 0) {
p_new_len *= animation->get_step();
}
@@ -1442,7 +1445,7 @@ void AnimationTimelineEdit::_notification(int p_what) {
float l = animation->get_length();
if (l <= 0) {
- l = 0.0001; // Avoid crashor.
+ l = SECOND_DECIMAL; // Avoid crashor.
}
Ref<Texture2D> hsize_icon = get_editor_theme_icon(SNAME("Hsize"));
@@ -1723,7 +1726,7 @@ void AnimationTimelineEdit::update_values() {
editing = true;
if (use_fps && animation->get_step() > 0) {
length->set_value(animation->get_length() / animation->get_step());
- length->set_step(1);
+ length->set_step(FPS_DECIMAL);
length->set_tooltip_text(TTR("Animation length (frames)"));
time_icon->set_tooltip_text(TTR("Animation length (frames)"));
if (track_edit) {
@@ -1731,7 +1734,7 @@ void AnimationTimelineEdit::update_values() {
}
} else {
length->set_value(animation->get_length());
- length->set_step(0.0001);
+ length->set_step(SECOND_DECIMAL);
length->set_tooltip_text(TTR("Animation length (seconds)"));
time_icon->set_tooltip_text(TTR("Animation length (seconds)"));
}
@@ -1912,9 +1915,9 @@ AnimationTimelineEdit::AnimationTimelineEdit() {
time_icon->set_tooltip_text(TTR("Animation length (seconds)"));
len_hb->add_child(time_icon);
length = memnew(EditorSpinSlider);
- length->set_min(0.0001);
+ length->set_min(SECOND_DECIMAL);
length->set_max(36000);
- length->set_step(0.0001);
+ length->set_step(SECOND_DECIMAL);
length->set_allow_greater(true);
length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0));
length->set_hide_slider(true);
@@ -2645,7 +2648,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
}
if (key_idx != -1) {
- String text = TTR("Time (s):") + " " + TS->format_number(rtos(Math::snapped(animation->track_get_key_time(track, key_idx), 0.0001))) + "\n";
+ String text = TTR("Time (s):") + " " + TS->format_number(rtos(Math::snapped(animation->track_get_key_time(track, key_idx), SECOND_DECIMAL))) + "\n";
switch (animation->track_get_type(track)) {
case Animation::TYPE_POSITION_3D: {
Vector3 t = animation->track_get_key_value(track, key_idx);
@@ -4758,10 +4761,12 @@ void AnimationTrackEditor::_animation_changed() {
}
void AnimationTrackEditor::_snap_mode_changed(int p_mode) {
- timeline->set_use_fps(p_mode == 1);
+ bool use_fps = p_mode == 1;
+ timeline->set_use_fps(use_fps);
if (key_edit) {
- key_edit->set_use_fps(p_mode == 1);
+ key_edit->set_use_fps(use_fps);
}
+ step->set_step(use_fps ? FPS_DECIMAL : SECOND_DECIMAL);
_update_step_spinbox();
}
@@ -4775,7 +4780,9 @@ void AnimationTrackEditor::_update_step_spinbox() {
if (animation->get_step() == 0) {
step->set_value(0);
} else {
- step->set_value(1.0 / animation->get_step());
+ // The value stored within tscn cannot restored the original FPS due to lack of precision,
+ // so the value should be limited to integer.
+ step->set_value(Math::round(1.0 / animation->get_step()));
}
} else {
@@ -4783,6 +4790,7 @@ void AnimationTrackEditor::_update_step_spinbox() {
}
step->set_block_signals(false);
+ _update_snap_unit();
}
void AnimationTrackEditor::_animation_update() {
@@ -4876,6 +4884,8 @@ void AnimationTrackEditor::_update_step(double p_new_step) {
return;
}
+ _update_snap_unit();
+
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Change Animation Step"));
float step_value = p_new_step;
@@ -5162,7 +5172,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
p_ofs = snap_time(p_ofs);
}
while (animation->track_find_key(p_track, p_ofs, Animation::FIND_MODE_APPROX) != -1) { // Make sure insertion point is valid.
- p_ofs += 0.0001;
+ p_ofs += SECOND_DECIMAL;
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
@@ -7007,25 +7017,31 @@ void AnimationTrackEditor::_selection_changed() {
}
}
+void AnimationTrackEditor::_update_snap_unit() {
+ if (step->get_value() <= 0) {
+ snap_unit = 0;
+ return; // Avoid zero div.
+ }
+
+ if (timeline->is_using_fps()) {
+ snap_unit = 1.0 / step->get_value();
+ } else {
+ snap_unit = 1.0 / Math::round(1.0 / step->get_value()); // Follow the snap behavior of the timeline editor.
+ }
+}
+
float AnimationTrackEditor::snap_time(float p_value, bool p_relative) {
if (is_snap_enabled()) {
- double snap_increment;
- if (timeline->is_using_fps() && step->get_value() > 0) {
- snap_increment = 1.0 / step->get_value();
- } else {
- snap_increment = step->get_value();
- }
-
if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
// Use more precise snapping when holding Shift.
- snap_increment *= 0.25;
+ snap_unit *= 0.25;
}
if (p_relative) {
- double rel = Math::fmod(timeline->get_value(), snap_increment);
- p_value = Math::snapped(p_value + rel, snap_increment) - rel;
+ double rel = Math::fmod(timeline->get_value(), snap_unit);
+ p_value = Math::snapped(p_value + rel, snap_unit) - rel;
} else {
- p_value = Math::snapped(p_value, snap_increment);
+ p_value = Math::snapped(p_value, snap_unit);
}
}
@@ -7282,7 +7298,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
step = memnew(EditorSpinSlider);
step->set_min(0);
step->set_max(1000000);
- step->set_step(0.0001);
+ step->set_step(SECOND_DECIMAL);
step->set_hide_slider(true);
step->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
step->set_tooltip_text(TTR("Animation step value."));
@@ -7524,9 +7540,9 @@ AnimationTrackEditor::AnimationTrackEditor() {
ease_selection->select(Tween::EASE_IN_OUT); // Default
ease_selection->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Translation context is needed.
ease_fps = memnew(SpinBox);
- ease_fps->set_min(1);
+ ease_fps->set_min(FPS_DECIMAL);
ease_fps->set_max(999);
- ease_fps->set_step(1);
+ ease_fps->set_step(FPS_DECIMAL);
ease_fps->set_value(30); // Default
ease_grid->add_child(memnew(Label(TTR("Transition Type:"))));
ease_grid->add_child(transition_selection);
@@ -7550,9 +7566,9 @@ AnimationTrackEditor::AnimationTrackEditor() {
bake_value = memnew(CheckBox);
bake_value->set_pressed(true);
bake_fps = memnew(SpinBox);
- bake_fps->set_min(1);
+ bake_fps->set_min(FPS_DECIMAL);
bake_fps->set_max(999);
- bake_fps->set_step(1);
+ bake_fps->set_step(FPS_DECIMAL);
bake_fps->set_value(30); // Default
bake_grid->add_child(memnew(Label(TTR("3D Pos/Rot/Scl Track:"))));
bake_grid->add_child(bake_trs);
@@ -7671,15 +7687,14 @@ AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animat
spinner->set_allow_lesser(true);
if (use_fps) {
- spinner->set_step(1);
- spinner->set_hide_slider(true);
+ spinner->set_step(FPS_DECIMAL);
real_t fps = animation->get_step();
if (fps > 0) {
fps = 1.0 / fps;
}
spinner->set_value(key_ofs * fps);
} else {
- spinner->set_step(0.0001);
+ spinner->set_step(SECOND_DECIMAL);
spinner->set_value(key_ofs);
spinner->set_max(animation->get_length());
}
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index f449b51b81..09f751c8dd 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -649,6 +649,9 @@ class AnimationTrackEditor : public VBoxContainer {
void _pick_track_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates);
void _pick_track_filter_input(const Ref<InputEvent> &p_ie);
+ double snap_unit;
+ void _update_snap_unit();
+
protected:
static void _bind_methods();
void _notification(int p_what);
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 1145a10f71..cede2c0ab6 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -294,6 +294,13 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me
}
for (const MethodInfo &mi : p_methods) {
+ if (mi.name.begins_with("@")) {
+ // GH-92782. GDScript inline setters/getters are historically present in `get_method_list()`
+ // and can be called using `Object.call()`. However, these functions are meant to be internal
+ // and their names are not valid identifiers, so let's hide them from the user.
+ continue;
+ }
+
if (!p_search_string.is_empty() && !mi.name.containsn(p_search_string)) {
continue;
}
@@ -324,8 +331,10 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me
continue;
}
}
+
ret.push_back(mi);
}
+
return ret;
}
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 3adff84e40..37e00bf042 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1714,105 +1714,112 @@ HashSet<StringName> EditorFileSystem::_get_scene_groups(const String &p_path) {
void EditorFileSystem::update_file(const String &p_file) {
ERR_FAIL_COND(p_file.is_empty());
- EditorFileSystemDirectory *fs = nullptr;
- int cpos = -1;
+ update_files({ p_file });
+}
- if (!_find_file(p_file, &fs, cpos)) {
- if (!fs) {
- return;
+void EditorFileSystem::update_files(const Vector<String> &p_script_paths) {
+ for (const String &file : p_script_paths) {
+ ERR_CONTINUE(file.is_empty());
+ EditorFileSystemDirectory *fs = nullptr;
+ int cpos = -1;
+
+ if (!_find_file(file, &fs, cpos)) {
+ if (!fs) {
+ return;
+ }
}
- }
- if (!FileAccess::exists(p_file)) {
- //was removed
- _delete_internal_files(p_file);
- if (cpos != -1) { // Might've never been part of the editor file system (*.* files deleted in Open dialog).
- if (fs->files[cpos]->uid != ResourceUID::INVALID_ID) {
- if (ResourceUID::get_singleton()->has_id(fs->files[cpos]->uid)) {
- ResourceUID::get_singleton()->remove_id(fs->files[cpos]->uid);
+ if (!FileAccess::exists(file)) {
+ //was removed
+ _delete_internal_files(file);
+ if (cpos != -1) { // Might've never been part of the editor file system (*.* files deleted in Open dialog).
+ if (fs->files[cpos]->uid != ResourceUID::INVALID_ID) {
+ if (ResourceUID::get_singleton()->has_id(fs->files[cpos]->uid)) {
+ ResourceUID::get_singleton()->remove_id(fs->files[cpos]->uid);
+ }
}
- }
- if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) {
- _queue_update_script_class(p_file);
- }
- if (fs->files[cpos]->type == SNAME("PackedScene")) {
- _queue_update_scene_groups(p_file);
+ if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) {
+ _queue_update_script_class(file);
+ }
+ if (fs->files[cpos]->type == SNAME("PackedScene")) {
+ _queue_update_scene_groups(file);
+ }
+
+ memdelete(fs->files[cpos]);
+ fs->files.remove_at(cpos);
}
- memdelete(fs->files[cpos]);
- fs->files.remove_at(cpos);
+ _update_pending_script_classes();
+ _update_pending_scene_groups();
+ call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later
+ return;
}
- _update_pending_script_classes();
- _update_pending_scene_groups();
- call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later
- return;
- }
-
- String type = ResourceLoader::get_resource_type(p_file);
- if (type.is_empty() && textfile_extensions.has(p_file.get_extension())) {
- type = "TextFile";
- }
- String script_class = ResourceLoader::get_resource_script_class(p_file);
+ String type = ResourceLoader::get_resource_type(file);
+ if (type.is_empty() && textfile_extensions.has(file.get_extension())) {
+ type = "TextFile";
+ }
+ String script_class = ResourceLoader::get_resource_script_class(file);
- ResourceUID::ID uid = ResourceLoader::get_resource_uid(p_file);
+ ResourceUID::ID uid = ResourceLoader::get_resource_uid(file);
- if (cpos == -1) {
- // The file did not exist, it was added.
- int idx = 0;
- String file_name = p_file.get_file();
+ if (cpos == -1) {
+ // The file did not exist, it was added.
+ int idx = 0;
+ String file_name = file.get_file();
- for (int i = 0; i < fs->files.size(); i++) {
- if (p_file.filenocasecmp_to(fs->files[i]->file) < 0) {
- break;
+ for (int i = 0; i < fs->files.size(); i++) {
+ if (file.filenocasecmp_to(fs->files[i]->file) < 0) {
+ break;
+ }
+ idx++;
}
- idx++;
- }
- EditorFileSystemDirectory::FileInfo *fi = memnew(EditorFileSystemDirectory::FileInfo);
- fi->file = file_name;
- fi->import_modified_time = 0;
- fi->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(p_file);
+ EditorFileSystemDirectory::FileInfo *fi = memnew(EditorFileSystemDirectory::FileInfo);
+ fi->file = file_name;
+ fi->import_modified_time = 0;
+ fi->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file);
- if (idx == fs->files.size()) {
- fs->files.push_back(fi);
+ if (idx == fs->files.size()) {
+ fs->files.push_back(fi);
+ } else {
+ fs->files.insert(idx, fi);
+ }
+ cpos = idx;
} else {
- fs->files.insert(idx, fi);
+ //the file exists and it was updated, and was not added in this step.
+ //this means we must force upon next restart to scan it again, to get proper type and dependencies
+ late_update_files.insert(file);
+ _save_late_updated_files(); //files need to be updated in the re-scan
}
- cpos = idx;
- } else {
- //the file exists and it was updated, and was not added in this step.
- //this means we must force upon next restart to scan it again, to get proper type and dependencies
- late_update_files.insert(p_file);
- _save_late_updated_files(); //files need to be updated in the re-scan
- }
- fs->files[cpos]->type = type;
- fs->files[cpos]->resource_script_class = script_class;
- fs->files[cpos]->uid = uid;
- fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path);
- fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(p_file);
- fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
- fs->files[cpos]->deps = _get_dependencies(p_file);
- fs->files[cpos]->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(p_file);
+ fs->files[cpos]->type = type;
+ fs->files[cpos]->resource_script_class = script_class;
+ fs->files[cpos]->uid = uid;
+ fs->files[cpos]->script_class_name = _get_global_script_class(type, file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path);
+ fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(file);
+ fs->files[cpos]->modified_time = FileAccess::get_modified_time(file);
+ fs->files[cpos]->deps = _get_dependencies(file);
+ fs->files[cpos]->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file);
- if (uid != ResourceUID::INVALID_ID) {
- if (ResourceUID::get_singleton()->has_id(uid)) {
- ResourceUID::get_singleton()->set_id(uid, p_file);
- } else {
- ResourceUID::get_singleton()->add_id(uid, p_file);
- }
+ if (uid != ResourceUID::INVALID_ID) {
+ if (ResourceUID::get_singleton()->has_id(uid)) {
+ ResourceUID::get_singleton()->set_id(uid, file);
+ } else {
+ ResourceUID::get_singleton()->add_id(uid, file);
+ }
- ResourceUID::get_singleton()->update_cache();
- }
- // Update preview
- EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
+ ResourceUID::get_singleton()->update_cache();
+ }
+ // Update preview
+ EditorResourcePreview::get_singleton()->check_for_invalidation(file);
- if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) {
- _queue_update_script_class(p_file);
- }
- if (fs->files[cpos]->type == SNAME("PackedScene")) {
- _queue_update_scene_groups(p_file);
+ if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) {
+ _queue_update_script_class(file);
+ }
+ if (fs->files[cpos]->type == SNAME("PackedScene")) {
+ _queue_update_scene_groups(file);
+ }
}
_update_pending_script_classes();
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index 84ae1e182c..cd95d5fb95 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -310,6 +310,7 @@ public:
void scan();
void scan_changes();
void update_file(const String &p_file);
+ void update_files(const Vector<String> &p_script_paths);
HashSet<String> get_valid_extensions() const;
void register_global_class_script(const String &p_search_path, const String &p_target_path);
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 80e2302e91..33f460e23d 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -652,6 +652,19 @@ void EditorProperty::add_focusable(Control *p_control) {
focusables.push_back(p_control);
}
+void EditorProperty::grab_focus(int p_focusable) {
+ if (focusables.is_empty()) {
+ return;
+ }
+
+ if (p_focusable >= 0) {
+ ERR_FAIL_INDEX(p_focusable, focusables.size());
+ focusables[p_focusable]->grab_focus();
+ } else {
+ focusables[0]->grab_focus();
+ }
+}
+
void EditorProperty::select(int p_focusable) {
bool already_selected = selected;
if (!selectable) {
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index a0ced55bd8..f9b0d1f094 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -192,6 +192,7 @@ public:
void set_deletable(bool p_enable);
bool is_deletable() const;
void add_focusable(Control *p_control);
+ void grab_focus(int p_focusable = -1);
void select(int p_focusable = -1);
void deselect();
bool is_selected() const;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index ebdad467ff..45d276dd79 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -3706,12 +3706,15 @@ void EditorNode::_remove_scene(int index, bool p_change_tab) {
}
void EditorNode::set_edited_scene(Node *p_scene) {
+ set_edited_scene_root(p_scene, true);
+}
+
+void EditorNode::set_edited_scene_root(Node *p_scene, bool p_auto_add) {
Node *old_edited_scene_root = get_editor_data().get_edited_scene_root();
- if (old_edited_scene_root) {
- if (old_edited_scene_root->get_parent() == scene_root) {
- scene_root->remove_child(old_edited_scene_root);
- }
- old_edited_scene_root->disconnect(SNAME("replacing_by"), callable_mp(this, &EditorNode::set_edited_scene));
+ ERR_FAIL_COND_MSG(p_scene && p_scene != old_edited_scene_root && p_scene->get_parent(), "Non-null nodes that are set as edited scene should not have a parent node.");
+
+ if (p_auto_add && old_edited_scene_root && old_edited_scene_root->get_parent() == scene_root) {
+ scene_root->remove_child(old_edited_scene_root);
}
get_editor_data().set_edited_scene_root(p_scene);
@@ -3723,11 +3726,8 @@ void EditorNode::set_edited_scene(Node *p_scene) {
get_tree()->set_edited_scene_root(p_scene);
}
- if (p_scene) {
- if (p_scene->get_parent() != scene_root) {
- scene_root->add_child(p_scene, true);
- }
- p_scene->connect(SNAME("replacing_by"), callable_mp(this, &EditorNode::set_edited_scene));
+ if (p_auto_add && p_scene) {
+ scene_root->add_child(p_scene, true);
}
}
@@ -5967,6 +5967,8 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins
instantiated_node->set_scene_file_path(String());
}
current_edited_scene = instantiated_node;
+
+ editor_data.set_edited_scene_root(current_edited_scene);
}
// Replace the original node with the instantiated version.
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 5d7bd5b4f8..899da99450 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -783,6 +783,7 @@ public:
SubViewport *get_scene_root() { return scene_root; } // Root of the scene being edited.
void set_edited_scene(Node *p_scene);
+ void set_edited_scene_root(Node *p_scene, bool p_auto_add);
Node *get_edited_scene() { return editor_data.get_edited_scene_root(); }
void fix_dependencies(const String &p_for_file);
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 555165c156..53a10a779f 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -255,6 +255,10 @@ void EditorPropertyArray::_change_type_menu(int p_index) {
return;
}
+ ERR_FAIL_COND_MSG(
+ changing_type_index == EditorPropertyArrayObject::NOT_CHANGING_TYPE,
+ "Tried to change type of an array item, but no item was selected.");
+
Variant value;
VariantInternal::initialize(&value, Variant::Type(p_index));
@@ -444,6 +448,10 @@ void EditorPropertyArray::update_property() {
slot.prop = new_prop;
slot.set_index(idx);
}
+ if (slot.index == changing_type_index) {
+ callable_mp(slot.prop, &EditorProperty::grab_focus).call_deferred(0);
+ changing_type_index = EditorPropertyArrayObject::NOT_CHANGING_TYPE;
+ }
slot.prop->update_property();
}
@@ -921,6 +929,10 @@ void EditorPropertyDictionary::_create_new_property_slot(int p_idx) {
}
void EditorPropertyDictionary::_change_type_menu(int p_index) {
+ ERR_FAIL_COND_MSG(
+ changing_type_index == EditorPropertyDictionaryObject::NOT_CHANGING_TYPE,
+ "Tried to change the type of a dict key or value, but nothing was selected.");
+
Variant value;
switch (changing_type_index) {
case EditorPropertyDictionaryObject::NEW_KEY_INDEX:
@@ -1062,6 +1074,14 @@ void EditorPropertyDictionary::update_property() {
new_prop->set_read_only(is_read_only());
slot.set_prop(new_prop);
}
+
+ // We need to grab the focus of the property that is being changed, even if the type didn't actually changed.
+ // Otherwise, focus will stay on the change type button, which is not very user friendly.
+ if (changing_type_index == slot.index) {
+ callable_mp(slot.prop, &EditorProperty::grab_focus).call_deferred(0);
+ changing_type_index = EditorPropertyDictionaryObject::NOT_CHANGING_TYPE; // Reset to avoid grabbing focus again.
+ }
+
slot.prop->update_property();
}
updating = false;
diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h
index 8b939ab0b0..267cb1e86d 100644
--- a/editor/editor_properties_array_dict.h
+++ b/editor/editor_properties_array_dict.h
@@ -49,6 +49,10 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
public:
+ enum {
+ NOT_CHANGING_TYPE = -1,
+ };
+
void set_array(const Variant &p_array);
Variant get_array();
@@ -68,7 +72,8 @@ protected:
public:
enum {
- NEW_KEY_INDEX = -2,
+ NOT_CHANGING_TYPE = -3,
+ NEW_KEY_INDEX,
NEW_VALUE_INDEX,
};
@@ -111,7 +116,7 @@ class EditorPropertyArray : public EditorProperty {
int page_length = 20;
int page_index = 0;
- int changing_type_index;
+ int changing_type_index = EditorPropertyArrayObject::NOT_CHANGING_TYPE;
Button *edit = nullptr;
PanelContainer *container = nullptr;
VBoxContainer *property_vbox = nullptr;
@@ -206,7 +211,7 @@ class EditorPropertyDictionary : public EditorProperty {
Ref<EditorPropertyDictionaryObject> object;
int page_length = 20;
int page_index = 0;
- int changing_type_index;
+ int changing_type_index = EditorPropertyDictionaryObject::NOT_CHANGING_TYPE;
Button *edit = nullptr;
PanelContainer *container = nullptr;
VBoxContainer *property_vbox = nullptr;
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index dd698d74b6..742d29fef0 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -138,7 +138,6 @@ void EditorResourcePreview::_preview_ready(const String &p_path, int p_hash, con
}
Item item;
- item.order = order++;
item.preview = p_texture;
item.small_preview = p_small_texture;
item.last_hash = p_hash;
@@ -412,7 +411,6 @@ void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p
String path_id = "ID:" + itos(p_res->get_instance_id());
if (cache.has(path_id) && cache[path_id].last_hash == p_res->hash_edited_version_for_preview()) {
- cache[path_id].order = order++;
p_receiver->call(p_receiver_func, path_id, cache[path_id].preview, cache[path_id].small_preview, p_userdata);
return;
}
@@ -439,7 +437,6 @@ void EditorResourcePreview::queue_resource_preview(const String &p_path, Object
MutexLock lock(preview_mutex);
if (cache.has(p_path)) {
- cache[p_path].order = order++;
p_receiver->call(p_receiver_func, p_path, cache[p_path].preview, cache[p_path].small_preview, p_userdata);
return;
}
@@ -533,7 +530,6 @@ void EditorResourcePreview::stop() {
EditorResourcePreview::EditorResourcePreview() {
singleton = this;
- order = 0;
}
EditorResourcePreview::~EditorResourcePreview() {
diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h
index 6b67acceaa..2870f9a201 100644
--- a/editor/editor_resource_preview.h
+++ b/editor/editor_resource_preview.h
@@ -99,13 +99,10 @@ class EditorResourcePreview : public Node {
Ref<Texture2D> preview;
Ref<Texture2D> small_preview;
Dictionary preview_metadata;
- int order = 0;
uint32_t last_hash = 0;
uint64_t modified_time = 0;
};
- int order;
-
HashMap<String, Item> cache;
void _preview_ready(const String &p_path, int p_hash, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud, const Dictionary &p_metadata);
diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp
index 2622645d7c..4862b3436e 100644
--- a/editor/gui/editor_scene_tabs.cpp
+++ b/editor/gui/editor_scene_tabs.cpp
@@ -405,7 +405,7 @@ EditorSceneTabs::EditorSceneTabs() {
scene_tabs->connect(SceneStringName(mouse_exited), callable_mp(this, &EditorSceneTabs::_scene_tab_exit));
scene_tabs->connect(SceneStringName(gui_input), callable_mp(this, &EditorSceneTabs::_scene_tab_input));
scene_tabs->connect("active_tab_rearranged", callable_mp(this, &EditorSceneTabs::_reposition_active_tab));
- scene_tabs->connect(SceneStringName(resized), callable_mp(this, &EditorSceneTabs::_scene_tabs_resized));
+ scene_tabs->connect(SceneStringName(resized), callable_mp(this, &EditorSceneTabs::_scene_tabs_resized), CONNECT_DEFERRED);
scene_tabs_context_menu = memnew(PopupMenu);
tabbar_container->add_child(scene_tabs_context_menu);
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index 7e2d6c9d1e..fc52d7a0ae 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -232,23 +232,27 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
item->set_icon(0, icon);
item->set_metadata(0, p_node->get_path());
+ if (connecting_signal) {
+ // Add script icons for all scripted nodes.
+ Ref<Script> scr = p_node->get_script();
+ if (scr.is_valid()) {
+ item->add_button(0, get_editor_theme_icon(SNAME("Script")), BUTTON_SCRIPT);
+ if (EditorNode::get_singleton()->get_object_custom_type_base(p_node) == scr) {
+ // Disable button on custom scripts (pure visual cue).
+ item->set_button_disabled(0, item->get_button_count(0) - 1, true);
+ }
+ }
+ }
+
if (connect_to_script_mode) {
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
Ref<Script> scr = p_node->get_script();
- if (!scr.is_null() && EditorNode::get_singleton()->get_object_custom_type_base(p_node) != scr) {
- //has script
- item->add_button(0, get_editor_theme_icon(SNAME("Script")), BUTTON_SCRIPT);
- } else {
- //has no script (or script is a custom type)
+ bool has_custom_script = scr.is_valid() && EditorNode::get_singleton()->get_object_custom_type_base(p_node) == scr;
+ if (scr.is_null() || has_custom_script) {
_set_item_custom_color(item, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
item->set_selectable(0, false);
- if (!scr.is_null()) { // make sure to mark the script if a custom type
- item->add_button(0, get_editor_theme_icon(SNAME("Script")), BUTTON_SCRIPT);
- item->set_button_disabled(0, item->get_button_count(0) - 1, true);
- }
-
accent.a *= 0.7;
}
diff --git a/editor/gui/scene_tree_editor.h b/editor/gui/scene_tree_editor.h
index 9ae1e99a27..b4d9644f16 100644
--- a/editor/gui/scene_tree_editor.h
+++ b/editor/gui/scene_tree_editor.h
@@ -160,7 +160,6 @@ public:
void set_marked(const HashSet<Node *> &p_marked, bool p_selectable = true, bool p_children_selectable = true);
void set_marked(Node *p_marked, bool p_selectable = true, bool p_children_selectable = true);
- bool has_marked() const { return !marked.is_empty(); }
void set_selected(Node *p_node, bool p_emit_selected = true);
Node *get_selected();
void set_can_rename(bool p_can_rename) { can_rename = p_can_rename; }
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index fe56f48889..0a2c192ea4 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -243,12 +243,12 @@ void AnimationPlayerEditor::_play_from_pressed() {
String current = _get_current();
if (!current.is_empty()) {
- float time = player->get_current_animation_position();
+ double time = player->get_current_animation_position();
if (current == player->get_assigned_animation() && player->is_playing()) {
- player->stop(); //so it won't blend with itself
+ player->clear_caches(); //so it won't blend with itself
}
ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing.");
- player->seek(time);
+ player->seek(time, true, true);
player->play(current);
}
@@ -281,12 +281,12 @@ void AnimationPlayerEditor::_play_bw_from_pressed() {
String current = _get_current();
if (!current.is_empty()) {
- float time = player->get_current_animation_position();
- if (current == player->get_assigned_animation()) {
- player->stop(); //so it won't blend with itself
+ double time = player->get_current_animation_position();
+ if (current == player->get_assigned_animation() && player->is_playing()) {
+ player->clear_caches(); //so it won't blend with itself
}
ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing.");
- player->seek(time);
+ player->seek(time, true, true);
player->play_backwards(current);
}
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 202817f6ee..e4eaab7325 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -922,7 +922,7 @@ void vertex() {
VERTEX = VERTEX;
POSITION = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * vec4(VERTEX.xyz, 1.0);
- POSITION.z = mix(POSITION.z, 0.0, 0.999);
+ POSITION.z = mix(POSITION.z, POSITION.w, 0.999);
POINT_SIZE = point_size;
}
@@ -1201,7 +1201,7 @@ void vertex() {
}
VERTEX = VERTEX;
POSITION = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * vec4(VERTEX.xyz, 1.0);
- POSITION.z = mix(POSITION.z, 0, 0.998);
+ POSITION.z = mix(POSITION.z, POSITION.w, 0.998);
}
void fragment() {
ALBEDO = COLOR.rgb;
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index cf1ad36adc..6a6e2b83ab 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -743,20 +743,24 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
localization_editor->connect("localization_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
tab_container->add_child(localization_editor);
+ TabContainer *globals_container = memnew(TabContainer);
+ globals_container->set_name(TTR("Globals"));
+ tab_container->add_child(globals_container);
+
autoload_settings = memnew(EditorAutoloadSettings);
autoload_settings->set_name(TTR("Autoload"));
autoload_settings->connect("autoload_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
- tab_container->add_child(autoload_settings);
+ globals_container->add_child(autoload_settings);
shaders_global_shader_uniforms_editor = memnew(ShaderGlobalsEditor);
shaders_global_shader_uniforms_editor->set_name(TTR("Shader Globals"));
shaders_global_shader_uniforms_editor->connect("globals_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
- tab_container->add_child(shaders_global_shader_uniforms_editor);
+ globals_container->add_child(shaders_global_shader_uniforms_editor);
group_settings = memnew(GroupSettingsEditor);
- group_settings->set_name(TTR("Global Groups"));
+ group_settings->set_name(TTR("Groups"));
group_settings->connect("group_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
- tab_container->add_child(group_settings);
+ globals_container->add_child(group_settings);
plugin_settings = memnew(EditorPluginSettings);
plugin_settings->set_name(TTR("Plugins"));
diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp
index d123d8ef59..a5157bd394 100644
--- a/editor/property_selector.cpp
+++ b/editor/property_selector.cpp
@@ -225,11 +225,24 @@ void PropertySelector::_update_search() {
} else {
Ref<Script> script_ref = Object::cast_to<Script>(ObjectDB::get_instance(script));
if (script_ref.is_valid()) {
- methods.push_back(MethodInfo("*Script Methods"));
if (script_ref->is_built_in()) {
script_ref->reload(true);
}
- script_ref->get_script_method_list(&methods);
+
+ List<MethodInfo> script_methods;
+ script_ref->get_script_method_list(&script_methods);
+
+ methods.push_back(MethodInfo("*Script Methods")); // TODO: Split by inheritance.
+
+ for (const MethodInfo &mi : script_methods) {
+ if (mi.name.begins_with("@")) {
+ // GH-92782. GDScript inline setters/getters are historically present in `get_method_list()`
+ // and can be called using `Object.call()`. However, these functions are meant to be internal
+ // and their names are not valid identifiers, so let's hide them from the user.
+ continue;
+ }
+ methods.push_back(mi);
+ }
}
StringName base = base_type;
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 2f57dc5610..4448f165d3 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -76,12 +76,15 @@ void SceneTreeDock::_quick_open() {
}
void SceneTreeDock::_inspect_hovered_node() {
- scene_tree->set_selected(node_hovered_now);
- scene_tree->set_marked(node_hovered_now);
+ select_node_hovered_at_end_of_drag = true;
+ if (tree_item_inspected != nullptr) {
+ tree_item_inspected->clear_custom_color(0);
+ }
Tree *tree = scene_tree->get_scene_tree();
- TreeItem *item = tree->get_item_at_position(tree->get_local_mouse_position());
+ TreeItem *item = tree->get_item_with_metadata(node_hovered_now->get_path());
if (item) {
- item->set_as_cursor(0);
+ tree_item_inspected = item;
+ tree_item_inspected->set_custom_color(0, get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));
}
InspectorDock::get_inspector_singleton()->edit(node_hovered_now);
InspectorDock::get_inspector_singleton()->propagate_notification(NOTIFICATION_DRAG_BEGIN); // Enable inspector drag preview after it updated.
@@ -130,8 +133,8 @@ void SceneTreeDock::input(const Ref<InputEvent> &p_event) {
}
if (mb->is_released()) {
- if (scene_tree->has_marked()) {
- scene_tree->set_marked(nullptr);
+ if (tree_item_inspected != nullptr) {
+ tree_item_inspected->clear_custom_color(0);
}
_reset_hovering_timer();
}
@@ -1658,6 +1661,16 @@ void SceneTreeDock::_notification(int p_what) {
case NOTIFICATION_DRAG_END: {
_reset_hovering_timer();
+ if (select_node_hovered_at_end_of_drag && !hovered_but_reparenting) {
+ Node *node_inspected = Object::cast_to<Node>(InspectorDock::get_inspector_singleton()->get_edited_object());
+ if (node_inspected) {
+ editor_selection->clear();
+ editor_selection->add_node(node_inspected);
+ scene_tree->set_selected(node_inspected);
+ select_node_hovered_at_end_of_drag = false;
+ }
+ }
+ hovered_but_reparenting = false;
} break;
}
}
@@ -2185,6 +2198,7 @@ void SceneTreeDock::_node_reparent(NodePath p_path, bool p_keep_global_xform) {
ERR_FAIL_NULL(new_parent);
List<Node *> selection = editor_selection->get_selected_node_list();
+ List<Node *> full_selection = editor_selection->get_full_selected_node_list();
if (selection.is_empty()) {
return; // Nothing to reparent.
@@ -2197,6 +2211,10 @@ void SceneTreeDock::_node_reparent(NodePath p_path, bool p_keep_global_xform) {
}
_do_reparent(new_parent, -1, nodes, p_keep_global_xform);
+
+ for (Node *E : full_selection) {
+ editor_selection->add_node(E);
+ }
}
void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform) {
@@ -2238,6 +2256,9 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
return; // Position and parent didn't change.
}
+ // Prevent selecting the hovered node and keep the reparented node(s) selected instead.
+ hovered_but_reparenting = true;
+
Node *validate = new_parent;
while (validate) {
ERR_FAIL_COND_MSG(p_nodes.has(validate), "Selection changed at some point. Can't reparent.");
@@ -2992,6 +3013,10 @@ void SceneTreeDock::_replace_node(Node *p_node, Node *p_by_node, bool p_keep_pro
to_erase.push_back(oldnode->get_child(i));
}
}
+
+ if (oldnode == edited_scene) {
+ EditorNode::get_singleton()->set_edited_scene_root(newnode, false);
+ }
oldnode->replace_by(newnode, true);
// Re-apply size of anchored control.
@@ -3396,6 +3421,7 @@ void SceneTreeDock::_nodes_dragged(const Array &p_nodes, NodePath p_to, int p_ty
}
List<Node *> selection = editor_selection->get_selected_node_list();
+ List<Node *> full_selection = editor_selection->get_full_selected_node_list();
if (selection.is_empty()) {
return; //nothing to reparent
@@ -3415,7 +3441,8 @@ void SceneTreeDock::_nodes_dragged(const Array &p_nodes, NodePath p_to, int p_ty
_normalize_drop(to_node, to_pos, p_type);
_do_reparent(to_node, to_pos, nodes, !Input::get_singleton()->is_key_pressed(Key::SHIFT));
- for (Node *E : nodes) {
+
+ for (Node *E : full_selection) {
editor_selection->add_node(E);
}
}
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index abef990995..5028cd5cc9 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -239,8 +239,11 @@ class SceneTreeDock : public VBoxContainer {
void _inspect_hovered_node();
void _reset_hovering_timer();
Timer *inspect_hovered_node_delay = nullptr;
+ TreeItem *tree_item_inspected = nullptr;
Node *node_hovered_now = nullptr;
Node *node_hovered_previously = nullptr;
+ bool select_node_hovered_at_end_of_drag = false;
+ bool hovered_but_reparenting = false;
virtual void input(const Ref<InputEvent> &p_event) override;
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index b27f80ee29..03c7a8291a 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -364,3 +364,9 @@ GH-92322
Validate extension JSON: Error: Field 'classes/EditorInspectorPlugin/methods/add_property_editor/arguments': size changed value in new API, from 3 to 4.
Optional arguments added. Compatibility methods registered.
+
+GH-86661
+--------
+Validate extension JSON: Error: Field 'classes/Animation/methods/track_find_key/arguments': size changed value in new API, from 3 to 5.
+
+Added optional argument to track_find_key to handle backward seeking. Compatibility method registered.
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 771ccf47b7..f1a35c84b7 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -166,7 +166,7 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
panic_mode = true;
// TODO: Improve positional information.
if (p_origin == nullptr) {
- errors.push_back({ p_message, current.start_line, current.start_column });
+ errors.push_back({ p_message, previous.start_line, previous.start_column });
} else {
errors.push_back({ p_message, p_origin->start_line, p_origin->leftmost_column });
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
index eef26cdd4e..5cc2a8026e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -427,10 +427,11 @@ namespace Godot.Bridge
// This method may be called before initialization.
if (NativeFuncs.godotsharp_dotnet_module_is_initialized().ToBool() && Engine.IsEditorHint())
{
- foreach (var scriptPath in _pathTypeBiMap.Paths)
+ if (_pathTypeBiMap.Paths.Count > 0)
{
- using godot_string nativeScriptPath = Marshaling.ConvertStringToNative(scriptPath);
- NativeFuncs.godotsharp_internal_editor_file_system_update_file(nativeScriptPath);
+ string[] scriptPaths = _pathTypeBiMap.Paths.ToArray();
+ using godot_packed_string_array scriptPathsNative = Marshaling.ConvertSystemArrayToNativePackedStringArray(scriptPaths);
+ NativeFuncs.godotsharp_internal_editor_file_system_update_files(scriptPathsNative);
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
index 1ec1a75516..29fa13d625 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@@ -64,7 +65,7 @@ public static partial class ScriptManagerBridge
private System.Collections.Generic.Dictionary<string, Type> _pathTypeMap = new();
private System.Collections.Generic.Dictionary<Type, string> _typePathMap = new();
- public System.Collections.Generic.IEnumerable<string> Paths => _pathTypeMap.Keys;
+ public IReadOnlyCollection<string> Paths => _pathTypeMap.Keys;
public void Add(string scriptPath, Type scriptType)
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
index 04b6c2e743..6b5d7fed78 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
@@ -143,7 +143,7 @@ namespace Godot.NativeInterop
if (error.Error != godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_OK)
{
using godot_variant instanceVariant = VariantUtils.CreateFromGodotObjectPtr(instance);
- string where = GetCallErrorWhere(method, &instanceVariant, args, argCount);
+ string where = GetCallErrorWhere(ref error, method, &instanceVariant, args, argCount);
string errorText = GetCallErrorMessage(error, where, args);
GD.PushError(errorText);
}
@@ -161,7 +161,7 @@ namespace Godot.NativeInterop
}
}
- private unsafe static string GetCallErrorWhere(godot_string_name method, godot_variant* instance, godot_variant** args, int argCount)
+ private unsafe static string GetCallErrorWhere(ref godot_variant_call_error error, godot_string_name method, godot_variant* instance, godot_variant** args, int argCount)
{
string? methodstr = null;
string basestr = GetVariantTypeName(instance);
@@ -171,6 +171,10 @@ namespace Godot.NativeInterop
if (argCount >= 1)
{
methodstr = VariantUtils.ConvertToString(*args[0]);
+ if (error.Error == godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT)
+ {
+ error.Argument += 1;
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
index c4fd639cce..cfd9ed7acc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -59,7 +59,7 @@ namespace Godot.NativeInterop
internal static partial void godotsharp_stack_info_vector_destroy(
ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
- internal static partial void godotsharp_internal_editor_file_system_update_file(in godot_string p_script_path);
+ internal static partial void godotsharp_internal_editor_file_system_update_files(in godot_packed_string_array p_script_paths);
internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func,
in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr,
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index 1af462dafd..80e9fdf77f 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -315,13 +315,13 @@ void godotsharp_internal_new_csharp_script(Ref<CSharpScript> *r_dest) {
memnew_placement(r_dest, Ref<CSharpScript>(memnew(CSharpScript)));
}
-void godotsharp_internal_editor_file_system_update_file(const String *p_script_path) {
+void godotsharp_internal_editor_file_system_update_files(const PackedStringArray &p_script_paths) {
#ifdef TOOLS_ENABLED
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem *efs = EditorFileSystem::get_singleton();
if (efs) {
- efs->update_file(*p_script_path);
+ efs->update_files(p_script_paths);
}
#else
// EditorFileSystem is only available when running in the Godot editor.
@@ -1450,7 +1450,7 @@ static const void *unmanaged_callbacks[]{
(void *)godotsharp_engine_get_singleton,
(void *)godotsharp_stack_info_vector_resize,
(void *)godotsharp_stack_info_vector_destroy,
- (void *)godotsharp_internal_editor_file_system_update_file,
+ (void *)godotsharp_internal_editor_file_system_update_files,
(void *)godotsharp_internal_script_debugger_send_error,
(void *)godotsharp_internal_script_debugger_is_active,
(void *)godotsharp_internal_object_get_associated_gchandle,
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index dfbc92a919..1779be2cc2 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -617,7 +617,7 @@ Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector
const Face3 f(p.points[0].pos, p.points[point_id - 1].pos, p.points[point_id].pos);
Vector3 inters;
if (f.intersects_segment(p_from, p_to, &inters)) {
- const real_t d = closest_point_d = p_from.distance_to(inters);
+ const real_t d = p_from.distance_to(inters);
if (use_collision == false) {
closest_point = inters;
use_collision = true;
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
index b8a2f58935..d9a66aa827 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
@@ -293,7 +293,12 @@ void OpenXRHandTrackingExtension::on_process() {
}
godot_tracker->set_hand_tracking_source(source);
- godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity);
+ if (location.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) {
+ godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity);
+ } else {
+ godot_tracker->set_has_tracking_data(false);
+ godot_tracker->invalidate_pose("default");
+ }
}
}
} else {
diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h
index 01da0c7935..087fd1bace 100644
--- a/platform/android/api/jni_singleton.h
+++ b/platform/android/api/jni_singleton.h
@@ -219,19 +219,12 @@ public:
}
void add_signal(const StringName &p_name, const Vector<Variant::Type> &p_args) {
- if (p_args.size() == 0) {
- ADD_SIGNAL(MethodInfo(p_name));
- } else if (p_args.size() == 1) {
- ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1")));
- } else if (p_args.size() == 2) {
- ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2")));
- } else if (p_args.size() == 3) {
- ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3")));
- } else if (p_args.size() == 4) {
- ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4")));
- } else if (p_args.size() == 5) {
- ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4"), PropertyInfo(p_args[4], "arg5")));
+ MethodInfo mi;
+ mi.name = p_name;
+ for (int i = 0; i < p_args.size(); i++) {
+ mi.arguments.push_back(PropertyInfo(p_args[i], "arg" + itos(i + 1)));
}
+ ADD_SIGNAL(mi);
}
#endif
diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index 12d3a6fd2f..aff83ddeee 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -1018,8 +1018,7 @@ void DisplayServerWayland::cursor_set_custom_image(const Ref<Resource> &p_cursor
wayland_thread.cursor_shape_clear_custom_image(p_shape);
}
- Rect2 atlas_rect;
- Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect);
+ Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);
ERR_FAIL_COND(image.is_null());
CustomCursor &cursor = custom_cursors[p_shape];
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 34b6d0219a..0e4c723a87 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -3108,8 +3108,7 @@ void DisplayServerX11::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu
cursors_cache.erase(p_shape);
}
- Rect2 atlas_rect;
- Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect);
+ Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);
ERR_FAIL_COND(image.is_null());
Vector2i texture_size = image->get_size();
@@ -3127,13 +3126,8 @@ void DisplayServerX11::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu
cursor_image->pixels = (XcursorPixel *)memalloc(size);
for (XcursorPixel index = 0; index < image_size; index++) {
- int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
- int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
-
- if (atlas_rect.has_area()) {
- column_index = MIN(column_index, atlas_rect.size.width - 1);
- row_index = MIN(row_index, atlas_rect.size.height - 1);
- }
+ int row_index = floor(index / texture_size.width);
+ int column_index = index % int(texture_size.width);
*(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32();
}
@@ -4190,8 +4184,11 @@ void DisplayServerX11::popup_open(WindowID p_window) {
}
}
+ // Detect tooltips and other similar popups that shouldn't block input to their parent.
+ bool ignores_input = window_get_flag(WINDOW_FLAG_NO_FOCUS, p_window) && window_get_flag(WINDOW_FLAG_MOUSE_PASSTHROUGH, p_window);
+
WindowData &wd = windows[p_window];
- if (wd.is_popup || has_popup_ancestor) {
+ if (wd.is_popup || (has_popup_ancestor && !ignores_input)) {
// Find current popup parent, or root popup if new window is not transient.
List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 50313cfe67..a1a91345ac 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -2845,8 +2845,7 @@ void DisplayServerMacOS::cursor_set_custom_image(const Ref<Resource> &p_cursor,
cursors_cache.erase(p_shape);
}
- Rect2 atlas_rect;
- Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect);
+ Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);
ERR_FAIL_COND(image.is_null());
Vector2i texture_size = image->get_size();
@@ -2868,13 +2867,8 @@ void DisplayServerMacOS::cursor_set_custom_image(const Ref<Resource> &p_cursor,
int len = int(texture_size.width * texture_size.height);
for (int i = 0; i < len; i++) {
- int row_index = floor(i / texture_size.width) + atlas_rect.position.y;
- int column_index = (i % int(texture_size.width)) + atlas_rect.position.x;
-
- if (atlas_rect.has_area()) {
- column_index = MIN(column_index, atlas_rect.size.width - 1);
- row_index = MIN(row_index, atlas_rect.size.height - 1);
- }
+ int row_index = floor(i / texture_size.width);
+ int column_index = i % int(texture_size.width);
uint32_t color = image->get_pixel(column_index, row_index).to_argb32();
@@ -3172,42 +3166,13 @@ void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) {
DisplayServer::IndicatorID DisplayServerMacOS::create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) {
NSImage *nsimg = nullptr;
- if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) {
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
Ref<Image> img = p_icon->get_image();
img = img->duplicate();
- img->convert(Image::FORMAT_RGBA8);
-
- NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:nullptr
- pixelsWide:img->get_width()
- pixelsHigh:img->get_height()
- bitsPerSample:8
- samplesPerPixel:4
- hasAlpha:YES
- isPlanar:NO
- colorSpaceName:NSDeviceRGBColorSpace
- bytesPerRow:img->get_width() * 4
- bitsPerPixel:32];
- if (imgrep) {
- uint8_t *pixels = [imgrep bitmapData];
-
- int len = img->get_width() * img->get_height();
- const uint8_t *r = img->get_data().ptr();
-
- /* Premultiply the alpha channel */
- for (int i = 0; i < len; i++) {
- uint8_t alpha = r[i * 4 + 3];
- pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
- pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
- pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
- pixels[i * 4 + 3] = alpha;
- }
-
- nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())];
- if (nsimg) {
- [nsimg addRepresentation:imgrep];
- }
+ if (img->is_compressed()) {
+ img->decompress();
}
+ nsimg = _convert_to_nsimg(img);
}
IndicatorData idat;
@@ -3235,42 +3200,13 @@ void DisplayServerMacOS::status_indicator_set_icon(IndicatorID p_id, const Ref<T
ERR_FAIL_COND(!indicators.has(p_id));
NSImage *nsimg = nullptr;
- if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) {
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
Ref<Image> img = p_icon->get_image();
img = img->duplicate();
- img->convert(Image::FORMAT_RGBA8);
-
- NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:nullptr
- pixelsWide:img->get_width()
- pixelsHigh:img->get_height()
- bitsPerSample:8
- samplesPerPixel:4
- hasAlpha:YES
- isPlanar:NO
- colorSpaceName:NSDeviceRGBColorSpace
- bytesPerRow:img->get_width() * 4
- bitsPerPixel:32];
- if (imgrep) {
- uint8_t *pixels = [imgrep bitmapData];
-
- int len = img->get_width() * img->get_height();
- const uint8_t *r = img->get_data().ptr();
-
- /* Premultiply the alpha channel */
- for (int i = 0; i < len; i++) {
- uint8_t alpha = r[i * 4 + 3];
- pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
- pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
- pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
- pixels[i * 4 + 3] = alpha;
- }
-
- nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())];
- if (nsimg) {
- [nsimg addRepresentation:imgrep];
- }
+ if (img->is_compressed()) {
+ img->decompress();
}
+ nsimg = _convert_to_nsimg(img);
}
NSStatusItem *item = indicators[p_id].item;
@@ -3430,8 +3366,11 @@ void DisplayServerMacOS::popup_open(WindowID p_window) {
}
}
+ // Detect tooltips and other similar popups that shouldn't block input to their parent.
+ bool ignores_input = window_get_flag(WINDOW_FLAG_NO_FOCUS, p_window) && window_get_flag(WINDOW_FLAG_MOUSE_PASSTHROUGH, p_window);
+
WindowData &wd = windows[p_window];
- if (wd.is_popup || has_popup_ancestor) {
+ if (wd.is_popup || (has_popup_ancestor && !ignores_input)) {
bool was_empty = popup_list.is_empty();
// Find current popup parent, or root popup if new window is not transient.
List<WindowID>::Element *C = nullptr;
diff --git a/platform/macos/godot_menu_delegate.mm b/platform/macos/godot_menu_delegate.mm
index 5c1e849715..3f7dfac3de 100644
--- a/platform/macos/godot_menu_delegate.mm
+++ b/platform/macos/godot_menu_delegate.mm
@@ -40,13 +40,20 @@
- (void)doNothing:(id)sender {
}
-- (void)menuNeedsUpdate:(NSMenu *)menu {
+- (void)menuWillOpen:(NSMenu *)menu {
if (NativeMenu::get_singleton()) {
NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton();
nmenu->_menu_open(menu);
}
}
+- (void)menuNeedsUpdate:(NSMenu *)menu {
+ if (NativeMenu::get_singleton()) {
+ NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton();
+ nmenu->_menu_need_update(menu);
+ }
+}
+
- (void)menuDidClose:(NSMenu *)menu {
if (NativeMenu::get_singleton()) {
NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton();
diff --git a/platform/macos/native_menu_macos.h b/platform/macos/native_menu_macos.h
index b5dbb8b9b0..42cf6740d9 100644
--- a/platform/macos/native_menu_macos.h
+++ b/platform/macos/native_menu_macos.h
@@ -73,8 +73,11 @@ class NativeMenuMacOS : public NativeMenu {
public:
void _register_system_menus(NSMenu *p_main_menu, NSMenu *p_application_menu, NSMenu *p_window_menu, NSMenu *p_help_menu, NSMenu *p_dock_menu);
NSMenu *_get_dock_menu();
+
+ void _menu_need_update(NSMenu *p_menu);
void _menu_open(NSMenu *p_menu);
void _menu_close(NSMenu *p_menu);
+ void _menu_close_cb(const RID &p_rid);
virtual bool has_feature(Feature p_feature) const override;
@@ -98,6 +101,8 @@ public:
virtual void set_minimum_width(const RID &p_rid, float p_width) override;
virtual float get_minimum_width(const RID &p_rid) const override;
+ virtual bool is_opened(const RID &p_rid) const override;
+
virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1) override;
virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
diff --git a/platform/macos/native_menu_macos.mm b/platform/macos/native_menu_macos.mm
index 1cf13a2d69..1ae1137ca0 100644
--- a/platform/macos/native_menu_macos.mm
+++ b/platform/macos/native_menu_macos.mm
@@ -91,11 +91,22 @@ void NativeMenuMacOS::_menu_open(NSMenu *p_menu) {
if (menu_lookup.has(p_menu)) {
MenuData *md = menus.get_or_null(menu_lookup[p_menu]);
if (md) {
+ // Note: Set "is_open" flag, but do not call callback, menu items can't be modified during this call and "_menu_need_update" will be called right before it.
md->is_open = true;
+ }
+ }
+}
+
+void NativeMenuMacOS::_menu_need_update(NSMenu *p_menu) {
+ if (menu_lookup.has(p_menu)) {
+ MenuData *md = menus.get_or_null(menu_lookup[p_menu]);
+ if (md) {
+ // Note: "is_open" flag is set by "_menu_open", this method is always called before menu is shown, but might be called for the other reasons as well.
if (md->open_cb.is_valid()) {
Variant ret;
Callable::CallError ce;
+ // Callback is called directly, since it's expected to modify menu items before it's shown.
md->open_cb.callp(nullptr, 0, ret, ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_PRINT(vformat("Failed to execute menu open callback: %s.", Variant::get_callable_error_text(md->open_cb, nullptr, 0, ce)));
@@ -110,15 +121,22 @@ void NativeMenuMacOS::_menu_close(NSMenu *p_menu) {
MenuData *md = menus.get_or_null(menu_lookup[p_menu]);
if (md) {
md->is_open = false;
- if (md->close_cb.is_valid()) {
- Variant ret;
- Callable::CallError ce;
- md->close_cb.callp(nullptr, 0, ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- ERR_PRINT(vformat("Failed to execute menu close callback: %s.", Variant::get_callable_error_text(md->close_cb, nullptr, 0, ce)));
- }
- }
+ // Callback called deferred, since it should not modify menu items during "_menu_close" call.
+ callable_mp(this, &NativeMenuMacOS::_menu_close_cb).call_deferred(menu_lookup[p_menu]);
+ }
+ }
+}
+
+void NativeMenuMacOS::_menu_close_cb(const RID &p_rid) {
+ MenuData *md = menus.get_or_null(p_rid);
+ if (md->close_cb.is_valid()) {
+ Variant ret;
+ Callable::CallError ce;
+
+ md->close_cb.callp(nullptr, 0, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute menu close callback: %s.", Variant::get_callable_error_text(md->close_cb, nullptr, 0, ce)));
}
}
}
@@ -328,6 +346,13 @@ float NativeMenuMacOS::get_minimum_width(const RID &p_rid) const {
return md->menu.minimumWidth * DisplayServer::get_singleton()->screen_get_max_scale();
}
+bool NativeMenuMacOS::is_opened(const RID &p_rid) const {
+ const MenuData *md = menus.get_or_null(p_rid);
+ ERR_FAIL_NULL_V(md, false);
+
+ return md->is_open;
+}
+
int NativeMenuMacOS::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) {
MenuData *md = menus.get_or_null(p_rid);
MenuData *md_sub = menus.get_or_null(p_submenu_rid);
@@ -436,7 +461,7 @@ int NativeMenuMacOS::add_icon_item(const RID &p_rid, const Ref<Texture2D> &p_ico
obj->max_states = 0;
obj->state = 0;
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
- if (ds && p_icon.is_valid()) {
+ if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
obj->img = p_icon->get_image();
obj->img = obj->img->duplicate();
if (obj->img->is_compressed()) {
@@ -467,7 +492,7 @@ int NativeMenuMacOS::add_icon_check_item(const RID &p_rid, const Ref<Texture2D>
obj->max_states = 0;
obj->state = 0;
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
- if (ds && p_icon.is_valid()) {
+ if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
obj->img = p_icon->get_image();
obj->img = obj->img->duplicate();
if (obj->img->is_compressed()) {
@@ -518,7 +543,7 @@ int NativeMenuMacOS::add_icon_radio_check_item(const RID &p_rid, const Ref<Textu
obj->max_states = 0;
obj->state = 0;
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
- if (ds && p_icon.is_valid()) {
+ if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
obj->img = p_icon->get_image();
obj->img = obj->img->duplicate();
if (obj->img->is_compressed()) {
@@ -1212,7 +1237,7 @@ void NativeMenuMacOS::set_item_icon(const RID &p_rid, int p_idx, const Ref<Textu
GodotMenuItem *obj = [menu_item representedObject];
ERR_FAIL_NULL(obj);
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
- if (ds && p_icon.is_valid()) {
+ if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
obj->img = p_icon->get_image();
obj->img = obj->img->duplicate();
if (obj->img->is_compressed()) {
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index fab92b1894..40de4e523b 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -517,19 +517,10 @@ DisplayServer::CursorShape DisplayServerWeb::cursor_get_shape() const {
void DisplayServerWeb::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
if (p_cursor.is_valid()) {
- Rect2 atlas_rect;
- Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect);
+ Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);
ERR_FAIL_COND(image.is_null());
Vector2i texture_size = image->get_size();
- if (atlas_rect.has_area()) {
- image->crop_from_point(
- atlas_rect.position.x,
- atlas_rect.position.y,
- texture_size.width,
- texture_size.height);
- }
-
if (image->get_format() != Image::FORMAT_RGBA8) {
image->convert(Image::FORMAT_RGBA8);
}
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index c114adff84..10cec9f5ed 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -2408,8 +2408,7 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor
cursors_cache.erase(p_shape);
}
- Rect2 atlas_rect;
- Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot, atlas_rect);
+ Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);
ERR_FAIL_COND(image.is_null());
Vector2i texture_size = image->get_size();
@@ -2437,13 +2436,9 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor
bool fully_transparent = true;
for (UINT index = 0; index < image_size; index++) {
- int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
- int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
+ int row_index = floor(index / texture_size.width);
+ int column_index = index % int(texture_size.width);
- if (atlas_rect.has_area()) {
- column_index = MIN(column_index, atlas_rect.size.width - 1);
- row_index = MIN(row_index, atlas_rect.size.height - 1);
- }
const Color &c = image->get_pixel(column_index, row_index);
fully_transparent = fully_transparent && (c.a == 0.f);
@@ -3171,9 +3166,12 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
DisplayServer::IndicatorID DisplayServerWindows::create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) {
HICON hicon = nullptr;
- if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) {
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
Ref<Image> img = p_icon->get_image();
img = img->duplicate();
+ if (img->is_compressed()) {
+ img->decompress();
+ }
img->convert(Image::FORMAT_RGBA8);
int w = img->get_width();
@@ -3241,9 +3239,12 @@ void DisplayServerWindows::status_indicator_set_icon(IndicatorID p_id, const Ref
ERR_FAIL_COND(!indicators.has(p_id));
HICON hicon = nullptr;
- if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) {
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
Ref<Image> img = p_icon->get_image();
img = img->duplicate();
+ if (img->is_compressed()) {
+ img->decompress();
+ }
img->convert(Image::FORMAT_RGBA8);
int w = img->get_width();
@@ -3574,8 +3575,11 @@ void DisplayServerWindows::popup_open(WindowID p_window) {
}
}
+ // Detect tooltips and other similar popups that shouldn't block input to their parent.
+ bool ignores_input = window_get_flag(WINDOW_FLAG_NO_FOCUS, p_window) && window_get_flag(WINDOW_FLAG_MOUSE_PASSTHROUGH, p_window);
+
WindowData &wd = windows[p_window];
- if (wd.is_popup || has_popup_ancestor) {
+ if (wd.is_popup || (has_popup_ancestor && !ignores_input)) {
// Find current popup parent, or root popup if new window is not transient.
List<WindowID>::Element *C = nullptr;
List<WindowID>::Element *E = popup_list.back();
@@ -4727,6 +4731,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} break;
case WM_EXITSIZEMOVE: {
KillTimer(windows[window_id].hWnd, windows[window_id].move_timer_id);
+ windows[window_id].move_timer_id = 0;
} break;
case WM_TIMER: {
if (wParam == windows[window_id].move_timer_id) {
diff --git a/platform/windows/native_menu_windows.cpp b/platform/windows/native_menu_windows.cpp
index 40a08f87df..d9dc28e9d9 100644
--- a/platform/windows/native_menu_windows.cpp
+++ b/platform/windows/native_menu_windows.cpp
@@ -35,6 +35,8 @@
#include "scene/resources/image_texture.h"
HBITMAP NativeMenuWindows::_make_bitmap(const Ref<Image> &p_img) const {
+ p_img->convert(Image::FORMAT_RGBA8);
+
Vector2i texture_size = p_img->get_size();
UINT image_size = texture_size.width * texture_size.height;
@@ -231,6 +233,11 @@ float NativeMenuWindows::get_minimum_width(const RID &p_rid) const {
return 0.f;
}
+bool NativeMenuWindows::is_opened(const RID &p_rid) const {
+ // Not supported.
+ return false;
+}
+
int NativeMenuWindows::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) {
MenuData *md = menus.get_or_null(p_rid);
MenuData *md_sub = menus.get_or_null(p_submenu_rid);
@@ -349,12 +356,14 @@ int NativeMenuWindows::add_icon_item(const RID &p_rid, const Ref<Texture2D> &p_i
item_data->checkable_type = CHECKABLE_TYPE_NONE;
item_data->max_states = 0;
item_data->state = 0;
- item_data->img = p_icon->get_image();
- item_data->img = item_data->img->duplicate();
- if (item_data->img->is_compressed()) {
- item_data->img->decompress();
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
+ item_data->img = p_icon->get_image();
+ item_data->img = item_data->img->duplicate();
+ if (item_data->img->is_compressed()) {
+ item_data->img->decompress();
+ }
+ item_data->bmp = _make_bitmap(item_data->img);
}
- item_data->bmp = _make_bitmap(item_data->img);
Char16String label = p_label.utf16();
MENUITEMINFOW item;
@@ -389,12 +398,14 @@ int NativeMenuWindows::add_icon_check_item(const RID &p_rid, const Ref<Texture2D
item_data->checkable_type = CHECKABLE_TYPE_CHECK_BOX;
item_data->max_states = 0;
item_data->state = 0;
- item_data->img = p_icon->get_image();
- item_data->img = item_data->img->duplicate();
- if (item_data->img->is_compressed()) {
- item_data->img->decompress();
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
+ item_data->img = p_icon->get_image();
+ item_data->img = item_data->img->duplicate();
+ if (item_data->img->is_compressed()) {
+ item_data->img->decompress();
+ }
+ item_data->bmp = _make_bitmap(item_data->img);
}
- item_data->bmp = _make_bitmap(item_data->img);
Char16String label = p_label.utf16();
MENUITEMINFOW item;
@@ -462,12 +473,14 @@ int NativeMenuWindows::add_icon_radio_check_item(const RID &p_rid, const Ref<Tex
item_data->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;
item_data->max_states = 0;
item_data->state = 0;
- item_data->img = p_icon->get_image();
- item_data->img = item_data->img->duplicate();
- if (item_data->img->is_compressed()) {
- item_data->img->decompress();
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
+ item_data->img = p_icon->get_image();
+ item_data->img = item_data->img->duplicate();
+ if (item_data->img->is_compressed()) {
+ item_data->img->decompress();
+ }
+ item_data->bmp = _make_bitmap(item_data->img);
}
- item_data->bmp = _make_bitmap(item_data->img);
Char16String label = p_label.utf16();
MENUITEMINFOW item;
@@ -1082,7 +1095,7 @@ void NativeMenuWindows::set_item_icon(const RID &p_rid, int p_idx, const Ref<Tex
if (item_data->bmp) {
DeleteObject(item_data->bmp);
}
- if (p_icon.is_valid()) {
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
item_data->img = p_icon->get_image();
item_data->img = item_data->img->duplicate();
if (item_data->img->is_compressed()) {
diff --git a/platform/windows/native_menu_windows.h b/platform/windows/native_menu_windows.h
index 74fd231903..5c4aaa52c8 100644
--- a/platform/windows/native_menu_windows.h
+++ b/platform/windows/native_menu_windows.h
@@ -90,6 +90,8 @@ public:
virtual void set_minimum_width(const RID &p_rid, float p_width) override;
virtual float get_minimum_width(const RID &p_rid) const override;
+ virtual bool is_opened(const RID &p_rid) const override;
+
virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1) override;
virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp
index 0ac236eaa7..cdd7b15110 100644
--- a/scene/2d/tile_map_layer.cpp
+++ b/scene/2d/tile_map_layer.cpp
@@ -205,11 +205,12 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) {
// Check if anything changed that might change the quadrant shape.
// If so, recreate everything.
- bool quandrant_shape_changed = dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE] ||
- (is_y_sort_enabled() && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM] || dirty.flags[DIRTY_FLAGS_TILE_SET]));
+ bool quadrant_shape_changed = dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_TILE_SET] ||
+ (is_y_sort_enabled() && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_LAYER_X_DRAW_ORDER_REVERSED] || dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM])) ||
+ (!is_y_sort_enabled() && dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE]);
// Free all quadrants.
- if (forced_cleanup || quandrant_shape_changed) {
+ if (forced_cleanup || quadrant_shape_changed) {
for (const KeyValue<Vector2i, Ref<RenderingQuadrant>> &kv : rendering_quadrant_map) {
for (const RID &ci : kv.value->canvas_items) {
if (ci.is_valid()) {
@@ -264,9 +265,8 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) {
rendering_quadrant->canvas_items.clear();
// Sort the quadrant cells.
- if (is_y_sort_enabled()) {
- // For compatibility reasons, we use another comparator for Y-sorted layers.
- rendering_quadrant->cells.sort_custom<CellDataYSortedComparator>();
+ if (is_y_sort_enabled() && x_draw_order_reversed) {
+ rendering_quadrant->cells.sort_custom<CellDataYSortedXReversedComparator>();
} else {
rendering_quadrant->cells.sort();
}
@@ -1770,6 +1770,8 @@ void TileMapLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_y_sort_origin", "y_sort_origin"), &TileMapLayer::set_y_sort_origin);
ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileMapLayer::get_y_sort_origin);
+ ClassDB::bind_method(D_METHOD("set_x_draw_order_reversed", "x_draw_order_reversed"), &TileMapLayer::set_x_draw_order_reversed);
+ ClassDB::bind_method(D_METHOD("is_x_draw_order_reversed"), &TileMapLayer::is_x_draw_order_reversed);
ClassDB::bind_method(D_METHOD("set_rendering_quadrant_size", "size"), &TileMapLayer::set_rendering_quadrant_size);
ClassDB::bind_method(D_METHOD("get_rendering_quadrant_size"), &TileMapLayer::get_rendering_quadrant_size);
@@ -1796,6 +1798,7 @@ void TileMapLayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tile_set", "get_tile_set");
ADD_GROUP("Rendering", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "x_draw_order_reversed"), "set_x_draw_order_reversed", "is_x_draw_order_reversed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "rendering_quadrant_size"), "set_rendering_quadrant_size", "get_rendering_quadrant_size");
ADD_GROUP("Physics", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_enabled"), "set_collision_enabled", "is_collision_enabled");
@@ -1814,6 +1817,18 @@ void TileMapLayer::_bind_methods() {
BIND_ENUM_CONSTANT(DEBUG_VISIBILITY_MODE_FORCE_SHOW);
}
+void TileMapLayer::_validate_property(PropertyInfo &p_property) const {
+ if (is_y_sort_enabled()) {
+ if (p_property.name == "rendering_quadrant_size") {
+ p_property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ } else {
+ if (p_property.name == "x_draw_order_reversed") {
+ p_property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ }
+}
+
void TileMapLayer::_update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter) {
// Set a default texture filter for the whole tilemap.
CanvasItem::_update_self_texture_filter(p_texture_filter);
@@ -2772,6 +2787,7 @@ void TileMapLayer::set_y_sort_enabled(bool p_y_sort_enabled) {
_queue_internal_update();
emit_signal(CoreStringName(changed));
+ notify_property_list_changed();
_update_notify_local_transform();
}
@@ -2789,6 +2805,20 @@ int TileMapLayer::get_y_sort_origin() const {
return y_sort_origin;
}
+void TileMapLayer::set_x_draw_order_reversed(bool p_x_draw_order_reversed) {
+ if (x_draw_order_reversed == p_x_draw_order_reversed) {
+ return;
+ }
+ x_draw_order_reversed = p_x_draw_order_reversed;
+ dirty.flags[DIRTY_FLAGS_LAYER_X_DRAW_ORDER_REVERSED] = true;
+ _queue_internal_update();
+ emit_signal(CoreStringName(changed));
+}
+
+bool TileMapLayer::is_x_draw_order_reversed() const {
+ return x_draw_order_reversed;
+}
+
void TileMapLayer::set_z_index(int p_z_index) {
if (get_z_index() == p_z_index) {
return;
@@ -2813,10 +2843,9 @@ void TileMapLayer::set_rendering_quadrant_size(int p_size) {
if (rendering_quadrant_size == p_size) {
return;
}
- dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE] = true;
ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1.");
-
rendering_quadrant_size = p_size;
+ dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE] = true;
_queue_internal_update();
emit_signal(CoreStringName(changed));
}
@@ -2840,6 +2869,9 @@ bool TileMapLayer::is_collision_enabled() const {
}
void TileMapLayer::set_use_kinematic_bodies(bool p_use_kinematic_bodies) {
+ if (use_kinematic_bodies == p_use_kinematic_bodies) {
+ return;
+ }
use_kinematic_bodies = p_use_kinematic_bodies;
dirty.flags[DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES] = p_use_kinematic_bodies;
_queue_internal_update();
diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h
index 57c83d7c4c..19c6fc128b 100644
--- a/scene/2d/tile_map_layer.h
+++ b/scene/2d/tile_map_layer.h
@@ -160,8 +160,8 @@ struct CellData {
}
};
-// For compatibility reasons, we use another comparator for Y-sorted layers.
-struct CellDataYSortedComparator {
+// We use another comparator for Y-sorted layers with reversed X drawing order.
+struct CellDataYSortedXReversedComparator {
_FORCE_INLINE_ bool operator()(const CellData &p_a, const CellData &p_b) const {
return p_a.coords.x == p_b.coords.x ? (p_a.coords.y < p_b.coords.y) : (p_a.coords.x > p_b.coords.x);
}
@@ -245,6 +245,7 @@ public:
DIRTY_FLAGS_LAYER_SELF_MODULATE,
DIRTY_FLAGS_LAYER_Y_SORT_ENABLED,
DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN,
+ DIRTY_FLAGS_LAYER_X_DRAW_ORDER_REVERSED,
DIRTY_FLAGS_LAYER_Z_INDEX,
DIRTY_FLAGS_LAYER_LIGHT_MASK,
DIRTY_FLAGS_LAYER_TEXTURE_FILTER,
@@ -280,6 +281,7 @@ private:
HighlightMode highlight_mode = HIGHLIGHT_MODE_DEFAULT;
int y_sort_origin = 0;
+ bool x_draw_order_reversed = false;
int rendering_quadrant_size = 16;
bool collision_enabled = true;
@@ -383,6 +385,7 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const;
virtual void _update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter) override;
virtual void _update_self_texture_repeat(RS::CanvasItemTextureRepeat p_texture_repeat) override;
@@ -470,6 +473,8 @@ public:
virtual void set_y_sort_enabled(bool p_y_sort_enabled) override;
void set_y_sort_origin(int p_y_sort_origin);
int get_y_sort_origin() const;
+ void set_x_draw_order_reversed(bool p_x_draw_order_reversed);
+ bool is_x_draw_order_reversed() const;
virtual void set_z_index(int p_z_index) override;
virtual void set_light_mask(int p_light_mask) override;
void set_rendering_quadrant_size(int p_size);
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 2716738684..6aade24e4e 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -44,16 +44,8 @@ void BoneAttachment3D::_validate_property(PropertyInfo &p_property) const {
}
if (parent) {
- String names;
- for (int i = 0; i < parent->get_bone_count(); i++) {
- if (i > 0) {
- names += ",";
- }
- names += parent->get_bone_name(i);
- }
-
p_property.hint = PROPERTY_HINT_ENUM;
- p_property.hint_string = names;
+ p_property.hint_string = parent->get_concatenated_bone_names();
} else {
p_property.hint = PROPERTY_HINT_NONE;
p_property.hint_string = "";
diff --git a/scene/3d/physics/physical_bone_3d.cpp b/scene/3d/physics/physical_bone_3d.cpp
index c6be2a9da8..c290f16c0d 100644
--- a/scene/3d/physics/physical_bone_3d.cpp
+++ b/scene/3d/physics/physical_bone_3d.cpp
@@ -738,15 +738,7 @@ bool PhysicalBone3D::_get(const StringName &p_name, Variant &r_ret) const {
void PhysicalBone3D::_get_property_list(List<PropertyInfo> *p_list) const {
Skeleton3D *skeleton = get_skeleton();
if (skeleton) {
- String names;
- for (int i = 0; i < skeleton->get_bone_count(); i++) {
- if (i > 0) {
- names += ",";
- }
- names += skeleton->get_bone_name(i);
- }
-
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name"), PROPERTY_HINT_ENUM, names));
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name"), PROPERTY_HINT_ENUM, skeleton->get_concatenated_bone_names()));
} else {
p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name")));
}
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index a4804e928a..21e82adf47 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -265,11 +265,31 @@ void Skeleton3D::_update_process_order() {
bones_backup.resize(bones.size());
+ concatenated_bone_names = StringName();
+
process_order_dirty = false;
emit_signal("bone_list_changed");
}
+void Skeleton3D::_update_bone_names() const {
+ String names;
+ for (int i = 0; i < bones.size(); i++) {
+ if (i > 0) {
+ names += ",";
+ }
+ names += bones[i].name;
+ }
+ concatenated_bone_names = StringName(names);
+}
+
+StringName Skeleton3D::get_concatenated_bone_names() const {
+ if (concatenated_bone_names == StringName()) {
+ _update_bone_names();
+ }
+ return concatenated_bone_names;
+}
+
#ifndef DISABLE_DEPRECATED
void Skeleton3D::setup_simulator() {
if (simulator && simulator->get_parent() == this) {
@@ -983,6 +1003,8 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &Skeleton3D::get_bone_name);
ClassDB::bind_method(D_METHOD("set_bone_name", "bone_idx", "name"), &Skeleton3D::set_bone_name);
+ ClassDB::bind_method(D_METHOD("get_concatenated_bone_names"), &Skeleton3D::get_concatenated_bone_names);
+
ClassDB::bind_method(D_METHOD("get_bone_parent", "bone_idx"), &Skeleton3D::get_bone_parent);
ClassDB::bind_method(D_METHOD("set_bone_parent", "bone_idx", "parent_idx"), &Skeleton3D::set_bone_parent);
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index 23b9423993..b8e38242b9 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -150,6 +150,9 @@ private:
Vector<int> parentless_bones;
HashMap<String, int> name_to_bone_index;
+ mutable StringName concatenated_bone_names = StringName();
+ void _update_bone_names() const;
+
void _make_dirty();
bool dirty = false;
bool rest_dirty = false;
@@ -200,6 +203,7 @@ public:
int find_bone(const String &p_name) const;
String get_bone_name(int p_bone) const;
void set_bone_name(int p_bone, const String &p_name);
+ StringName get_concatenated_bone_names() const;
bool is_bone_parent_of(int p_bone_id, int p_parent_bone_id) const;
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index 78a21ba9e1..0d6316ee35 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -306,16 +306,8 @@ void SkeletonIK3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "root_bone" || p_property.name == "tip_bone") {
Skeleton3D *skeleton = get_skeleton();
if (skeleton) {
- String names("--,");
- for (int i = 0; i < skeleton->get_bone_count(); i++) {
- if (i > 0) {
- names += ",";
- }
- names += skeleton->get_bone_name(i);
- }
-
p_property.hint = PROPERTY_HINT_ENUM;
- p_property.hint_string = names;
+ p_property.hint_string = skeleton->get_concatenated_bone_names();
} else {
p_property.hint = PROPERTY_HINT_NONE;
p_property.hint_string = "";
diff --git a/scene/3d/skeleton_modifier_3d.cpp b/scene/3d/skeleton_modifier_3d.cpp
index 8d806ef5fc..9851214194 100644
--- a/scene/3d/skeleton_modifier_3d.cpp
+++ b/scene/3d/skeleton_modifier_3d.cpp
@@ -123,6 +123,8 @@ void SkeletonModifier3D::_notification(int p_what) {
}
void SkeletonModifier3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_skeleton"), &SkeletonModifier3D::get_skeleton);
+
ClassDB::bind_method(D_METHOD("set_active", "active"), &SkeletonModifier3D::set_active);
ClassDB::bind_method(D_METHOD("is_active"), &SkeletonModifier3D::is_active);
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index e600de6b8b..5074145168 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -1109,6 +1109,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
real_t weight = ai.playback_info.weight;
Vector<real_t> track_weights = ai.playback_info.track_weights;
bool backward = signbit(delta); // This flag is used by the root motion calculates or detecting the end of audio stream.
+ bool seeked_backward = signbit(p_delta);
#ifndef _3D_DISABLED
bool calc_root = !seeked || is_external_seeking;
#endif // _3D_DISABLED
@@ -1463,7 +1464,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
}
} else {
if (seeked) {
- int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true);
+ int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, false, seeked_backward);
if (idx < 0) {
continue;
}
@@ -1744,8 +1745,10 @@ void AnimationMixer::_blend_apply() {
case Animation::TYPE_VALUE: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
- if (t->use_discrete && !t->use_continuous) {
- t->is_init = true; // If only disctere value is applied, no more RESET.
+ if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) {
+ t->is_init = false; // Always update in Force Continuous.
+ } else if (!t->use_continuous && (t->use_discrete || !deterministic)) {
+ t->is_init = true; // If there is no continuous value and only disctere value is applied or just started, don't RESET.
}
if ((t->is_init && (is_zero_amount || !t->use_continuous)) ||
@@ -1756,9 +1759,7 @@ void AnimationMixer::_blend_apply() {
break; // Don't overwrite the value set by UPDATE_DISCRETE.
}
- if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) {
- t->is_init = false; // Always update in Force Continuous.
- } else {
+ if (callback_mode_discrete != ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) {
t->is_init = !t->use_continuous; // If there is no Continuous in non-Force Continuous type, it means RESET.
}
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 0c24d79ad7..5756edaa48 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -234,6 +234,9 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
pi.delta = delta;
pi.seeked = p_seeked;
}
+ if (Math::is_zero_approx(pi.delta) && backwards) {
+ pi.delta = -0.0; // Sign is needed to handle converted Continuous track from Discrete track correctly.
+ }
// AnimationPlayer doesn't have internal seeking.
// However, immediately after playback, discrete keys should be retrieved with EXACT mode since behind keys must be ignored at that time.
pi.is_external_seeking = !p_started;
@@ -257,7 +260,7 @@ void AnimationPlayer::_blend_playback_data(double p_delta, bool p_started) {
bool seeked = c.seeked; // The animation may be changed during process, so it is safer that the state is changed before process.
- if (p_delta != 0) {
+ if (!Math::is_zero_approx(p_delta)) {
c.seeked = false;
}
@@ -581,6 +584,8 @@ void AnimationPlayer::seek(double p_time, bool p_update, bool p_update_only) {
return;
}
+ bool is_backward = p_time < playback.current.pos;
+
_check_immediately_after_start();
playback.current.pos = p_time;
@@ -596,7 +601,7 @@ void AnimationPlayer::seek(double p_time, bool p_update, bool p_update_only) {
playback.seeked = true;
if (p_update) {
- _process_animation(0, p_update_only);
+ _process_animation(is_backward ? -0.0 : 0.0, p_update_only);
playback.seeked = false; // If animation was proceeded here, no more seek in internal process.
}
}
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index d8fcbbb883..b7db730b49 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -243,8 +243,8 @@ Size2 BoxContainer::get_minimum_size() const {
bool first = true;
for (int i = 0; i < get_child_count(); i++) {
- Control *c = Object::cast_to<Control>(get_child(i));
- if (!c || !c->is_visible() || c->is_set_as_top_level()) {
+ Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
+ if (!c) {
continue;
}
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index f1faf3e899..c328022d4f 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -139,9 +139,15 @@ void Container::queue_sort() {
pending_sort = true;
}
-Control *Container::as_sortable_control(Node *p_node) const {
+Control *Container::as_sortable_control(Node *p_node, SortableVisbilityMode p_visibility_mode) const {
Control *c = Object::cast_to<Control>(p_node);
- if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) {
+ if (!c || c->is_set_as_top_level()) {
+ return nullptr;
+ }
+ if (p_visibility_mode == SortableVisbilityMode::VISIBLE && !c->is_visible()) {
+ return nullptr;
+ }
+ if (p_visibility_mode == SortableVisbilityMode::VISIBLE_IN_TREE && !c->is_visible_in_tree()) {
return nullptr;
}
return c;
@@ -177,13 +183,9 @@ Vector<int> Container::get_allowed_size_flags_vertical() const {
void Container::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- pending_sort = false;
- queue_sort();
- } break;
-
case NOTIFICATION_RESIZED:
- case NOTIFICATION_THEME_CHANGED: {
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_ENTER_TREE: {
queue_sort();
} break;
diff --git a/scene/gui/container.h b/scene/gui/container.h
index 405220cee6..0561d0d219 100644
--- a/scene/gui/container.h
+++ b/scene/gui/container.h
@@ -41,8 +41,14 @@ class Container : public Control {
void _child_minsize_changed();
protected:
+ enum class SortableVisbilityMode {
+ VISIBLE,
+ VISIBLE_IN_TREE,
+ IGNORE,
+ };
+
void queue_sort();
- Control *as_sortable_control(Node *p_node) const;
+ Control *as_sortable_control(Node *p_node, SortableVisbilityMode p_visibility_mode = SortableVisbilityMode::VISIBLE_IN_TREE) const;
virtual void add_child_notify(Node *p_child) override;
virtual void move_child_notify(Node *p_child) override;
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 0c146ce173..a23ee6db71 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -99,7 +99,9 @@ void FileDialog::set_visible(bool p_visible) {
#endif
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
- _native_popup();
+ if (p_visible) {
+ _native_popup();
+ }
} else {
ConfirmationDialog::set_visible(p_visible);
}
diff --git a/scene/gui/graph_element.cpp b/scene/gui/graph_element.cpp
index e231b05d7f..b63ed8d1ad 100644
--- a/scene/gui/graph_element.cpp
+++ b/scene/gui/graph_element.cpp
@@ -60,8 +60,8 @@ void GraphElement::_resort() {
Size2 GraphElement::get_minimum_size() const {
Size2 minsize;
for (int i = 0; i < get_child_count(); i++) {
- Control *child = Object::cast_to<Control>(get_child(i));
- if (!child || child->is_set_as_top_level()) {
+ Control *child = as_sortable_control(get_child(i), SortableVisbilityMode::IGNORE);
+ if (!child) {
continue;
}
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index d804f83e1c..72e59bfc8a 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -130,8 +130,8 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const {
void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const {
int idx = 0;
for (int i = 0; i < get_child_count(false); i++) {
- Control *child = Object::cast_to<Control>(get_child(i, false));
- if (!child || child->is_set_as_top_level()) {
+ Control *child = as_sortable_control(get_child(i, false), SortableVisbilityMode::IGNORE);
+ if (!child) {
continue;
}
@@ -658,8 +658,8 @@ void GraphNode::_port_pos_update() {
int slot_index = 0;
for (int i = 0; i < get_child_count(false); i++) {
- Control *child = Object::cast_to<Control>(get_child(i, false));
- if (!child || child->is_set_as_top_level()) {
+ Control *child = as_sortable_control(get_child(i, false), SortableVisbilityMode::IGNORE);
+ if (!child) {
continue;
}
diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp
index 06e4a7cc13..a47b131708 100644
--- a/scene/gui/margin_container.cpp
+++ b/scene/gui/margin_container.cpp
@@ -36,8 +36,8 @@ Size2 MarginContainer::get_minimum_size() const {
Size2 max;
for (int i = 0; i < get_child_count(); i++) {
- Control *c = Object::cast_to<Control>(get_child(i));
- if (!c || !c->is_visible() || c->is_set_as_top_level()) {
+ Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
+ if (!c) {
continue;
}
diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp
index 76fde26b26..2c39e148a0 100644
--- a/scene/gui/panel_container.cpp
+++ b/scene/gui/panel_container.cpp
@@ -35,7 +35,7 @@
Size2 PanelContainer::get_minimum_size() const {
Size2 ms;
for (int i = 0; i < get_child_count(); i++) {
- Control *c = as_sortable_control(get_child(i));
+ Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
if (!c) {
continue;
}
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 1f4d1dbf52..824bb77694 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -42,7 +42,7 @@ Size2 ScrollContainer::get_minimum_size() const {
largest_child_min_size = Size2();
for (int i = 0; i < get_child_count(); i++) {
- Control *c = as_sortable_control(get_child(i));
+ Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
if (!c) {
continue;
}
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index d0c3f3d65e..58724cf4e9 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -362,8 +362,8 @@ void TabContainer::_on_mouse_exited() {
Vector<Control *> TabContainer::_get_tab_controls() const {
Vector<Control *> controls;
for (int i = 0; i < get_child_count(); i++) {
- Control *control = Object::cast_to<Control>(get_child(i));
- if (!control || control->is_set_as_top_level() || control == tab_bar || children_removing.has(control)) {
+ Control *control = as_sortable_control(get_child(i), SortableVisbilityMode::IGNORE);
+ if (!control || control == tab_bar || children_removing.has(control)) {
continue;
}
@@ -539,8 +539,8 @@ void TabContainer::add_child_notify(Node *p_child) {
return;
}
- Control *c = Object::cast_to<Control>(p_child);
- if (!c || c->is_set_as_top_level()) {
+ Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE);
+ if (!c) {
return;
}
c->hide();
@@ -569,8 +569,8 @@ void TabContainer::move_child_notify(Node *p_child) {
return;
}
- Control *c = Object::cast_to<Control>(p_child);
- if (c && !c->is_set_as_top_level()) {
+ Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE);
+ if (c) {
tab_bar->move_tab(c->get_meta("_tab_index"), get_tab_idx_from_control(c));
}
@@ -584,8 +584,8 @@ void TabContainer::remove_child_notify(Node *p_child) {
return;
}
- Control *c = Object::cast_to<Control>(p_child);
- if (!c || c->is_set_as_top_level()) {
+ Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE);
+ if (!c) {
return;
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index a46d77c61e..2b2ea54dde 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -286,6 +286,7 @@ void TextEdit::Text::invalidate_all_lines() {
}
tab_size_dirty = false;
+ max_width = -1;
_calculate_max_line_width();
}
@@ -404,10 +405,12 @@ void TextEdit::Text::remove_range(int p_from_line, int p_to_line) {
text.resize(text.size() - diff);
if (dirty_height) {
+ line_height = -1;
_calculate_line_height();
}
if (dirty_width) {
+ max_width = -1;
_calculate_max_line_width();
}
}
@@ -5174,7 +5177,7 @@ void TextEdit::add_selection_for_next_occurrence() {
const String &highlighted_text = get_selected_text(caret);
int column = get_selection_from_column(caret) + 1;
- int line = get_caret_line(caret);
+ int line = get_selection_from_line(caret);
const Point2i next_occurrence = search(highlighted_text, SEARCH_MATCH_CASE, line, column);
@@ -5188,6 +5191,7 @@ void TextEdit::add_selection_for_next_occurrence() {
if (new_caret != -1) {
select(next_occurrence.y, next_occurrence.x, next_occurrence.y, end, new_caret);
+ _unhide_carets();
adjust_viewport_to_caret(new_caret);
merge_overlapping_carets();
}
@@ -5211,8 +5215,8 @@ void TextEdit::skip_selection_for_next_occurrence() {
// Due to const and &(reference) presence, ternary operator is a way to avoid errors and warnings.
const String &searched_text = has_selection(caret) ? get_selected_text(caret) : get_word_under_caret(caret);
- int column = (has_selection(caret) ? get_selection_from_column(caret) : get_caret_column(caret)) + 1;
- int line = get_caret_line(caret);
+ int column = get_selection_from_column(caret) + 1;
+ int line = get_selection_from_line(caret);
const Point2i next_occurrence = search(searched_text, SEARCH_MATCH_CASE, line, column);
@@ -5220,12 +5224,13 @@ void TextEdit::skip_selection_for_next_occurrence() {
return;
}
- int to_column = (has_selection(caret) ? get_selection_to_column(caret) : get_caret_column(caret)) + 1;
+ int to_column = get_selection_to_column(caret) + 1;
int end = next_occurrence.x + (to_column - column);
int new_caret = add_caret(next_occurrence.y, end);
if (new_caret != -1) {
select(next_occurrence.y, next_occurrence.x, next_occurrence.y, end, new_caret);
+ _unhide_carets();
adjust_viewport_to_caret(new_caret);
merge_overlapping_carets();
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 38bd7141c2..468d4e3c0f 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1430,7 +1430,7 @@ void Viewport::_gui_show_tooltip() {
Control *tooltip_owner = nullptr;
gui.tooltip_text = _gui_get_tooltip(
gui.tooltip_control,
- gui.tooltip_control->get_global_transform().xform_inv(gui.last_mouse_pos),
+ gui.tooltip_control->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos),
&tooltip_owner);
gui.tooltip_text = gui.tooltip_text.strip_edges();
@@ -1910,7 +1910,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (gui.tooltip_popup) {
if (gui.tooltip_control) {
- String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform().xform_inv(mpos));
+ String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform_with_canvas().affine_inverse().xform(mpos));
tooltip = tooltip.strip_edges();
if (tooltip.is_empty() || tooltip != gui.tooltip_text) {
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 1c5f9cc663..39e5b6de33 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -1229,14 +1229,12 @@ void Window::_update_viewport_size() {
}
if (!Math::is_equal_approx(TS->font_get_global_oversampling(), font_oversampling)) {
TS->font_set_global_oversampling(font_oversampling);
- ci_updated = false;
+ if (!ci_updated) {
+ update_canvas_items();
+ }
}
}
- if (!ci_updated) {
- update_canvas_items();
- }
-
notification(NOTIFICATION_WM_SIZE_CHANGED);
if (embedder) {
diff --git a/scene/resources/animation.compat.inc b/scene/resources/animation.compat.inc
index d7d8867748..fc2672bb25 100644
--- a/scene/resources/animation.compat.inc
+++ b/scene/resources/animation.compat.inc
@@ -50,8 +50,8 @@ Variant Animation::_value_track_interpolate_bind_compat_86629(int p_track, doubl
return value_track_interpolate(p_track, p_time, false);
}
-int Animation::_track_find_key_bind_compat_86661(int p_track, double p_time, FindMode p_find_mode) const {
- return track_find_key(p_track, p_time, p_find_mode, false);
+int Animation::_track_find_key_bind_compat_92861(int p_track, double p_time, FindMode p_find_mode) const {
+ return track_find_key(p_track, p_time, p_find_mode, false, false);
}
void Animation::_bind_compatibility_methods() {
@@ -60,7 +60,7 @@ void Animation::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("scale_track_interpolate", "track_idx", "time_sec"), &Animation::_scale_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("blend_shape_track_interpolate", "track_idx", "time_sec"), &Animation::_blend_shape_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec"), &Animation::_value_track_interpolate_bind_compat_86629);
- ClassDB::bind_compatibility_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode"), &Animation::_track_find_key_bind_compat_86661, DEFVAL(FIND_MODE_NEAREST));
+ ClassDB::bind_compatibility_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode"), &Animation::_track_find_key_bind_compat_92861, DEFVAL(FIND_MODE_NEAREST));
}
#endif // DISABLE_DEPRECATED
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index a3bfa987c6..254bd38be7 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -1496,7 +1496,7 @@ void Animation::track_remove_key(int p_track, int p_idx) {
emit_changed();
}
-int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, bool p_limit) const {
+int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, bool p_limit, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
@@ -1518,7 +1518,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
return key_index;
}
- int k = _find(tt->positions, p_time, false, p_limit);
+ int k = _find(tt->positions, p_time, p_backward, p_limit);
if (k < 0 || k >= tt->positions.size()) {
return -1;
}
@@ -1545,7 +1545,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
return key_index;
}
- int k = _find(rt->rotations, p_time, false, p_limit);
+ int k = _find(rt->rotations, p_time, p_backward, p_limit);
if (k < 0 || k >= rt->rotations.size()) {
return -1;
}
@@ -1572,7 +1572,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
return key_index;
}
- int k = _find(st->scales, p_time, false, p_limit);
+ int k = _find(st->scales, p_time, p_backward, p_limit);
if (k < 0 || k >= st->scales.size()) {
return -1;
}
@@ -1599,7 +1599,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
return key_index;
}
- int k = _find(bst->blend_shapes, p_time, false, p_limit);
+ int k = _find(bst->blend_shapes, p_time, p_backward, p_limit);
if (k < 0 || k >= bst->blend_shapes.size()) {
return -1;
}
@@ -1611,7 +1611,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
- int k = _find(vt->values, p_time, false, p_limit);
+ int k = _find(vt->values, p_time, p_backward, p_limit);
if (k < 0 || k >= vt->values.size()) {
return -1;
}
@@ -1623,7 +1623,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break;
case TYPE_METHOD: {
MethodTrack *mt = static_cast<MethodTrack *>(t);
- int k = _find(mt->methods, p_time, false, p_limit);
+ int k = _find(mt->methods, p_time, p_backward, p_limit);
if (k < 0 || k >= mt->methods.size()) {
return -1;
}
@@ -1635,7 +1635,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break;
case TYPE_BEZIER: {
BezierTrack *bt = static_cast<BezierTrack *>(t);
- int k = _find(bt->values, p_time, false, p_limit);
+ int k = _find(bt->values, p_time, p_backward, p_limit);
if (k < 0 || k >= bt->values.size()) {
return -1;
}
@@ -1647,7 +1647,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break;
case TYPE_AUDIO: {
AudioTrack *at = static_cast<AudioTrack *>(t);
- int k = _find(at->values, p_time, false, p_limit);
+ int k = _find(at->values, p_time, p_backward, p_limit);
if (k < 0 || k >= at->values.size()) {
return -1;
}
@@ -1659,7 +1659,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode,
} break;
case TYPE_ANIMATION: {
AnimationTrack *at = static_cast<AnimationTrack *>(t);
- int k = _find(at->values, p_time, false, p_limit);
+ int k = _find(at->values, p_time, p_backward, p_limit);
if (k < 0 || k >= at->values.size()) {
return -1;
}
@@ -3836,7 +3836,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_get_key_count", "track_idx"), &Animation::track_get_key_count);
ClassDB::bind_method(D_METHOD("track_get_key_value", "track_idx", "key_idx"), &Animation::track_get_key_value);
ClassDB::bind_method(D_METHOD("track_get_key_time", "track_idx", "key_idx"), &Animation::track_get_key_time);
- ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode", "limit"), &Animation::track_find_key, DEFVAL(FIND_MODE_NEAREST), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode", "limit", "backward"), &Animation::track_find_key, DEFVAL(FIND_MODE_NEAREST), DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "track_idx", "interpolation"), &Animation::track_set_interpolation_type);
ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "track_idx"), &Animation::track_get_interpolation_type);
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 604bce497a..e9bfc298a5 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -388,7 +388,7 @@ protected:
Vector3 _scale_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
float _blend_shape_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
Variant _value_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
- int _track_find_key_bind_compat_86661(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST) const;
+ int _track_find_key_bind_compat_92861(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST) const;
static void _bind_compatibility_methods();
#endif // DISABLE_DEPRECATED
@@ -423,7 +423,7 @@ public:
void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition);
void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value);
void track_set_key_time(int p_track, int p_key_idx, double p_time);
- int track_find_key(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST, bool p_limit = false) const;
+ int track_find_key(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST, bool p_limit = false, bool p_backward = false) const;
void track_remove_key(int p_track, int p_idx);
void track_remove_key_at_time(int p_track, double p_time);
int track_get_key_count(int p_track) const;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 601e8c52a4..277d568aaf 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -2204,6 +2204,10 @@ Error VisualShader::_write_node(Type type, StringBuilder *p_global_code, StringB
Vector3 val = defval;
inputs[i] = "n_in" + itos(p_node) + "p" + itos(i);
node_code += " vec3 " + inputs[i] + " = " + vformat("vec3(%.5f, %.5f, %.5f);\n", val.x, val.y, val.z);
+ } else if (defval.get_type() == Variant::VECTOR4) {
+ Vector4 val = defval;
+ inputs[i] = "n_in" + itos(p_node) + "p" + itos(i);
+ node_code += " vec4 " + inputs[i] + " = " + vformat("vec4(%.5f, %.5f, %.5f, %.5f);\n", val.x, val.y, val.z, val.w);
} else if (defval.get_type() == Variant::QUATERNION) {
Quaternion val = defval;
inputs[i] = "n_in" + itos(p_node) + "p" + itos(i);
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index d5394c8af5..cb8719fbef 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -7825,7 +7825,7 @@ bool VisualShaderNodeDistanceFade::has_output_port_preview(int p_port) const {
String VisualShaderNodeDistanceFade::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
- code += vformat(" %s = clamp(smoothstep(%s, %s,-VERTEX.z),0.0,1.0);\n", p_output_vars[0], p_input_vars[0], p_input_vars[1]);
+ code += vformat(" %s = clamp(smoothstep(%s, %s, length(VERTEX)), 0.0, 1.0);\n", p_output_vars[0], p_input_vars[0], p_input_vars[1]);
return code;
}
diff --git a/servers/display/native_menu.cpp b/servers/display/native_menu.cpp
index ca46560c7c..c7346637d8 100644
--- a/servers/display/native_menu.cpp
+++ b/servers/display/native_menu.cpp
@@ -56,6 +56,8 @@ void NativeMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_minimum_width", "rid", "width"), &NativeMenu::set_minimum_width);
ClassDB::bind_method(D_METHOD("get_minimum_width", "rid"), &NativeMenu::get_minimum_width);
+ ClassDB::bind_method(D_METHOD("is_opened", "rid"), &NativeMenu::is_opened);
+
ClassDB::bind_method(D_METHOD("add_submenu_item", "rid", "label", "submenu_rid", "tag", "index"), &NativeMenu::add_submenu_item, DEFVAL(Variant()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_check_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
@@ -200,6 +202,11 @@ Callable NativeMenu::get_popup_close_callback(const RID &p_rid) const {
return Callable();
}
+bool NativeMenu::is_opened(const RID &p_rid) const {
+ WARN_PRINT("Global menus are not supported on this platform.");
+ return false;
+}
+
void NativeMenu::set_minimum_width(const RID &p_rid, float p_width) {
WARN_PRINT("Global menus are not supported on this platform.");
}
diff --git a/servers/display/native_menu.h b/servers/display/native_menu.h
index 2bc061a216..29d22e03aa 100644
--- a/servers/display/native_menu.h
+++ b/servers/display/native_menu.h
@@ -90,6 +90,8 @@ public:
virtual void set_minimum_width(const RID &p_rid, float p_width);
virtual float get_minimum_width(const RID &p_rid) const;
+ virtual bool is_opened(const RID &p_rid) const;
+
virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1);
virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 2150a3038e..b6e0d58af7 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -31,7 +31,6 @@
#include "display_server.h"
#include "core/input/input.h"
-#include "scene/resources/atlas_texture.h"
#include "scene/resources/texture.h"
#include "servers/display_server_headless.h"
@@ -1124,35 +1123,22 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(TTS_UTTERANCE_BOUNDARY);
}
-Ref<Image> DisplayServer::_get_cursor_image_from_resource(const Ref<Resource> &p_cursor, const Vector2 &p_hotspot, Rect2 &r_atlas_rect) {
+Ref<Image> DisplayServer::_get_cursor_image_from_resource(const Ref<Resource> &p_cursor, const Vector2 &p_hotspot) {
Ref<Image> image;
ERR_FAIL_COND_V_MSG(p_hotspot.x < 0 || p_hotspot.y < 0, image, "Hotspot outside cursor image.");
- Size2 texture_size;
-
Ref<Texture2D> texture = p_cursor;
if (texture.is_valid()) {
- Ref<AtlasTexture> atlas_texture = p_cursor;
-
- if (atlas_texture.is_valid()) {
- texture = atlas_texture->get_atlas();
- r_atlas_rect.size = texture->get_size();
- r_atlas_rect.position = atlas_texture->get_region().position;
- texture_size = atlas_texture->get_region().size;
- } else {
- texture_size = texture->get_size();
- }
image = texture->get_image();
} else {
image = p_cursor;
- ERR_FAIL_COND_V(image.is_null(), image);
- texture_size = image->get_size();
}
+ ERR_FAIL_COND_V(image.is_null(), image);
- ERR_FAIL_COND_V_MSG(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height, image, "Hotspot outside cursor image.");
- ERR_FAIL_COND_V_MSG(texture_size.width > 256 || texture_size.height > 256, image, "Cursor image too big. Max supported size is 256x256.");
+ Size2 image_size = image->get_size();
+ ERR_FAIL_COND_V_MSG(p_hotspot.x > image_size.width || p_hotspot.y > image_size.height, image, "Hotspot outside cursor image.");
+ ERR_FAIL_COND_V_MSG(image_size.width > 256 || image_size.height > 256, image, "Cursor image too big. Max supported size is 256x256.");
- ERR_FAIL_COND_V(image.is_null(), image);
if (image->is_compressed()) {
image = image->duplicate(true);
Error err = image->decompress();
diff --git a/servers/display_server.h b/servers/display_server.h
index 5224d59c04..5d82b6c13c 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -101,7 +101,7 @@ private:
protected:
static void _bind_methods();
- static Ref<Image> _get_cursor_image_from_resource(const Ref<Resource> &p_cursor, const Vector2 &p_hotspot, Rect2 &r_atlas_rect);
+ static Ref<Image> _get_cursor_image_from_resource(const Ref<Resource> &p_cursor, const Vector2 &p_hotspot);
enum {
MAX_SERVERS = 64
@@ -248,8 +248,8 @@ public:
virtual void tts_set_utterance_callback(TTSUtteranceEvent p_event, const Callable &p_callable);
virtual void tts_post_utterance_event(TTSUtteranceEvent p_event, int p_id, int p_pos = 0);
- virtual bool is_dark_mode_supported() const { return false; };
- virtual bool is_dark_mode() const { return false; };
+ virtual bool is_dark_mode_supported() const { return false; }
+ virtual bool is_dark_mode() const { return false; }
virtual Color get_accent_color() const { return Color(0, 0, 0, 0); }
virtual Color get_base_color() const { return Color(0, 0, 0, 0); }
virtual void set_system_theme_change_callback(const Callable &p_callable) {}
@@ -338,8 +338,8 @@ public:
return scale;
}
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
- virtual Color screen_get_pixel(const Point2i &p_position) const { return Color(); };
- virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return Ref<Image>(); };
+ virtual Color screen_get_pixel(const Point2i &p_position) const { return Color(); }
+ virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return Ref<Image>(); }
virtual bool is_touchscreen_available() const;
// Keep the ScreenOrientation enum values in sync with the `display/window/handheld/orientation`
@@ -398,9 +398,9 @@ public:
virtual void show_window(WindowID p_id);
virtual void delete_sub_window(WindowID p_id);
- virtual WindowID window_get_active_popup() const { return INVALID_WINDOW_ID; };
- virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect){};
- virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const { return Rect2i(); };
+ virtual WindowID window_get_active_popup() const { return INVALID_WINDOW_ID; }
+ virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {}
+ virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const { return Rect2i(); }
virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const;
@@ -555,10 +555,10 @@ public:
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const;
virtual Key keyboard_get_label_from_physical(Key p_keycode) const;
- virtual int tablet_get_driver_count() const { return 1; };
- virtual String tablet_get_driver_name(int p_driver) const { return "default"; };
- virtual String tablet_get_current_driver() const { return "default"; };
- virtual void tablet_set_current_driver(const String &p_driver){};
+ virtual int tablet_get_driver_count() const { return 1; }
+ virtual String tablet_get_driver_name(int p_driver) const { return "default"; }
+ virtual String tablet_get_current_driver() const { return "default"; }
+ virtual void tablet_set_current_driver(const String &p_driver) {}
virtual void process_events() = 0;
diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h
index c02830b6df..317dbe9ab9 100644
--- a/tests/scene/test_code_edit.h
+++ b/tests/scene/test_code_edit.h
@@ -3331,6 +3331,45 @@ TEST_CASE("[SceneTree][CodeEdit] folding") {
CHECK_FALSE(code_edit->is_line_folded(1));
}
+ SUBCASE("[CodeEdit] actions unfold") {
+ // add_selection_for_next_occurrence unfolds.
+ code_edit->set_text("test\n\tline1 test\n\t\tline 2\ntest2");
+ code_edit->select(0, 0, 0, 4);
+ code_edit->fold_line(0);
+ CHECK(code_edit->is_line_folded(0));
+ code_edit->add_selection_for_next_occurrence();
+
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_caret_column() == 4);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->has_selection(1));
+ CHECK(code_edit->get_caret_line(1) == 1);
+ CHECK(code_edit->get_selection_origin_line(1) == 1);
+ CHECK(code_edit->get_caret_column(1) == 11);
+ CHECK(code_edit->get_selection_origin_column(1) == 7);
+ CHECK_FALSE(code_edit->is_line_folded(0));
+ code_edit->remove_secondary_carets();
+
+ // skip_selection_for_next_occurrence unfolds.
+ code_edit->select(0, 0, 0, 4);
+ code_edit->fold_line(0);
+ CHECK(code_edit->is_line_folded(0));
+ code_edit->skip_selection_for_next_occurrence();
+
+ CHECK(code_edit->get_caret_count() == 1);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_caret_column() == 11);
+ CHECK(code_edit->get_selection_origin_column() == 7);
+ CHECK_FALSE(code_edit->is_line_folded(0));
+ code_edit->remove_secondary_carets();
+ code_edit->deselect();
+ }
+
SUBCASE("[CodeEdit] toggle folding carets") {
code_edit->set_text("test\n\tline1\ntest2\n\tline2");
diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h
index 8778ea86a6..cf6b89c330 100644
--- a/tests/servers/test_navigation_server_3d.h
+++ b/tests/servers/test_navigation_server_3d.h
@@ -697,12 +697,16 @@ TEST_SUITE("[Navigation]") {
CHECK_NE(navigation_server->map_get_closest_point(map, Vector3(0, 0, 0)), Vector3(0, 0, 0));
CHECK_NE(navigation_server->map_get_closest_point_normal(map, Vector3(0, 0, 0)), Vector3());
CHECK(navigation_server->map_get_closest_point_owner(map, Vector3(0, 0, 0)).is_valid());
- // TODO: Test map_get_closest_point_to_segment() with p_use_collision=true as well.
CHECK_NE(navigation_server->map_get_closest_point_to_segment(map, Vector3(0, 0, 0), Vector3(1, 1, 1), false), Vector3());
+ CHECK_NE(navigation_server->map_get_closest_point_to_segment(map, Vector3(0, 0, 0), Vector3(1, 1, 1), true), Vector3());
CHECK_NE(navigation_server->map_get_path(map, Vector3(0, 0, 0), Vector3(10, 0, 10), true).size(), 0);
CHECK_NE(navigation_server->map_get_path(map, Vector3(0, 0, 0), Vector3(10, 0, 10), false).size(), 0);
}
+ SUBCASE("'map_get_closest_point_to_segment' with 'use_collision' should return default if segment doesn't intersect map") {
+ CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(1, 2, 1), Vector3(1, 1, 1), true), Vector3());
+ }
+
SUBCASE("Elaborate query with 'CORRIDORFUNNEL' post-processing should yield non-empty result") {
Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
query_parameters->set_map(map);
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 137e3a0cbc..6878af863f 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -882,7 +882,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/thorvg/thorvg
-- Version: 0.13.3 (6235068cad8cad176ccd0cbcf82f25e985fbc258, 2024)
+- Version: 0.13.7 (d2c0428a99f7305c086caffe0c730add601ebd6e, 2024)
- License: MIT
Files extracted from upstream source:
diff --git a/thirdparty/embree/common/sys/sysinfo.cpp b/thirdparty/embree/common/sys/sysinfo.cpp
index d01eab3c9d..4ecab05265 100644
--- a/thirdparty/embree/common/sys/sysinfo.cpp
+++ b/thirdparty/embree/common/sys/sysinfo.cpp
@@ -295,7 +295,7 @@ namespace embree
if (nIds >= 1) __cpuid (cpuid_leaf_1,0x00000001);
#if _WIN32
#if _MSC_VER && (_MSC_FULL_VER < 160040219)
-#else
+#elif defined(_MSC_VER)
if (nIds >= 7) __cpuidex(cpuid_leaf_7,0x00000007,0);
#endif
#else
diff --git a/thirdparty/embree/patches/mingw-no-cpuidex.patch b/thirdparty/embree/patches/mingw-no-cpuidex.patch
new file mode 100644
index 0000000000..5480334ceb
--- /dev/null
+++ b/thirdparty/embree/patches/mingw-no-cpuidex.patch
@@ -0,0 +1,13 @@
+diff --git a/thirdparty/embree/common/sys/sysinfo.cpp b/thirdparty/embree/common/sys/sysinfo.cpp
+index d01eab3c9d..4ecab05265 100644
+--- a/thirdparty/embree/common/sys/sysinfo.cpp
++++ b/thirdparty/embree/common/sys/sysinfo.cpp
+@@ -295,7 +295,7 @@ namespace embree
+ if (nIds >= 1) __cpuid (cpuid_leaf_1,0x00000001);
+ #if _WIN32
+ #if _MSC_VER && (_MSC_FULL_VER < 160040219)
+-#else
++#elif defined(_MSC_VER)
+ if (nIds >= 7) __cpuidex(cpuid_leaf_7,0x00000007,0);
+ #endif
+ #else
diff --git a/thirdparty/glslang/glslang/Include/InfoSink.h b/thirdparty/glslang/glslang/Include/InfoSink.h
index b1b537df54..137ede8510 100644
--- a/thirdparty/glslang/glslang/Include/InfoSink.h
+++ b/thirdparty/glslang/glslang/Include/InfoSink.h
@@ -36,7 +36,7 @@
#define _INFOSINK_INCLUDED_
#include "../Include/Common.h"
-#include <filesystem>
+//#include <filesystem>
#include <cmath>
namespace glslang {
diff --git a/thirdparty/glslang/glslang/patches/disable-absolute-paths-for-apple-compat.patch b/thirdparty/glslang/patches/disable-absolute-paths-for-apple-compat.patch
index d15a531b7c..135020737e 100644
--- a/thirdparty/glslang/glslang/patches/disable-absolute-paths-for-apple-compat.patch
+++ b/thirdparty/glslang/patches/disable-absolute-paths-for-apple-compat.patch
@@ -1,7 +1,16 @@
diff --git a/thirdparty/glslang/glslang/Include/InfoSink.h b/thirdparty/glslang/glslang/Include/InfoSink.h
-index 23f495dcb7..b1b537df54 100644
+index 23f495dc..137ede85 100644
--- a/thirdparty/glslang/glslang/Include/InfoSink.h
+++ b/thirdparty/glslang/glslang/Include/InfoSink.h
+@@ -36,7 +36,7 @@
+ #define _INFOSINK_INCLUDED_
+
+ #include "../Include/Common.h"
+-#include <filesystem>
++//#include <filesystem>
+ #include <cmath>
+
+ namespace glslang {
@@ -101,14 +101,14 @@ public:
snprintf(locText, maxSize, ":%d", loc.line);
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
index 9c83f25c79..699076fdf1 100644
--- a/thirdparty/thorvg/inc/config.h
+++ b/thirdparty/thorvg/inc/config.h
@@ -13,5 +13,5 @@
// For internal debugging:
//#define THORVG_LOG_ENABLED
-#define THORVG_VERSION_STRING "0.13.5"
+#define THORVG_VERSION_STRING "0.13.7"
#endif
diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h
index f7396050d7..8285aa1c4c 100644
--- a/thirdparty/thorvg/inc/thorvg.h
+++ b/thirdparty/thorvg/inc/thorvg.h
@@ -80,7 +80,7 @@ enum class Result
InsufficientCondition, ///< The value returned in case the request cannot be processed - e.g. asking for properties of an object, which does not exist.
FailedAllocation, ///< The value returned in case of unsuccessful memory allocation.
MemoryCorruption, ///< The value returned in the event of bad memory handling - e.g. failing in pointer releasing or casting
- NonSupport, ///< The value returned in case of choosing unsupported options.
+ NonSupport, ///< The value returned in case of choosing unsupported engine features(options).
Unknown ///< The value returned in all other cases.
};
@@ -982,7 +982,7 @@ public:
*
* @param[in] width The width of the stroke. The default value is 0.
*
- * @retval Result::Success when succeed, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed.
*/
Result stroke(float width) noexcept;
@@ -994,7 +994,7 @@ public:
* @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0.
* @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0.
*
- * @retval Result::Success when succeed, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed.
*/
Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept;
@@ -1004,8 +1004,7 @@ public:
* @param[in] f The gradient fill.
*
* @retval Result::Success When succeed.
- * @retval Result::FailedAllocation An internal error with a memory allocation for an object to be filled.
- * @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument.
+ * @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument or an error with accessing it.
*/
Result stroke(std::unique_ptr<Fill> f) noexcept;
@@ -1029,7 +1028,7 @@ public:
*
* @param[in] cap The cap style value. The default value is @c StrokeCap::Square.
*
- * @retval Result::Success when succeed, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed.
*/
Result stroke(StrokeCap cap) noexcept;
@@ -1040,23 +1039,38 @@ public:
*
* @param[in] join The join style value. The default value is @c StrokeJoin::Bevel.
*
- * @retval Result::Success when succeed, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed.
*/
Result stroke(StrokeJoin join) noexcept;
-
/**
* @brief Sets the stroke miterlimit.
*
* @param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join, when the @c StrokeJoin::Miter join style is set. The default value is 4.
*
- * @retval Result::Success when succeed, Result::NonSupport unsupported value, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed or Result::InvalidArgument for @p miterlimit values less than zero.
*
* @since 0.11
*/
Result strokeMiterlimit(float miterlimit) noexcept;
/**
+ * @brief Sets the trim of the stroke along the defined path segment, allowing control over which part of the stroke is visible.
+ *
+ * The values of the arguments @p begin, @p end, and @p offset are in the range of 0.0 to 1.0, representing the beginning of the path and the end, respectively.
+ *
+ * @param[in] begin Specifies the start of the segment to display along the path.
+ * @param[in] end Specifies the end of the segment to display along the path.
+ * @param[in] simultaneous Determines how to trim multiple paths within a single shape. If set to @c true (default), trimming is applied simultaneously to all paths;
+ * Otherwise, all paths are treated as a single entity with a combined length equal to the sum of their individual lengths and are trimmed as such.
+ *
+ * @retval Result::Success when succeed.
+ *
+ * @note Experimental API
+ */
+ Result strokeTrim(float begin, float end, bool simultaneous = true) noexcept;
+
+ /**
* @brief Sets the solid color for all of the figures from the path.
*
* The parts of the shape defined as inner are colored.
@@ -1095,19 +1109,17 @@ public:
*/
Result fill(FillRule r) noexcept;
-
/**
* @brief Sets the rendering order of the stroke and the fill.
*
* @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option).
*
- * @retval Result::Success when succeed, Result::FailedAllocation otherwise.
+ * @retval Result::Success when succeed.
*
* @since 0.10
*/
Result order(bool strokeFirst) noexcept;
-
/**
* @brief Gets the commands data of the path.
*
@@ -1211,6 +1223,18 @@ public:
float strokeMiterlimit() const noexcept;
/**
+ * @brief Gets the trim of the stroke along the defined path segment.
+ *
+ * @param[out] begin The starting point of the segment to display along the path.
+ * @param[out] end Specifies the end of the segment to display along the path.
+ *
+ * @retval @c true if trimming is applied simultaneously to all paths of the shape, @c false otherwise.
+ *
+ * @note Experimental API
+ */
+ bool strokeTrim(float* begin, float* end) const noexcept;
+
+ /**
* @brief Creates a new Shape object.
*
* @return A new Shape object.
diff --git a/thirdparty/thorvg/src/common/tvgCompressor.cpp b/thirdparty/thorvg/src/common/tvgCompressor.cpp
index 9a1dc54632..b61718f9a7 100644
--- a/thirdparty/thorvg/src/common/tvgCompressor.cpp
+++ b/thirdparty/thorvg/src/common/tvgCompressor.cpp
@@ -472,4 +472,19 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded)
}
+/************************************************************************/
+/* DJB2 Implementation */
+/************************************************************************/
+
+unsigned long djb2Encode(const char* str)
+{
+ unsigned long hash = 5381;
+ int c;
+
+ while ((c = *str++)) {
+ hash = ((hash << 5) + hash) + c; // hash * 33 + c
+ }
+ return hash;
+}
+
}
diff --git a/thirdparty/thorvg/src/common/tvgCompressor.h b/thirdparty/thorvg/src/common/tvgCompressor.h
index 0756127ec6..b043cc77de 100644
--- a/thirdparty/thorvg/src/common/tvgCompressor.h
+++ b/thirdparty/thorvg/src/common/tvgCompressor.h
@@ -30,6 +30,7 @@ namespace tvg
uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits);
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes);
size_t b64Decode(const char* encoded, const size_t len, char** decoded);
+ unsigned long djb2Encode(const char* str);
}
#endif //_TVG_COMPRESSOR_H_
diff --git a/thirdparty/thorvg/src/common/tvgMath.cpp b/thirdparty/thorvg/src/common/tvgMath.cpp
index 37a8879cb5..e6b5d47050 100644
--- a/thirdparty/thorvg/src/common/tvgMath.cpp
+++ b/thirdparty/thorvg/src/common/tvgMath.cpp
@@ -47,23 +47,14 @@ bool mathInverse(const Matrix* m, Matrix* out)
}
-Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
+bool mathIdentity(const Matrix* m)
{
- Matrix m;
-
- m.e11 = lhs->e11 * rhs->e11 + lhs->e12 * rhs->e21 + lhs->e13 * rhs->e31;
- m.e12 = lhs->e11 * rhs->e12 + lhs->e12 * rhs->e22 + lhs->e13 * rhs->e32;
- m.e13 = lhs->e11 * rhs->e13 + lhs->e12 * rhs->e23 + lhs->e13 * rhs->e33;
-
- m.e21 = lhs->e21 * rhs->e11 + lhs->e22 * rhs->e21 + lhs->e23 * rhs->e31;
- m.e22 = lhs->e21 * rhs->e12 + lhs->e22 * rhs->e22 + lhs->e23 * rhs->e32;
- m.e23 = lhs->e21 * rhs->e13 + lhs->e22 * rhs->e23 + lhs->e23 * rhs->e33;
-
- m.e31 = lhs->e31 * rhs->e11 + lhs->e32 * rhs->e21 + lhs->e33 * rhs->e31;
- m.e32 = lhs->e31 * rhs->e12 + lhs->e32 * rhs->e22 + lhs->e33 * rhs->e32;
- m.e33 = lhs->e31 * rhs->e13 + lhs->e32 * rhs->e23 + lhs->e33 * rhs->e33;
-
- return m;
+ if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f ||
+ m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f ||
+ m->e31 != 0.0f || m->e32 != 0.0f || m->e33 != 1.0f) {
+ return false;
+ }
+ return true;
}
@@ -82,21 +73,41 @@ void mathRotate(Matrix* m, float degree)
}
-bool mathIdentity(const Matrix* m)
+Matrix operator*(const Matrix& lhs, const Matrix& rhs)
{
- if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f ||
- m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f ||
- m->e31 != 0.0f || m->e32 != 0.0f || m->e33 != 1.0f) {
- return false;
+ Matrix m;
+
+ m.e11 = lhs.e11 * rhs.e11 + lhs.e12 * rhs.e21 + lhs.e13 * rhs.e31;
+ m.e12 = lhs.e11 * rhs.e12 + lhs.e12 * rhs.e22 + lhs.e13 * rhs.e32;
+ m.e13 = lhs.e11 * rhs.e13 + lhs.e12 * rhs.e23 + lhs.e13 * rhs.e33;
+
+ m.e21 = lhs.e21 * rhs.e11 + lhs.e22 * rhs.e21 + lhs.e23 * rhs.e31;
+ m.e22 = lhs.e21 * rhs.e12 + lhs.e22 * rhs.e22 + lhs.e23 * rhs.e32;
+ m.e23 = lhs.e21 * rhs.e13 + lhs.e22 * rhs.e23 + lhs.e23 * rhs.e33;
+
+ m.e31 = lhs.e31 * rhs.e11 + lhs.e32 * rhs.e21 + lhs.e33 * rhs.e31;
+ m.e32 = lhs.e31 * rhs.e12 + lhs.e32 * rhs.e22 + lhs.e33 * rhs.e32;
+ m.e33 = lhs.e31 * rhs.e13 + lhs.e32 * rhs.e23 + lhs.e33 * rhs.e33;
+
+ return m;
+}
+
+
+bool operator==(const Matrix& lhs, const Matrix& rhs)
+{
+ if (!mathEqual(lhs.e11, rhs.e11) || !mathEqual(lhs.e12, rhs.e12) || !mathEqual(lhs.e13, rhs.e13) ||
+ !mathEqual(lhs.e21, rhs.e21) || !mathEqual(lhs.e22, rhs.e22) || !mathEqual(lhs.e23, rhs.e23) ||
+ !mathEqual(lhs.e31, rhs.e31) || !mathEqual(lhs.e32, rhs.e32) || !mathEqual(lhs.e33, rhs.e33)) {
+ return false;
}
return true;
}
-void mathMultiply(Point* pt, const Matrix* transform)
+void operator*=(Point& pt, const Matrix& m)
{
- auto tx = pt->x * transform->e11 + pt->y * transform->e12 + transform->e13;
- auto ty = pt->x * transform->e21 + pt->y * transform->e22 + transform->e23;
- pt->x = tx;
- pt->y = ty;
+ auto tx = pt.x * m.e11 + pt.y * m.e12 + m.e13;
+ auto ty = pt.x * m.e21 + pt.y * m.e22 + m.e23;
+ pt.x = tx;
+ pt.y = ty;
}
diff --git a/thirdparty/thorvg/src/common/tvgMath.h b/thirdparty/thorvg/src/common/tvgMath.h
index 5c2966956a..0f877d919e 100644
--- a/thirdparty/thorvg/src/common/tvgMath.h
+++ b/thirdparty/thorvg/src/common/tvgMath.h
@@ -38,11 +38,9 @@
#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
-bool mathInverse(const Matrix* m, Matrix* out);
-Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs);
-void mathRotate(Matrix* m, float degree);
-bool mathIdentity(const Matrix* m);
-void mathMultiply(Point* pt, const Matrix* transform);
+/************************************************************************/
+/* General functions */
+/************************************************************************/
static inline float mathDeg2Rad(float degree)
@@ -63,28 +61,21 @@ static inline bool mathZero(float a)
}
-static inline bool mathZero(const Point& p)
-{
- return mathZero(p.x) && mathZero(p.y);
-}
-
-
static inline bool mathEqual(float a, float b)
{
return mathZero(a - b);
}
-static inline bool mathEqual(const Matrix& a, const Matrix& b)
-{
- if (!mathEqual(a.e11, b.e11) || !mathEqual(a.e12, b.e12) || !mathEqual(a.e13, b.e13) ||
- !mathEqual(a.e21, b.e21) || !mathEqual(a.e22, b.e22) || !mathEqual(a.e23, b.e23) ||
- !mathEqual(a.e31, b.e31) || !mathEqual(a.e32, b.e32) || !mathEqual(a.e33, b.e33)) {
- return false;
- }
- return true;
-}
+/************************************************************************/
+/* Matrix functions */
+/************************************************************************/
+void mathRotate(Matrix* m, float degree);
+bool mathInverse(const Matrix* m, Matrix* out);
+bool mathIdentity(const Matrix* m);
+Matrix operator*(const Matrix& lhs, const Matrix& rhs);
+bool operator==(const Matrix& lhs, const Matrix& rhs);
static inline bool mathRightAngle(const Matrix* m)
{
@@ -114,15 +105,6 @@ static inline void mathIdentity(Matrix* m)
}
-static inline void mathTransform(Matrix* transform, Point* coord)
-{
- auto x = coord->x;
- auto y = coord->y;
- coord->x = x * transform->e11 + y * transform->e12 + transform->e13;
- coord->y = x * transform->e21 + y * transform->e22 + transform->e23;
-}
-
-
static inline void mathScale(Matrix* m, float sx, float sy)
{
m->e11 *= sx;
@@ -158,12 +140,37 @@ static inline void mathTranslateR(Matrix* m, float x, float y)
}
+static inline bool operator!=(const Matrix& lhs, const Matrix& rhs)
+{
+ return !(lhs == rhs);
+}
+
+
+static inline void operator*=(Matrix& lhs, const Matrix& rhs)
+{
+ lhs = lhs * rhs;
+}
+
+
static inline void mathLog(Matrix* m)
{
TVGLOG("MATH", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m->e11, m->e12, m->e13, m->e21, m->e22, m->e23, m->e31, m->e32, m->e33);
}
+/************************************************************************/
+/* Point functions */
+/************************************************************************/
+
+void operator*=(Point& pt, const Matrix& m);
+
+
+static inline bool mathZero(const Point& p)
+{
+ return mathZero(p.x) && mathZero(p.y);
+}
+
+
static inline float mathLength(const Point* a, const Point* b)
{
auto x = b->x - a->x;
@@ -182,6 +189,18 @@ static inline float mathLength(const Point& a)
}
+static inline bool operator==(const Point& lhs, const Point& rhs)
+{
+ return mathEqual(lhs.x, rhs.x) && mathEqual(lhs.y, rhs.y);
+}
+
+
+static inline bool operator!=(const Point& lhs, const Point& rhs)
+{
+ return !(lhs == rhs);
+}
+
+
static inline Point operator-(const Point& lhs, const Point& rhs)
{
return {lhs.x - rhs.x, lhs.y - rhs.y};
@@ -212,6 +231,10 @@ static inline Point operator/(const Point& lhs, const float rhs)
}
+/************************************************************************/
+/* Interpolation functions */
+/************************************************************************/
+
template <typename T>
static inline T mathLerp(const T &start, const T &end, float t)
{
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index 0a47112084..f59994aae6 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -709,7 +709,7 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
*ref = _idFromUrl((const char*)(str + 3));
return true;
} else if (len >= 10 && (str[0] == 'h' || str[0] == 'H') && (str[1] == 's' || str[1] == 'S') && (str[2] == 'l' || str[2] == 'L') && str[3] == '(' && str[len - 1] == ')') {
- float th, ts, tb;
+ float_t th, ts, tb;
const char *content, *hue, *satuation, *brightness;
content = str + 4;
content = _skipSpace(content, nullptr);
@@ -840,14 +840,14 @@ static Matrix* _parseTransformationMatrix(const char* value)
if (state == MatrixState::Matrix) {
if (ptCount != 6) goto error;
Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1};
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else if (state == MatrixState::Translate) {
if (ptCount == 1) {
Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else if (ptCount == 2) {
Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else goto error;
} else if (state == MatrixState::Rotate) {
//Transform to signed.
@@ -857,14 +857,14 @@ static Matrix* _parseTransformationMatrix(const char* value)
auto s = sinf(mathDeg2Rad(points[0]));
if (ptCount == 1) {
Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else if (ptCount == 3) {
Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else {
goto error;
}
@@ -874,17 +874,17 @@ static Matrix* _parseTransformationMatrix(const char* value)
auto sy = sx;
if (ptCount == 2) sy = points[1];
Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else if (state == MatrixState::SkewX) {
if (ptCount != 1) goto error;
auto deg = tanf(mathDeg2Rad(points[0]));
Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
} else if (state == MatrixState::SkewY) {
if (ptCount != 1) goto error;
auto deg = tanf(mathDeg2Rad(points[0]));
Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
- *matrix = mathMultiply(matrix, &tmp);
+ *matrix *= tmp;
}
}
return matrix;
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
index 0b2113dd07..7e7efed3fc 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
@@ -203,9 +203,9 @@ static bool _appendClipUseNode(SvgLoaderData& loaderData, SvgNode* node, Shape*
if (node->transform) finalTransform = *node->transform;
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
- finalTransform = mathMultiply(&finalTransform, &m);
+ finalTransform *= m;
}
- if (child->transform) finalTransform = mathMultiply(child->transform, &finalTransform);
+ if (child->transform) finalTransform = *child->transform * finalTransform;
return _appendClipShape(loaderData, child, shape, vBox, svgPath, mathIdentity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
}
@@ -228,13 +228,13 @@ static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const Svg
m = *node->transform;
}
if (compNode->transform) {
- m = mathMultiply(&m, compNode->transform);
+ m *= *compNode->transform;
}
if (!compNode->node.clip.userSpace) {
float x, y, w, h;
P(paint)->bounds(&x, &y, &w, &h, false, false);
Matrix mBBox = {w, 0, x, 0, h, y, 0, 0, 1};
- m = mathMultiply(&m, &mBBox);
+ m *= mBBox;
}
return m;
}
@@ -474,7 +474,10 @@ static bool _appendClipShape(SvgLoaderData& loaderData, SvgNode* node, Shape* sh
auto ptsCnt = shape->pathCoords(&pts);
auto p = const_cast<Point*>(pts) + currentPtsCnt;
- while (currentPtsCnt++ < ptsCnt) mathMultiply(p++, m);
+ while (currentPtsCnt++ < ptsCnt) {
+ *p *= *m;
+ ++p;
+ }
}
_applyProperty(loaderData, node, shape, vBox, svgPath, true);
@@ -505,6 +508,7 @@ static constexpr struct
} imageMimeTypes[] = {
{"jpeg", sizeof("jpeg"), imageMimeTypeEncoding::base64},
{"png", sizeof("png"), imageMimeTypeEncoding::base64},
+ {"webp", sizeof("webp"), imageMimeTypeEncoding::base64},
{"svg+xml", sizeof("svg+xml"), imageMimeTypeEncoding::base64 | imageMimeTypeEncoding::utf8},
};
@@ -615,7 +619,7 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
auto sy = node->node.image.h / h;
m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1};
}
- if (node->transform) m = mathMultiply(node->transform, &m);
+ if (node->transform) m = *node->transform * m;
picture->transform(m);
_applyComposition(loaderData, picture.get(), node, vBox, svgPath);
@@ -708,7 +712,7 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
if (node->transform) mUseTransform = *node->transform;
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
Matrix mTranslate = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
- mUseTransform = mathMultiply(&mUseTransform, &mTranslate);
+ mUseTransform *= mTranslate;
}
if (node->node.use.symbol) {
@@ -732,9 +736,9 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
// mSceneTransform = mUseTransform * mSymbolTransform * mViewBox
Matrix mSceneTransform = mViewBox;
if (node->node.use.symbol->transform) {
- mSceneTransform = mathMultiply(node->node.use.symbol->transform, &mViewBox);
+ mSceneTransform = *node->node.use.symbol->transform * mViewBox;
}
- mSceneTransform = mathMultiply(&mUseTransform, &mSceneTransform);
+ mSceneTransform = mUseTransform * mSceneTransform;
scene->transform(mSceneTransform);
if (node->node.use.symbol->node.symbol.overflowVisible) {
@@ -746,7 +750,7 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
// mClipTransform = mUseTransform * mSymbolTransform
Matrix mClipTransform = mUseTransform;
if (node->node.use.symbol->transform) {
- mClipTransform = mathMultiply(&mUseTransform, node->node.use.symbol->transform);
+ mClipTransform = mUseTransform * *node->node.use.symbol->transform;
}
viewBoxClip->transform(mClipTransform);
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp
index 3d582d291c..be1662daeb 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp
@@ -150,7 +150,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
if (isTransformation) {
- if (transform) gradTransform = mathMultiply(transform, &gradTransform);
+ if (transform) gradTransform = *transform * gradTransform;
} else if (transform) {
gradTransform = *transform;
isTransformation = true;
@@ -216,7 +216,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
if (transform) {
- if (isTransformation) gradTransform = mathMultiply(transform, &gradTransform);
+ if (isTransformation) gradTransform = *transform * gradTransform;
else {
gradTransform = *transform;
isTransformation = true;
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp
index b85d943873..3431f03411 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp
@@ -81,7 +81,7 @@ SwMpool* mpoolInit(uint32_t threads)
{
auto allocSize = threads + 1;
- auto mpool = static_cast<SwMpool*>(calloc(sizeof(SwMpool), 1));
+ auto mpool = static_cast<SwMpool*>(calloc(1, sizeof(SwMpool)));
mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
mpool->strokeOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
mpool->dashOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
index 731f6984e3..8ec2bc0c47 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
@@ -1108,7 +1108,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
float ys = FLT_MAX, ye = -1.0f;
for (int i = 0; i < 4; i++) {
- if (transform) mathMultiply(&vertices[i].pt, transform);
+ if (transform) vertices[i].pt *= *transform;
if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
}
@@ -1169,9 +1169,9 @@ static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, c
float ys = FLT_MAX, ye = -1.0f;
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
transformedTris[i] = mesh->triangles[i];
- mathMultiply(&transformedTris[i].vertex[0].pt, transform);
- mathMultiply(&transformedTris[i].vertex[1].pt, transform);
- mathMultiply(&transformedTris[i].vertex[2].pt, transform);
+ transformedTris[i].vertex[0].pt *= *transform;
+ transformedTris[i].vertex[1].pt *= *transform;
+ transformedTris[i].vertex[2].pt *= *transform;
if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y;
else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y;
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
index 0b2940c32d..f689179928 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
@@ -78,7 +78,6 @@ struct SwShapeTask : SwTask
{
SwShape shape;
const RenderShape* rshape = nullptr;
- bool cmpStroking = false;
bool clipper = false;
/* We assume that if the stroke width is greater than 2,
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
index 386cc594b4..1e5c4ef409 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
@@ -928,7 +928,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
rw.ySpan = 0;
rw.outline = const_cast<SwOutline*>(outline);
- rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64
+ rw.bandSize = rw.bufferSize / (sizeof(Cell) * 2); //bandSize: 256
rw.bandShoot = 0;
rw.antiAlias = antiAlias;
@@ -966,10 +966,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
if (cellMod > 0) cellStart += sizeof(Cell) - cellMod;
- auto cellEnd = rw.bufferSize;
- cellEnd -= cellEnd % sizeof(Cell);
-
- auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + cellEnd);
+ auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + rw.bufferSize);
rw.cells = reinterpret_cast<Cell*>((char*)rw.buffer + cellStart);
if (rw.cells >= cellsMax) goto reduce_bands;
diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
index b9327374b6..d8dd40d45b 100644
--- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
+++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
@@ -107,7 +107,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
if (mathZero(len)) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
//draw the current line fully
- } else if (len < dash.curLen) {
+ } else if (len <= dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
if (dash.move) {
@@ -168,7 +168,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
//draw the current line fully
if (mathZero(len)) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
- } else if (len < dash.curLen) {
+ } else if (len <= dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
if (dash.move) {
@@ -245,7 +245,86 @@ static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const
}
-static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, float length, SwMpool* mpool, unsigned tid)
+static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length)
+{
+ auto begin = length * rshape->stroke->trim.begin;
+ auto end = length * rshape->stroke->trim.end;
+
+ //default
+ if (end > begin) {
+ if (begin > 0.0f) dash->cnt = 4;
+ else dash->cnt = 2;
+ //looping
+ } else dash->cnt = 3;
+
+ if (dash->cnt == 2) {
+ dash->pattern[0] = end - begin;
+ dash->pattern[1] = length - (end - begin);
+ } else if (dash->cnt == 3) {
+ dash->pattern[0] = end;
+ dash->pattern[1] = (begin - end);
+ dash->pattern[2] = length - begin;
+ } else {
+ dash->pattern[0] = 0; //zero dash to start with a space.
+ dash->pattern[1] = begin;
+ dash->pattern[2] = end - begin;
+ dash->pattern[3] = length - end;
+ }
+}
+
+
+static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32_t shiftCmds, bool subpath)
+{
+ const PathCommand* cmds = rshape->path.cmds.data + shiftCmds;
+ auto cmdCnt = rshape->path.cmds.count - shiftCmds;
+ const Point* pts = rshape->path.pts.data + shiftPts;
+ auto ptsCnt = rshape->path.pts.count - shiftPts;
+
+ //No actual shape data
+ if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f;
+
+ const Point* close = nullptr;
+ auto length = 0.0f;
+
+ //must begin with moveTo
+ if (cmds[0] == PathCommand::MoveTo) {
+ close = pts;
+ cmds++;
+ pts++;
+ cmdCnt--;
+ }
+
+ while (cmdCnt-- > 0) {
+ switch (*cmds) {
+ case PathCommand::Close: {
+ length += mathLength(pts - 1, close);
+ if (subpath) return length;
+ break;
+ }
+ case PathCommand::MoveTo: {
+ if (subpath) return length;
+ close = pts;
+ ++pts;
+ break;
+ }
+ case PathCommand::LineTo: {
+ length += mathLength(pts - 1, pts);
+ ++pts;
+ break;
+ }
+ case PathCommand::CubicTo: {
+ length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
+ pts += 3;
+ break;
+ }
+ }
+ ++cmds;
+ }
+ return length;
+}
+
+
+static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, bool trimmed, SwMpool* mpool, unsigned tid)
{
const PathCommand* cmds = rshape->path.cmds.data;
auto cmdCnt = rshape->path.cmds.count;
@@ -255,49 +334,23 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
//No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
+ auto startPts = pts;
+ auto startCmds = cmds;
+
SwDashStroke dash;
auto offset = 0.0f;
- auto trimmed = false;
-
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
+ auto simultaneous = rshape->stroke->trim.simultaneous;
- //dash by trimming.
- if (length > 0.0f && dash.cnt == 0) {
- auto begin = length * rshape->stroke->trim.begin;
- auto end = length * rshape->stroke->trim.end;
-
- //TODO: mix trimming + dash style
-
- //default
- if (end > begin) {
- if (begin > 0.0f) dash.cnt += 4;
- else dash.cnt += 2;
- //looping
- } else dash.cnt += 3;
-
- dash.pattern = (float*)malloc(sizeof(float) * dash.cnt);
-
- if (dash.cnt == 2) {
- dash.pattern[0] = end - begin;
- dash.pattern[1] = length - (end - begin);
- } else if (dash.cnt == 3) {
- dash.pattern[0] = end;
- dash.pattern[1] = (begin - end);
- dash.pattern[2] = length - begin;
- } else {
- dash.pattern[0] = 0; //zero dash to start with a space.
- dash.pattern[1] = begin;
- dash.pattern[2] = end - begin;
- dash.pattern[3] = length - end;
- }
-
- trimmed = true;
- //just a dasy style.
+ if (dash.cnt == 0) {
+ if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4);
+ else return nullptr;
} else {
- if (dash.cnt == 0) return nullptr;
+ //TODO: handle dash + trim - for now trimming ignoring is forced
+ trimmed = false;
}
- //offset?
+ //offset
auto patternLength = 0.0f;
uint32_t offIdx = 0;
if (!mathZero(offset)) {
@@ -319,6 +372,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
//must begin with moveTo
if (cmds[0] == PathCommand::MoveTo) {
+ if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous));
_dashMoveTo(dash, offIdx, offset, pts);
cmds++;
pts++;
@@ -331,8 +385,12 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
break;
}
case PathCommand::MoveTo: {
- if (rshape->stroke->trim.individual) _dashMoveTo(dash, pts);
- else _dashMoveTo(dash, offIdx, offset, pts);
+ if (trimmed) {
+ if (simultaneous) {
+ _trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true));
+ _dashMoveTo(dash, offIdx, offset, pts);
+ } else _dashMoveTo(dash, pts);
+ } else _dashMoveTo(dash, offIdx, offset, pts);
++pts;
break;
}
@@ -358,56 +416,6 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
}
-static float _outlineLength(const RenderShape* rshape)
-{
- const PathCommand* cmds = rshape->path.cmds.data;
- auto cmdCnt = rshape->path.cmds.count;
- const Point* pts = rshape->path.pts.data;
- auto ptsCnt = rshape->path.pts.count;
-
- //No actual shape data
- if (cmdCnt == 0 || ptsCnt == 0) return 0.0f;
-
- const Point* close = nullptr;
- auto length = 0.0f;
- auto slength = -1.0f;
- auto simultaneous = !rshape->stroke->trim.individual;
-
- //Compute the whole length
- while (cmdCnt-- > 0) {
- switch (*cmds) {
- case PathCommand::Close: {
- length += mathLength(pts - 1, close);
- //retrieve the max length of the shape if the simultaneous mode.
- if (simultaneous) {
- if (slength < length) slength = length;
- length = 0.0f;
- }
- break;
- }
- case PathCommand::MoveTo: {
- close = pts;
- ++pts;
- break;
- }
- case PathCommand::LineTo: {
- length += mathLength(pts - 1, pts);
- ++pts;
- break;
- }
- case PathCommand::CubicTo: {
- length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
- pts += 3;
- break;
- }
- }
- ++cmds;
- }
- if (simultaneous && slength > length) return slength;
- else return length;
-}
-
-
static bool _axisAlignedRect(const SwOutline* outline)
{
//Fast Track: axis-aligned rectangle?
@@ -584,11 +592,10 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
auto dashStroking = false;
auto ret = true;
- auto length = rshape->strokeTrim() ? _outlineLength(rshape) : 0.0f;
-
//Dash style (+trimming)
- if (rshape->stroke->dashCnt > 0 || length > 0) {
- shapeOutline = _genDashOutline(rshape, transform, length, mpool, tid);
+ auto trimmed = rshape->strokeTrim();
+ if (rshape->stroke->dashCnt > 0 || trimmed) {
+ shapeOutline = _genDashOutline(rshape, transform, trimmed, mpool, tid);
if (!shapeOutline) return false;
dashStroking = true;
//Normal style
diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp
index 227ce10a0d..fcb632e2b1 100644
--- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp
+++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp
@@ -75,13 +75,13 @@ static Result _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform
auto v2 = *pt3;
if (rTransform) {
- mathMultiply(&v1, &rTransform->m);
- mathMultiply(&v2, &rTransform->m);
+ v1 *= rTransform->m;
+ v2 *= rTransform->m;
}
if (pTransform) {
- mathMultiply(&v1, &pTransform->m);
- mathMultiply(&v2, &pTransform->m);
+ v1 *= pTransform->m;
+ v2 *= pTransform->m;
}
//sorting
@@ -327,7 +327,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
//Compute the AABB after transformation
for (int i = 0; i < 4; i++) {
- mathMultiply(&pt[i], m);
+ pt[i] *= *m;
if (pt[i].x < x1) x1 = pt[i].x;
if (pt[i].x > x2) x2 = pt[i].x;
diff --git a/thirdparty/thorvg/src/renderer/tvgRender.cpp b/thirdparty/thorvg/src/renderer/tvgRender.cpp
index 14f77571fb..9c779f7c00 100644
--- a/thirdparty/thorvg/src/renderer/tvgRender.cpp
+++ b/thirdparty/thorvg/src/renderer/tvgRender.cpp
@@ -47,7 +47,7 @@ void RenderTransform::update()
mathScale(&m, scale, scale);
- if (!mathZero(degree)) mathRotate(&m, degree);
+ mathRotate(&m, degree);
mathTranslate(&m, x, y);
}
@@ -55,7 +55,7 @@ void RenderTransform::update()
RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
{
- if (lhs && rhs) m = mathMultiply(&lhs->m, &rhs->m);
+ if (lhs && rhs) m = lhs->m * rhs->m;
else if (lhs) m = lhs->m;
else if (rhs) m = rhs->m;
else mathIdentity(&m);
diff --git a/thirdparty/thorvg/src/renderer/tvgRender.h b/thirdparty/thorvg/src/renderer/tvgRender.h
index 6ea516c2f9..8f28d37dbc 100644
--- a/thirdparty/thorvg/src/renderer/tvgRender.h
+++ b/thirdparty/thorvg/src/renderer/tvgRender.h
@@ -142,7 +142,7 @@ struct RenderStroke
struct {
float begin = 0.0f;
float end = 1.0f;
- bool individual = false;
+ bool simultaneous = true;
} trim;
~RenderStroke()
diff --git a/thirdparty/thorvg/src/renderer/tvgShape.cpp b/thirdparty/thorvg/src/renderer/tvgShape.cpp
index 4cc8f64900..c010aa7bbf 100644
--- a/thirdparty/thorvg/src/renderer/tvgShape.cpp
+++ b/thirdparty/thorvg/src/renderer/tvgShape.cpp
@@ -287,16 +287,14 @@ const Fill* Shape::fill() const noexcept
Result Shape::order(bool strokeFirst) noexcept
{
- if (!pImpl->strokeFirst(strokeFirst)) return Result::FailedAllocation;
-
+ pImpl->strokeFirst(strokeFirst);
return Result::Success;
}
Result Shape::stroke(float width) noexcept
{
- if (!pImpl->strokeWidth(width)) return Result::FailedAllocation;
-
+ pImpl->strokeWidth(width);
return Result::Success;
}
@@ -309,8 +307,7 @@ float Shape::strokeWidth() const noexcept
Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
{
- if (!pImpl->strokeColor(r, g, b, a)) return Result::FailedAllocation;
-
+ pImpl->strokeColor(r, g, b, a);
return Result::Success;
}
@@ -349,27 +346,25 @@ uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
Result Shape::stroke(StrokeCap cap) noexcept
{
- if (!pImpl->strokeCap(cap)) return Result::FailedAllocation;
-
+ pImpl->strokeCap(cap);
return Result::Success;
}
Result Shape::stroke(StrokeJoin join) noexcept
{
- if (!pImpl->strokeJoin(join)) return Result::FailedAllocation;
-
+ pImpl->strokeJoin(join);
return Result::Success;
}
+
Result Shape::strokeMiterlimit(float miterlimit) noexcept
{
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
// - A negative value for stroke-miterlimit must be treated as an illegal value.
- if (miterlimit < 0.0f) return Result::NonSupport;
+ if (miterlimit < 0.0f) return Result::InvalidArguments;
// TODO Find out a reasonable max value.
- if (!pImpl->strokeMiterlimit(miterlimit)) return Result::FailedAllocation;
-
+ pImpl->strokeMiterlimit(miterlimit);
return Result::Success;
}
@@ -385,12 +380,26 @@ StrokeJoin Shape::strokeJoin() const noexcept
return pImpl->rs.strokeJoin();
}
+
float Shape::strokeMiterlimit() const noexcept
{
return pImpl->rs.strokeMiterlimit();
}
+Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept
+{
+ pImpl->strokeTrim(begin, end, simultaneous);
+ return Result::Success;
+}
+
+
+bool Shape::strokeTrim(float* begin, float* end) const noexcept
+{
+ return pImpl->strokeTrim(begin, end);
+}
+
+
Result Shape::fill(FillRule r) noexcept
{
pImpl->rs.rule = r;
diff --git a/thirdparty/thorvg/src/renderer/tvgShape.h b/thirdparty/thorvg/src/renderer/tvgShape.h
index 55335214be..4e85db37d0 100644
--- a/thirdparty/thorvg/src/renderer/tvgShape.h
+++ b/thirdparty/thorvg/src/renderer/tvgShape.h
@@ -207,60 +207,81 @@ struct Shape::Impl
flag |= RenderUpdateFlag::Path;
}
- bool strokeWidth(float width)
+ void strokeWidth(float width)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->width = width;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
- bool strokeTrim(float begin, float end, bool individual)
+ void strokeTrim(float begin, float end, bool simultaneous)
{
if (!rs.stroke) {
- if (begin == 0.0f && end == 1.0f) return true;
+ if (begin == 0.0f && end == 1.0f) return;
rs.stroke = new RenderStroke();
}
- if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end)) return true;
+ if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) &&
+ rs.stroke->trim.simultaneous == simultaneous) return;
+
+ auto loop = true;
+
+ if (begin > 1.0f && end > 1.0f) loop = false;
+ if (begin < 0.0f && end < 0.0f) loop = false;
+ if (begin >= 0.0f && begin <= 1.0f && end >= 0.0f && end <= 1.0f) loop = false;
+
+ if (begin > 1.0f) begin -= 1.0f;
+ if (begin < 0.0f) begin += 1.0f;
+ if (end > 1.0f) end -= 1.0f;
+ if (end < 0.0f) end += 1.0f;
+
+ if ((loop && begin < end) || (!loop && begin > end)) {
+ auto tmp = begin;
+ begin = end;
+ end = tmp;
+ }
rs.stroke->trim.begin = begin;
rs.stroke->trim.end = end;
- rs.stroke->trim.individual = individual;
+ rs.stroke->trim.simultaneous = simultaneous;
flag |= RenderUpdateFlag::Stroke;
+ }
- return true;
+ bool strokeTrim(float* begin, float* end)
+ {
+ if (rs.stroke) {
+ if (begin) *begin = rs.stroke->trim.begin;
+ if (end) *end = rs.stroke->trim.end;
+ return rs.stroke->trim.simultaneous;
+ } else {
+ if (begin) *begin = 0.0f;
+ if (end) *end = 1.0f;
+ return false;
+ }
}
- bool strokeCap(StrokeCap cap)
+ void strokeCap(StrokeCap cap)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->cap = cap;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
- bool strokeJoin(StrokeJoin join)
+ void strokeJoin(StrokeJoin join)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->join = join;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
- bool strokeMiterlimit(float miterlimit)
+ void strokeMiterlimit(float miterlimit)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->miterlimit = miterlimit;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
- bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+ void strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
if (rs.stroke->fill) {
@@ -275,8 +296,6 @@ struct Shape::Impl
rs.stroke->color[3] = a;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
Result strokeFill(unique_ptr<Fill> f)
@@ -335,13 +354,11 @@ struct Shape::Impl
return rs.stroke->strokeFirst;
}
- bool strokeFirst(bool strokeFirst)
+ void strokeFirst(bool strokeFirst)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->strokeFirst = strokeFirst;
flag |= RenderUpdateFlag::Stroke;
-
- return true;
}
void update(RenderUpdateFlag flag)
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
index cd1aeadec0..c980b89c4b 100755
--- a/thirdparty/thorvg/update-thorvg.sh
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -1,6 +1,6 @@
#!/bin/bash -e
-VERSION=0.13.5
+VERSION=0.13.7
cd thirdparty/thorvg/ || true
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/