summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/math/expression.cpp43
-rw-r--r--core/math/rect2.h12
-rw-r--r--core/variant/variant_parser.cpp44
-rw-r--r--doc/classes/ButtonGroup.xml2
-rw-r--r--doc/classes/CodeEdit.xml3
-rw-r--r--doc/classes/CurveTexture.xml2
-rw-r--r--doc/classes/CurveXYZTexture.xml2
-rw-r--r--doc/classes/EditorInspector.xml4
-rw-r--r--doc/classes/EditorPlugin.xml4
-rw-r--r--doc/classes/GradientTexture1D.xml2
-rw-r--r--doc/classes/LineEdit.xml6
-rw-r--r--doc/classes/RayCast2D.xml8
-rw-r--r--doc/classes/RayCast3D.xml8
-rw-r--r--doc/classes/Rect2i.xml2
-rw-r--r--doc/classes/ShapeCast2D.xml8
-rw-r--r--doc/classes/TabBar.xml62
-rw-r--r--doc/classes/TextEdit.xml6
-rw-r--r--doc/classes/TileMap.xml2
-rw-r--r--doc/classes/TreeItem.xml6
-rw-r--r--doc/classes/VisualShaderNodeDerivativeFunc.xml (renamed from doc/classes/VisualShaderNodeScalarDerivativeFunc.xml)20
-rw-r--r--doc/classes/VisualShaderNodeVectorDerivativeFunc.xml30
-rw-r--r--editor/editor_inspector.cpp3
-rw-r--r--editor/editor_node.cpp8
-rw-r--r--editor/editor_plugin.cpp6
-rw-r--r--editor/editor_plugin.h3
-rw-r--r--editor/editor_themes.cpp7
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/theme_editor_plugin.cpp4
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp34
-rw-r--r--editor/script_create_dialog.cpp8
-rwxr-xr-xmisc/scripts/black_format.sh2
-rwxr-xr-xmisc/scripts/clang_format.sh12
-rwxr-xr-xmisc/scripts/clang_tidy.sh12
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp66
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp23
-rw-r--r--modules/navigation/navigation_mesh_generator.h2
-rw-r--r--modules/visual_script/visual_script_expression.cpp43
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml2
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp2
-rw-r--r--platform/javascript/js/engine/config.js41
-rw-r--r--platform/javascript/js/libs/library_godot_input.js2
-rw-r--r--platform/linuxbsd/display_server_x11.h2
-rw-r--r--platform/windows/godot_windows.cpp4
-rw-r--r--scene/2d/ray_cast_2d.cpp20
-rw-r--r--scene/2d/ray_cast_2d.h6
-rw-r--r--scene/2d/shape_cast_2d.cpp20
-rw-r--r--scene/2d/shape_cast_2d.h6
-rw-r--r--scene/3d/ray_cast_3d.cpp20
-rw-r--r--scene/3d/ray_cast_3d.h6
-rw-r--r--scene/gui/base_button.cpp4
-rw-r--r--scene/gui/button.cpp5
-rw-r--r--scene/gui/code_edit.cpp43
-rw-r--r--scene/gui/control.cpp5
-rw-r--r--scene/gui/line_edit.cpp17
-rw-r--r--scene/gui/line_edit.h4
-rw-r--r--scene/gui/tab_bar.cpp552
-rw-r--r--scene/gui/tab_bar.h16
-rw-r--r--scene/gui/text_edit.cpp16
-rw-r--r--scene/gui/text_edit.h6
-rw-r--r--scene/gui/texture_button.cpp5
-rw-r--r--scene/gui/tree.cpp6
-rw-r--r--scene/gui/tree.h10
-rw-r--r--scene/register_scene_types.cpp5
-rw-r--r--scene/resources/default_theme/default_theme.cpp8
-rw-r--r--scene/resources/texture.cpp4
-rw-r--r--scene/resources/texture.h6
-rw-r--r--scene/resources/visual_shader_nodes.cpp141
-rw-r--r--scene/resources/visual_shader_nodes.h59
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp4
-rw-r--r--tests/core/math/test_rect2.h6
-rw-r--r--tests/scene/test_code_edit.h46
-rw-r--r--thirdparty/README.md4
-rw-r--r--thirdparty/thorvg/AUTHORS2
-rw-r--r--thirdparty/thorvg/inc/config.h2
-rw-r--r--thirdparty/thorvg/patches/thorvg-pr1159-mingw-fix.patch73
-rw-r--r--thirdparty/thorvg/patches/thorvg-pr1166-vs2017-minmax.patch49
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp4
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp36
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h6
-rw-r--r--thirdparty/thorvg/src/lib/tvgMath.h2
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp5
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp5
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h2
-rw-r--r--thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp9
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp224
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp6
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh2
88 files changed, 1195 insertions, 850 deletions
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index 4f8e79038f..203566579d 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -197,6 +197,7 @@ Error Expression::_get_token(Token &r_token) {
case '\'':
case '"': {
String str;
+ char32_t prev = 0;
while (true) {
char32_t ch = GET_CHAR();
@@ -234,9 +235,11 @@ Error Expression::_get_token(Token &r_token) {
case 'r':
res = 13;
break;
+ case 'U':
case 'u': {
- // hex number
- for (int j = 0; j < 4; j++) {
+ // Hexadecimal sequence.
+ int hex_len = (next == 'U') ? 6 : 4;
+ for (int j = 0; j < hex_len; j++) {
char32_t c = GET_CHAR();
if (c == 0) {
@@ -273,12 +276,46 @@ Error Expression::_get_token(Token &r_token) {
} break;
}
+ // Parse UTF-16 pair.
+ if ((res & 0xfffffc00) == 0xd800) {
+ if (prev == 0) {
+ prev = res;
+ continue;
+ } else {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ } else if ((res & 0xfffffc00) == 0xdc00) {
+ if (prev == 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired trail surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ } else {
+ res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev = 0;
+ }
+ }
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += res;
-
} else {
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += ch;
}
}
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
r_token.type = TK_CONSTANT;
r_token.value = str;
diff --git a/core/math/rect2.h b/core/math/rect2.h
index 4ea24e8f88..b14c69302c 100644
--- a/core/math/rect2.h
+++ b/core/math/rect2.h
@@ -382,16 +382,16 @@ struct _NO_DISCARD_ Rect2i {
ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
}
#endif
- if (position.x > (p_rect.position.x + p_rect.size.width)) {
+ if (position.x >= (p_rect.position.x + p_rect.size.width)) {
return false;
}
- if ((position.x + size.width) < p_rect.position.x) {
+ if ((position.x + size.width) <= p_rect.position.x) {
return false;
}
- if (position.y > (p_rect.position.y + p_rect.size.height)) {
+ if (position.y >= (p_rect.position.y + p_rect.size.height)) {
return false;
}
- if ((position.y + size.height) < p_rect.position.y) {
+ if ((position.y + size.height) <= p_rect.position.y) {
return false;
}
@@ -405,8 +405,8 @@ struct _NO_DISCARD_ Rect2i {
}
#endif
return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) &&
- ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) &&
- ((p_rect.position.y + p_rect.size.y) < (position.y + size.y));
+ ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) &&
+ ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y));
}
_FORCE_INLINE_ bool has_no_area() const {
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 57875bf50f..96cdc0678e 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -217,6 +217,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
}
case '"': {
String str;
+ char32_t prev = 0;
while (true) {
char32_t ch = p_stream->get_char();
@@ -252,10 +253,13 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
case 'r':
res = 13;
break;
+ case 'U':
case 'u': {
- //hex number
- for (int j = 0; j < 4; j++) {
+ // Hexadecimal sequence.
+ int hex_len = (next == 'U') ? 6 : 4;
+ for (int j = 0; j < hex_len; j++) {
char32_t c = p_stream->get_char();
+
if (c == 0) {
r_err_str = "Unterminated String";
r_token.type = TK_ERROR;
@@ -290,15 +294,49 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
} break;
}
+ // Parse UTF-16 pair.
+ if ((res & 0xfffffc00) == 0xd800) {
+ if (prev == 0) {
+ prev = res;
+ continue;
+ } else {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate";
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ } else if ((res & 0xfffffc00) == 0xdc00) {
+ if (prev == 0) {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired trail surrogate";
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ } else {
+ res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev = 0;
+ }
+ }
+ if (prev != 0) {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate";
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += res;
-
} else {
+ if (prev != 0) {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate";
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
if (ch == '\n') {
line++;
}
str += ch;
}
}
+ if (prev != 0) {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate";
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
if (p_stream->is_utf8()) {
str.parse_utf8(str.ascii(true).get_data());
diff --git a/doc/classes/ButtonGroup.xml b/doc/classes/ButtonGroup.xml
index e3e781ac10..24bdc95baf 100644
--- a/doc/classes/ButtonGroup.xml
+++ b/doc/classes/ButtonGroup.xml
@@ -28,7 +28,7 @@
</members>
<signals>
<signal name="pressed">
- <argument index="0" name="button" type="Object" />
+ <argument index="0" name="button" type="BaseButton" />
<description>
Emitted when one of the buttons of the group is pressed.
</description>
diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml
index b3add6cfa2..09696d4d2a 100644
--- a/doc/classes/CodeEdit.xml
+++ b/doc/classes/CodeEdit.xml
@@ -607,6 +607,9 @@
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the [CodeEdit].
</theme_item>
+ <theme_item name="font_placeholder_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.6)">
+ Font color for [member TextEdit.placeholder_text].
+ </theme_item>
<theme_item name="font_readonly_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
Sets the font [Color] when [member TextEdit.editable] is disabled.
</theme_item>
diff --git a/doc/classes/CurveTexture.xml b/doc/classes/CurveTexture.xml
index fe75f029f0..99b5ef2f4f 100644
--- a/doc/classes/CurveTexture.xml
+++ b/doc/classes/CurveTexture.xml
@@ -14,7 +14,7 @@
</member>
<member name="texture_mode" type="int" setter="set_texture_mode" getter="get_texture_mode" enum="CurveTexture.TextureMode" default="0">
</member>
- <member name="width" type="int" setter="set_width" getter="get_width" default="2048">
+ <member name="width" type="int" setter="set_width" getter="get_width" default="256">
The width of the texture.
</member>
</members>
diff --git a/doc/classes/CurveXYZTexture.xml b/doc/classes/CurveXYZTexture.xml
index 815653e987..b6dd90c7d3 100644
--- a/doc/classes/CurveXYZTexture.xml
+++ b/doc/classes/CurveXYZTexture.xml
@@ -13,7 +13,7 @@
</member>
<member name="curve_z" type="Curve" setter="set_curve_z" getter="get_curve_z">
</member>
- <member name="width" type="int" setter="set_width" getter="get_width" default="2048">
+ <member name="width" type="int" setter="set_width" getter="get_width" default="256">
</member>
</members>
</class>
diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml
index 27dab3d422..de122cb8bb 100644
--- a/doc/classes/EditorInspector.xml
+++ b/doc/classes/EditorInspector.xml
@@ -59,8 +59,8 @@
</description>
</signal>
<signal name="resource_selected">
- <argument index="0" name="res" type="Object" />
- <argument index="1" name="prop" type="String" />
+ <argument index="0" name="resource" type="Resource" />
+ <argument index="1" name="path" type="String" />
<description>
Emitted when a resource is selected in the inspector.
</description>
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 7b0a300782..cc1243898f 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -438,9 +438,9 @@
<method name="add_tool_submenu_item">
<return type="void" />
<argument index="0" name="name" type="String" />
- <argument index="1" name="submenu" type="Object" />
+ <argument index="1" name="submenu" type="PopupMenu" />
<description>
- Adds a custom submenu under [b]Project &gt; Tools &gt;[/b] [code]name[/code]. [code]submenu[/code] should be an object of class [PopupMenu]. Use [code]remove_tool_menu_item(name)[/code] on plugin clean up to remove the menu.
+ Adds a custom [PopupMenu] submenu under [b]Project &gt; Tools &gt;[/b] [code]name[/code]. Use [code]remove_tool_menu_item(name)[/code] on plugin clean up to remove the menu.
</description>
</method>
<method name="add_translation_parser_plugin">
diff --git a/doc/classes/GradientTexture1D.xml b/doc/classes/GradientTexture1D.xml
index 223439956c..4730410ea9 100644
--- a/doc/classes/GradientTexture1D.xml
+++ b/doc/classes/GradientTexture1D.xml
@@ -15,7 +15,7 @@
<member name="use_hdr" type="bool" setter="set_use_hdr" getter="is_using_hdr" default="false">
If [code]true[/code], the generated texture will support high dynamic range ([constant Image.FORMAT_RGBAF] format). This allows for glow effects to work if [member Environment.glow_enabled] is [code]true[/code]. If [code]false[/code], the generated texture will use low dynamic range; overbright colors will be clamped ([constant Image.FORMAT_RGBA8] format).
</member>
- <member name="width" type="int" setter="set_width" getter="get_width" default="2048">
+ <member name="width" type="int" setter="set_width" getter="get_width" default="256">
The number of color samples that will be obtained from the [Gradient].
</member>
</members>
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index 224579e8c5..0b97865770 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -233,9 +233,6 @@
[b]Note:[/b] This method is only implemented on Linux.
</member>
<member name="mouse_default_cursor_shape" type="int" setter="set_default_cursor_shape" getter="get_default_cursor_shape" overrides="Control" enum="Control.CursorShape" default="1" />
- <member name="placeholder_alpha" type="float" setter="set_placeholder_alpha" getter="get_placeholder_alpha" default="0.6">
- Opacity of the [member placeholder_text]. From [code]0[/code] to [code]1[/code].
- </member>
<member name="placeholder_text" type="String" setter="set_placeholder" getter="get_placeholder" default="&quot;&quot;">
Text shown when the [LineEdit] is empty. It is [b]not[/b] the [LineEdit]'s default value (see [member text]).
</member>
@@ -397,6 +394,9 @@
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the [LineEdit].
</theme_item>
+ <theme_item name="font_placeholder_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.6)">
+ Font color for [member placeholder_text].
+ </theme_item>
<theme_item name="font_selected_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Font color for selected text (inside the selection rectangle).
</theme_item>
diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml
index fe2885378a..27e02d0ee5 100644
--- a/doc/classes/RayCast2D.xml
+++ b/doc/classes/RayCast2D.xml
@@ -16,9 +16,9 @@
<methods>
<method name="add_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject2D" />
<description>
- Adds a collision exception so the ray does not report collisions with the specified node.
+ Adds a collision exception so the ray does not report collisions with the specified [CollisionObject2D] node.
</description>
</method>
<method name="add_exception_rid">
@@ -81,9 +81,9 @@
</method>
<method name="remove_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject2D" />
<description>
- Removes a collision exception so the ray does report collisions with the specified node.
+ Removes a collision exception so the ray does report collisions with the specified [CollisionObject2D] node.
</description>
</method>
<method name="remove_exception_rid">
diff --git a/doc/classes/RayCast3D.xml b/doc/classes/RayCast3D.xml
index 8973857ace..6be5861b84 100644
--- a/doc/classes/RayCast3D.xml
+++ b/doc/classes/RayCast3D.xml
@@ -17,9 +17,9 @@
<methods>
<method name="add_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject3D" />
<description>
- Adds a collision exception so the ray does not report collisions with the specified node.
+ Adds a collision exception so the ray does not report collisions with the specified [CollisionObject3D] node.
</description>
</method>
<method name="add_exception_rid">
@@ -82,9 +82,9 @@
</method>
<method name="remove_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject3D" />
<description>
- Removes a collision exception so the ray does report collisions with the specified node.
+ Removes a collision exception so the ray does report collisions with the specified [CollisionObject3D] node.
</description>
</method>
<method name="remove_exception_rid">
diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml
index 5909784135..e070824297 100644
--- a/doc/classes/Rect2i.xml
+++ b/doc/classes/Rect2i.xml
@@ -70,7 +70,7 @@
<return type="Rect2i" />
<argument index="0" name="to" type="Vector2i" />
<description>
- Returns a copy of this [Rect2i] expanded to include a given point.
+ Returns a copy of this [Rect2i] expanded so that the borders align with the given point.
[codeblocks]
[gdscript]
# position (-3, 2), size (1, 1)
diff --git a/doc/classes/ShapeCast2D.xml b/doc/classes/ShapeCast2D.xml
index 74ebafe069..7229d6f72a 100644
--- a/doc/classes/ShapeCast2D.xml
+++ b/doc/classes/ShapeCast2D.xml
@@ -14,9 +14,9 @@
<methods>
<method name="add_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject2D" />
<description>
- Adds a collision exception so the shape does not report collisions with the specified node.
+ Adds a collision exception so the shape does not report collisions with the specified [CollisionObject2D] node.
</description>
</method>
<method name="add_exception_rid">
@@ -101,9 +101,9 @@
</method>
<method name="remove_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject2D" />
<description>
- Removes a collision exception so the shape does report collisions with the specified node.
+ Removes a collision exception so the shape does report collisions with the specified [CollisionObject2D] node.
</description>
</method>
<method name="remove_exception_rid">
diff --git a/doc/classes/TabBar.xml b/doc/classes/TabBar.xml
index 59a4743d80..c286629395 100644
--- a/doc/classes/TabBar.xml
+++ b/doc/classes/TabBar.xml
@@ -43,10 +43,11 @@
Returns the previously active tab index.
</description>
</method>
- <method name="get_select_with_rmb" qualifiers="const">
- <return type="bool" />
+ <method name="get_tab_button_icon" qualifiers="const">
+ <return type="Texture2D" />
+ <argument index="0" name="tab_idx" type="int" />
<description>
- Returns [code]true[/code] if select with right mouse button is enabled.
+ Returns the [Texture2D] for the right button of the tab at index [code]tab_idx[/code] or [code]null[/code] if the button has no [Texture2D].
</description>
</method>
<method name="get_tab_icon" qualifiers="const">
@@ -111,6 +112,13 @@
Returns [code]true[/code] if the tab at index [code]tab_idx[/code] is disabled.
</description>
</method>
+ <method name="is_tab_hidden" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="tab_idx" type="int" />
+ <description>
+ Returns [code]true[/code] if the tab at index [code]tab_idx[/code] is hidden.
+ </description>
+ </method>
<method name="move_tab">
<return type="void" />
<argument index="0" name="from" type="int" />
@@ -126,11 +134,12 @@
Removes the tab at index [code]tab_idx[/code].
</description>
</method>
- <method name="set_select_with_rmb">
+ <method name="set_tab_button_icon">
<return type="void" />
- <argument index="0" name="enabled" type="bool" />
+ <argument index="0" name="tab_idx" type="int" />
+ <argument index="1" name="icon" type="Texture2D" />
<description>
- If [code]true[/code], enables selecting a tab with the right mouse button.
+ Sets an [code]icon[/code] for the button of the tab at index [code]tab_idx[/code] (located to the right, before the close button), making it visible and clickable (See [signal tab_button_pressed]). Giving it a [code]null[/code] value will hide the button.
</description>
</method>
<method name="set_tab_disabled">
@@ -141,6 +150,14 @@
If [code]disabled[/code] is [code]true[/code], disables the tab at index [code]tab_idx[/code], making it non-interactable.
</description>
</method>
+ <method name="set_tab_hidden">
+ <return type="void" />
+ <argument index="0" name="tab_idx" type="int" />
+ <argument index="1" name="hidden" type="bool" />
+ <description>
+ If [code]hidden[/code] is [code]true[/code], hides the tab at index [code]tab_idx[/code], making it disappear from the tab area.
+ </description>
+ </method>
<method name="set_tab_icon">
<return type="void" />
<argument index="0" name="tab_idx" type="int" />
@@ -200,10 +217,17 @@
<member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled" default="false">
If [code]true[/code], tabs can be rearranged with mouse drag.
</member>
+ <member name="scroll_to_selected" type="bool" setter="set_scroll_to_selected" getter="get_scroll_to_selected" default="true">
+ If [code]true[/code], the tab offset will be changed to keep the the currently selected tab visible.
+ </member>
<member name="scrolling_enabled" type="bool" setter="set_scrolling_enabled" getter="get_scrolling_enabled" default="true">
if [code]true[/code], the mouse's scroll wheel can be used to navigate the scroll view.
</member>
+ <member name="select_with_rmb" type="bool" setter="set_select_with_rmb" getter="get_select_with_rmb" default="false">
+ If [code]true[/code], enables selecting a tab with the right mouse button.
+ </member>
<member name="tab_alignment" type="int" setter="set_tab_alignment" getter="get_tab_alignment" enum="TabBar.AlignmentMode" default="1">
+ Sets the position at which tabs will be placed. See [enum AlignmentMode] for details.
</member>
<member name="tab_close_display_policy" type="int" setter="set_tab_close_display_policy" getter="get_tab_close_display_policy" enum="TabBar.CloseButtonDisplayPolicy" default="0">
Sets when the close button will appear on the tabs. See [enum CloseButtonDisplayPolicy] for details.
@@ -219,6 +243,12 @@
Emitted when the active tab is rearranged via mouse drag. See [member drag_to_rearrange_enabled].
</description>
</signal>
+ <signal name="tab_button_pressed">
+ <argument index="0" name="tab" type="int" />
+ <description>
+ Emitted when a tab's right button is pressed. See [method set_tab_button_icon].
+ </description>
+ </signal>
<signal name="tab_changed">
<argument index="0" name="tab" type="int" />
<description>
@@ -255,18 +285,28 @@
<signal name="tab_rmb_clicked">
<argument index="0" name="tab" type="int" />
<description>
- Emitted when a tab is right-clicked.
+ Emitted when a tab is right-clicked. [member select_with_rmb] must be enabled.
+ </description>
+ </signal>
+ <signal name="tab_selected">
+ <argument index="0" name="tab" type="int" />
+ <description>
+ Emitted when a tab is selected via click or script, even if it is the current tab.
</description>
</signal>
</signals>
<constants>
<constant name="ALIGNMENT_LEFT" value="0" enum="AlignmentMode">
+ Places tabs to the left.
</constant>
<constant name="ALIGNMENT_CENTER" value="1" enum="AlignmentMode">
+ Places tabs in the middle.
</constant>
<constant name="ALIGNMENT_RIGHT" value="2" enum="AlignmentMode">
+ Places tabs to the right.
</constant>
<constant name="ALIGNMENT_MAX" value="3" enum="AlignmentMode">
+ Represents the size of the [enum AlignmentMode] enum.
</constant>
<constant name="CLOSE_BUTTON_SHOW_NEVER" value="0" enum="CloseButtonDisplayPolicy">
Never show the close buttons.
@@ -321,11 +361,11 @@
<theme_item name="increment_highlight" data_type="icon" type="Texture2D">
Icon for the right arrow button that appears when there are too many tabs to fit in the container width. Used when the button is being hovered with the cursor.
</theme_item>
- <theme_item name="close_bg_highlight" data_type="style" type="StyleBox">
- Background of the close button when it's being hovered with the cursor.
+ <theme_item name="button_highlight" data_type="style" type="StyleBox">
+ Background of the tab and close buttons when they're being hovered with the cursor.
</theme_item>
- <theme_item name="close_bg_pressed" data_type="style" type="StyleBox">
- Background of the close button when it's being pressed.
+ <theme_item name="button_pressed" data_type="style" type="StyleBox">
+ Background of the tab and close buttons when it's being pressed.
</theme_item>
<theme_item name="tab_disabled" data_type="style" type="StyleBox">
The style of disabled tabs.
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 6d602d58ee..6b9eb6efc3 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -992,9 +992,6 @@
<member name="override_selected_font_color" type="bool" setter="set_override_selected_font_color" getter="is_overriding_selected_font_color" default="false">
If [code]true[/code], custom [code]font_selected_color[/code] will be used for selected text.
</member>
- <member name="placeholder_alpha" type="float" setter="set_placeholder_alpha" getter="get_placeholder_alpha" default="0.6">
- Opacity of the [member placeholder_text]. From [code]0[/code] to [code]1[/code].
- </member>
<member name="placeholder_text" type="String" setter="set_placeholder" getter="get_placeholder" default="&quot;&quot;">
Text shown when the [TextEdit] is empty. It is [b]not[/b] the [TextEdit]'s default value (see [member text]).
</member>
@@ -1237,6 +1234,9 @@
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the [TextEdit].
</theme_item>
+ <theme_item name="font_placeholder_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.6)">
+ Font color for [member placeholder_text].
+ </theme_item>
<theme_item name="font_readonly_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
Sets the font [Color] when [member editable] is disabled.
</theme_item>
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 07b9543823..80a6458cec 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -218,7 +218,7 @@
<return type="void" />
<argument index="0" name="layer" type="int" />
<description>
- Moves the layer at index [code]layer_index[/code] to the given position [code]to_position[/code] in the array.
+ Removes the layer at index [code]layer[/code].
</description>
</method>
<method name="set_cell">
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index 13f2c7120c..12c91cdd10 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -371,7 +371,7 @@
</method>
<method name="move_after">
<return type="void" />
- <argument index="0" name="item" type="Object" />
+ <argument index="0" name="item" type="TreeItem" />
<description>
Moves this TreeItem right after the given [code]item[/code].
[b]Note:[/b] You can't move to the root or move the root.
@@ -379,7 +379,7 @@
</method>
<method name="move_before">
<return type="void" />
- <argument index="0" name="item" type="Object" />
+ <argument index="0" name="item" type="TreeItem" />
<description>
Moves this TreeItem right before the given [code]item[/code].
[b]Note:[/b] You can't move to the root or move the root.
@@ -395,7 +395,7 @@
</method>
<method name="remove_child">
<return type="void" />
- <argument index="0" name="child" type="Object" />
+ <argument index="0" name="child" type="TreeItem" />
<description>
Removes the given child [TreeItem] and all its children from the [Tree]. Note that it doesn't free the item from memory, so it can be reused later. To completely remove a [TreeItem] use [method Object.free].
</description>
diff --git a/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml b/doc/classes/VisualShaderNodeDerivativeFunc.xml
index 8d108a5d28..f7edac8597 100644
--- a/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml
+++ b/doc/classes/VisualShaderNodeDerivativeFunc.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeScalarDerivativeFunc" inherits="VisualShaderNode" version="4.0">
+<class name="VisualShaderNodeDerivativeFunc" inherits="VisualShaderNode" version="4.0">
<brief_description>
- Calculates a scalar derivative within the visual shader graph.
+ Calculates a derivative within the visual shader graph.
</brief_description>
<description>
This node is only available in [code]Fragment[/code] and [code]Light[/code] visual shaders.
@@ -9,11 +9,23 @@
<tutorials>
</tutorials>
<members>
- <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeScalarDerivativeFunc.Function" default="0">
- The derivative type. See [enum Function] for options.
+ <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeDerivativeFunc.Function" default="0">
+ A derivative function type. See [enum Function] for options.
+ </member>
+ <member name="op_type" type="int" setter="set_op_type" getter="get_op_type" enum="VisualShaderNodeDerivativeFunc.OpType" default="0">
+ A type of operands and returned value. See [enum OpType] for options.
</member>
</members>
<constants>
+ <constant name="OP_TYPE_SCALAR" value="0" enum="OpType">
+ A floating-point scalar.
+ </constant>
+ <constant name="OP_TYPE_VECTOR" value="1" enum="OpType">
+ A 3D vector type.
+ </constant>
+ <constant name="OP_TYPE_MAX" value="2" enum="OpType">
+ Represents the size of the [enum OpType] enum.
+ </constant>
<constant name="FUNC_SUM" value="0" enum="Function">
Sum of absolute derivative in [code]x[/code] and [code]y[/code].
</constant>
diff --git a/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml b/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml
deleted file mode 100644
index e0c7c8618c..0000000000
--- a/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeVectorDerivativeFunc" inherits="VisualShaderNode" version="4.0">
- <brief_description>
- Calculates a vector derivative within the visual shader graph.
- </brief_description>
- <description>
- This node is only available in [code]Fragment[/code] and [code]Light[/code] visual shaders.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeVectorDerivativeFunc.Function" default="0">
- A derivative type. See [enum Function] for options.
- </member>
- </members>
- <constants>
- <constant name="FUNC_SUM" value="0" enum="Function">
- Sum of absolute derivative in [code]x[/code] and [code]y[/code].
- </constant>
- <constant name="FUNC_X" value="1" enum="Function">
- Derivative in [code]x[/code] using local differencing.
- </constant>
- <constant name="FUNC_Y" value="2" enum="Function">
- Derivative in [code]y[/code] using local differencing.
- </constant>
- <constant name="FUNC_MAX" value="3" enum="Function">
- Represents the size of the [enum Function] enum.
- </constant>
- </constants>
-</class>
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index d19032da8b..50c4a6200f 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -919,6 +919,7 @@ void EditorProperty::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_warning"), "set_draw_warning", "is_draw_warning");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keying"), "set_keying", "is_keying");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deletable"), "set_deletable", "is_deletable");
+
ADD_SIGNAL(MethodInfo("property_changed", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("multiple_properties_changed", PropertyInfo(Variant::PACKED_STRING_ARRAY, "properties"), PropertyInfo(Variant::ARRAY, "value")));
ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING_NAME, "property")));
@@ -3543,7 +3544,7 @@ void EditorInspector::_bind_methods() {
ADD_SIGNAL(MethodInfo("property_selected", PropertyInfo(Variant::STRING, "property")));
ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::BOOL, "advance")));
ADD_SIGNAL(MethodInfo("property_deleted", PropertyInfo(Variant::STRING, "property")));
- ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop")));
+ ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::STRING, "path")));
ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property")));
ADD_SIGNAL(MethodInfo("property_toggled", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::BOOL, "checked")));
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index a1f259c864..deafe65baf 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -340,7 +340,7 @@ void EditorNode::_update_scene_tabs() {
}
if (show_rb && editor_data.get_scene_root_script(i).is_valid()) {
- scene_tabs->set_tab_right_button(i, script_icon);
+ scene_tabs->set_tab_button_icon(i, script_icon);
}
}
@@ -4222,6 +4222,8 @@ void EditorNode::_dock_floating_close_request(Control *p_control) {
_update_dock_containers();
floating_docks.erase(p_control);
+
+ _edit_current();
}
void EditorNode::_dock_make_float() {
@@ -4264,6 +4266,8 @@ void EditorNode::_dock_make_float() {
_update_dock_containers();
floating_docks.push_back(dock);
+
+ _edit_current();
}
void EditorNode::_update_dock_containers() {
@@ -6233,7 +6237,7 @@ EditorNode::EditorNode() {
scene_tabs->set_min_width(int(EDITOR_DEF("interface/scene_tabs/minimum_width", 50)) * EDSCALE);
scene_tabs->set_drag_to_rearrange_enabled(true);
scene_tabs->connect("tab_changed", callable_mp(this, &EditorNode::_scene_tab_changed));
- scene_tabs->connect("tab_rmb_clicked", callable_mp(this, &EditorNode::_scene_tab_script_edited));
+ scene_tabs->connect("tab_button_pressed", callable_mp(this, &EditorNode::_scene_tab_script_edited));
scene_tabs->connect("tab_close_pressed", callable_mp(this, &EditorNode::_scene_tab_closed), varray(SCENE_TAB_CLOSE));
scene_tabs->connect("tab_hovered", callable_mp(this, &EditorNode::_scene_tab_hovered));
scene_tabs->connect("mouse_exited", callable_mp(this, &EditorNode::_scene_tab_exit));
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 29f6079fcf..a1c031aa1b 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -513,11 +513,9 @@ void EditorPlugin::add_tool_menu_item(const String &p_name, const Callable &p_ca
EditorNode::get_singleton()->add_tool_menu_item(p_name, p_callable);
}
-void EditorPlugin::add_tool_submenu_item(const String &p_name, Object *p_submenu) {
+void EditorPlugin::add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu) {
ERR_FAIL_NULL(p_submenu);
- PopupMenu *submenu = Object::cast_to<PopupMenu>(p_submenu);
- ERR_FAIL_NULL(submenu);
- EditorNode::get_singleton()->add_tool_submenu_item(p_name, submenu);
+ EditorNode::get_singleton()->add_tool_submenu_item(p_name, p_submenu);
}
void EditorPlugin::remove_tool_menu_item(const String &p_name) {
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index faa8ae1ce6..06517190f6 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -42,6 +42,7 @@
#include "scene/3d/camera_3d.h"
#include "scene/main/node.h"
#include "scene/resources/texture.h"
+
class EditorNode;
class Node3D;
class Camera3D;
@@ -217,7 +218,7 @@ public:
void remove_control_from_bottom_panel(Control *p_control);
void add_tool_menu_item(const String &p_name, const Callable &p_callable);
- void add_tool_submenu_item(const String &p_name, Object *p_submenu);
+ void add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu);
void remove_tool_menu_item(const String &p_name);
void set_input_event_forwarding_always_enabled();
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 238df4a8aa..308a268e42 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -427,6 +427,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color font_focus_color = mono_color.lerp(base_color, 0.125);
const Color font_disabled_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.3);
const Color font_readonly_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.65);
+ const Color font_placeholder_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.6);
const Color selection_color = accent_color * Color(1, 1, 1, 0.4);
const Color disabled_color = mono_color.inverted().lerp(base_color, 0.7);
const Color disabled_bg_color = mono_color.inverted().lerp(base_color, 0.9);
@@ -1016,8 +1017,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("SceneTabFG", "EditorStyles", style_tab_selected);
theme->set_stylebox("SceneTabBG", "EditorStyles", style_tab_unselected);
theme->set_icon("close", "TabBar", theme->get_icon("GuiClose", "EditorIcons"));
- theme->set_stylebox("close_bg_pressed", "TabBar", style_menu);
- theme->set_stylebox("close_bg_highlight", "TabBar", style_menu);
+ theme->set_stylebox("button_pressed", "TabBar", style_menu);
+ theme->set_stylebox("button_highlight", "TabBar", style_menu);
theme->set_icon("increment", "TabContainer", theme->get_icon("GuiScrollArrowRight", "EditorIcons"));
theme->set_icon("decrement", "TabContainer", theme->get_icon("GuiScrollArrowLeft", "EditorIcons"));
theme->set_icon("increment", "TabBar", theme->get_icon("GuiScrollArrowRight", "EditorIcons"));
@@ -1109,6 +1110,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_color", "LineEdit", font_color);
theme->set_color("font_selected_color", "LineEdit", mono_color);
theme->set_color("font_uneditable_color", "LineEdit", font_readonly_color);
+ theme->set_color("font_placeholder_color", "LineEdit", font_placeholder_color);
theme->set_color("caret_color", "LineEdit", font_color);
theme->set_color("selection_color", "LineEdit", selection_color);
theme->set_color("clear_button_color", "LineEdit", font_color);
@@ -1123,6 +1125,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("space", "TextEdit", theme->get_icon("GuiSpace", "EditorIcons"));
theme->set_color("font_color", "TextEdit", font_color);
theme->set_color("font_readonly_color", "TextEdit", font_readonly_color);
+ theme->set_color("font_placeholder_color", "TextEdit", font_placeholder_color);
theme->set_color("caret_color", "TextEdit", font_color);
theme->set_color("selection_color", "TextEdit", selection_color);
theme->set_constant("line_spacing", "TextEdit", 4 * EDSCALE);
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 169ce29438..5dd24983ff 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -383,7 +383,7 @@ void Skeleton3DEditor::create_physical_skeleton() {
if (!bones_infos[parent].physical_bone) {
bones_infos.write[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos);
- ur->create_action(TTR("Create physical bones"));
+ ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL);
ur->add_do_method(skeleton, "add_child", bones_infos[parent].physical_bone);
ur->add_do_reference(bones_infos[parent].physical_bone);
ur->add_undo_method(skeleton, "remove_child", bones_infos[parent].physical_bone);
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 611f81db33..bc2739bdac 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -3469,7 +3469,7 @@ void ThemeEditor::_add_preview_tab(ThemeEditorPreview *p_preview_tab, const Stri
preview_tabs->add_tab(p_preview_name, p_icon);
preview_tabs_content->add_child(p_preview_tab);
- preview_tabs->set_tab_right_button(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("close"), SNAME("TabBar")));
+ preview_tabs->set_tab_button_icon(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("close"), SNAME("TabBar")));
p_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked));
preview_tabs->set_current_tab(preview_tabs->get_tab_count() - 1);
@@ -3600,7 +3600,7 @@ ThemeEditor::ThemeEditor() {
preview_tabs->set_h_size_flags(SIZE_EXPAND_FILL);
preview_tabbar_hb->add_child(preview_tabs);
preview_tabs->connect("tab_changed", callable_mp(this, &ThemeEditor::_change_preview_tab));
- preview_tabs->connect("tab_rmb_clicked", callable_mp(this, &ThemeEditor::_remove_preview_tab));
+ preview_tabs->connect("tab_button_pressed", callable_mp(this, &ThemeEditor::_remove_preview_tab));
HBoxContainer *add_preview_button_hb = memnew(HBoxContainer);
preview_tabbar_hb->add_child(add_preview_button_hb);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 30c13cbf65..ef6d7b3462 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -2345,19 +2345,13 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, const Vector<Vari
// DERIVATIVE
{
- VisualShaderNodeScalarDerivativeFunc *sderFunc = Object::cast_to<VisualShaderNodeScalarDerivativeFunc>(p_node);
+ VisualShaderNodeDerivativeFunc *derFunc = Object::cast_to<VisualShaderNodeDerivativeFunc>(p_node);
- if (sderFunc) {
+ if (derFunc) {
ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
- sderFunc->set_function((VisualShaderNodeScalarDerivativeFunc::Function)(int)p_ops[0]);
- return;
- }
-
- VisualShaderNodeVectorDerivativeFunc *vderFunc = Object::cast_to<VisualShaderNodeVectorDerivativeFunc>(p_node);
-
- if (vderFunc) {
- ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
- vderFunc->set_function((VisualShaderNodeVectorDerivativeFunc::Function)(int)p_ops[0]);
+ ERR_FAIL_COND(p_ops[1].get_type() != Variant::INT);
+ derFunc->set_function((VisualShaderNodeDerivativeFunc::Function)(int)p_ops[0]);
+ derFunc->set_op_type((VisualShaderNodeDerivativeFunc::OpType)(int)p_ops[1]);
return;
}
}
@@ -4547,8 +4541,8 @@ VisualShaderEditor::VisualShaderEditor() {
// SCALAR
add_options.push_back(AddOption("FloatFunc", "Scalar", "Common", "VisualShaderNodeFloatFunc", TTR("Float function."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("IntFunc", "Scalar", "Common", "VisualShaderNodeIntFunc", TTR("Integer function."), {}, VisualShaderNode::PORT_TYPE_SCALAR_INT));
add_options.push_back(AddOption("FloatOp", "Scalar", "Common", "VisualShaderNodeFloatOp", TTR("Float operator."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("IntFunc", "Scalar", "Common", "VisualShaderNodeIntFunc", TTR("Integer function."), {}, VisualShaderNode::PORT_TYPE_SCALAR_INT));
add_options.push_back(AddOption("IntOp", "Scalar", "Common", "VisualShaderNodeIntOp", TTR("Integer operator."), {}, VisualShaderNode::PORT_TYPE_SCALAR_INT));
// CONSTANTS
@@ -4574,6 +4568,8 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Cos", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the cosine of the parameter."), { VisualShaderNodeFloatFunc::FUNC_COS }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("CosH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the hyperbolic cosine of the parameter."), { VisualShaderNodeFloatFunc::FUNC_COSH }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Degrees", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Converts a quantity in radians to degrees."), { VisualShaderNodeFloatFunc::FUNC_DEGREES }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("DFdX", "Scalar", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Derivative in 'x' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_X, VisualShaderNodeDerivativeFunc::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("DFdY", "Scalar", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Derivative in 'y' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_Y, VisualShaderNodeDerivativeFunc::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
add_options.push_back(AddOption("Exp", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-e Exponential."), { VisualShaderNodeFloatFunc::FUNC_EXP }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Exp2", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-2 Exponential."), { VisualShaderNodeFloatFunc::FUNC_EXP2 }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Floor", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeFloatFunc::FUNC_FLOOR }, VisualShaderNode::PORT_TYPE_SCALAR));
@@ -4601,6 +4597,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Sqrt", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the square root of the parameter."), { VisualShaderNodeFloatFunc::FUNC_SQRT }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("SmoothStep", "Scalar", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Step", "Scalar", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Sum", "Scalar", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
add_options.push_back(AddOption("Tan", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeFloatFunc::FUNC_TAN }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("TanH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeFloatFunc::FUNC_TANH }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Trunc", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeFloatFunc::FUNC_TRUNC }, VisualShaderNode::PORT_TYPE_SCALAR));
@@ -4706,6 +4703,8 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COSH }, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("Cross", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Calculates the cross product of two vectors."), { VisualShaderNodeVectorOp::OP_CROSS }, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), { VisualShaderNodeVectorFunc::FUNC_DEGREES }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("DFdX", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_X, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("DFdY", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_Y, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
add_options.push_back(AddOption("Distance", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Dot", "Vector", "Functions", "VisualShaderNodeDotProduct", TTR("Calculates the dot product of two vectors."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP }, VisualShaderNode::PORT_TYPE_VECTOR));
@@ -4741,6 +4740,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR }, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Sum", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN }, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TANH }, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC }, VisualShaderNode::PORT_TYPE_VECTOR));
@@ -4757,20 +4757,12 @@ VisualShaderEditor::VisualShaderEditor() {
// SPECIAL
add_options.push_back(AddOption("Comment", "Special", "", "VisualShaderNodeComment", TTR("A rectangular area with a description string for better graph organization.")));
+ add_options.push_back(AddOption("DerivativeFunc", "Special", "", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) Derivative function."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
add_options.push_back(AddOption("Expression", "Special", "", "VisualShaderNodeExpression", TTR("Custom Godot Shader Language expression, with custom amount of input and output ports. This is a direct injection of code into the vertex/fragment/light function, do not use it to write the function declarations inside.")));
add_options.push_back(AddOption("Fresnel", "Special", "", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("GlobalExpression", "Special", "", "VisualShaderNodeGlobalExpression", TTR("Custom Godot Shader Language expression, which is placed on top of the resulted shader. You can place various function definitions inside and call it later in the Expressions. You can also declare varyings, uniforms and constants.")));
add_options.push_back(AddOption("UniformRef", "Special", "", "VisualShaderNodeUniformRef", TTR("A reference to an existing uniform.")));
- add_options.push_back(AddOption("ScalarDerivativeFunc", "Special", "Common", "VisualShaderNodeScalarDerivativeFunc", TTR("(Fragment/Light mode only) Scalar derivative function."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
- add_options.push_back(AddOption("VectorDerivativeFunc", "Special", "Common", "VisualShaderNodeVectorDerivativeFunc", TTR("(Fragment/Light mode only) Vector derivative function."), {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
-
- add_options.push_back(AddOption("DdX", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), { VisualShaderNodeVectorDerivativeFunc::FUNC_X }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
- add_options.push_back(AddOption("DdXS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Derivative in 'x' using local differencing."), { VisualShaderNodeScalarDerivativeFunc::FUNC_X }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
- add_options.push_back(AddOption("DdY", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), { VisualShaderNodeVectorDerivativeFunc::FUNC_Y }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
- add_options.push_back(AddOption("DdYS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Derivative in 'y' using local differencing."), { VisualShaderNodeScalarDerivativeFunc::FUNC_Y }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
- add_options.push_back(AddOption("Sum", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeVectorDerivativeFunc::FUNC_SUM }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
- add_options.push_back(AddOption("SumS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeScalarDerivativeFunc::FUNC_SUM }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
custom_node_option_idx = add_options.size();
/////////////////////////////////////////////////////////////////////
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 2098fa2c85..360ba5780c 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -656,14 +656,18 @@ void ScriptCreateDialog::_update_dialog() {
if (is_new_script_created) {
class_name->set_editable(true);
class_name->set_placeholder(TTR("Allowed: a-z, A-Z, 0-9, _ and ."));
- class_name->set_placeholder_alpha(0.3);
+ Color placeholder_color = class_name->get_theme_color("font_placeholder_color");
+ placeholder_color.a = 0.3;
+ class_name->add_theme_color_override("font_placeholder_color", placeholder_color);
} else {
class_name->set_editable(false);
}
} else {
class_name->set_editable(false);
class_name->set_placeholder(TTR("N/A"));
- class_name->set_placeholder_alpha(1);
+ Color placeholder_color = class_name->get_theme_color("font_placeholder_color");
+ placeholder_color.a = 1;
+ class_name->add_theme_color_override("font_placeholder_color", placeholder_color);
class_name->set_text("");
}
diff --git a/misc/scripts/black_format.sh b/misc/scripts/black_format.sh
index 45d7f9dd2b..99343f1c5a 100755
--- a/misc/scripts/black_format.sh
+++ b/misc/scripts/black_format.sh
@@ -6,7 +6,7 @@ set -uo pipefail
# Apply black.
echo -e "Formatting Python files..."
-PY_FILES=$(git ls-files '*SConstruct' '*SCsub' '*.py' --exclude='.git/*' --exclude='thirdparty/*')
+PY_FILES=$(git ls-files -- '*SConstruct' '*SCsub' '*.py' ':!:.git/*' ':!:thirdparty/*')
black -l 120 $PY_FILES
diff=$(git diff --color)
diff --git a/misc/scripts/clang_format.sh b/misc/scripts/clang_format.sh
index 311944d3b4..13b5ab79b6 100755
--- a/misc/scripts/clang_format.sh
+++ b/misc/scripts/clang_format.sh
@@ -6,17 +6,9 @@
set -uo pipefail
# Loops through all code files tracked by Git.
-git ls-files '*.c' '*.h' '*.cpp' '*.hpp' '*.cc' '*.hh' '*.cxx' '*.m' '*.mm' '*.inc' '*.java' '*.glsl' |
+git ls-files -- '*.c' '*.h' '*.cpp' '*.hpp' '*.cc' '*.hh' '*.cxx' '*.m' '*.mm' '*.inc' '*.java' '*.glsl' \
+ ':!:.git/*' ':!:thirdparty/*' ':!:platform/android/java/lib/src/com/google/*' ':!:*-so_wrap.*' |
while read -r f; do
- # Exclude some files.
- if [[ "$f" == "thirdparty"* ]]; then
- continue
- elif [[ "$f" == "platform/android/java/lib/src/com/google"* ]]; then
- continue
- elif [[ "$f" == *"-so_wrap."* ]]; then
- continue
- fi
-
# Run clang-format.
clang-format --Wno-error=unknown -i "$f"
diff --git a/misc/scripts/clang_tidy.sh b/misc/scripts/clang_tidy.sh
index 85519c0e72..e49f6ac9f4 100755
--- a/misc/scripts/clang_tidy.sh
+++ b/misc/scripts/clang_tidy.sh
@@ -6,17 +6,9 @@
set -uo pipefail
# Loops through all code files tracked by Git.
-git ls-files '*.c' '*.h' '*.cpp' '*.hpp' '*.cc' '*.hh' '*.cxx' '*.m' '*.mm' '*.inc' '*.java' '*.glsl' |
+git ls-files -- '*.c' '*.h' '*.cpp' '*.hpp' '*.cc' '*.hh' '*.cxx' '*.m' '*.mm' '*.inc' '*.java' '*.glsl' \
+ ':!:.git/*' ':!:thirdparty/*' ':!:platform/android/java/lib/src/com/google/*' ':!:*-so_wrap.*' |
while read -r f; do
- # Exclude some files.
- if [[ "$f" == "thirdparty"* ]]; then
- continue
- elif [[ "$f" == "platform/android/java/lib/src/com/google"* ]]; then
- continue
- elif [[ "$f" == *"-so_wrap."* ]]; then
- continue
- fi
-
# Run clang-tidy.
clang-tidy --quiet --fix "$f" &> /dev/null
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 05ea061798..9977b88aa1 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -786,6 +786,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
}
String result;
+ char32_t prev = 0;
+ int prev_pos = 0;
for (;;) {
// Consume actual string.
@@ -852,9 +854,11 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
case '\\':
escaped = '\\';
break;
- case 'u':
+ case 'U':
+ case 'u': {
// Hexadecimal sequence.
- for (int i = 0; i < 4; i++) {
+ int hex_len = (code == 'U') ? 6 : 4;
+ for (int j = 0; j < hex_len; j++) {
if (_is_at_end()) {
return make_error("Unterminated string.");
}
@@ -886,7 +890,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
_advance();
}
- break;
+ } break;
case '\r':
if (_peek() != '\n') {
// Carriage return without newline in string. (???)
@@ -909,11 +913,53 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
valid_escape = false;
break;
}
+ // Parse UTF-16 pair.
+ if (valid_escape) {
+ if ((escaped & 0xfffffc00) == 0xd800) {
+ if (prev == 0) {
+ prev = escaped;
+ prev_pos = column - 2;
+ continue;
+ } else {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = column - 2;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ valid_escape = false;
+ prev = 0;
+ }
+ } else if ((escaped & 0xfffffc00) == 0xdc00) {
+ if (prev == 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired trail surrogate");
+ error.start_column = column - 2;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ valid_escape = false;
+ } else {
+ escaped = (prev << 10UL) + escaped - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev = 0;
+ }
+ }
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
+ }
if (valid_escape) {
result += escaped;
}
} else if (ch == quote_char) {
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
_advance();
if (is_multiline) {
if (_peek() == quote_char && _peek(1) == quote_char) {
@@ -930,6 +976,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
break;
}
} else {
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
result += ch;
_advance();
if (ch == '\n') {
@@ -937,6 +990,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
}
}
}
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
// Make the literal.
Variant string;
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 77fe9cdd19..52d5379e8b 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -140,12 +140,12 @@ void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, cons
}
}
-void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
+void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
if (Object::cast_to<MeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(p_node);
Ref<Mesh> mesh = mesh_instance->get_mesh();
if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * mesh_instance->get_transform(), p_vertices, p_indices);
+ _add_mesh(mesh, p_navmesh_transform * mesh_instance->get_global_transform(), p_vertices, p_indices);
}
}
@@ -159,7 +159,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
n = multimesh->get_instance_count();
}
for (int i = 0; i < n; i++) {
- _add_mesh(mesh, p_accumulated_transform * multimesh->get_instance_transform(i), p_vertices, p_indices);
+ _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices);
}
}
}
@@ -171,7 +171,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
if (!meshes.is_empty()) {
Ref<Mesh> mesh = meshes[1];
if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * csg_shape->get_transform(), p_vertices, p_indices);
+ _add_mesh(mesh, p_navmesh_transform * csg_shape->get_global_transform(), p_vertices, p_indices);
}
}
}
@@ -186,7 +186,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
if (Object::cast_to<CollisionShape3D>(child)) {
CollisionShape3D *col_shape = Object::cast_to<CollisionShape3D>(child);
- Transform3D transform = p_accumulated_transform * static_body->get_transform() * col_shape->get_transform();
+ Transform3D transform = p_navmesh_transform * static_body->get_global_transform() * col_shape->get_transform();
Ref<Mesh> mesh;
Ref<Shape3D> s = col_shape->get_shape();
@@ -270,11 +270,11 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
if (gridmap) {
if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
Array meshes = gridmap->get_meshes();
- Transform3D xform = gridmap->get_transform();
+ Transform3D xform = gridmap->get_global_transform();
for (int i = 0; i < meshes.size(); i += 2) {
Ref<Mesh> mesh = meshes[i + 1];
if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices);
+ _add_mesh(mesh, p_navmesh_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices);
}
}
}
@@ -364,14 +364,9 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
}
#endif
- if (Object::cast_to<Node3D>(p_node)) {
- Node3D *spatial = Object::cast_to<Node3D>(p_node);
- p_accumulated_transform = p_accumulated_transform * spatial->get_transform();
- }
-
if (p_recurse_children) {
for (int i = 0; i < p_node->get_child_count(); i++) {
- _parse_geometry(p_accumulated_transform, p_node->get_child(i), p_vertices, p_indices, p_generate_from, p_collision_mask, p_recurse_children);
+ _parse_geometry(p_navmesh_transform, p_node->get_child(i), p_vertices, p_indices, p_generate_from, p_collision_mask, p_recurse_children);
}
}
}
@@ -616,7 +611,7 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
p_node->get_tree()->get_nodes_in_group(p_nav_mesh->get_source_group_name(), &parse_nodes);
}
- Transform3D navmesh_xform = Object::cast_to<Node3D>(p_node)->get_transform().affine_inverse();
+ Transform3D navmesh_xform = Object::cast_to<Node3D>(p_node)->get_global_transform().affine_inverse();
for (Node *E : parse_nodes) {
NavigationMesh::ParsedGeometryType geometry_type = p_nav_mesh->get_parsed_geometry_type();
uint32_t collision_mask = p_nav_mesh->get_collision_mask();
diff --git a/modules/navigation/navigation_mesh_generator.h b/modules/navigation/navigation_mesh_generator.h
index 1ffdd39aee..21f7a4941b 100644
--- a/modules/navigation/navigation_mesh_generator.h
+++ b/modules/navigation/navigation_mesh_generator.h
@@ -52,7 +52,7 @@ protected:
static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_vertices);
static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
static void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
- static void _parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
+ static void _parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh);
static void _build_recast_navigation_mesh(
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index 2abbd19e12..17a3566ed2 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -328,6 +328,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
};
case '"': {
String str;
+ char32_t prev = 0;
while (true) {
char32_t ch = GET_CHAR();
@@ -364,9 +365,11 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
case 'r':
res = 13;
break;
+ case 'U':
case 'u': {
- // hex number
- for (int j = 0; j < 4; j++) {
+ // Hexadecimal sequence.
+ int hex_len = (next == 'U') ? 6 : 4;
+ for (int j = 0; j < hex_len; j++) {
char32_t c = GET_CHAR();
if (c == 0) {
@@ -403,12 +406,46 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
} break;
}
+ // Parse UTF-16 pair.
+ if ((res & 0xfffffc00) == 0xd800) {
+ if (prev == 0) {
+ prev = res;
+ continue;
+ } else {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ } else if ((res & 0xfffffc00) == 0xdc00) {
+ if (prev == 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired trail surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ } else {
+ res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev = 0;
+ }
+ }
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += res;
-
} else {
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += ch;
}
}
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
r_token.type = TK_CONSTANT;
r_token.value = str;
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index 2f2c2c379d..416a674435 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -117,7 +117,7 @@
</methods>
<signals>
<signal name="data_channel_received">
- <argument index="0" name="channel" type="Object" />
+ <argument index="0" name="channel" type="WebRTCDataChannel" />
<description>
Emitted when a new in-band channel is received, i.e. when the channel was created with [code]negotiated: false[/code] (default).
The object will be an instance of [WebRTCDataChannel]. You must keep a reference of it or it will be closed automatically. See [method create_data_channel].
diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp
index 96cf518c06..7fdf26d3cd 100644
--- a/modules/webrtc/webrtc_peer_connection.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -69,7 +69,7 @@ void WebRTCPeerConnection::_bind_methods() {
ADD_SIGNAL(MethodInfo("session_description_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
ADD_SIGNAL(MethodInfo("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
- ADD_SIGNAL(MethodInfo("data_channel_received", PropertyInfo(Variant::OBJECT, "channel")));
+ ADD_SIGNAL(MethodInfo("data_channel_received", PropertyInfo(Variant::OBJECT, "channel", PROPERTY_HINT_RESOURCE_TYPE, "WebRTCDataChannel")));
BIND_ENUM_CONSTANT(STATE_NEW);
BIND_ENUM_CONSTANT(STATE_CONNECTING);
diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js
index ba61b14eb7..5628ddbab5 100644
--- a/platform/javascript/js/engine/config.js
+++ b/platform/javascript/js/engine/config.js
@@ -225,33 +225,34 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
*/
Config.prototype.update = function (opts) {
const config = opts || {};
- function parse(key, def) {
+ const me = this;
+ function parse(key) {
if (typeof (config[key]) === 'undefined') {
- return def;
+ return me[key];
}
return config[key];
}
// Module config
- this.unloadAfterInit = parse('unloadAfterInit', this.unloadAfterInit);
- this.onPrintError = parse('onPrintError', this.onPrintError);
- this.onPrint = parse('onPrint', this.onPrint);
- this.onProgress = parse('onProgress', this.onProgress);
+ this.unloadAfterInit = parse('unloadAfterInit');
+ this.onPrintError = parse('onPrintError');
+ this.onPrint = parse('onPrint');
+ this.onProgress = parse('onProgress');
// Godot config
- this.canvas = parse('canvas', this.canvas);
- this.executable = parse('executable', this.executable);
- this.mainPack = parse('mainPack', this.mainPack);
- this.locale = parse('locale', this.locale);
- this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy);
- this.persistentPaths = parse('persistentPaths', this.persistentPaths);
- this.persistentDrops = parse('persistentDrops', this.persistentDrops);
- this.experimentalVK = parse('experimentalVK', this.experimentalVK);
- this.focusCanvas = parse('focusCanvas', this.focusCanvas);
- this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
- this.fileSizes = parse('fileSizes', this.fileSizes);
- this.args = parse('args', this.args);
- this.onExecute = parse('onExecute', this.onExecute);
- this.onExit = parse('onExit', this.onExit);
+ this.canvas = parse('canvas');
+ this.executable = parse('executable');
+ this.mainPack = parse('mainPack');
+ this.locale = parse('locale');
+ this.canvasResizePolicy = parse('canvasResizePolicy');
+ this.persistentPaths = parse('persistentPaths');
+ this.persistentDrops = parse('persistentDrops');
+ this.experimentalVK = parse('experimentalVK');
+ this.focusCanvas = parse('focusCanvas');
+ this.gdnativeLibs = parse('gdnativeLibs');
+ this.fileSizes = parse('fileSizes');
+ this.args = parse('args');
+ this.onExecute = parse('onExecute');
+ this.onExit = parse('onExit');
};
/**
diff --git a/platform/javascript/js/libs/library_godot_input.js b/platform/javascript/js/libs/library_godot_input.js
index 7a4d0d8126..1e64c260f8 100644
--- a/platform/javascript/js/libs/library_godot_input.js
+++ b/platform/javascript/js/libs/library_godot_input.js
@@ -87,7 +87,7 @@ const GodotInputGamepads = {
},
init: function (onchange) {
- GodotEventListeners.samples = [];
+ GodotInputGamepads.samples = [];
function add(pad) {
const guid = GodotInputGamepads.get_guid(pad);
const c_id = GodotRuntime.allocString(pad.id);
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index 6a6df664db..c17e120db5 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -143,7 +143,7 @@ class DisplayServerX11 : public DisplayServer {
bool borderless = false;
bool resize_disabled = false;
Vector2i last_position_before_fs;
- bool focused = false;
+ bool focused = true;
bool minimized = false;
unsigned int focus_order = 0;
diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp
index c4926209c7..618d5670d2 100644
--- a/platform/windows/godot_windows.cpp
+++ b/platform/windows/godot_windows.cpp
@@ -39,7 +39,7 @@
#ifndef TOOLS_ENABLED
#if defined _MSC_VER
#pragma section("pck", read)
-__declspec(allocate("pck")) static char dummy[8] = { 0 };
+__declspec(allocate("pck")) static const char dummy[8] = { 0 };
#elif defined __GNUC__
static const char dummy[8] __attribute__((section("pck"), used)) = { 0 };
#endif
@@ -142,7 +142,7 @@ int widechar_main(int argc, wchar_t **argv) {
#ifndef TOOLS_ENABLED
// Workaround to prevent LTCG (MSVC LTO) from removing "pck" section
- char *dummy_guard = dummy;
+ const char *dummy_guard = dummy;
#endif
char **argv_utf8 = new char *[argc];
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 1fdd8b05a6..9521667854 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -263,26 +263,18 @@ void RayCast2D::add_exception_rid(const RID &p_rid) {
exclude.insert(p_rid);
}
-void RayCast2D::add_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- add_exception_rid(co->get_rid());
+void RayCast2D::add_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ add_exception_rid(p_node->get_rid());
}
void RayCast2D::remove_exception_rid(const RID &p_rid) {
exclude.erase(p_rid);
}
-void RayCast2D::remove_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- remove_exception_rid(co->get_rid());
+void RayCast2D::remove_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ remove_exception_rid(p_node->get_rid());
}
void RayCast2D::clear_exceptions() {
diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h
index a1015c6ce0..2c6f2d5c00 100644
--- a/scene/2d/ray_cast_2d.h
+++ b/scene/2d/ray_cast_2d.h
@@ -33,6 +33,8 @@
#include "scene/2d/node_2d.h"
+class CollisionObject2D;
+
class RayCast2D : public Node2D {
GDCLASS(RayCast2D, Node2D);
@@ -94,9 +96,9 @@ public:
Vector2 get_collision_normal() const;
void add_exception_rid(const RID &p_rid);
- void add_exception(const Object *p_object);
+ void add_exception(const CollisionObject2D *p_node);
void remove_exception_rid(const RID &p_rid);
- void remove_exception(const Object *p_object);
+ void remove_exception(const CollisionObject2D *p_node);
void clear_exceptions();
RayCast2D();
diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp
index 10194861b4..24199c96b5 100644
--- a/scene/2d/shape_cast_2d.cpp
+++ b/scene/2d/shape_cast_2d.cpp
@@ -322,26 +322,18 @@ void ShapeCast2D::add_exception_rid(const RID &p_rid) {
exclude.insert(p_rid);
}
-void ShapeCast2D::add_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- add_exception_rid(co->get_rid());
+void ShapeCast2D::add_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ add_exception_rid(p_node->get_rid());
}
void ShapeCast2D::remove_exception_rid(const RID &p_rid) {
exclude.erase(p_rid);
}
-void ShapeCast2D::remove_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- remove_exception_rid(co->get_rid());
+void ShapeCast2D::remove_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ remove_exception_rid(p_node->get_rid());
}
void ShapeCast2D::clear_exceptions() {
diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h
index 7e1ebeb315..ea36b25068 100644
--- a/scene/2d/shape_cast_2d.h
+++ b/scene/2d/shape_cast_2d.h
@@ -34,6 +34,8 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/shape_2d.h"
+class CollisionObject2D;
+
class ShapeCast2D : public Node2D {
GDCLASS(ShapeCast2D, Node2D);
@@ -109,9 +111,9 @@ public:
real_t get_closest_collision_unsafe_fraction() const;
void add_exception_rid(const RID &p_rid);
- void add_exception(const Object *p_object);
+ void add_exception(const CollisionObject2D *p_node);
void remove_exception_rid(const RID &p_rid);
- void remove_exception(const Object *p_object);
+ void remove_exception(const CollisionObject2D *p_node);
void clear_exceptions();
TypedArray<String> get_configuration_warnings() const override;
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index 3bb65d07a0..b71c54dcf9 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -243,26 +243,18 @@ void RayCast3D::add_exception_rid(const RID &p_rid) {
exclude.insert(p_rid);
}
-void RayCast3D::add_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object);
- if (!co) {
- return;
- }
- add_exception_rid(co->get_rid());
+void RayCast3D::add_exception(const CollisionObject3D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D.");
+ add_exception_rid(p_node->get_rid());
}
void RayCast3D::remove_exception_rid(const RID &p_rid) {
exclude.erase(p_rid);
}
-void RayCast3D::remove_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object);
- if (!co) {
- return;
- }
- remove_exception_rid(co->get_rid());
+void RayCast3D::remove_exception(const CollisionObject3D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D.");
+ remove_exception_rid(p_node->get_rid());
}
void RayCast3D::clear_exceptions() {
diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h
index a53e2c83fc..ad85001591 100644
--- a/scene/3d/ray_cast_3d.h
+++ b/scene/3d/ray_cast_3d.h
@@ -33,6 +33,8 @@
#include "scene/3d/node_3d.h"
+class CollisionObject3D;
+
class RayCast3D : public Node3D {
GDCLASS(RayCast3D, Node3D);
@@ -116,9 +118,9 @@ public:
Vector3 get_collision_normal() const;
void add_exception_rid(const RID &p_rid);
- void add_exception(const Object *p_object);
+ void add_exception(const CollisionObject3D *p_node);
void remove_exception_rid(const RID &p_rid);
- void remove_exception(const Object *p_object);
+ void remove_exception(const CollisionObject3D *p_node);
void clear_exceptions();
RayCast3D();
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 5f937acb8d..ec451b07cf 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -444,6 +444,7 @@ void BaseButton::_bind_methods() {
ADD_SIGNAL(MethodInfo("button_up"));
ADD_SIGNAL(MethodInfo("button_down"));
ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "button_pressed")));
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_in_tooltip"), "set_shortcut_in_tooltip", "is_shortcut_in_tooltip_enabled");
@@ -503,7 +504,8 @@ BaseButton *ButtonGroup::get_pressed_button() {
void ButtonGroup::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pressed_button"), &ButtonGroup::get_pressed_button);
ClassDB::bind_method(D_METHOD("get_buttons"), &ButtonGroup::_get_buttons);
- ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button")));
+
+ ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button", PROPERTY_HINT_RESOURCE_TYPE, "BaseButton")));
}
ButtonGroup::ButtonGroup() {
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 552dd28513..3ed1b873af 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -197,6 +197,8 @@ void Button::_notification(int p_what) {
color = get_theme_color(SNAME("font_disabled_color"));
if (has_theme_color(SNAME("icon_disabled_color"))) {
color_icon = get_theme_color(SNAME("icon_disabled_color"));
+ } else {
+ color_icon.a = 0.4;
}
} break;
@@ -232,9 +234,6 @@ void Button::_notification(int p_what) {
}
if (!_icon.is_null()) {
int valign = size.height - style->get_minimum_size().y;
- if (is_disabled()) {
- color_icon.a = 0.4;
- }
float icon_ofs_region = 0.0;
Point2 style_offset;
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 5511a1d910..22def607ed 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -571,6 +571,8 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
// Overridable actions
void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode) {
bool had_selection = has_selection();
+ String selection_text = (had_selection ? get_selected_text() : "");
+
if (had_selection) {
begin_complex_operation();
delete_selection();
@@ -591,27 +593,38 @@ void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode) {
if (auto_brace_completion_enabled) {
int cl = get_caret_line();
int cc = get_caret_column();
- int caret_move_offset = 1;
-
- int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
- if (has_string_delimiter(chr) && cc > 0 && _is_char(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
- insert_text_at_caret(chr);
- } else if (cc < get_line(cl).length() && _is_char(get_line(cl)[cc])) {
- insert_text_at_caret(chr);
- } else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
- caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
- } else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
+ if (had_selection) {
insert_text_at_caret(chr);
+
+ String close_key = get_auto_brace_completion_close_key(chr);
+ if (!close_key.is_empty()) {
+ insert_text_at_caret(selection_text + close_key);
+ set_caret_column(get_caret_column() - 1);
+ }
} else {
- insert_text_at_caret(chr);
+ int caret_move_offset = 1;
+
+ int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
+
+ if (has_string_delimiter(chr) && cc > 0 && _is_char(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
+ insert_text_at_caret(chr);
+ } else if (cc < get_line(cl).length() && _is_char(get_line(cl)[cc])) {
+ insert_text_at_caret(chr);
+ } else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
+ caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
+ } else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
+ insert_text_at_caret(chr);
+ } else {
+ insert_text_at_caret(chr);
- int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
- if (pre_brace_pair != -1) {
- insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
+ int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
+ if (pre_brace_pair != -1) {
+ insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
+ }
}
+ set_caret_column(cc + caret_move_offset);
}
- set_caret_column(cc + caret_move_offset);
} else {
insert_text_at_caret(chr);
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 562ea8a8f7..7ebc5c27f8 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -576,6 +576,11 @@ void Control::_update_canvas_item_transform() {
Transform2D xform = _get_internal_transform();
xform[2] += get_position();
+ // We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot()
+ if (is_inside_tree() && Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
+ xform[2] = xform[2].round();
+ }
+
RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), xform);
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 88953fa7db..3aae3377bc 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -752,7 +752,7 @@ void LineEdit::_notification(int p_what) {
// Draw placeholder color.
if (using_placeholder) {
- font_color.a *= placeholder_alpha;
+ font_color = get_theme_color(SNAME("font_placeholder_color"));
}
bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
@@ -1476,15 +1476,6 @@ String LineEdit::get_placeholder() const {
return placeholder;
}
-void LineEdit::set_placeholder_alpha(float p_alpha) {
- placeholder_alpha = p_alpha;
- update();
-}
-
-float LineEdit::get_placeholder_alpha() const {
- return placeholder_alpha;
-}
-
void LineEdit::set_caret_column(int p_column) {
if (p_column > (int)text.length()) {
p_column = text.length();
@@ -2245,8 +2236,6 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &LineEdit::get_structured_text_bidi_override_options);
ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &LineEdit::set_placeholder);
ClassDB::bind_method(D_METHOD("get_placeholder"), &LineEdit::get_placeholder);
- ClassDB::bind_method(D_METHOD("set_placeholder_alpha", "alpha"), &LineEdit::set_placeholder_alpha);
- ClassDB::bind_method(D_METHOD("get_placeholder_alpha"), &LineEdit::get_placeholder_alpha);
ClassDB::bind_method(D_METHOD("set_caret_column", "position"), &LineEdit::set_caret_column);
ClassDB::bind_method(D_METHOD("get_caret_column"), &LineEdit::get_caret_column);
ClassDB::bind_method(D_METHOD("get_scroll_offset"), &LineEdit::get_scroll_offset);
@@ -2328,6 +2317,7 @@ void LineEdit::_bind_methods() {
BIND_ENUM_CONSTANT(MENU_MAX);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_max_length", "get_max_length");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
@@ -2349,9 +2339,6 @@ void LineEdit::_bind_methods() {
ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
- ADD_GROUP("Placeholder", "placeholder_");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
ADD_GROUP("Caret", "caret_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed");
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 0c313f71c2..1519c09d73 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -82,7 +82,6 @@ private:
String placeholder;
String placeholder_translated;
String secret_character = "*";
- float placeholder_alpha = 0.6;
String ime_text;
Point2 ime_selection;
@@ -262,9 +261,6 @@ public:
void set_placeholder(String p_text);
String get_placeholder() const;
- void set_placeholder_alpha(float p_alpha);
- float get_placeholder_alpha() const;
-
void set_caret_column(int p_column);
int get_caret_column() const;
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 9da030f0a2..5a551ec5a5 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -38,48 +38,71 @@
#include "scene/gui/texture_rect.h"
Size2 TabBar::get_minimum_size() const {
+ Size2 ms;
+
+ if (tabs.is_empty()) {
+ return ms;
+ }
+
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
+ Ref<StyleBox> button_highlight = get_theme_stylebox(SNAME("button_highlight"));
+ Ref<Texture2D> close = get_theme_icon(SNAME("close"));
+ int hseparation = get_theme_constant(SNAME("hseparation"));
int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height);
- Size2 ms(0, 0);
-
for (int i = 0; i < tabs.size(); i++) {
- Ref<Texture2D> tex = tabs[i].icon;
- if (tex.is_valid()) {
- ms.height = MAX(ms.height, tex->get_size().height);
- if (!tabs[i].text.is_empty()) {
- ms.width += get_theme_constant(SNAME("hseparation"));
- }
+ if (tabs[i].hidden) {
+ continue;
}
- ms.width += Math::ceil(tabs[i].text_buf->get_size().x);
- ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin);
+ int ofs = ms.width;
+ Ref<StyleBox> style;
if (tabs[i].disabled) {
- ms.width += tab_disabled->get_minimum_size().width;
+ style = tab_disabled;
} else if (current == i) {
- ms.width += tab_selected->get_minimum_size().width;
+ style = tab_selected;
} else {
- ms.width += tab_unselected->get_minimum_size().width;
+ style = tab_unselected;
+ }
+ ms.width += style->get_minimum_size().width;
+
+ Ref<Texture2D> tex = tabs[i].icon;
+ if (tex.is_valid()) {
+ ms.height = MAX(ms.height, tex->get_size().height);
+ ms.width += tex->get_size().width + hseparation;
}
+ if (!tabs[i].text.is_empty()) {
+ ms.width += tabs[i].size_text + hseparation;
+ }
+ ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin);
+
+ bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current);
+
if (tabs[i].right_button.is_valid()) {
Ref<Texture2D> rb = tabs[i].right_button;
- Size2 bms = rb->get_size();
- bms.width += get_theme_constant(SNAME("hseparation"));
- ms.width += bms.width;
- ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height);
+
+ if (close_visible) {
+ ms.width += button_highlight->get_minimum_size().width + rb->get_width();
+ } else {
+ ms.width += button_highlight->get_margin(SIDE_LEFT) + rb->get_width() + hseparation;
+ }
+
+ ms.height = MAX(rb->get_height() + style->get_minimum_size().height, ms.height);
+ }
+
+ if (close_visible) {
+ ms.width += button_highlight->get_margin(SIDE_LEFT) + close->get_width() + hseparation;
+
+ ms.height = MAX(close->get_height() + style->get_minimum_size().height, ms.height);
}
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- Size2 bms = cb->get_size();
- bms.width += get_theme_constant(SNAME("hseparation"));
- ms.width += bms.width;
- ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height);
+ if (ms.width - ofs > style->get_minimum_size().width) {
+ ms.width -= hseparation;
}
}
@@ -165,8 +188,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (rb_hover != -1) {
- // Right mouse button clicked.
- emit_signal(SNAME("tab_rmb_clicked"), rb_hover);
+ emit_signal(SNAME("tab_button_pressed"), rb_hover);
}
rb_pressing = false;
@@ -175,7 +197,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (cb_hover != -1) {
- // Close button pressed.
emit_signal(SNAME("tab_close_pressed"), cb_hover);
}
@@ -184,7 +205,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
}
if (mb->is_pressed() && (mb->get_button_index() == MouseButton::LEFT || (select_with_rmb && mb->get_button_index() == MouseButton::RIGHT))) {
- // Clicks.
Point2 pos = mb->get_position();
if (buttons_visible) {
@@ -234,6 +254,10 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
int found = -1;
for (int i = offset; i <= max_drawn_tab; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
if (tabs[i].rb_rect.has_point(pos)) {
rb_pressing = true;
update();
@@ -256,6 +280,12 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (found != -1) {
set_current_tab(found);
+
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ // Right mouse button clicked.
+ emit_signal(SNAME("tab_rmb_clicked"), found);
+ }
+
emit_signal(SNAME("tab_clicked"), found);
}
}
@@ -275,13 +305,12 @@ void TabBar::_shape(int p_tab) {
tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction);
}
- tabs.write[p_tab].text_buf->add_string(tabs.write[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, !tabs[p_tab].language.is_empty() ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale());
+ tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, !tabs[p_tab].language.is_empty() ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale());
}
void TabBar::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
- _update_cache();
update();
} break;
case NOTIFICATION_THEME_CHANGED:
@@ -289,14 +318,19 @@ void TabBar::_notification(int p_what) {
for (int i = 0; i < tabs.size(); ++i) {
_shape(i);
}
- _update_cache();
- update_minimum_size();
- update();
- } break;
+
+ [[fallthrough]];
+ }
case NOTIFICATION_RESIZED: {
+ int ofs_old = offset;
+ int max_old = max_drawn_tab;
+
_update_cache();
_ensure_no_over_offset();
- ensure_tab_visible(current);
+
+ if (scroll_to_selected && (offset != ofs_old || max_drawn_tab != max_old)) {
+ ensure_tab_visible(current);
+ }
} break;
case NOTIFICATION_DRAW: {
if (tabs.is_empty()) {
@@ -322,6 +356,10 @@ void TabBar::_notification(int p_what) {
// Draw unselected tabs in the back.
for (int i = offset; i <= max_drawn_tab; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
if (i != current) {
Ref<StyleBox> sb;
Color col;
@@ -344,14 +382,14 @@ void TabBar::_notification(int p_what) {
}
// Draw selected tab in the front, but only if it's visible.
- if (current >= offset && current <= max_drawn_tab) {
+ if (current >= offset && current <= max_drawn_tab && !tabs[current].hidden) {
Ref<StyleBox> sb = tabs[current].disabled ? tab_disabled : tab_selected;
float x = rtl ? size.width - tabs[current].ofs_cache - tabs[current].size_cache : tabs[current].ofs_cache;
_draw_tab(sb, font_selected_color, current, x);
}
- if (offset > 0 || missing_right) {
+ if (buttons_visible) {
int vofs = (get_size().height - incr->get_size().height) / 2;
if (rtl) {
@@ -386,47 +424,52 @@ void TabBar::_notification(int p_what) {
void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) {
RID ci = get_canvas_item();
+ bool rtl = is_layout_rtl();
Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
int outline_size = get_theme_constant(SNAME("outline_size"));
+ int hseparation = get_theme_constant(SNAME("hseparation"));
- Tab tab = tabs[p_index];
-
- Rect2 sb_rect = Rect2(p_x, 0, tab.size_cache, get_size().height);
+ Rect2 sb_rect = Rect2(p_x, 0, tabs[p_index].size_cache, get_size().height);
p_tab_style->draw(ci, sb_rect);
- p_x += p_tab_style->get_margin(SIDE_LEFT);
+ p_x += rtl ? tabs[p_index].size_cache - p_tab_style->get_margin(SIDE_LEFT) : p_tab_style->get_margin(SIDE_LEFT);
Size2i sb_ms = p_tab_style->get_minimum_size();
- Ref<Texture2D> icon = tab.icon;
+ // Draw the icon.
+ Ref<Texture2D> icon = tabs[p_index].icon;
if (icon.is_valid()) {
- icon->draw(ci, Point2i(p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
+ icon->draw(ci, Point2i(rtl ? p_x - icon->get_width() : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
- if (!tab.text.is_empty()) {
- p_x += icon->get_width() + get_theme_constant(SNAME("hseparation"));
- }
+ p_x = rtl ? p_x - icon->get_width() - hseparation : p_x + icon->get_width() + hseparation;
}
- Vector2 text_pos = Point2i(p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tab.text_buf->get_size().y) / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- tab.text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
- }
- tab.text_buf->draw(ci, text_pos, p_font_color);
+ // Draw the text.
+ if (!tabs[p_index].text.is_empty()) {
+ Point2i text_pos = Point2i(rtl ? p_x - tabs[p_index].size_text : p_x,
+ p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[p_index].text_buf->get_size().y) / 2);
- p_x += tab.size_text;
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ tabs[p_index].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ tabs[p_index].text_buf->draw(ci, text_pos, p_font_color);
- if (tab.right_button.is_valid()) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
- Ref<Texture2D> rb = tab.right_button;
+ p_x = rtl ? p_x - tabs[p_index].size_text - hseparation : p_x + tabs[p_index].size_text + hseparation;
+ }
- p_x += get_theme_constant(SNAME("hseparation"));
+ // Draw and calculate rect of the right button.
+ if (tabs[p_index].right_button.is_valid()) {
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight"));
+ Ref<Texture2D> rb = tabs[p_index].right_button;
Rect2 rb_rect;
rb_rect.size = style->get_minimum_size() + rb->get_size();
- rb_rect.position.x = p_x;
+ rb_rect.position.x = rtl ? p_x - rb_rect.size.width : p_x;
rb_rect.position.y = p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2;
+ tabs.write[p_index].rb_rect = rb_rect;
+
if (rb_hover == p_index) {
if (rb_pressing) {
get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect);
@@ -435,41 +478,61 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in
}
}
- rb->draw(ci, Point2i(p_x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
- p_x += rb->get_width();
- tabs.write[p_index].rb_rect = rb_rect;
+ rb->draw(ci, Point2i(rb_rect.position.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
+
+ p_x = rtl ? rb_rect.position.x : rb_rect.position.x + rb_rect.size.width;
}
+ // Draw and calculate rect of the close button.
if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_index == current)) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight"));
Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- p_x += get_theme_constant(SNAME("hseparation"));
-
Rect2 cb_rect;
cb_rect.size = style->get_minimum_size() + cb->get_size();
- cb_rect.position.x = p_x;
+ cb_rect.position.x = rtl ? p_x - cb_rect.size.width : p_x;
cb_rect.position.y = p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2;
- if (!tab.disabled && cb_hover == p_index) {
+ tabs.write[p_index].cb_rect = cb_rect;
+
+ if (!tabs[p_index].disabled && cb_hover == p_index) {
if (cb_pressing) {
- get_theme_stylebox(SNAME("close_bg_pressed"))->draw(ci, cb_rect);
+ get_theme_stylebox(SNAME("button_pressed"))->draw(ci, cb_rect);
} else {
style->draw(ci, cb_rect);
}
}
- cb->draw(ci, Point2i(p_x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
- p_x += cb->get_width();
- tabs.write[p_index].cb_rect = cb_rect;
+ cb->draw(ci, Point2i(cb_rect.position.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
}
}
void TabBar::set_tab_count(int p_count) {
+ if (p_count == tabs.size()) {
+ return;
+ }
+
ERR_FAIL_COND(p_count < 0);
tabs.resize(p_count);
+
+ if (p_count == 0) {
+ offset = 0;
+ max_drawn_tab = 0;
+ current = 0;
+ previous = 0;
+ } else {
+ offset = MIN(offset, p_count - 1);
+ max_drawn_tab = MIN(max_drawn_tab, p_count - 1);
+ current = MIN(current, p_count - 1);
+ }
+
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
notify_property_list_changed();
}
@@ -478,15 +541,26 @@ int TabBar::get_tab_count() const {
}
void TabBar::set_current_tab(int p_current) {
- if (current == p_current) {
- return;
- }
ERR_FAIL_INDEX(p_current, get_tab_count());
previous = current;
current = p_current;
+ if (current == previous) {
+ emit_signal(SNAME("tab_selected"), current);
+ return;
+ }
+ // Triggered by dragging a tab from another TabBar to the selected index, to ensure that tab_changed is emitted.
+ if (previous == -1) {
+ previous = current;
+ }
+
+ emit_signal(SNAME("tab_selected"), current);
+
_update_cache();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
emit_signal(SNAME("tab_changed"), p_current);
@@ -515,8 +589,13 @@ bool TabBar::get_offset_buttons_visible() const {
void TabBar::set_tab_title(int p_tab, const String &p_title) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].text = p_title;
+
_shape(p_tab);
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
@@ -529,6 +608,7 @@ String TabBar::get_tab_title(int p_tab) const {
void TabBar::set_tab_text_direction(int p_tab, Control::TextDirection p_text_direction) {
ERR_FAIL_INDEX(p_tab, tabs.size());
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
+
if (tabs[p_tab].text_direction != p_text_direction) {
tabs.write[p_tab].text_direction = p_text_direction;
_shape(p_tab);
@@ -544,24 +624,38 @@ Control::TextDirection TabBar::get_tab_text_direction(int p_tab) const {
void TabBar::clear_tab_opentype_features(int p_tab) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].opentype_features.clear();
+
_shape(p_tab);
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
void TabBar::set_tab_opentype_feature(int p_tab, const String &p_name, int p_value) {
ERR_FAIL_INDEX(p_tab, tabs.size());
+
int32_t tag = TS->name_to_tag(p_name);
if (!tabs[p_tab].opentype_features.has(tag) || (int)tabs[p_tab].opentype_features[tag] != p_value) {
tabs.write[p_tab].opentype_features[tag] = p_value;
+
_shape(p_tab);
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
}
int TabBar::get_tab_opentype_feature(int p_tab, const String &p_name) const {
ERR_FAIL_INDEX_V(p_tab, tabs.size(), -1);
+
int32_t tag = TS->name_to_tag(p_name);
if (!tabs[p_tab].opentype_features.has(tag)) {
return -1;
@@ -571,10 +665,17 @@ int TabBar::get_tab_opentype_feature(int p_tab, const String &p_name) const {
void TabBar::set_tab_language(int p_tab, const String &p_language) {
ERR_FAIL_INDEX(p_tab, tabs.size());
+
if (tabs[p_tab].language != p_language) {
tabs.write[p_tab].language = p_language;
_shape(p_tab);
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
}
@@ -586,7 +687,12 @@ String TabBar::get_tab_language(int p_tab) const {
void TabBar::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].icon = p_icon;
+
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
@@ -599,7 +705,14 @@ Ref<Texture2D> TabBar::get_tab_icon(int p_tab) const {
void TabBar::set_tab_disabled(int p_tab, bool p_disabled) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].disabled = p_disabled;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
bool TabBar::is_tab_disabled(int p_tab) const {
@@ -607,15 +720,38 @@ bool TabBar::is_tab_disabled(int p_tab) const {
return tabs[p_tab].disabled;
}
-void TabBar::set_tab_right_button(int p_tab, const Ref<Texture2D> &p_right_button) {
+void TabBar::set_tab_hidden(int p_tab, bool p_hidden) {
+ ERR_FAIL_INDEX(p_tab, tabs.size());
+ tabs.write[p_tab].hidden = p_hidden;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
+ update();
+ update_minimum_size();
+}
+
+bool TabBar::is_tab_hidden(int p_tab) const {
+ ERR_FAIL_INDEX_V(p_tab, tabs.size(), false);
+ return tabs[p_tab].hidden;
+}
+
+void TabBar::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) {
ERR_FAIL_INDEX(p_tab, tabs.size());
- tabs.write[p_tab].right_button = p_right_button;
+ tabs.write[p_tab].right_button = p_icon;
+
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
-Ref<Texture2D> TabBar::get_tab_right_button(int p_tab) const {
+Ref<Texture2D> TabBar::get_tab_button_icon(int p_tab) const {
ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture2D>());
return tabs[p_tab].right_button;
}
@@ -626,34 +762,53 @@ void TabBar::_update_hover() {
}
const Point2 &pos = get_local_mouse_position();
- // test hovering to display right or close button.
+ // Test hovering to display right or close button.
int hover_now = -1;
int hover_buttons = -1;
- for (int i = offset; i < tabs.size(); i++) {
+ for (int i = offset; i <= max_drawn_tab; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
Rect2 rect = get_tab_rect(i);
if (rect.has_point(pos)) {
hover_now = i;
}
+
if (tabs[i].rb_rect.has_point(pos)) {
rb_hover = i;
cb_hover = -1;
hover_buttons = i;
- break;
} else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) {
cb_hover = i;
rb_hover = -1;
hover_buttons = i;
+ }
+
+ if (hover_buttons != -1) {
+ update();
break;
}
}
+
if (hover != hover_now) {
hover = hover_now;
- emit_signal(SNAME("tab_hovered"), hover);
+
+ if (hover != -1) {
+ emit_signal(SNAME("tab_hovered"), hover);
+ }
}
if (hover_buttons == -1) { // No hover.
+ int rb_hover_old = rb_hover;
+ int cb_hover_old = cb_hover;
+
rb_hover = hover_buttons;
cb_hover = hover_buttons;
+
+ if (rb_hover != rb_hover_old || cb_hover != cb_hover_old) {
+ update();
+ }
}
}
@@ -677,16 +832,20 @@ void TabBar::_update_cache() {
int count_resize = 0;
for (int i = 0; i < tabs.size(); i++) {
- tabs.write[i].ofs_cache = 0;
- tabs.write[i].size_cache = get_tab_width(i);
tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x);
tabs.write[i].text_buf->set_width(-1);
- mw += tabs[i].size_cache;
- if (tabs[i].size_cache <= min_width || i == current) {
- size_fixed += tabs[i].size_cache;
- } else {
- count_resize++;
+ tabs.write[i].ofs_cache = 0;
+ tabs.write[i].size_cache = get_tab_width(i);
+
+ if (!tabs[i].hidden) {
+ mw += tabs[i].size_cache;
+
+ if (tabs[i].size_cache <= min_width || i == current) {
+ size_fixed += tabs[i].size_cache;
+ } else {
+ count_resize++;
+ }
}
}
@@ -696,34 +855,20 @@ void TabBar::_update_cache() {
}
for (int i = offset; i < tabs.size(); i++) {
- Ref<StyleBox> sb;
- if (tabs[i].disabled) {
- sb = tab_disabled;
- } else if (i == current) {
- sb = tab_selected;
- } else {
- sb = tab_unselected;
+ if (tabs[i].hidden) {
+ tabs.write[i].ofs_cache = w;
+ max_drawn_tab = i;
+
+ continue;
}
int lsize = tabs[i].size_cache;
int slen = tabs[i].size_text;
- if (min_width > 0 && mw > limit_minus_buttons && i != current) {
- if (lsize > m_width) {
- slen = m_width - (sb->get_margin(SIDE_LEFT) + sb->get_margin(SIDE_RIGHT));
- if (tabs[i].icon.is_valid()) {
- slen -= tabs[i].icon->get_width();
- slen -= get_theme_constant(SNAME("hseparation"));
- }
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- slen -= cb->get_width();
- slen -= get_theme_constant(SNAME("hseparation"));
- }
-
- slen = MAX(slen, 1);
- lsize = m_width;
- }
+ // FIXME: This is completely broken.
+ if (min_width > 0 && (mw > limit || (offset > 0 && mw > limit_minus_buttons)) && i != current && lsize > m_width) {
+ slen = MAX(m_width - tabs[i].size_cache + tabs[i].size_text, 1);
+ lsize = m_width;
}
tabs.write[i].ofs_cache = w;
@@ -735,13 +880,22 @@ void TabBar::_update_cache() {
max_drawn_tab = i;
// Check if all tabs would fit inside the area.
- if (i > offset && (w > limit || (offset > 0 && w > limit_minus_buttons))) {
- w -= get_tab_width(i);
- max_drawn_tab -= 1;
+ if (clip_tabs && i > offset && (w > limit || (offset > 0 && w > limit_minus_buttons))) {
+ tabs.write[i].ofs_cache = 0;
+ tabs.write[i].text_buf->set_width(-1);
+
+ w -= tabs[i].size_cache;
+ max_drawn_tab--;
while (w > limit_minus_buttons && max_drawn_tab > offset) {
- w -= get_tab_width(max_drawn_tab);
- max_drawn_tab -= 1;
+ tabs.write[max_drawn_tab].ofs_cache = 0;
+
+ if (!tabs[max_drawn_tab].hidden) {
+ tabs.write[max_drawn_tab].text_buf->set_width(-1);
+ w -= tabs[max_drawn_tab].size_cache;
+ }
+
+ max_drawn_tab--;
}
break;
@@ -752,21 +906,25 @@ void TabBar::_update_cache() {
buttons_visible = offset > 0 || missing_right;
if (tab_alignment == ALIGNMENT_LEFT) {
+ _update_hover();
return;
- } else if (tab_alignment == ALIGNMENT_CENTER) {
+ }
+
+ if (tab_alignment == ALIGNMENT_CENTER) {
w = ((buttons_visible ? limit_minus_buttons : limit) - w) / 2;
} else if (tab_alignment == ALIGNMENT_RIGHT) {
w = (buttons_visible ? limit_minus_buttons : limit) - w;
}
- if (w < 0) {
- w = 0;
- }
-
for (int i = offset; i <= max_drawn_tab; i++) {
tabs.write[i].ofs_cache = w;
- w += tabs.write[i].size_cache;
+
+ if (!tabs[i].hidden) {
+ w += tabs[i].size_cache;
+ }
}
+
+ _update_hover();
}
void TabBar::_on_mouse_exited() {
@@ -784,20 +942,26 @@ void TabBar::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) {
t.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
t.text_buf->add_string(t.xl_text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
t.icon = p_icon;
-
tabs.push_back(t);
+
_update_cache();
- call_deferred(SNAME("_update_hover"));
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
void TabBar::clear_tabs() {
tabs.clear();
+ offset = 0;
+ max_drawn_tab = 0;
current = 0;
previous = 0;
- call_deferred(SNAME("_update_hover"));
+
+ _update_cache();
update();
+ update_minimum_size();
notify_property_list_changed();
}
@@ -807,10 +971,6 @@ void TabBar::remove_tab(int p_idx) {
if (current >= p_idx) {
current--;
}
- _update_cache();
- call_deferred(SNAME("_update_hover"));
- update();
- update_minimum_size();
if (current < 0) {
current = 0;
@@ -820,7 +980,13 @@ void TabBar::remove_tab(int p_idx) {
current = tabs.size() - 1;
}
+ _update_cache();
_ensure_no_over_offset();
+ if (scroll_to_selected && !tabs.is_empty()) {
+ ensure_tab_visible(current);
+ }
+ update();
+ update_minimum_size();
notify_property_list_changed();
}
@@ -840,15 +1006,13 @@ Variant TabBar::get_drag_data(const Point2 &p_point) {
if (!tabs[tab_over].icon.is_null()) {
TextureRect *tf = memnew(TextureRect);
tf->set_texture(tabs[tab_over].icon);
+ tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
drag_preview->add_child(tf);
}
+
Label *label = memnew(Label(tabs[tab_over].xl_text));
drag_preview->add_child(label);
- if (!tabs[tab_over].right_button.is_null()) {
- TextureRect *tf = memnew(TextureRect);
- tf->set_texture(tabs[tab_over].right_button);
- drag_preview->add_child(tf);
- }
+
set_drag_preview(drag_preview);
Dictionary drag_data;
@@ -901,31 +1065,40 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
int tab_from_id = d["tab_element"];
NodePath from_path = d["from_path"];
NodePath to_path = get_path();
+
if (from_path == to_path) {
if (hover_now < 0) {
hover_now = get_tab_count() - 1;
}
+
move_tab(tab_from_id, hover_now);
emit_signal(SNAME("active_tab_rearranged"), hover_now);
set_current_tab(hover_now);
} else if (get_tabs_rearrange_group() != -1) {
// Drag and drop between Tabs.
+
Node *from_node = get_node(from_path);
TabBar *from_tabs = Object::cast_to<TabBar>(from_node);
+
if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
if (tab_from_id >= from_tabs->get_tab_count()) {
return;
}
+
Tab moving_tab = from_tabs->tabs[tab_from_id];
if (hover_now < 0) {
hover_now = get_tab_count();
}
+
+ // Workaround to ensure that tab_changed is emitted.
+ if (current == hover_now) {
+ current = -1;
+ }
+
tabs.insert(hover_now, moving_tab);
from_tabs->remove_tab(tab_from_id);
set_current_tab(hover_now);
- emit_signal(SNAME("tab_changed"), hover_now);
- _update_cache();
- update();
+ update_minimum_size();
}
}
}
@@ -946,6 +1119,7 @@ int TabBar::get_tab_idx_at_point(const Point2 &p_point) const {
void TabBar::set_tab_alignment(AlignmentMode p_alignment) {
ERR_FAIL_INDEX(p_alignment, ALIGNMENT_MAX);
tab_alignment = p_alignment;
+
_update_cache();
update();
}
@@ -959,7 +1133,16 @@ void TabBar::set_clip_tabs(bool p_clip_tabs) {
return;
}
clip_tabs = p_clip_tabs;
+
+ if (!clip_tabs) {
+ offset = 0;
+ max_drawn_tab = 0;
+ }
+
_update_cache();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
@@ -981,6 +1164,10 @@ void TabBar::move_tab(int from, int to) {
tabs.insert(to, tab_from);
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
notify_property_list_changed();
}
@@ -991,37 +1178,49 @@ int TabBar::get_tab_width(int p_idx) const {
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
+ int hseparation = get_theme_constant(SNAME("hseparation"));
- int x = 0;
+ Ref<StyleBox> style;
+
+ if (tabs[p_idx].disabled) {
+ style = tab_disabled;
+ } else if (current == p_idx) {
+ style = tab_selected;
+ } else {
+ style = tab_unselected;
+ }
+ int x = style->get_minimum_size().width;
Ref<Texture2D> tex = tabs[p_idx].icon;
if (tex.is_valid()) {
- x += tex->get_width();
- if (!tabs[p_idx].text.is_empty()) {
- x += get_theme_constant(SNAME("hseparation"));
- }
+ x += tex->get_width() + hseparation;
}
- x += Math::ceil(tabs[p_idx].text_buf->get_size().x);
-
- if (tabs[p_idx].disabled) {
- x += tab_disabled->get_minimum_size().width;
- } else if (current == p_idx) {
- x += tab_selected->get_minimum_size().width;
- } else {
- x += tab_unselected->get_minimum_size().width;
+ if (!tabs[p_idx].text.is_empty()) {
+ x += tabs[p_idx].size_text + hseparation;
}
+ bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current);
+
if (tabs[p_idx].right_button.is_valid()) {
+ Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight"));
Ref<Texture2D> rb = tabs[p_idx].right_button;
- x += rb->get_width();
- x += get_theme_constant(SNAME("hseparation"));
+
+ if (close_visible) {
+ x += btn_style->get_minimum_size().width + rb->get_width();
+ } else {
+ x += btn_style->get_margin(SIDE_LEFT) + rb->get_width() + hseparation;
+ }
}
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current)) {
+ if (close_visible) {
+ Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight"));
Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- x += cb->get_width();
- x += get_theme_constant(SNAME("hseparation"));
+ x += btn_style->get_margin(SIDE_LEFT) + cb->get_width() + hseparation;
+ }
+
+ if (x > style->get_minimum_size().width) {
+ x -= hseparation;
}
return x;
@@ -1039,8 +1238,12 @@ void TabBar::_ensure_no_over_offset() {
int prev_offset = offset;
int total_w = tabs[max_drawn_tab].ofs_cache + tabs[max_drawn_tab].size_cache - tabs[offset].ofs_cache;
- while (offset > 0) {
- total_w += tabs[offset - 1].size_cache;
+ for (int i = offset; i > 0; i--) {
+ if (tabs[i - 1].hidden) {
+ continue;
+ }
+
+ total_w += tabs[i - 1].size_cache;
if (total_w < limit_minus_buttons) {
offset--;
@@ -1061,7 +1264,7 @@ void TabBar::ensure_tab_visible(int p_idx) {
}
ERR_FAIL_INDEX(p_idx, tabs.size());
- if (p_idx >= offset && p_idx <= max_drawn_tab) {
+ if (tabs[p_idx].hidden || (p_idx >= offset && p_idx <= max_drawn_tab)) {
return;
}
@@ -1079,12 +1282,20 @@ void TabBar::ensure_tab_visible(int p_idx) {
int total_w = tabs[max_drawn_tab].ofs_cache - tabs[offset].ofs_cache;
for (int i = max_drawn_tab; i <= p_idx; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
total_w += tabs[i].size_cache;
}
int prev_offset = offset;
for (int i = offset; i < p_idx; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
if (total_w > limit_minus_buttons) {
total_w -= tabs[i].size_cache;
offset++;
@@ -1111,8 +1322,14 @@ Rect2 TabBar::get_tab_rect(int p_tab) const {
void TabBar::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) {
ERR_FAIL_INDEX(p_policy, CLOSE_BUTTON_MAX);
cb_displaypolicy = p_policy;
+
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
TabBar::CloseButtonDisplayPolicy TabBar::get_tab_close_display_policy() const {
@@ -1147,6 +1364,17 @@ int TabBar::get_tabs_rearrange_group() const {
return tabs_rearrange_group;
}
+void TabBar::set_scroll_to_selected(bool p_enabled) {
+ scroll_to_selected = p_enabled;
+ if (p_enabled) {
+ ensure_tab_visible(current);
+ }
+}
+
+bool TabBar::get_scroll_to_selected() const {
+ return scroll_to_selected;
+}
+
void TabBar::set_select_with_rmb(bool p_enabled) {
select_with_rmb = p_enabled;
}
@@ -1225,8 +1453,12 @@ void TabBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &TabBar::get_tab_language);
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabBar::set_tab_icon);
ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabBar::get_tab_icon);
+ ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabBar::set_tab_button_icon);
+ ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabBar::get_tab_button_icon);
ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabBar::set_tab_disabled);
ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabBar::is_tab_disabled);
+ ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabBar::set_tab_hidden);
+ ClassDB::bind_method(D_METHOD("is_tab_hidden", "tab_idx"), &TabBar::is_tab_hidden);
ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &TabBar::remove_tab);
ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &TabBar::add_tab, DEFVAL(""), DEFVAL(Ref<Texture2D>()));
ClassDB::bind_method(D_METHOD("set_tab_alignment", "alignment"), &TabBar::set_tab_alignment);
@@ -1246,16 +1478,19 @@ void TabBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabBar::get_drag_to_rearrange_enabled);
ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabBar::set_tabs_rearrange_group);
ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabBar::get_tabs_rearrange_group);
-
+ ClassDB::bind_method(D_METHOD("set_scroll_to_selected", "enabled"), &TabBar::set_scroll_to_selected);
+ ClassDB::bind_method(D_METHOD("get_scroll_to_selected"), &TabBar::get_scroll_to_selected);
ClassDB::bind_method(D_METHOD("set_select_with_rmb", "enabled"), &TabBar::set_select_with_rmb);
ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &TabBar::get_select_with_rmb);
+ ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_rmb_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_close_pressed", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_button_pressed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to")));
- ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment");
@@ -1263,6 +1498,8 @@ void TabBar::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_to_selected"), "set_scroll_to_selected", "get_scroll_to_selected");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_with_rmb"), "set_select_with_rmb", "get_select_with_rmb");
ADD_ARRAY_COUNT("Tabs", "tab_count", "set_tab_count", "get_tab_count", "tab_");
@@ -1278,5 +1515,6 @@ void TabBar::_bind_methods() {
}
TabBar::TabBar() {
+ set_size(Size2(get_size().width, get_minimum_size().height));
connect("mouse_exited", callable_mp(this, &TabBar::_on_mouse_exited));
}
diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h
index d0055ae4d2..b428538570 100644
--- a/scene/gui/tab_bar.h
+++ b/scene/gui/tab_bar.h
@@ -63,12 +63,11 @@ private:
Ref<TextLine> text_buf;
Ref<Texture2D> icon;
- int ofs_cache = 0;
bool disabled = false;
+ bool hidden = false;
+ int ofs_cache = 0;
int size_cache = 0;
int size_text = 0;
- int x_cache = 0;
- int x_size_cache = 0;
Ref<Texture2D> right_button;
Rect2 rb_rect;
@@ -102,6 +101,7 @@ private:
int min_width = 0;
bool scrolling_enabled = true;
bool drag_to_rearrange_enabled = false;
+ bool scroll_to_selected = true;
int tabs_rearrange_group = -1;
int get_tab_width(int p_idx) const;
@@ -150,8 +150,11 @@ public:
void set_tab_disabled(int p_tab, bool p_disabled);
bool is_tab_disabled(int p_tab) const;
- void set_tab_right_button(int p_tab, const Ref<Texture2D> &p_right_button);
- Ref<Texture2D> get_tab_right_button(int p_tab) const;
+ void set_tab_hidden(int p_tab, bool p_hidden);
+ bool is_tab_hidden(int p_tab) const;
+
+ void set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon);
+ Ref<Texture2D> get_tab_button_icon(int p_tab) const;
void set_tab_alignment(AlignmentMode p_alignment);
AlignmentMode get_tab_alignment() const;
@@ -187,6 +190,9 @@ public:
void set_tabs_rearrange_group(int p_group_id);
int get_tabs_rearrange_group() const;
+ void set_scroll_to_selected(bool p_enabled);
+ bool get_scroll_to_selected() const;
+
void set_select_with_rmb(bool p_enabled);
bool get_select_with_rmb() const;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 7db1fae2b6..e060d3b901 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -874,7 +874,7 @@ void TextEdit::_notification(int p_what) {
// Ensure we at least use the font color.
Color current_color = !editable ? font_readonly_color : font_color;
if (draw_placeholder) {
- current_color.a *= placeholder_alpha;
+ current_color = font_placeholder_color;
}
const Ref<TextParagraph> ldata = draw_placeholder ? placeholder_data_buf : text.get_line_data(line);
@@ -2515,6 +2515,7 @@ void TextEdit::_update_caches() {
font_size = get_theme_font_size(SNAME("font_size"));
font_color = get_theme_color(SNAME("font_color"));
font_readonly_color = get_theme_color(SNAME("font_readonly_color"));
+ font_placeholder_color = get_theme_color(SNAME("font_placeholder_color"));
outline_size = get_theme_constant(SNAME("outline_size"));
outline_color = get_theme_color(SNAME("font_outline_color"));
@@ -2947,15 +2948,6 @@ String TextEdit::get_placeholder() const {
return placeholder_text;
}
-void TextEdit::set_placeholder_alpha(float p_alpha) {
- placeholder_alpha = p_alpha;
- update();
-}
-
-float TextEdit::get_placeholder_alpha() const {
- return placeholder_alpha;
-}
-
void TextEdit::set_line(int p_line, const String &p_new_text) {
if (p_line < 0 || p_line >= text.size()) {
return;
@@ -4944,9 +4936,6 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &TextEdit::set_placeholder);
ClassDB::bind_method(D_METHOD("get_placeholder"), &TextEdit::get_placeholder);
- ClassDB::bind_method(D_METHOD("set_placeholder_alpha", "alpha"), &TextEdit::set_placeholder_alpha);
- ClassDB::bind_method(D_METHOD("get_placeholder_alpha"), &TextEdit::get_placeholder_alpha);
-
ClassDB::bind_method(D_METHOD("set_line", "line", "new_text"), &TextEdit::set_line);
ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line);
@@ -5256,7 +5245,6 @@ void TextEdit::_bind_methods() {
/* Inspector */
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text", PROPERTY_HINT_MULTILINE_TEXT), "set_placeholder", "get_placeholder");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index fdaa928598..079890249e 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -251,8 +251,6 @@ private:
Point2 ime_selection;
// Placeholder
- float placeholder_alpha = 0.6;
-
String placeholder_text = "";
Array placeholder_bidi_override;
Ref<TextParagraph> placeholder_data_buf;
@@ -525,6 +523,7 @@ private:
int font_size = 16;
Color font_color = Color(1, 1, 1);
Color font_readonly_color = Color(1, 1, 1);
+ Color font_placeholder_color = Color(1, 1, 1, 0.6);
int outline_size = 0;
Color outline_color = Color(1, 1, 1);
@@ -684,9 +683,6 @@ public:
void set_placeholder(const String &p_text);
String get_placeholder() const;
- void set_placeholder_alpha(float p_alpha);
- float get_placeholder_alpha() const;
-
void set_line(int p_line, const String &p_new_text);
String get_line(int p_line) const;
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index da202c1c8f..89a17ae854 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -173,7 +173,8 @@ void TextureButton::_notification(int p_what) {
bool draw_focus = (has_focus() && focused.is_valid());
// If no other texture is valid, try using focused texture.
- if (!texdraw.is_valid() && draw_focus) {
+ bool draw_focus_only = draw_focus && !texdraw.is_valid();
+ if (draw_focus_only) {
texdraw = focused;
}
@@ -232,7 +233,7 @@ void TextureButton::_notification(int p_what) {
size.width *= hflip ? -1.0f : 1.0f;
size.height *= vflip ? -1.0f : 1.0f;
- if (texdraw == focused) {
+ if (draw_focus_only) {
// Do nothing, we only needed to calculate the rectangle.
} else if (_tile) {
draw_texture_rect(texdraw, Rect2(ofs, size), _tile);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index bc8e81b1ae..1b32884880 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1319,10 +1319,10 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children);
ClassDB::bind_method(D_METHOD("get_index"), &TreeItem::get_index);
- ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::_move_before);
- ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::_move_after);
+ ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::move_before);
+ ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::move_after);
- ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child);
+ ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::remove_child);
{
MethodInfo mi;
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index fd65f90c49..c24763a0e4 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -188,16 +188,6 @@ protected:
return d;
}
- void _remove_child(Object *p_child) {
- remove_child(Object::cast_to<TreeItem>(p_child));
- }
-
- void _move_before(Object *p_item) {
- move_before(Object::cast_to<TreeItem>(p_item));
- }
- void _move_after(Object *p_item) {
- move_after(Object::cast_to<TreeItem>(p_item));
- }
Variant _call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 69b06c1803..f7036578e2 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -577,8 +577,7 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeDotProduct);
GDREGISTER_CLASS(VisualShaderNodeVectorLen);
GDREGISTER_CLASS(VisualShaderNodeDeterminant);
- GDREGISTER_CLASS(VisualShaderNodeScalarDerivativeFunc);
- GDREGISTER_CLASS(VisualShaderNodeVectorDerivativeFunc);
+ GDREGISTER_CLASS(VisualShaderNodeDerivativeFunc);
GDREGISTER_CLASS(VisualShaderNodeClamp);
GDREGISTER_CLASS(VisualShaderNodeFaceForward);
GDREGISTER_CLASS(VisualShaderNodeOuterProduct);
@@ -1024,6 +1023,8 @@ void register_scene_types() {
ClassDB::add_compatibility_class("VisualShaderNodeVectorScalarStep", "VisualShaderNodeStep");
ClassDB::add_compatibility_class("VisualShaderNodeScalarSwitch", "VisualShaderNodeSwitch");
ClassDB::add_compatibility_class("VisualShaderNodeScalarTransformMult", "VisualShaderNodeTransformOp");
+ ClassDB::add_compatibility_class("VisualShaderNodeScalarDerivativeFunc", "VisualShaderNodeDerivativeFunc");
+ ClassDB::add_compatibility_class("VisualShaderNodeVectorDerivativeFunc", "VisualShaderNodeDerivativeFunc");
ClassDB::add_compatibility_class("World", "World3D");
#endif /* DISABLE_DEPRECATED */
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 12ea1683c8..670b141080 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -113,6 +113,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te
const Color control_font_hover_color = Color(0.95, 0.95, 0.95);
const Color control_font_focus_color = Color(0.95, 0.95, 0.95);
const Color control_font_disabled_color = control_font_color * Color(1, 1, 1, 0.5);
+ const Color control_font_placeholder_color = Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.6f);
const Color control_font_pressed_color = Color(1, 1, 1);
const Color control_selection_color = Color(0.5, 0.5, 0.5);
@@ -385,6 +386,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te
theme->set_color("font_color", "LineEdit", control_font_color);
theme->set_color("font_selected_color", "LineEdit", control_font_pressed_color);
theme->set_color("font_uneditable_color", "LineEdit", control_font_disabled_color);
+ theme->set_color("font_placeholder_color", "LineEdit", control_font_placeholder_color);
theme->set_color("font_outline_color", "LineEdit", Color(1, 1, 1));
theme->set_color("caret_color", "LineEdit", control_font_hover_color);
theme->set_color("selection_color", "LineEdit", control_selection_color);
@@ -427,6 +429,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te
theme->set_color("font_color", "TextEdit", control_font_color);
theme->set_color("font_selected_color", "TextEdit", control_font_pressed_color);
theme->set_color("font_readonly_color", "TextEdit", control_font_disabled_color);
+ theme->set_color("font_placeholder_color", "TextEdit", control_font_placeholder_color);
theme->set_color("font_outline_color", "TextEdit", Color(1, 1, 1));
theme->set_color("selection_color", "TextEdit", control_selection_color);
theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8));
@@ -468,6 +471,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te
theme->set_color("font_color", "CodeEdit", control_font_color);
theme->set_color("font_selected_color", "CodeEdit", Color(0, 0, 0));
theme->set_color("font_readonly_color", "CodeEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+ theme->set_color("font_placeholder_color", "CodeEdit", control_font_placeholder_color);
theme->set_color("font_outline_color", "CodeEdit", Color(1, 1, 1));
theme->set_color("selection_color", "CodeEdit", control_selection_color);
theme->set_color("bookmark_color", "CodeEdit", Color(0.5, 0.64, 1, 0.8));
@@ -817,8 +821,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te
theme->set_stylebox("tab_selected", "TabBar", style_tab_selected);
theme->set_stylebox("tab_unselected", "TabBar", style_tab_unselected);
theme->set_stylebox("tab_disabled", "TabBar", style_tab_disabled);
- theme->set_stylebox("close_bg_pressed", "TabBar", button_pressed);
- theme->set_stylebox("close_bg_highlight", "TabBar", button_normal);
+ theme->set_stylebox("button_pressed", "TabBar", button_pressed);
+ theme->set_stylebox("button_highlight", "TabBar", button_normal);
theme->set_icon("increment", "TabBar", icons["scroll_button_right"]);
theme->set_icon("increment_highlight", "TabBar", icons["scroll_button_right_hl"]);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 331674d248..ee66a61da8 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -2103,8 +2103,8 @@ void GradientTexture2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update"), &GradientTexture2D::_update);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
ADD_GROUP("Fill", "fill_");
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index c3f29ad417..8075497c42 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -595,7 +595,7 @@ public:
private:
mutable RID _texture;
Ref<Curve> _curve;
- int _width = 2048;
+ int _width = 256;
int _current_width = 0;
TextureMode texture_mode = TEXTURE_MODE_RGB;
TextureMode _current_texture_mode = TEXTURE_MODE_RGB;
@@ -637,7 +637,7 @@ private:
Ref<Curve> _curve_x;
Ref<Curve> _curve_y;
Ref<Curve> _curve_z;
- int _width = 2048;
+ int _width = 256;
int _current_width = 0;
void _update();
@@ -685,7 +685,7 @@ private:
Ref<Gradient> gradient;
bool update_pending = false;
RID texture;
- int width = 2048;
+ int width = 256;
bool use_hdr = false;
void _queue_update();
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 0cfa9f31f7..9987408046 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -2996,37 +2996,43 @@ VisualShaderNodeDeterminant::VisualShaderNodeDeterminant() {
set_input_port_default_value(0, Transform3D());
}
-////////////// Scalar Derivative Function
+////////////// Derivative Function
-String VisualShaderNodeScalarDerivativeFunc::get_caption() const {
- return "ScalarDerivativeFunc";
+String VisualShaderNodeDerivativeFunc::get_caption() const {
+ return "DerivativeFunc";
}
-int VisualShaderNodeScalarDerivativeFunc::get_input_port_count() const {
+int VisualShaderNodeDerivativeFunc::get_input_port_count() const {
return 1;
}
-VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeScalarDerivativeFunc::get_input_port_type(int p_port) const {
+VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_input_port_type(int p_port) const {
+ if (op_type == OP_TYPE_VECTOR) {
+ return PORT_TYPE_VECTOR;
+ }
return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeScalarDerivativeFunc::get_input_port_name(int p_port) const {
- return "";
+String VisualShaderNodeDerivativeFunc::get_input_port_name(int p_port) const {
+ return "p";
}
-int VisualShaderNodeScalarDerivativeFunc::get_output_port_count() const {
+int VisualShaderNodeDerivativeFunc::get_output_port_count() const {
return 1;
}
-VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeScalarDerivativeFunc::get_output_port_type(int p_port) const {
+VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_output_port_type(int p_port) const {
+ if (op_type == OP_TYPE_VECTOR) {
+ return PORT_TYPE_VECTOR;
+ }
return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeScalarDerivativeFunc::get_output_port_name(int p_port) const {
- return "";
+String VisualShaderNodeDerivativeFunc::get_output_port_name(int p_port) const {
+ return "result";
}
-String VisualShaderNodeScalarDerivativeFunc::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 VisualShaderNodeDerivativeFunc::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 {
static const char *functions[FUNC_MAX] = {
"fwidth($)",
"dFdx($)",
@@ -3038,84 +3044,30 @@ String VisualShaderNodeScalarDerivativeFunc::generate_code(Shader::Mode p_mode,
return code;
}
-void VisualShaderNodeScalarDerivativeFunc::set_function(Function p_func) {
- ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
- if (func == p_func) {
+void VisualShaderNodeDerivativeFunc::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
return;
}
- func = p_func;
+ switch (p_op_type) {
+ case OP_TYPE_SCALAR:
+ set_input_port_default_value(0, 0.0);
+ break;
+ case OP_TYPE_VECTOR:
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
+ break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
emit_changed();
}
-VisualShaderNodeScalarDerivativeFunc::Function VisualShaderNodeScalarDerivativeFunc::get_function() const {
- return func;
-}
-
-Vector<StringName> VisualShaderNodeScalarDerivativeFunc::get_editable_properties() const {
- Vector<StringName> props;
- props.push_back("function");
- return props;
-}
-
-void VisualShaderNodeScalarDerivativeFunc::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarDerivativeFunc::set_function);
- ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarDerivativeFunc::get_function);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function");
-
- BIND_ENUM_CONSTANT(FUNC_SUM);
- BIND_ENUM_CONSTANT(FUNC_X);
- BIND_ENUM_CONSTANT(FUNC_Y);
- BIND_ENUM_CONSTANT(FUNC_MAX);
-}
-
-VisualShaderNodeScalarDerivativeFunc::VisualShaderNodeScalarDerivativeFunc() {
- set_input_port_default_value(0, 0.0);
-}
-
-////////////// Vector Derivative Function
-
-String VisualShaderNodeVectorDerivativeFunc::get_caption() const {
- return "VectorDerivativeFunc";
-}
-
-int VisualShaderNodeVectorDerivativeFunc::get_input_port_count() const {
- return 1;
-}
-
-VisualShaderNodeVectorDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
-String VisualShaderNodeVectorDerivativeFunc::get_input_port_name(int p_port) const {
- return "";
-}
-
-int VisualShaderNodeVectorDerivativeFunc::get_output_port_count() const {
- return 1;
-}
-
-VisualShaderNodeVectorDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
-String VisualShaderNodeVectorDerivativeFunc::get_output_port_name(int p_port) const {
- return "";
-}
-
-String VisualShaderNodeVectorDerivativeFunc::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 {
- static const char *functions[FUNC_MAX] = {
- "fwidth($)",
- "dFdx($)",
- "dFdy($)"
- };
-
- String code;
- code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
- return code;
+VisualShaderNodeDerivativeFunc::OpType VisualShaderNodeDerivativeFunc::get_op_type() const {
+ return op_type;
}
-void VisualShaderNodeVectorDerivativeFunc::set_function(Function p_func) {
+void VisualShaderNodeDerivativeFunc::set_function(Function p_func) {
ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
if (func == p_func) {
return;
@@ -3124,30 +3076,39 @@ void VisualShaderNodeVectorDerivativeFunc::set_function(Function p_func) {
emit_changed();
}
-VisualShaderNodeVectorDerivativeFunc::Function VisualShaderNodeVectorDerivativeFunc::get_function() const {
+VisualShaderNodeDerivativeFunc::Function VisualShaderNodeDerivativeFunc::get_function() const {
return func;
}
-Vector<StringName> VisualShaderNodeVectorDerivativeFunc::get_editable_properties() const {
+Vector<StringName> VisualShaderNodeDerivativeFunc::get_editable_properties() const {
Vector<StringName> props;
+ props.push_back("op_type");
props.push_back("function");
return props;
}
-void VisualShaderNodeVectorDerivativeFunc::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorDerivativeFunc::set_function);
- ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorDerivativeFunc::get_function);
+void VisualShaderNodeDerivativeFunc::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeDerivativeFunc::set_op_type);
+ ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeDerivativeFunc::get_op_type);
+
+ ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeDerivativeFunc::set_function);
+ ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeDerivativeFunc::get_function);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector"), "set_op_type", "get_op_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function");
+ BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
+
BIND_ENUM_CONSTANT(FUNC_SUM);
BIND_ENUM_CONSTANT(FUNC_X);
BIND_ENUM_CONSTANT(FUNC_Y);
BIND_ENUM_CONSTANT(FUNC_MAX);
}
-VisualShaderNodeVectorDerivativeFunc::VisualShaderNodeVectorDerivativeFunc() {
- set_input_port_default_value(0, Vector3());
+VisualShaderNodeDerivativeFunc::VisualShaderNodeDerivativeFunc() {
+ set_input_port_default_value(0, 0.0);
}
////////////// Clamp
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index bf5777a3fb..16f916f240 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -1235,54 +1235,19 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeClamp::OpType)
///////////////////////////////////////
-/// DERIVATIVE FUNCTIONS
+/// DERIVATIVE FUNCTION
///////////////////////////////////////
-class VisualShaderNodeScalarDerivativeFunc : public VisualShaderNode {
- GDCLASS(VisualShaderNodeScalarDerivativeFunc, VisualShaderNode);
+class VisualShaderNodeDerivativeFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeDerivativeFunc, VisualShaderNode);
public:
- enum Function {
- FUNC_SUM,
- FUNC_X,
- FUNC_Y,
- FUNC_MAX,
+ enum OpType {
+ OP_TYPE_SCALAR,
+ OP_TYPE_VECTOR,
+ OP_TYPE_MAX,
};
-protected:
- Function func = FUNC_SUM;
-
- static void _bind_methods();
-
-public:
- virtual String get_caption() const override;
-
- virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
- virtual String get_input_port_name(int p_port) const override;
-
- virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
- virtual String get_output_port_name(int p_port) const override;
-
- virtual String 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 = false) const override;
-
- void set_function(Function p_func);
- Function get_function() const;
-
- virtual Vector<StringName> get_editable_properties() const override;
-
- VisualShaderNodeScalarDerivativeFunc();
-};
-
-VARIANT_ENUM_CAST(VisualShaderNodeScalarDerivativeFunc::Function)
-
-///////////////////////////////////////
-
-class VisualShaderNodeVectorDerivativeFunc : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorDerivativeFunc, VisualShaderNode);
-
-public:
enum Function {
FUNC_SUM,
FUNC_X,
@@ -1291,8 +1256,10 @@ public:
};
protected:
+ OpType op_type = OP_TYPE_SCALAR;
Function func = FUNC_SUM;
+protected:
static void _bind_methods();
public:
@@ -1308,15 +1275,19 @@ public:
virtual String 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 = false) const override;
+ void set_op_type(OpType p_op_type);
+ OpType get_op_type() const;
+
void set_function(Function p_func);
Function get_function() const;
virtual Vector<StringName> get_editable_properties() const override;
- VisualShaderNodeVectorDerivativeFunc();
+ VisualShaderNodeDerivativeFunc();
};
-VARIANT_ENUM_CAST(VisualShaderNodeVectorDerivativeFunc::Function)
+VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::OpType)
+VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::Function)
///////////////////////////////////////
/// FACEFORWARD
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index 36604073cc..87301a9d3a 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -1081,6 +1081,10 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
distance = -distance_max;
}
+ if (p_render_data->cam_ortogonal) {
+ distance = 1.0;
+ }
+
uint32_t indices;
surf->sort.lod_index = storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, &indices);
if (p_render_data->render_info) {
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index 5654fd4ca2..778d7baa5d 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -1429,6 +1429,10 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const
distance = -distance_max;
}
+ if (p_render_data->cam_ortogonal) {
+ distance = 1.0;
+ }
+
uint32_t indices;
surf->lod_index = storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, &indices);
if (p_render_data->render_info) {
diff --git a/tests/core/math/test_rect2.h b/tests/core/math/test_rect2.h
index e07250a8a2..d98a94b1b5 100644
--- a/tests/core/math/test_rect2.h
+++ b/tests/core/math/test_rect2.h
@@ -439,6 +439,9 @@ TEST_CASE("[Rect2i] Enclosing") {
CHECK_MESSAGE(
!Rect2i(0, 100, 1280, 720).encloses(Rect2i(-4000, -4000, 100, 100)),
"encloses() with non-contained Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).encloses(Rect2i(0, 100, 1280, 720)),
+ "encloses() with identical Rect2i should return the expected result.");
}
TEST_CASE("[Rect2i] Expanding") {
@@ -557,6 +560,9 @@ TEST_CASE("[Rect2i] Intersection") {
CHECK_MESSAGE(
!Rect2i(0, 100, 1280, 720).intersects(Rect2i(-4000, -4000, 100, 100)),
"intersects() with non-enclosed Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ !Rect2i(0, 0, 2, 2).intersects(Rect2i(2, 2, 2, 2)),
+ "intersects() with adjacent Rect2i should return the expected result.");
}
TEST_CASE("[Rect2i] Merging") {
diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h
index 84e71150c7..8bd35df107 100644
--- a/tests/scene/test_code_edit.h
+++ b/tests/scene/test_code_edit.h
@@ -2786,6 +2786,52 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
SEND_GUI_KEY_EVENT(code_edit, Key::APOSTROPHE);
SEND_GUI_KEY_EVENT(code_edit, Key::QUOTEDBL);
CHECK(code_edit->get_line(0) == "'\"'");
+
+ /* Wrap single line selection with brackets */
+ code_edit->clear();
+ code_edit->insert_text_at_caret("abc");
+ code_edit->select_all();
+ SEND_GUI_KEY_EVENT(code_edit, Key::BRACKETLEFT);
+ CHECK(code_edit->get_line(0) == "[abc]");
+
+ /* Caret should be after the last character of the single line selection */
+ CHECK(code_edit->get_caret_column() == 4);
+
+ /* Wrap multi line selection with brackets */
+ code_edit->clear();
+ code_edit->insert_text_at_caret("abc\nabc");
+ code_edit->select_all();
+ SEND_GUI_KEY_EVENT(code_edit, Key::BRACKETLEFT);
+ CHECK(code_edit->get_text() == "[abc\nabc]");
+
+ /* Caret should be after the last character of the multi line selection */
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 3);
+
+ /* If inserted character is not a auto brace completion open key, replace selected text with the inserted character */
+ code_edit->clear();
+ code_edit->insert_text_at_caret("abc");
+ code_edit->select_all();
+ SEND_GUI_KEY_EVENT(code_edit, Key::KEY_1);
+ CHECK(code_edit->get_text() == "1");
+
+ /* If potential multichar and single brace completion is matched, it should wrap the single. */
+ code_edit->clear();
+ code_edit->insert_text_at_caret("\'\'abc");
+ code_edit->select(0, 2, 0, 5);
+ SEND_GUI_KEY_EVENT(code_edit, Key::APOSTROPHE);
+ CHECK(code_edit->get_text() == "\'\'\'abc\'");
+
+ /* If only the potential multichar brace completion is matched, it does not wrap or complete. */
+ auto_brace_completion_pairs.erase("\'");
+ code_edit->set_auto_brace_completion_pairs(auto_brace_completion_pairs);
+ CHECK_FALSE(code_edit->has_auto_brace_completion_open_key("\'"));
+
+ code_edit->clear();
+ code_edit->insert_text_at_caret("\'\'abc");
+ code_edit->select(0, 2, 0, 5);
+ SEND_GUI_KEY_EVENT(code_edit, Key::APOSTROPHE);
+ CHECK(code_edit->get_text() == "\'\'\'");
}
SUBCASE("[CodeEdit] autocomplete") {
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 6333a0fe87..dc2f174b2b 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -606,7 +606,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/Samsung/thorvg
-- Version: 0.7.0 (e527f565b770f0a41df821e6618ccaeea94f465e, 2021)
+- Version: 0.7.1 (d53eb2a880002cb770ace1c1ace9c5dfcfc28252, 2022)
- License: MIT
Files extracted from upstream source:
@@ -614,8 +614,6 @@ Files extracted from upstream source:
See `thorvg/update-thorvg.sh` for extraction instructions. Set the version
number and run the script.
-Patches in the `patches` directory should be re-applied after updates.
-
## vhacd
diff --git a/thirdparty/thorvg/AUTHORS b/thirdparty/thorvg/AUTHORS
index 66057232b6..ec06c49118 100644
--- a/thirdparty/thorvg/AUTHORS
+++ b/thirdparty/thorvg/AUTHORS
@@ -13,3 +13,5 @@ Pankaj Kumar <pankaj.m1@samsung.com>
Patryk Kaczmarek <patryk.k@partner.samsung.com>
Michal Maciola <m.maciola@samsung.com>
Peter Vullings <peter@projectitis.com>
+K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com>
+RĂ©mi Verschelde <rverschelde@gmail.com>
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
index 04a450b1bb..41e8f6dafa 100644
--- a/thirdparty/thorvg/inc/config.h
+++ b/thirdparty/thorvg/inc/config.h
@@ -13,5 +13,5 @@
#define THORVG_JPG_LOADER_SUPPORT 1
-#define THORVG_VERSION_STRING "0.7.0"
+#define THORVG_VERSION_STRING "0.7.1"
#endif
diff --git a/thirdparty/thorvg/patches/thorvg-pr1159-mingw-fix.patch b/thirdparty/thorvg/patches/thorvg-pr1159-mingw-fix.patch
deleted file mode 100644
index a174880306..0000000000
--- a/thirdparty/thorvg/patches/thorvg-pr1159-mingw-fix.patch
+++ /dev/null
@@ -1,73 +0,0 @@
-diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
-index def8ae169a..cf103774c5 100644
---- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
-+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
-@@ -51,6 +51,7 @@
-
- #define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++.
-
-+#include <cstring>
- #include <fstream>
- #include <float.h>
- #include <math.h>
-diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
-index 2b62315de8..32685ee620 100644
---- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
-+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
-@@ -50,6 +50,7 @@
-
- #define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++.
-
-+#include <cstring>
- #include <math.h>
- #include <clocale>
- #include <ctype.h>
-diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
-index 8701fe32b1..ae17634f31 100644
---- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
-+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
-@@ -49,6 +49,7 @@
- */
-
-
-+#include <cstring>
- #include <string>
- #include "tvgMath.h"
- #include "tvgSvgLoaderCommon.h"
-diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
-index d5b9cdcf7b..9f269b29a2 100644
---- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
-+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
-@@ -20,6 +20,7 @@
- * SOFTWARE.
- */
-
-+#include <cstring>
- #include <math.h>
- #include <memory.h>
- #include "tvgSvgUtil.h"
-diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
-index 2e3d5928d9..1571aa4e25 100644
---- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
-+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
-@@ -20,6 +20,7 @@
- * SOFTWARE.
- */
-
-+#include <cstring>
- #include <ctype.h>
- #include <string>
-
-diff --git a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
-index 9450d80e88..9dd57e5a89 100644
---- a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
-+++ b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
-@@ -24,6 +24,8 @@
- #include "tvgTvgSaver.h"
- #include "tvgLzw.h"
-
-+#include <cstring>
-+
- #ifdef _WIN32
- #include <malloc.h>
- #else
diff --git a/thirdparty/thorvg/patches/thorvg-pr1166-vs2017-minmax.patch b/thirdparty/thorvg/patches/thorvg-pr1166-vs2017-minmax.patch
deleted file mode 100644
index 0b045bd05a..0000000000
--- a/thirdparty/thorvg/patches/thorvg-pr1166-vs2017-minmax.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
-index 78537e7726..c75e73760e 100644
---- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
-+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
-@@ -23,6 +23,7 @@
- #include "tvgSwCommon.h"
- #include "tvgTaskScheduler.h"
- #include "tvgSwRenderer.h"
-+#include "tvgMath.h"
-
- /************************************************************************/
- /* Internal Class Implementation */
-@@ -594,10 +595,10 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
- task->surface = surface;
- task->mpool = mpool;
- task->flags = flags;
-- task->bbox.min.x = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
-- task->bbox.min.y = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
-- task->bbox.max.x = min(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
-- task->bbox.max.y = min(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
-+ task->bbox.min.x = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
-+ task->bbox.min.y = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
-+ task->bbox.max.x = mathMin(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
-+ task->bbox.max.y = mathMin(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
-
- if (!task->pushed) {
- task->pushed = true;
-diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/lib/tvgMath.h
-index 9e5c915fc3..94b4fe1cf1 100644
---- a/thirdparty/thorvg/src/lib/tvgMath.h
-+++ b/thirdparty/thorvg/src/lib/tvgMath.h
-@@ -29,6 +29,10 @@
- #include "tvgCommon.h"
-
-
-+#define mathMin(x, y) (((x) < (y)) ? (x) : (y))
-+#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
-+
-+
- static inline bool mathZero(float a)
- {
- return (fabsf(a) < FLT_EPSILON) ? true : false;
-@@ -154,4 +158,4 @@ static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
- }
-
-
--#endif //_TVG_MATH_H_
-\ No newline at end of file
-+#endif //_TVG_MATH_H_
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
index fe22fce017..f9974d9847 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
@@ -84,8 +84,8 @@ bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipReg
//Fast track: Non-transformed image but just shifted.
if (image->direct) {
- image->ox = -static_cast<uint32_t>(round(transform->e13));
- image->oy = -static_cast<uint32_t>(round(transform->e23));
+ image->ox = -static_cast<int32_t>(round(transform->e13));
+ image->oy = -static_cast<int32_t>(round(transform->e23));
//Figure out the scale factor by transform matrix
} else {
auto scaleX = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21));
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
index deebed16ee..56bc2f77dc 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
@@ -481,7 +481,10 @@ static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image,
static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity)
{
Matrix itransform;
- if (transform && !mathInverse(transform, &itransform)) return false;
+
+ if (transform) {
+ if (!mathInverse(transform, &itransform)) return false;
+ } else mathIdentity(&itransform);
auto halfScale = _halfScale(image->scale);
@@ -816,7 +819,10 @@ static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, con
static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity)
{
Matrix itransform;
- if (transform && !mathInverse(transform, &itransform)) return false;
+
+ if (transform) {
+ if (!mathInverse(transform, &itransform)) return false;
+ } else mathIdentity(&itransform);
auto halfScale = _halfScale(image->scale);
@@ -1113,12 +1119,12 @@ static bool _rasterTranslucentLinearGradientRle(SwSurface* surface, const SwRleD
auto dst = &surface->buffer[span->y * surface->stride + span->x];
fillFetchLinear(fill, buffer, span->y, span->x, span->len);
if (span->coverage == 255) {
- for (uint32_t i = 0; i < span->len; ++i, ++dst) {
- *dst = buffer[i] + ALPHA_BLEND(*dst, _ialpha(buffer[i]));
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = buffer[x] + ALPHA_BLEND(*dst, _ialpha(buffer[x]));
}
} else {
- for (uint32_t i = 0; i < span->len; ++i, ++dst) {
- auto tmp = ALPHA_BLEND(buffer[i], span->coverage);
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ auto tmp = ALPHA_BLEND(buffer[x], span->coverage);
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
}
@@ -1142,8 +1148,8 @@ static bool _rasterSolidLinearGradientRle(SwSurface* surface, const SwRleData* r
} else {
fillFetchLinear(fill, buf, span->y, span->x, span->len);
auto dst = &surface->buffer[span->y * surface->stride + span->x];
- for (uint32_t i = 0; i < span->len; ++i) {
- dst[i] = INTERPOLATE(span->coverage, buf[i], dst[i]);
+ for (uint32_t x = 0; x < span->len; ++x) {
+ dst[x] = INTERPOLATE(span->coverage, buf[x], dst[x]);
}
}
}
@@ -1302,12 +1308,12 @@ static bool _rasterTranslucentRadialGradientRle(SwSurface* surface, const SwRleD
auto dst = &surface->buffer[span->y * surface->stride + span->x];
fillFetchRadial(fill, buffer, span->y, span->x, span->len);
if (span->coverage == 255) {
- for (uint32_t i = 0; i < span->len; ++i, ++dst) {
- *dst = buffer[i] + ALPHA_BLEND(*dst, _ialpha(buffer[i]));
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = buffer[x] + ALPHA_BLEND(*dst, _ialpha(buffer[x]));
}
} else {
- for (uint32_t i = 0; i < span->len; ++i, ++dst) {
- auto tmp = ALPHA_BLEND(buffer[i], span->coverage);
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ auto tmp = ALPHA_BLEND(buffer[x], span->coverage);
*dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
}
}
@@ -1332,8 +1338,8 @@ static bool _rasterSolidRadialGradientRle(SwSurface* surface, const SwRleData* r
} else {
fillFetchRadial(fill, buf, span->y, span->x, span->len);
auto ialpha = 255 - span->coverage;
- for (uint32_t i = 0; i < span->len; ++i, ++dst) {
- *dst = ALPHA_BLEND(buf[i], span->coverage) + ALPHA_BLEND(*dst, ialpha);
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = ALPHA_BLEND(buf[x], span->coverage) + ALPHA_BLEND(*dst, ialpha);
}
}
}
@@ -1487,7 +1493,7 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity)
{
//Verify Boundary
- if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= surface->w || bbox.min.y >= surface->h) return false;
+ if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return false;
//TOOD: switch (image->format)
//TODO: case: _rasterRGBImage()
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
index 4e8d342137..e96307c874 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
@@ -58,8 +58,8 @@
y = yStart;
while (y < yEnd) {
- x1 = _xa;
- x2 = _xb;
+ x1 = (int32_t)_xa;
+ x2 = (int32_t)_xb;
if (!region) {
minx = INT32_MAX;
@@ -160,4 +160,4 @@ next:
xb = _xb;
ua = _ua;
va = _va;
-} \ No newline at end of file
+}
diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/lib/tvgMath.h
index 94b4fe1cf1..423fb6eb1b 100644
--- a/thirdparty/thorvg/src/lib/tvgMath.h
+++ b/thirdparty/thorvg/src/lib/tvgMath.h
@@ -47,7 +47,7 @@ static inline bool mathEqual(float a, float b)
static inline bool mathRightAngle(const Matrix* m)
{
- auto radian = fabsf(atan2(m->e21, m->e11));
+ auto radian = fabsf(atan2f(m->e21, m->e11));
if (radian < FLT_EPSILON || mathEqual(radian, float(M_PI_2)) || mathEqual(radian, float(M_PI))) return true;
return false;
}
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
index 8846613c6b..f27881da42 100644
--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
@@ -47,6 +47,7 @@ JpgLoader::~JpgLoader()
{
jpgdDelete(decoder);
if (freeData) free(data);
+ free(image);
}
@@ -128,5 +129,9 @@ unique_ptr<Surface> JpgLoader::bitmap()
void JpgLoader::run(unsigned tid)
{
+ if (image) {
+ free(image);
+ image = nullptr;
+ }
image = jpgdDecompress(decoder);
} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
index fa72734ec4..4ccc5788d5 100644
--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
@@ -1080,7 +1080,9 @@ namespace DCT_Upsample
// Unconditionally frees all allocated m_blocks.
void jpeg_decoder::free_all_blocks()
{
+ delete(m_pStream);
m_pStream = nullptr;
+
for (mem_block *b = m_pMem_blocks; b; ) {
mem_block *n = b->m_pNext;
free(b);
@@ -2815,7 +2817,6 @@ int jpeg_decoder::begin_decoding()
jpeg_decoder::~jpeg_decoder()
{
free_all_blocks();
- delete(m_pStream);
}
@@ -3025,4 +3026,4 @@ unsigned char* jpgdDecompress(jpeg_decoder* decoder)
}
}
return pImage_data;
-} \ No newline at end of file
+}
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
index d32ffd99d4..ca9cb35c32 100644
--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp
index c6d95be5ba..3cc08e902b 100644
--- a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp
@@ -72,6 +72,7 @@ PngLoader::PngLoader()
PngLoader::~PngLoader()
{
if (freeData) free(data);
+ free(image);
}
@@ -121,7 +122,7 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy)
clear();
lodepng_state_init(&state);
-
+
unsigned int width, height;
if (lodepng_inspect(&width, &height, &state, (unsigned char*)(data), size) > 0) return false;
@@ -180,10 +181,14 @@ unique_ptr<Surface> PngLoader::bitmap()
void PngLoader::run(unsigned tid)
{
+ if (image) {
+ free(image);
+ image = nullptr;
+ }
auto width = static_cast<unsigned>(w);
auto height = static_cast<unsigned>(h);
lodepng_decode(&image, &width, &height, &state, data, size);
_premultiply((uint32_t*)(image), width, height);
-} \ No newline at end of file
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index cf103774c5..08b3308165 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -541,7 +541,7 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
}
}
}
- } else if (len >= 3 && !strncmp(str, "url", 3)) {
+ } else if (ref && len >= 3 && !strncmp(str, "url", 3)) {
*ref = _idFromUrl((const char*)(str + 3));
} else {
//Handle named color
@@ -789,7 +789,7 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
}
#ifdef THORVG_LOG_ENABLED
- else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON ) {
+ else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) {
TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
}
#endif
@@ -1611,6 +1611,7 @@ static bool _attrParseImageNode(void* data, const char* key, const char* value)
}
if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
+ if (image->href && value) free(image->href);
image->href = _idFromHref(value);
} else if (!strcmp(key, "id")) {
if (node->id && value) free(node->id);
@@ -1728,6 +1729,112 @@ error_grad_alloc:
}
+static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
+{
+ if (parent == nullptr) return;
+ //Inherit the property of parent if not present in child.
+ if (!child->curColorSet) {
+ child->color = parent->color;
+ child->curColorSet = parent->curColorSet;
+ }
+ //Fill
+ if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) {
+ child->fill.paint.color = parent->fill.paint.color;
+ child->fill.paint.none = parent->fill.paint.none;
+ child->fill.paint.curColor = parent->fill.paint.curColor;
+ if (parent->fill.paint.url) child->fill.paint.url = _copyId(parent->fill.paint.url);
+ }
+ if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
+ child->fill.opacity = parent->fill.opacity;
+ }
+ if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) {
+ child->fill.fillRule = parent->fill.fillRule;
+ }
+ //Stroke
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) {
+ child->stroke.paint.color = parent->stroke.paint.color;
+ child->stroke.paint.none = parent->stroke.paint.none;
+ child->stroke.paint.curColor = parent->stroke.paint.curColor;
+ child->stroke.paint.url = parent->stroke.paint.url ? _copyId(parent->stroke.paint.url) : nullptr;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
+ child->stroke.opacity = parent->stroke.opacity;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) {
+ child->stroke.width = parent->stroke.width;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) {
+ if (parent->stroke.dash.array.count > 0) {
+ child->stroke.dash.array.clear();
+ child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
+ for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
+ child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
+ }
+ }
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) {
+ child->stroke.cap = parent->stroke.cap;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) {
+ child->stroke.join = parent->stroke.join;
+ }
+}
+
+
+static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
+{
+ if (from == nullptr) return;
+ //Copy the properties of 'from' only if they were explicitly set (not the default ones).
+ if (from->curColorSet) {
+ to->color = from->color;
+ to->curColorSet = true;
+ }
+ //Fill
+ to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)from->fill.flags);
+ if (((int)from->fill.flags & (int)SvgFillFlags::Paint)) {
+ to->fill.paint.color = from->fill.paint.color;
+ to->fill.paint.none = from->fill.paint.none;
+ to->fill.paint.curColor = from->fill.paint.curColor;
+ if (from->fill.paint.url) to->fill.paint.url = _copyId(from->fill.paint.url);
+ }
+ if (((int)from->fill.flags & (int)SvgFillFlags::Opacity)) {
+ to->fill.opacity = from->fill.opacity;
+ }
+ if (((int)from->fill.flags & (int)SvgFillFlags::FillRule)) {
+ to->fill.fillRule = from->fill.fillRule;
+ }
+ //Stroke
+ to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)from->stroke.flags);
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint)) {
+ to->stroke.paint.color = from->stroke.paint.color;
+ to->stroke.paint.none = from->stroke.paint.none;
+ to->stroke.paint.curColor = from->stroke.paint.curColor;
+ to->stroke.paint.url = from->stroke.paint.url ? _copyId(from->stroke.paint.url) : nullptr;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
+ to->stroke.opacity = from->stroke.opacity;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width)) {
+ to->stroke.width = from->stroke.width;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash)) {
+ if (from->stroke.dash.array.count > 0) {
+ to->stroke.dash.array.clear();
+ to->stroke.dash.array.reserve(from->stroke.dash.array.count);
+ for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
+ to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+ }
+ }
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap)) {
+ to->stroke.cap = from->stroke.cap;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join)) {
+ to->stroke.join = from->stroke.join;
+ }
+}
+
+
static void _copyAttr(SvgNode* to, const SvgNode* from)
{
//Copy matrix attribute
@@ -1736,7 +1843,8 @@ static void _copyAttr(SvgNode* to, const SvgNode* from)
if (to->transform) *to->transform = *from->transform;
}
//Copy style attribute
- *to->style = *from->style;
+ _styleCopy(to->style, from->style);
+ to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)from->style->flags);
if (from->style->fill.paint.url) to->style->fill.paint.url = strdup(from->style->fill.paint.url);
if (from->style->stroke.paint.url) to->style->stroke.paint.url = strdup(from->style->stroke.paint.url);
if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url);
@@ -1780,15 +1888,17 @@ static void _copyAttr(SvgNode* to, const SvgNode* from)
break;
}
case SvgNodeType::Polygon: {
- to->node.polygon.pointsCount = from->node.polygon.pointsCount;
- to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
- memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
+ if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) {
+ to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
+ memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
+ }
break;
}
case SvgNodeType::Polyline: {
- to->node.polyline.pointsCount = from->node.polyline.pointsCount;
- to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
- memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
+ if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) {
+ to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
+ memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
+ }
break;
}
case SvgNodeType::Image: {
@@ -1806,35 +1916,45 @@ static void _copyAttr(SvgNode* to, const SvgNode* from)
}
-static void _cloneNode(SvgNode* from, SvgNode* parent)
+static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
{
+ /* Exception handling: Prevent invalid SVG data input.
+ The size is the arbitrary value, we need an experimental size. */
+ if (depth == 8192) {
+ TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
+ return;
+ }
+
SvgNode* newNode;
- if (!from || !parent) return;
+ if (!from || !parent || from == parent) return;
newNode = _createNode(parent, from->type);
-
if (!newNode) return;
+ _styleInherit(newNode->style, parent->style);
_copyAttr(newNode, from);
auto child = from->child.data;
for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
- _cloneNode(*child, newNode);
+ _cloneNode(*child, newNode, depth + 1);
}
}
-static void _postponeCloneNode(SvgLoaderData* loader, SvgNode *node, char* id) {
+static void _postponeCloneNode(SvgLoaderData* loader, SvgNode *node, char* id)
+{
loader->cloneNodes.push({node, id});
}
-static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes) {
+static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
+{
for (uint32_t i = 0; i < cloneNodes->count; ++i) {
auto nodeIdPair = cloneNodes->data[i];
auto defs = _getDefsNode(nodeIdPair.node);
auto nodeFrom = _findChildById(defs, nodeIdPair.id);
- _cloneNode(nodeFrom, nodeIdPair.node);
+ if (!nodeFrom) nodeFrom = _findChildById(doc, nodeIdPair.id);
+ _cloneNode(nodeFrom, nodeIdPair.node, 0);
free(nodeIdPair.id);
}
}
@@ -1875,7 +1995,7 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value)
defs = _getDefsNode(node);
nodeFrom = _findChildById(defs, id);
if (nodeFrom) {
- _cloneNode(nodeFrom, node);
+ _cloneNode(nodeFrom, node, 0);
free(id);
} else {
//some svg export software include <defs> element at the end of the file
@@ -1883,10 +2003,6 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value)
//after the whole file is parsed
_postponeCloneNode(loader, node, id);
}
- } else if (!strcmp(key, "clip-path")) {
- _handleClipPathAttr(loader, node, value);
- } else if (!strcmp(key, "mask")) {
- _handleMaskAttr(loader, node, value);
} else {
return _attrParseGNode(data, key, value);
}
@@ -2081,10 +2197,12 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char
}
if (!strcmp(key, "id")) {
+ if (grad->id && value) free(grad->id);
grad->id = _copyId(value);
} else if (!strcmp(key, "spreadMethod")) {
grad->spread = _parseSpreadValue(value);
} else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
+ if (grad->ref && value) free(grad->ref);
grad->ref = _idFromHref(value);
} else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
grad->userSpace = true;
@@ -2269,10 +2387,12 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char
}
if (!strcmp(key, "id")) {
+ if (grad->id && value) free(grad->id);
grad->id = _copyId(value);
} else if (!strcmp(key, "spreadMethod")) {
grad->spread = _parseSpreadValue(value);
} else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
+ if (grad->ref && value) free(grad->ref);
grad->ref = _idFromHref(value);
} else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
grad->userSpace = true;
@@ -2408,6 +2528,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
if ((method = _findGroupFactory(tagName))) {
//Group
+ if (empty) return;
if (!loader->doc) {
if (strcmp(tagName, "svg")) return; //Not a valid svg document
node = method(loader, nullptr, attrs, attrsLength);
@@ -2493,59 +2614,8 @@ static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content
}
-static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
+static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node)
{
- if (parent == nullptr) return;
- //Inherit the property of parent if not present in child.
- //Fill
- if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) {
- child->fill.paint.color = parent->fill.paint.color;
- child->fill.paint.none = parent->fill.paint.none;
- child->fill.paint.curColor = parent->fill.paint.curColor;
- if (parent->fill.paint.url) child->fill.paint.url = _copyId(parent->fill.paint.url);
- } else if (child->fill.paint.curColor && !child->curColorSet) {
- child->color = parent->color;
- }
- if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
- child->fill.opacity = parent->fill.opacity;
- }
- if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) {
- child->fill.fillRule = parent->fill.fillRule;
- }
- //Stroke
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) {
- child->stroke.paint.color = parent->stroke.paint.color;
- child->stroke.paint.none = parent->stroke.paint.none;
- child->stroke.paint.curColor = parent->stroke.paint.curColor;
- child->stroke.paint.url = parent->stroke.paint.url ? _copyId(parent->stroke.paint.url) : nullptr;
- } else if (child->stroke.paint.curColor && !child->curColorSet) {
- child->color = parent->color;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
- child->stroke.opacity = parent->stroke.opacity;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) {
- child->stroke.width = parent->stroke.width;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) {
- if (parent->stroke.dash.array.count > 0) {
- child->stroke.dash.array.clear();
- child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
- for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
- child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
- }
- }
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) {
- child->stroke.cap = parent->stroke.cap;
- }
- if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) {
- child->stroke.join = parent->stroke.join;
- }
-}
-
-
-static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node){
#ifdef THORVG_LOG_ENABLED
auto type = simpleXmlNodeTypeToString(node->type);
@@ -2838,14 +2908,14 @@ void SvgLoader::run(unsigned tid)
if (loaderData.doc) {
_updateStyle(loaderData.doc, nullptr);
auto defs = loaderData.doc->node.doc.defs;
- if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
-
- if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
_updateComposite(loaderData.doc, loaderData.doc);
if (defs) _updateComposite(loaderData.doc, defs);
- if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes);
+ if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc);
+
+ if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
+ if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
}
root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath);
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
index 1571aa4e25..ee199da231 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
@@ -220,15 +220,15 @@ static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &to
if ((itr + sizeof("<!DOCTYPE>") - 1 < itrEnd) && (!memcmp(itr + 2, "DOCTYPE", sizeof("DOCTYPE") - 1)) && ((itr[2 + sizeof("DOCTYPE") - 1] == '>') || (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1])))) {
toff = sizeof("!DOCTYPE") - 1;
return SimpleXMLType::Doctype;
- } else if (itr + sizeof("<!>") - 1 < itrEnd) {
- toff = sizeof("!") - 1;
- return SimpleXMLType::DoctypeChild;
} else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
toff = sizeof("![CDATA[") - 1;
return SimpleXMLType::CData;
} else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
toff = sizeof("!--") - 1;
return SimpleXMLType::Comment;
+ } else if (itr + sizeof("<!>") - 1 < itrEnd) {
+ toff = sizeof("!") - 1;
+ return SimpleXMLType::DoctypeChild;
}
return SimpleXMLType::Open;
}
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
index c200131eba..ce3d5eed1c 100755
--- a/thirdparty/thorvg/update-thorvg.sh
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -1,4 +1,4 @@
-VERSION=0.7.0
+VERSION=0.7.1
rm -rf AUTHORS inc LICENSE src *.zip
curl -L -O https://github.com/Samsung/thorvg/archive/refs/tags/v$VERSION.zip
bsdtar --strip-components=1 -xvf *.zip