summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/extension/gdextension.cpp9
-rw-r--r--core/extension/gdextension.h2
-rw-r--r--core/extension/gdextension_manager.cpp23
-rw-r--r--core/extension/gdextension_manager.h4
-rw-r--r--doc/classes/Button.xml5
-rw-r--r--doc/classes/Node.xml12
-rw-r--r--doc/classes/PopupMenu.xml18
-rw-r--r--doc/classes/TabBar.xml22
-rw-r--r--doc/classes/TabContainer.xml3
-rw-r--r--doc/classes/Tree.xml3
-rw-r--r--doc/classes/TreeItem.xml4
-rw-r--r--editor/code_editor.cpp19
-rw-r--r--editor/create_dialog.cpp5
-rw-r--r--editor/editor_data.cpp65
-rw-r--r--editor/editor_data.h8
-rw-r--r--editor/editor_help.cpp18
-rw-r--r--editor/editor_help.h2
-rw-r--r--editor/editor_inspector.cpp57
-rw-r--r--editor/editor_node.cpp157
-rw-r--r--editor/editor_node.h6
-rw-r--r--editor/editor_path.cpp9
-rw-r--r--editor/editor_quick_open.cpp5
-rw-r--r--editor/editor_resource_picker.cpp2
-rw-r--r--editor/editor_themes.cpp2
-rw-r--r--editor/renames_map_3_to_4.cpp38
-rw-r--r--editor/scene_tree_dock.cpp35
-rw-r--r--editor/scene_tree_editor.cpp2
-rw-r--r--modules/navigation/nav_map.cpp1
-rw-r--r--scene/3d/physics_body_3d.cpp4
-rw-r--r--scene/gui/button.cpp23
-rw-r--r--scene/gui/button.h3
-rw-r--r--scene/gui/popup_menu.cpp96
-rw-r--r--scene/gui/popup_menu.h5
-rw-r--r--scene/gui/tab_bar.cpp74
-rw-r--r--scene/gui/tab_bar.h7
-rw-r--r--scene/gui/tab_container.cpp2
-rw-r--r--scene/gui/tab_container.h1
-rw-r--r--scene/gui/texture_progress_bar.cpp49
-rw-r--r--scene/gui/texture_progress_bar.h2
-rw-r--r--scene/gui/tree.cpp54
-rw-r--r--scene/gui/tree.h6
-rw-r--r--scene/resources/default_theme/default_theme.cpp5
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/thorvg/AUTHORS2
-rw-r--r--thirdparty/thorvg/inc/config.h2
-rw-r--r--thirdparty/thorvg/inc/thorvg.h14
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp19
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h4
-rw-r--r--thirdparty/thorvg/src/lib/tvgAccessor.cpp2
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoadModule.h3
-rw-r--r--thirdparty/thorvg/src/lib/tvgMath.h6
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.cpp19
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.h2
-rw-r--r--thirdparty/thorvg/src/lib/tvgPictureImpl.h4
-rw-r--r--thirdparty/thorvg/src/lib/tvgRender.h2
-rw-r--r--thirdparty/thorvg/src/lib/tvgScene.cpp2
-rw-r--r--thirdparty/thorvg/src/lib/tvgSceneImpl.h13
-rw-r--r--thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp29
-rw-r--r--thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h4
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp28
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h2
-rw-r--r--thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp35
-rw-r--r--thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h4
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp34
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h1
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h3
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp3
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp4
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp21
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh4
70 files changed, 821 insertions, 314 deletions
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 829e1d8e5b..f158755a85 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -549,6 +549,15 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String
return Ref<Resource>();
}
+ // Handle icons if any are specified.
+ if (config->has_section("icons")) {
+ List<String> keys;
+ config->get_section_keys("icons", &keys);
+ for (const String &key : keys) {
+ lib->class_icon_paths[key] = config->get_value("icons", key);
+ }
+ }
+
return lib;
}
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 39523a142f..9d946ca7ba 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -67,6 +67,8 @@ protected:
static void _bind_methods();
public:
+ HashMap<String, String> class_icon_paths;
+
static String get_extension_list_config_file();
static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);
diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp
index 8701e6d77b..63e809bc7c 100644
--- a/core/extension/gdextension_manager.cpp
+++ b/core/extension/gdextension_manager.cpp
@@ -50,6 +50,11 @@ GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &
extension->initialize_library(GDExtension::InitializationLevel(i));
}
}
+
+ for (const KeyValue<String, String> &kv : extension->class_icon_paths) {
+ gdextension_class_icon_paths[kv.key] = kv.value;
+ }
+
gdextension_map[p_path] = extension;
return LOAD_STATUS_OK;
}
@@ -74,6 +79,11 @@ GDExtensionManager::LoadStatus GDExtensionManager::unload_extension(const String
extension->deinitialize_library(GDExtension::InitializationLevel(i));
}
}
+
+ for (const KeyValue<String, String> &kv : extension->class_icon_paths) {
+ gdextension_class_icon_paths.erase(kv.key);
+ }
+
gdextension_map.erase(p_path);
return LOAD_STATUS_OK;
}
@@ -95,6 +105,19 @@ Ref<GDExtension> GDExtensionManager::get_extension(const String &p_path) {
return E->value;
}
+bool GDExtensionManager::class_has_icon_path(const String &p_class) const {
+ // TODO: Check that the icon belongs to a registered class somehow.
+ return gdextension_class_icon_paths.has(p_class);
+}
+
+String GDExtensionManager::class_get_icon_path(const String &p_class) const {
+ // TODO: Check that the icon belongs to a registered class somehow.
+ if (gdextension_class_icon_paths.has(p_class)) {
+ return gdextension_class_icon_paths[p_class];
+ }
+ return "";
+}
+
void GDExtensionManager::initialize_extensions(GDExtension::InitializationLevel p_level) {
ERR_FAIL_COND(int32_t(p_level) - 1 != level);
for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) {
diff --git a/core/extension/gdextension_manager.h b/core/extension/gdextension_manager.h
index 456942af0d..3643f043d8 100644
--- a/core/extension/gdextension_manager.h
+++ b/core/extension/gdextension_manager.h
@@ -38,6 +38,7 @@ class GDExtensionManager : public Object {
int32_t level = -1;
HashMap<String, Ref<GDExtension>> gdextension_map;
+ HashMap<String, String> gdextension_class_icon_paths;
static void _bind_methods();
@@ -59,6 +60,9 @@ public:
Vector<String> get_loaded_extensions() const;
Ref<GDExtension> get_extension(const String &p_path);
+ bool class_has_icon_path(const String &p_class) const;
+ String class_get_icon_path(const String &p_class) const;
+
void initialize_extensions(GDExtension::InitializationLevel p_level);
void deinitialize_extensions(GDExtension::InitializationLevel p_level);
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index 9fce014125..ec2447dbbc 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -48,7 +48,7 @@
When this property is enabled, text that is too large to fit the button is clipped, when disabled the Button will always be wide enough to hold the text.
</member>
<member name="expand_icon" type="bool" setter="set_expand_icon" getter="is_expand_icon" default="false">
- When enabled, the button's icon will expand/shrink to fit the button's size while keeping its aspect.
+ When enabled, the button's icon will expand/shrink to fit the button's size while keeping its aspect. See also [theme_item icon_max_width].
</member>
<member name="flat" type="bool" setter="set_flat" getter="is_flat" default="false">
Flat buttons don't display decoration.
@@ -116,6 +116,9 @@
<theme_item name="h_separation" data_type="constant" type="int" default="2">
The horizontal space between [Button]'s icon and text. Negative values will be treated as [code]0[/code] when used.
</theme_item>
+ <theme_item name="icon_max_width" data_type="constant" type="int" default="0">
+ The maximum allowed width of the [Button]'s icon. This limit is applied on top of the default size of the icon, or its expanded size if [member expand_icon] is [code]true[/code]. The height is adjusted according to the icon's ratio.
+ </theme_item>
<theme_item name="outline_size" data_type="constant" type="int" default="0">
The size of the text outline.
[b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index dcc975f24c..5817c45f41 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -42,6 +42,18 @@
The elements in the array returned from this method are displayed as warnings in the Scene dock if the script that overrides it is a [code]tool[/code] script.
Returning an empty array produces no warnings.
Call [method update_configuration_warnings] when the warnings need to be updated for this node.
+ [codeblock]
+ @export var energy = 0:
+ set(value):
+ energy = value
+ update_configuration_warnings()
+
+ func _get_configuration_warnings():
+ if energy &lt; 0:
+ return ["Energy must be 0 or greater."]
+ else:
+ return []
+ [/codeblock]
</description>
</method>
<method name="_input" qualifiers="virtual">
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 6787b5d20a..6996061bd8 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -202,6 +202,13 @@
Returns the icon of the item at the given [param index].
</description>
</method>
+ <method name="get_item_icon_max_width" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the maximum allowed width of the icon for the item at the given [param index].
+ </description>
+ </method>
<method name="get_item_id" qualifiers="const">
<return type="int" />
<param index="0" name="index" type="int" />
@@ -397,6 +404,14 @@
Replaces the [Texture2D] icon of the item at the given [param index].
</description>
</method>
+ <method name="set_item_icon_max_width">
+ <return type="void" />
+ <param index="0" name="index" type="int" />
+ <param index="1" name="width" type="int" />
+ <description>
+ Sets the maximum allowed width of the icon for the item at the given [param index]. This limit is applied on top of the default size of the icon and on top of [theme_item icon_max_width]. The height is adjusted according to the icon's ratio.
+ </description>
+ </method>
<method name="set_item_id">
<return type="void" />
<param index="0" name="index" type="int" />
@@ -573,6 +588,9 @@
<theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal space between the item's elements.
</theme_item>
+ <theme_item name="icon_max_width" data_type="constant" type="int" default="0">
+ The maximum allowed width of the item's icon. This limit is applied on top of the default size of the icon, but before the value set with [method set_item_icon_max_width]. The height is adjusted according to the icon's ratio.
+ </theme_item>
<theme_item name="indent" data_type="constant" type="int" default="10">
Width of the single indentation level.
</theme_item>
diff --git a/doc/classes/TabBar.xml b/doc/classes/TabBar.xml
index ae6d589339..7211fc2137 100644
--- a/doc/classes/TabBar.xml
+++ b/doc/classes/TabBar.xml
@@ -46,14 +46,21 @@
<return type="Texture2D" />
<param index="0" name="tab_idx" type="int" />
<description>
- Returns the [Texture2D] for the right button of the tab at index [param tab_idx] or [code]null[/code] if the button has no [Texture2D].
+ Returns the icon for the right button of the tab at index [param tab_idx] or [code]null[/code] if the right button has no icon.
</description>
</method>
<method name="get_tab_icon" qualifiers="const">
<return type="Texture2D" />
<param index="0" name="tab_idx" type="int" />
<description>
- Returns the [Texture2D] for the tab at index [param tab_idx] or [code]null[/code] if the tab has no [Texture2D].
+ Returns the icon for the tab at index [param tab_idx] or [code]null[/code] if the tab has no icon.
+ </description>
+ </method>
+ <method name="get_tab_icon_max_width" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="tab_idx" type="int" />
+ <description>
+ Returns the maximum allowed width of the icon for the tab at index [param tab_idx].
</description>
</method>
<method name="get_tab_idx_at_point" qualifiers="const">
@@ -158,6 +165,14 @@
Sets an [param icon] for the tab at index [param tab_idx].
</description>
</method>
+ <method name="set_tab_icon_max_width">
+ <return type="void" />
+ <param index="0" name="tab_idx" type="int" />
+ <param index="1" name="width" type="int" />
+ <description>
+ Sets the maximum allowed width of the icon for the tab at index [param tab_idx]. This limit is applied on top of the default size of the icon and on top of [theme_item icon_max_width]. The height is adjusted according to the icon's ratio.
+ </description>
+ </method>
<method name="set_tab_language">
<return type="void" />
<param index="0" name="tab_idx" type="int" />
@@ -323,6 +338,9 @@
<theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal separation between the elements inside tabs.
</theme_item>
+ <theme_item name="icon_max_width" data_type="constant" type="int" default="0">
+ The maximum allowed width of the tab's icon. This limit is applied on top of the default size of the icon, but before the value set with [method set_tab_icon_max_width]. The height is adjusted according to the icon's ratio.
+ </theme_item>
<theme_item name="outline_size" data_type="constant" type="int" default="0">
The size of the tab text outline.
[b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.
diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml
index c34336353f..b0dcb932dc 100644
--- a/doc/classes/TabContainer.xml
+++ b/doc/classes/TabContainer.xml
@@ -209,6 +209,9 @@
<theme_item name="font_unselected_color" data_type="color" type="Color" default="Color(0.7, 0.7, 0.7, 1)">
Font color of the other, unselected tabs.
</theme_item>
+ <theme_item name="icon_max_width" data_type="constant" type="int" default="0">
+ The maximum allowed width of the tab's icon. This limit is applied on top of the default size of the icon, but before the value set with [method TabBar.set_tab_icon_max_width]. The height is adjusted according to the icon's ratio.
+ </theme_item>
<theme_item name="icon_separation" data_type="constant" type="int" default="4">
Space between tab's name and its icon.
</theme_item>
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index 95778f86c4..d09f61b8c8 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -518,6 +518,9 @@
<theme_item name="h_separation" data_type="constant" type="int" default="4">
The horizontal space between item cells. This is also used as the margin at the start of an item when folding is disabled.
</theme_item>
+ <theme_item name="icon_max_width" data_type="constant" type="int" default="0">
+ The maximum allowed width of the icon in item's cells. This limit is applied on top of the default size of the icon, but before the value set with [method TreeItem.set_icon_max_width]. The height is adjusted according to the icon's ratio.
+ </theme_item>
<theme_item name="item_margin" data_type="constant" type="int" default="16">
The horizontal margin at the start of an item. This is used when folding is enabled for the item.
</theme_item>
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index 3ce434e069..49b4622aed 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -183,7 +183,7 @@
<return type="int" />
<param index="0" name="column" type="int" />
<description>
- Returns the column's icon's maximum width.
+ Returns the maximum allowed width of the icon in the given [param column].
</description>
</method>
<method name="get_icon_modulate" qualifiers="const">
@@ -545,7 +545,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="width" type="int" />
<description>
- Sets the given column's icon's maximum width.
+ Sets the maximum allowed width of the icon in the given [param column]. This limit is applied on top of the default size of the icon and on top of [theme_item Tree.icon_max_width]. The height is adjusted according to the icon's ratio.
</description>
</method>
<method name="set_icon_modulate">
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 9a8a1097cd..121af0170c 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -42,7 +42,8 @@
void GotoLineDialog::popup_find_line(CodeEdit *p_edit) {
text_editor = p_edit;
- line->set_text(itos(text_editor->get_caret_line()));
+ // Add 1 because text_editor->get_caret_line() starts from 0, but the editor user interface starts from 1.
+ line->set_text(itos(text_editor->get_caret_line() + 1));
line->select_all();
popup_centered(Size2(180, 80) * EDSCALE);
line->grab_focus();
@@ -53,12 +54,14 @@ int GotoLineDialog::get_line() const {
}
void GotoLineDialog::ok_pressed() {
- if (get_line() < 1 || get_line() > text_editor->get_line_count()) {
+ // Subtract 1 because the editor user interface starts from 1, but text_editor->set_caret_line(n) starts from 0.
+ const int line_number = get_line() - 1;
+ if (line_number < 0 || line_number >= text_editor->get_line_count()) {
return;
}
text_editor->remove_secondary_carets();
- text_editor->unfold_line(get_line() - 1);
- text_editor->set_caret_line(get_line() - 1);
+ text_editor->unfold_line(line_number);
+ text_editor->set_caret_line(line_number);
hide();
}
@@ -1092,13 +1095,13 @@ void CodeTextEditor::remove_find_replace_bar() {
}
void CodeTextEditor::trim_trailing_whitespace() {
- bool trimed_whitespace = false;
+ bool trimmed_whitespace = false;
for (int i = 0; i < text_editor->get_line_count(); i++) {
String line = text_editor->get_line(i);
if (line.ends_with(" ") || line.ends_with("\t")) {
- if (!trimed_whitespace) {
+ if (!trimmed_whitespace) {
text_editor->begin_complex_operation();
- trimed_whitespace = true;
+ trimmed_whitespace = true;
}
int end = 0;
@@ -1112,7 +1115,7 @@ void CodeTextEditor::trim_trailing_whitespace() {
}
}
- if (trimed_whitespace) {
+ if (trimmed_whitespace) {
text_editor->merge_overlapping_carets();
text_editor->end_complex_operation();
text_editor->queue_redraw();
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index ee47ee5729..aaf106a1c7 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -462,6 +462,11 @@ void CreateDialog::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED: {
+ const int icon_width = get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"));
+ search_options->add_theme_constant_override("icon_max_width", icon_width);
+ favorites->add_theme_constant_override("icon_max_width", icon_width);
+ recent->set_fixed_icon_size(Size2(icon_width, icon_width));
+
_update_theme();
} break;
}
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 3059ce445c..c381c8c322 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -31,10 +31,13 @@
#include "editor_data.h"
#include "core/config/project_settings.h"
+#include "core/extension/gdextension_manager.h"
#include "core/io/file_access.h"
+#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
+#include "editor/editor_scale.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/plugins/script_editor_plugin.h"
#include "scene/resources/packed_scene.h"
@@ -457,10 +460,10 @@ void EditorData::add_custom_type(const String &p_type, const String &p_inherits,
ct.name = p_type;
ct.icon = p_icon;
ct.script = p_script;
+
if (!custom_types.has(p_inherits)) {
custom_types[p_inherits] = Vector<CustomType>();
}
-
custom_types[p_inherits].push_back(ct);
}
@@ -1028,8 +1031,66 @@ void EditorData::script_class_load_icon_paths() {
}
}
+Ref<Texture2D> EditorData::extension_class_get_icon(const String &p_class) const {
+ if (GDExtensionManager::get_singleton()->class_has_icon_path(p_class)) {
+ String icon_path = GDExtensionManager::get_singleton()->class_get_icon_path(p_class);
+ Ref<Texture2D> icon = _load_script_icon(icon_path);
+ if (icon.is_valid()) {
+ return icon;
+ }
+ }
+ return nullptr;
+}
+
+Ref<Texture2D> EditorData::_load_script_icon(const String &p_path) const {
+ if (!p_path.is_empty() && ResourceLoader::exists(p_path)) {
+ Ref<Texture2D> icon = ResourceLoader::load(p_path);
+ if (icon.is_valid()) {
+ return icon;
+ }
+ }
+ return nullptr;
+}
+
+Ref<Texture2D> EditorData::get_script_icon(const Ref<Script> &p_script) {
+ // Take from the local cache, if available.
+ if (_script_icon_cache.has(p_script) && _script_icon_cache[p_script].is_valid()) {
+ return _script_icon_cache[p_script];
+ }
+
+ Ref<Script> base_scr = p_script;
+ while (base_scr.is_valid()) {
+ // Check for scripted classes.
+ StringName class_name = script_class_get_name(base_scr->get_path());
+ String icon_path = script_class_get_icon_path(class_name);
+ Ref<Texture2D> icon = _load_script_icon(icon_path);
+ if (icon.is_valid()) {
+ _script_icon_cache[p_script] = icon;
+ return icon;
+ }
+
+ // Check for legacy custom classes defined by plugins.
+ // TODO: Should probably be deprecated in 4.x
+ const EditorData::CustomType *ctype = get_custom_type_by_path(base_scr->get_path());
+ if (ctype && ctype->icon.is_valid()) {
+ _script_icon_cache[p_script] = ctype->icon;
+ return ctype->icon;
+ }
+
+ // Move to the base class.
+ base_scr = base_scr->get_base_script();
+ }
+
+ // If no icon found, cache it as null.
+ _script_icon_cache[p_script] = Ref<Texture>();
+ return nullptr;
+}
+
+void EditorData::clear_script_icon_cache() {
+ _script_icon_cache.clear();
+}
+
EditorData::EditorData() {
- current_edited_scene = -1;
undo_redo_manager = memnew(EditorUndoRedoManager);
script_class_load_icon_paths();
}
diff --git a/editor/editor_data.h b/editor/editor_data.h
index d00501280d..370963074c 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -144,6 +144,9 @@ private:
HashMap<StringName, String> _script_class_icon_paths;
HashMap<String, StringName> _script_class_file_to_path;
+ HashMap<Ref<Script>, Ref<Texture>> _script_icon_cache;
+
+ Ref<Texture2D> _load_script_icon(const String &p_path) const;
public:
EditorPlugin *get_editor(Object *p_object);
@@ -240,6 +243,11 @@ public:
void script_class_save_icon_paths();
void script_class_load_icon_paths();
+ Ref<Texture2D> extension_class_get_icon(const String &p_class) const;
+
+ Ref<Texture2D> get_script_icon(const Ref<Script> &p_script);
+ void clear_script_icon_cache();
+
EditorData();
~EditorData();
};
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index cc569e777b..61dabb9541 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -263,16 +263,8 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum) {
class_desc->pop();
}
-void EditorHelp::_add_type_icon(const String &p_type, int p_size) {
- Ref<Texture2D> icon;
- if (has_theme_icon(p_type, SNAME("EditorIcons"))) {
- icon = get_theme_icon(p_type, SNAME("EditorIcons"));
- } else if (ClassDB::class_exists(p_type) && ClassDB::is_parent_class(p_type, "Object")) {
- icon = get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
- } else {
- icon = get_theme_icon(SNAME("ArrowRight"), SNAME("EditorIcons"));
- }
-
+void EditorHelp::_add_type_icon(const String &p_type, int p_size, const String &p_fallback) {
+ Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(p_type, p_fallback);
Vector2i size = Vector2i(icon->get_width(), icon->get_height());
if (p_size > 0) {
// Ensures icon scales proportionally on both axis, based on icon height.
@@ -644,7 +636,7 @@ void EditorHelp::_update_doc() {
section_line.push_back(Pair<String, int>(TTR("Top"), 0));
_push_title_font();
class_desc->add_text(TTR("Class:") + " ");
- _add_type_icon(edited_class, theme_cache.doc_title_font_size);
+ _add_type_icon(edited_class, theme_cache.doc_title_font_size, "Object");
class_desc->add_text(" ");
class_desc->push_color(theme_cache.headline_color);
_add_text(edited_class);
@@ -676,7 +668,7 @@ void EditorHelp::_update_doc() {
String inherits = cd.inherits;
while (!inherits.is_empty()) {
- _add_type_icon(inherits);
+ _add_type_icon(inherits, theme_cache.doc_font_size, "ArrowRight");
class_desc->add_text(non_breaking_space); // Otherwise icon borrows hyperlink from _add_type().
_add_type(inherits);
@@ -709,7 +701,7 @@ void EditorHelp::_update_doc() {
if (prev) {
class_desc->add_text(" , ");
}
- _add_type_icon(E.value.name);
+ _add_type_icon(E.value.name, theme_cache.doc_font_size, "ArrowRight");
class_desc->add_text(non_breaking_space); // Otherwise icon borrows hyperlink from _add_type().
_add_type(E.value.name);
prev = true;
diff --git a/editor/editor_help.h b/editor/editor_help.h
index b2ffe3bc29..a23dbca213 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -159,7 +159,7 @@ class EditorHelp : public VBoxContainer {
//void _button_pressed(int p_idx);
void _add_type(const String &p_type, const String &p_enum = String());
- void _add_type_icon(const String &p_type, int p_size = 0);
+ void _add_type_icon(const String &p_type, int p_size = 0, const String &p_fallback = "");
void _add_method(const DocData::MethodDoc &p_method, bool p_overview = true);
void _add_bulletpoint();
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 44426dc143..87c4dabd8d 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -1134,17 +1134,21 @@ void EditorInspectorCategory::_notification(int p_what) {
int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
int hs = get_theme_constant(SNAME("h_separation"), SNAME("Tree"));
+ int icon_size = get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"));
int w = font->get_string_size(label, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width;
if (icon.is_valid()) {
- w += hs + icon->get_width();
+ w += hs + icon_size;
}
int ofs = (get_size().width - w) / 2;
if (icon.is_valid()) {
- draw_texture(icon, Point2(ofs, (get_size().height - icon->get_height()) / 2).floor());
- ofs += hs + icon->get_width();
+ Size2 rect_size = Size2(icon_size, icon_size);
+ Point2 rect_pos = Point2(ofs, (get_size().height - icon_size) / 2).floor();
+ draw_texture_rect(icon, Rect2(rect_pos, rect_size));
+
+ ofs += hs + icon_size;
}
Color color = get_theme_color(SNAME("font_color"), SNAME("Tree"));
@@ -2753,46 +2757,28 @@ void EditorInspector::update_tree() {
String label = p.name;
doc_name = p.name;
- // Set the category icon.
+ // Use category's owner script to update some of its information.
if (!EditorNode::get_editor_data().is_type_recognized(type) && p.hint_string.length() && FileAccess::exists(p.hint_string)) {
- // If we have a category inside a script, search for the first script with a valid icon.
+ StringName script_name;
+
Ref<Script> scr = ResourceLoader::load(p.hint_string, "Script");
- StringName base_type;
- StringName name;
if (scr.is_valid()) {
- base_type = scr->get_instance_base_type();
- name = EditorNode::get_editor_data().script_class_get_name(scr->get_path());
+ script_name = EditorNode::get_editor_data().script_class_get_name(scr->get_path());
+
+ // Update the docs reference and the label based on the script.
Vector<DocData::ClassDoc> docs = scr->get_documentation();
if (!docs.is_empty()) {
doc_name = docs[0].name;
}
- if (name != StringName() && label != name) {
- label = name;
+ if (script_name != StringName() && label != script_name) {
+ label = script_name;
}
}
- while (scr.is_valid()) {
- name = EditorNode::get_editor_data().script_class_get_name(scr->get_path());
- String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name);
- if (name != StringName() && !icon_path.is_empty()) {
- category->icon = ResourceLoader::load(icon_path, "Texture");
- break;
- }
- const EditorData::CustomType *ctype = EditorNode::get_editor_data().get_custom_type_by_path(scr->get_path());
- if (ctype) {
- category->icon = ctype->icon;
- break;
- }
- scr = scr->get_base_script();
- }
- if (category->icon.is_null() && has_theme_icon(base_type, SNAME("EditorIcons"))) {
- category->icon = get_theme_icon(base_type, SNAME("EditorIcons"));
- }
- }
- if (category->icon.is_null()) {
- if (!type.is_empty()) { // Can happen for built-in scripts.
- category->icon = EditorNode::get_singleton()->get_class_icon(type, "Object");
- }
+ // Find the corresponding icon.
+ category->icon = EditorNode::get_singleton()->get_class_icon(script_name, "Object");
+ } else if (!type.is_empty()) {
+ category->icon = EditorNode::get_singleton()->get_class_icon(type, "Object");
}
// Set the category label.
@@ -3133,6 +3119,11 @@ void EditorInspector::update_tree() {
StringName propname = property_prefix + p.name;
bool found = false;
+ // Small hack for theme_overrides. They are listed under Control, but come from another class.
+ if (classname == "Control" && p.name.begins_with("theme_override_")) {
+ classname = get_edited_object()->get_class();
+ }
+
// Search for the property description in the cache.
HashMap<StringName, HashMap<StringName, PropertyDocInfo>>::Iterator E = doc_info_cache.find(classname);
if (E) {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 3e6faf99e7..4fe43f3892 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -34,7 +34,6 @@
#include "core/input/input.h"
#include "core/io/config_file.h"
#include "core/io/file_access.h"
-#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/class_db.h"
@@ -682,10 +681,6 @@ void EditorNode::_notification(int p_what) {
editor_data.clear_edited_scenes();
} break;
- case Control::NOTIFICATION_THEME_CHANGED: {
- scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size());
- } break;
-
case NOTIFICATION_READY: {
{
_initializing_plugins = true;
@@ -773,6 +768,9 @@ void EditorNode::_notification(int p_what) {
bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("BottomPanel"), SNAME("EditorStyles")));
tabbar_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("tabbar_background"), SNAME("TabContainer")));
+ scene_tabs->add_theme_constant_override("icon_max_width", gui_base->get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")));
+ scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size());
+
main_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles")));
}
@@ -3682,7 +3680,7 @@ void EditorNode::_remove_edited_scene(bool p_change_tab) {
void EditorNode::_remove_scene(int index, bool p_change_tab) {
// Clear icon cache in case some scripts are no longer needed.
- script_icon_cache.clear();
+ editor_data.clear_script_icon_cache();
if (editor_data.get_edited_scene() == index) {
// Scene to remove is current scene.
@@ -4446,18 +4444,6 @@ StringName EditorNode::get_object_custom_type_name(const Object *p_object) const
return StringName();
}
-Ref<ImageTexture> EditorNode::_load_custom_class_icon(const String &p_path) const {
- if (p_path.length()) {
- Ref<Image> img = memnew(Image);
- Error err = ImageLoader::load_image(p_path, img);
- if (err == OK) {
- img->resize(16 * EDSCALE, 16 * EDSCALE, Image::INTERPOLATE_LANCZOS);
- return ImageTexture::create_from_image(img);
- }
- }
- return nullptr;
-}
-
void EditorNode::_pick_main_scene_custom_action(const String &p_custom_action_name) {
if (p_custom_action_name == "select_current") {
Node *scene = editor_data.get_edited_scene_root();
@@ -4480,106 +4466,86 @@ void EditorNode::_pick_main_scene_custom_action(const String &p_custom_action_na
}
}
-Ref<Texture2D> EditorNode::get_object_icon(const Object *p_object, const String &p_fallback) {
- ERR_FAIL_COND_V(!p_object || !gui_base, nullptr);
+Ref<Texture2D> EditorNode::_get_class_or_script_icon(const String &p_class, const Ref<Script> &p_script, const String &p_fallback) {
+ ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty.");
+ EditorData &ed = EditorNode::get_editor_data();
- Ref<Script> scr = p_object->get_script();
- if (scr.is_null() && p_object->is_class("Script")) {
- scr = p_object;
- }
+ // Check for a script icon first.
+ if (p_script.is_valid()) {
+ Ref<Texture2D> script_icon = ed.get_script_icon(p_script);
+ if (script_icon.is_valid()) {
+ return script_icon;
+ }
- if (scr.is_valid() && !script_icon_cache.has(scr)) {
- Ref<Script> base_scr = scr;
- while (base_scr.is_valid()) {
- StringName name = EditorNode::get_editor_data().script_class_get_name(base_scr->get_path());
- String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name);
- Ref<ImageTexture> icon = _load_custom_class_icon(icon_path);
- if (icon.is_valid()) {
- script_icon_cache[scr] = icon;
- return icon;
- }
+ // No custom icon was found in the inheritance chain, so check the base
+ // class of the script instead.
+ String base_type;
+ p_script->get_language()->get_global_class_name(p_script->get_path(), &base_type);
- // TODO: should probably be deprecated in 4.x
- StringName base = base_scr->get_instance_base_type();
- if (base != StringName() && EditorNode::get_editor_data().get_custom_types().has(base)) {
- const Vector<EditorData::CustomType> &types = EditorNode::get_editor_data().get_custom_types()[base];
- for (int i = 0; i < types.size(); ++i) {
- if (types[i].script == base_scr && types[i].icon.is_valid()) {
- script_icon_cache[scr] = types[i].icon;
- return types[i].icon;
- }
- }
- }
- base_scr = base_scr->get_base_script();
+ // Check if the base type is an extension-defined type.
+ Ref<Texture2D> ext_icon = ed.extension_class_get_icon(base_type);
+ if (ext_icon.is_valid()) {
+ return ext_icon;
}
- // If no icon found, cache it as null.
- script_icon_cache[scr] = Ref<Texture>();
- } else if (scr.is_valid() && script_icon_cache.has(scr) && script_icon_cache[scr].is_valid()) {
- return script_icon_cache[scr];
+ // Look for the base type in the editor theme.
+ // This is only relevant for built-in classes.
+ if (gui_base && gui_base->has_theme_icon(base_type, "EditorIcons")) {
+ return gui_base->get_theme_icon(base_type, "EditorIcons");
+ }
}
- // TODO: Should probably be deprecated in 4.x.
- if (p_object->has_meta("_editor_icon")) {
- return p_object->get_meta("_editor_icon");
+ // Script was not valid or didn't yield any useful values, try the class name
+ // directly.
+
+ // Check if the class name is an extension-defined type.
+ Ref<Texture2D> ext_icon = ed.extension_class_get_icon(p_class);
+ if (ext_icon.is_valid()) {
+ return ext_icon;
}
- if (gui_base->has_theme_icon(p_object->get_class(), SNAME("EditorIcons"))) {
- return gui_base->get_theme_icon(p_object->get_class(), SNAME("EditorIcons"));
+ // Check if the class name is a custom type.
+ // TODO: Should probably be deprecated in 4.x
+ const EditorData::CustomType *ctype = ed.get_custom_type_by_name(p_class);
+ if (ctype && ctype->icon.is_valid()) {
+ return ctype->icon;
}
- if (p_fallback.length()) {
- return gui_base->get_theme_icon(p_fallback, SNAME("EditorIcons"));
+ // Look up the class name or the fallback name in the editor theme.
+ // This is only relevant for built-in classes.
+ if (gui_base) {
+ if (gui_base->has_theme_icon(p_class, SNAME("EditorIcons"))) {
+ return gui_base->get_theme_icon(p_class, SNAME("EditorIcons"));
+ }
+
+ if (p_fallback.length() && gui_base->has_theme_icon(p_fallback, SNAME("EditorIcons"))) {
+ return gui_base->get_theme_icon(p_fallback, SNAME("EditorIcons"));
+ }
}
return nullptr;
}
-Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) const {
- ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty.");
+Ref<Texture2D> EditorNode::get_object_icon(const Object *p_object, const String &p_fallback) {
+ ERR_FAIL_NULL_V_MSG(p_object, nullptr, "Object cannot be null.");
- if (ScriptServer::is_global_class(p_class)) {
- String class_name = p_class;
- Ref<Script> scr = EditorNode::get_editor_data().script_class_load_script(class_name);
-
- while (true) {
- String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(class_name);
- Ref<Texture> icon = _load_custom_class_icon(icon_path);
- if (icon.is_valid()) {
- return icon; // Current global class has icon.
- }
-
- // Find next global class along the inheritance chain.
- do {
- Ref<Script> base_scr = scr->get_base_script();
- if (base_scr.is_null()) {
- // We've reached a native class, use its icon.
- String base_type;
- scr->get_language()->get_global_class_name(scr->get_path(), &base_type);
- if (gui_base->has_theme_icon(base_type, "EditorIcons")) {
- return gui_base->get_theme_icon(base_type, "EditorIcons");
- }
- return gui_base->get_theme_icon(p_fallback, "EditorIcons");
- }
- scr = base_scr;
- class_name = EditorNode::get_editor_data().script_class_get_name(scr->get_path());
- } while (class_name.is_empty());
- }
+ Ref<Script> scr = p_object->get_script();
+ if (scr.is_null() && p_object->is_class("Script")) {
+ scr = p_object;
}
- if (const EditorData::CustomType *ctype = EditorNode::get_editor_data().get_custom_type_by_name(p_class)) {
- return ctype->icon;
- }
+ return _get_class_or_script_icon(p_object->get_class(), scr, p_fallback);
+}
- if (gui_base->has_theme_icon(p_class, SNAME("EditorIcons"))) {
- return gui_base->get_theme_icon(p_class, SNAME("EditorIcons"));
- }
+Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) {
+ ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty.");
- if (p_fallback.length() && gui_base->has_theme_icon(p_fallback, SNAME("EditorIcons"))) {
- return gui_base->get_theme_icon(p_fallback, SNAME("EditorIcons"));
+ Ref<Script> scr;
+ if (ScriptServer::is_global_class(p_class)) {
+ scr = EditorNode::get_editor_data().script_class_load_script(p_class);
}
- return nullptr;
+ return _get_class_or_script_icon(p_class, scr, p_fallback);
}
bool EditorNode::is_object_of_custom_type(const Object *p_object, const StringName &p_class) {
@@ -7159,6 +7125,7 @@ EditorNode::EditorNode() {
scene_tabs->set_select_with_rmb(true);
scene_tabs->add_tab("unsaved");
scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int());
+ scene_tabs->add_theme_constant_override("icon_max_width", gui_base->get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")));
scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE);
scene_tabs->set_drag_to_rearrange_enabled(true);
scene_tabs->set_auto_translate(false);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 66a3bf5be2..6bf2750fd4 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -513,7 +513,6 @@ private:
PrintHandlerList print_handler;
HashMap<String, Ref<Texture2D>> icon_type_cache;
- HashMap<Ref<Script>, Ref<Texture>> script_icon_cache;
static EditorBuildCallback build_callbacks[MAX_BUILD_CALLBACKS];
static EditorPluginInitializeCallback plugin_init_callbacks[MAX_INIT_CALLBACKS];
@@ -697,7 +696,8 @@ private:
void _feature_profile_changed();
bool _is_class_editor_disabled_by_feature_profile(const StringName &p_class);
- Ref<ImageTexture> _load_custom_class_icon(const String &p_path) const;
+
+ Ref<Texture2D> _get_class_or_script_icon(const String &p_class, const Ref<Script> &p_script, const String &p_fallback = "Object");
void _pick_main_scene_custom_action(const String &p_custom_action_name);
@@ -879,7 +879,7 @@ public:
Ref<Script> get_object_custom_type_base(const Object *p_object) const;
StringName get_object_custom_type_name(const Object *p_object) const;
Ref<Texture2D> get_object_icon(const Object *p_object, const String &p_fallback = "Object");
- Ref<Texture2D> get_class_icon(const String &p_class, const String &p_fallback = "Object") const;
+ Ref<Texture2D> get_class_icon(const String &p_class, const String &p_fallback = "Object");
bool is_object_of_custom_type(const Object *p_object, const StringName &p_class);
diff --git a/editor/editor_path.cpp b/editor/editor_path.cpp
index 01e3bff5c2..bb464ee366 100644
--- a/editor/editor_path.cpp
+++ b/editor/editor_path.cpp
@@ -201,8 +201,12 @@ void EditorPath::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
update_path();
- sub_objects_icon->set_texture(get_theme_icon(SNAME("arrow"), SNAME("OptionButton")));
+ int icon_size = get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"));
+
+ current_object_icon->set_custom_minimum_size(Size2(icon_size, icon_size));
current_object_label->add_theme_font_override("font", get_theme_font(SNAME("main"), SNAME("EditorFonts")));
+ sub_objects_icon->set_texture(get_theme_icon(SNAME("arrow"), SNAME("OptionButton")));
+ sub_objects_menu->add_theme_constant_override("icon_max_width", icon_size);
} break;
case NOTIFICATION_READY: {
@@ -227,7 +231,8 @@ EditorPath::EditorPath(EditorSelectionHistory *p_history) {
main_mc->add_child(main_hb);
current_object_icon = memnew(TextureRect);
- current_object_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
+ current_object_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
+ current_object_icon->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
main_hb->add_child(current_object_icon);
current_object_label = memnew(Label);
diff --git a/editor/editor_quick_open.cpp b/editor/editor_quick_open.cpp
index b90edb8f90..c7c41a6ed0 100644
--- a/editor/editor_quick_open.cpp
+++ b/editor/editor_quick_open.cpp
@@ -69,10 +69,9 @@ void EditorQuickOpen::_build_search_cache(EditorFileSystemDirectory *p_efsd) {
for (int i = 0; i < p_efsd->get_file_count(); i++) {
String file = p_efsd->get_file_path(i);
String engine_type = p_efsd->get_file_type(i);
-
String script_type = p_efsd->get_file_resource_script_class(i);
-
String actual_type = script_type.is_empty() ? engine_type : script_type;
+
// Iterate all possible base types.
for (String &parent_type : base_types) {
if (ClassDB::is_parent_class(engine_type, parent_type) || EditorNode::get_editor_data().script_class_is_parent(script_type, parent_type)) {
@@ -81,7 +80,7 @@ void EditorQuickOpen::_build_search_cache(EditorFileSystemDirectory *p_efsd) {
// Store refs to used icons.
String ext = file.get_extension();
if (!icons.has(ext)) {
- icons.insert(ext, get_theme_icon((has_theme_icon(actual_type, SNAME("EditorIcons")) ? actual_type : "Object"), SNAME("EditorIcons")));
+ icons.insert(ext, EditorNode::get_singleton()->get_class_icon(actual_type, "Object"));
}
// Stop testing base types as soon as we got a match.
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 17f005a579..4e7b7a8434 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -784,6 +784,7 @@ void EditorResourcePicker::_notification(int p_what) {
[[fallthrough]];
}
case NOTIFICATION_THEME_CHANGED: {
+ assign_button->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")));
edit_button->set_icon(get_theme_icon(SNAME("select_arrow"), SNAME("Tree")));
} break;
@@ -923,6 +924,7 @@ EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) {
assign_button = memnew(Button);
assign_button->set_flat(true);
assign_button->set_h_size_flags(SIZE_EXPAND_FILL);
+ assign_button->set_expand_icon(true);
assign_button->set_clip_text(true);
assign_button->set_auto_translate(false);
SET_DRAG_FORWARDING_GCD(assign_button, EditorResourcePicker);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 3e6b0ee07f..ea34c07740 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -587,9 +587,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
} else {
theme->set_color("highend_color", "Editor", Color(1.0, 0.0, 0.0));
}
+
const int thumb_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
theme->set_constant("scale", "Editor", EDSCALE);
theme->set_constant("thumb_size", "Editor", thumb_size);
+ theme->set_constant("class_icon_size", "Editor", 16 * EDSCALE);
theme->set_constant("dark_theme", "Editor", dark_theme);
theme->set_constant("color_picker_button_height", "Editor", 28 * EDSCALE);
diff --git a/editor/renames_map_3_to_4.cpp b/editor/renames_map_3_to_4.cpp
index b35c0987a3..1908e877e7 100644
--- a/editor/renames_map_3_to_4.cpp
+++ b/editor/renames_map_3_to_4.cpp
@@ -134,6 +134,7 @@ const char *RenamesMap3To4::enum_renames[][2] = {
{ "PAUSE_MODE_STOP", "PROCESS_MODE_PAUSABLE" }, // Node
{ "RENDER_DRAW_CALLS_IN_FRAME", "RENDER_TOTAL_DRAW_CALLS_IN_FRAME" }, // Performance
{ "RENDER_OBJECTS_IN_FRAME", "RENDER_TOTAL_OBJECTS_IN_FRAME" }, // Performance
+ { "SOURCE_GEOMETRY_NAVMESH_CHILDREN", "SOURCE_GEOMETRY_ROOT_NODE_CHILDREN" }, // NavigationMesh
{ "TEXTURE_TYPE_2D_ARRAY", "TEXTURE_LAYERED_2D_ARRAY" }, // RenderingServer
{ "TEXTURE_TYPE_CUBEMAP", "TEXTURE_LAYERED_CUBEMAP_ARRAY" }, // RenderingServer
{ "TRACKER_LEFT_HAND", "TRACKER_HAND_LEFT" }, // XRPositionalTracker
@@ -309,6 +310,7 @@ const char *RenamesMap3To4::gdscript_function_renames[][2] = {
{ "get_endian_swap", "is_big_endian" }, // File
{ "get_error_string", "get_error_message" }, // JSON
{ "get_filename", "get_scene_file_path" }, // Node -- WARNING: This may be used in a lot of other places.
+ { "get_final_location", "get_final_position" }, // NavigationAgent2D, NavigationAgent3D
{ "get_focus_neighbour", "get_focus_neighbor" }, // Control
{ "get_follow_smoothing", "get_position_smoothing_speed" }, // Camera2D
{ "get_font_types", "get_font_type_list" }, // Theme
@@ -328,6 +330,8 @@ const char *RenamesMap3To4::gdscript_function_renames[][2] = {
{ "get_importer_name", "_get_importer_name" }, // EditorImportPlugin
{ "get_interior_ambient", "get_ambient_color" }, // ReflectionProbe
{ "get_interior_ambient_energy", "get_ambient_color_energy" }, // ReflectionProbe
+ { "get_item_navmesh", "get_item_navigation_mesh" }, // MeshLibrary
+ { "get_item_navmesh_transform", "get_item_navigation_mesh_transform" }, // MeshLibrary
{ "get_iterations_per_second", "get_physics_ticks_per_second" }, // Engine
{ "get_last_mouse_speed", "get_last_mouse_velocity" }, // Input
{ "get_layer_mask_bit", "get_layer_mask_value" }, // VisualInstance3D
@@ -336,11 +340,14 @@ const char *RenamesMap3To4::gdscript_function_renames[][2] = {
{ "get_metakey", "is_meta_pressed" }, // InputEventWithModifiers
{ "get_mid_height", "get_height" }, // CapsuleMesh
{ "get_motion_remainder", "get_remainder" }, // PhysicsTestMotionResult2D
+ { "get_nav_path", "get_current_navigation_path" }, // NavigationAgent2D, NavigationAgent3D
+ { "get_nav_path_index", "get_current_navigation_path_index" }, // NavigationAgent2D, NavigationAgent3D
{ "get_neighbor_dist", "get_neighbor_distance" }, // NavigationAgent2D, NavigationAgent3D
{ "get_network_connected_peers", "get_peers" }, // Multiplayer API
{ "get_network_master", "get_multiplayer_authority" }, // Node
{ "get_network_peer", "get_multiplayer_peer" }, // Multiplayer API
{ "get_network_unique_id", "get_unique_id" }, // Multiplayer API
+ { "get_next_location", "get_next_path_position" }, // NavigationAgent2D, NavigationAgent3D
{ "get_ok", "get_ok_button" }, // AcceptDialog
{ "get_oneshot", "get_one_shot" }, // AnimatedTexture
{ "get_option_visibility", "_get_option_visibility" }, // EditorImportPlugin
@@ -379,6 +386,7 @@ const char *RenamesMap3To4::gdscript_function_renames[][2] = {
{ "get_surface_material_count", "get_surface_override_material_count" }, // MeshInstance3D
{ "get_tab_disabled", "is_tab_disabled" }, // Tab
{ "get_tab_hidden", "is_tab_hidden" }, // Tab
+ { "get_target_location", "get_target_position" }, // NavigationAgent2D, NavigationAgent3D
{ "get_text_align", "get_text_alignment" }, // Button
{ "get_theme_item_types", "get_theme_item_type_list" }, // Theme
{ "get_timer_process_mode", "get_timer_process_callback" }, // Timer
@@ -387,6 +395,7 @@ const char *RenamesMap3To4::gdscript_function_renames[][2] = {
{ "get_unit_db", "get_volume_db" }, // AudioStreamPlayer3D
{ "get_unit_offset", "get_progress_ratio" }, // PathFollow2D, PathFollow3D
{ "get_use_in_baked_light", "is_baking_navigation" }, // GridMap
+ { "get_verts_per_poly", "get_vertices_per_polygon" }, // NavigationMesh
{ "get_v_scrollbar", "get_v_scroll_bar" }, // ScrollContainer
{ "get_visible_name", "_get_visible_name" }, // EditorImportPlugin
{ "get_window_layout", "_get_window_layout" }, // EditorPlugin
@@ -467,6 +476,9 @@ const char *RenamesMap3To4::gdscript_function_renames[][2] = {
{ "property_list_changed_notify", "notify_property_list_changed" }, // Object
{ "recognize", "_recognize" }, // ResourceFormatLoader
{ "regen_normalmaps", "regen_normal_maps" }, // ArrayMesh
+ { "region_bake_navmesh", "region_bake_navigation_mesh" }, // Navigation3DServer
+ { "region_set_navmesh", "region_set_navigation_mesh" }, // Navigation3DServer
+ { "region_set_navpoly", "region_set_navigation_polygon" }, // Navigation2DServer
{ "remove_animation", "remove_animation_library" }, // AnimationPlayer
{ "remove_color_override", "remove_theme_color_override" }, // Control
{ "remove_constant_override", "remove_theme_constant_override" }, // Control
@@ -522,6 +534,8 @@ const char *RenamesMap3To4::gdscript_function_renames[][2] = {
{ "set_invert_faces", "set_flip_faces" }, // CSGPrimitive3D
{ "set_is_initialized", "_is_initialized" }, // XRInterface
{ "set_is_primary", "set_primary" }, // XRInterface
+ { "set_item_navmesh", "set_item_navigation_mesh" }, // MeshLibrary
+ { "set_item_navmesh_transform", "set_item_navigation_mesh_transform" }, // MeshLibrary
{ "set_iterations_per_second", "set_physics_ticks_per_second" }, // Engine
{ "set_layer_mask_bit", "set_layer_mask_value" }, // VisualInstance3D
{ "set_margins_preset", "set_offsets_preset" }, // Control
@@ -554,6 +568,7 @@ const char *RenamesMap3To4::gdscript_function_renames[][2] = {
{ "set_surface_material", "set_surface_override_material" }, // MeshInstance3D -- Breaks ImporterMesh.
{ "set_tab_align", "set_tab_alignment" }, // TabContainer
{ "set_tangent", "surface_set_tangent" }, // ImmediateGeometry -- Breaks SurfaceTool.
+ { "set_target_location", "set_target_position" }, // NavigationAgent2D, NavigationAgent3D
{ "set_text_align", "set_text_alignment" }, // Button
{ "set_timer_process_mode", "set_timer_process_callback" }, // Timer
{ "set_translation", "set_position" }, // Node3D -- This breaks GLTFNode, but it is used rarely.
@@ -561,6 +576,7 @@ const char *RenamesMap3To4::gdscript_function_renames[][2] = {
{ "set_unit_db", "set_volume_db" }, // AudioStreamPlayer3D
{ "set_unit_offset", "set_progress_ratio" }, // PathFollow2D, PathFollow3D
{ "set_uv2", "surface_set_uv2" }, // ImmediateMesh -- Breaks SurfaceTool.
+ { "set_verts_per_poly", "set_vertices_per_polygon" }, // NavigationMesh
{ "set_v_drag_enabled", "set_drag_vertical_enabled" }, // Camera2D
{ "set_valign", "set_vertical_alignment" }, // Label
{ "set_window_layout", "_set_window_layout" }, // EditorPlugin
@@ -718,6 +734,7 @@ const char *RenamesMap3To4::csharp_function_renames[][2] = {
{ "GetEnabledFocusMode", "GetFocusMode" }, // BaseButton
{ "GetEndianSwap", "IsBigEndian" }, // File
{ "GetErrorString", "GetErrorMessage" }, // JSON
+ { "GetFinalLocation", "GetFinalPosition" }, // NavigationAgent2D, NavigationAgent3D
{ "GetFocusNeighbour", "GetFocusNeighbor" }, // Control
{ "GetFollowSmoothing", "GetPositionSmoothingSpeed" }, // Camera2D
{ "GetFontTypes", "GetFontTypeList" }, // Theme
@@ -737,6 +754,8 @@ const char *RenamesMap3To4::csharp_function_renames[][2] = {
{ "GetImporterName", "_GetImporterName" }, // EditorImportPlugin
{ "GetInteriorAmbient", "GetAmbientColor" }, // ReflectionProbe
{ "GetInteriorAmbientEnergy", "GetAmbientColorEnergy" }, // ReflectionProbe
+ { "GetItemNavmesh", "GetItemMavigationMesh" }, // MeshLibrary
+ { "GetItemNavmeshTransform", "GetItemNavigationMeshTransform" }, // MeshLibrary
{ "GetIterationsPerSecond", "GetPhysicsTicksPerSecond" }, // Engine
{ "GetLastMouseSpeed", "GetLastMouseVelocity" }, // Input
{ "GetLayerMaskBit", "GetLayerMaskValue" }, // VisualInstance3D
@@ -745,11 +764,14 @@ const char *RenamesMap3To4::csharp_function_renames[][2] = {
{ "GetMetakey", "IsMetaPressed" }, // InputEventWithModifiers
{ "GetMidHeight", "GetHeight" }, // CapsuleMesh
{ "GetMotionRemainder", "GetRemainder" }, // PhysicsTestMotionResult2D
+ { "GetNavPath", "GetCurrentNavigationPath" }, // NavigationAgent2D, NavigationAgent3D
+ { "GetNavPathIndex", "GetCurrentNavigationPathIndex" }, // NavigationAgent2D, NavigationAgent3D
{ "GetNeighborDist", "GetNeighborDistance" }, // NavigationAgent2D, NavigationAgent3D
{ "GetNetworkConnectedPeers", "GetPeers" }, // Multiplayer API
{ "GetNetworkMaster", "GetMultiplayerAuthority" }, // Node
{ "GetNetworkPeer", "GetMultiplayerPeer" }, // Multiplayer API
{ "GetNetworkUniqueId", "GetUniqueId" }, // Multiplayer API
+ { "GetNextLocation", "GetNextPathPosition" }, // NavigationAgent2D, NavigationAgent3D
{ "GetOneshot", "GetOneShot" }, // AnimatedTexture
{ "GetOk", "GetOkButton" }, // AcceptDialog
{ "GetOptionVisibility", "_GetOptionVisibility" }, // EditorImportPlugin
@@ -785,6 +807,7 @@ const char *RenamesMap3To4::csharp_function_renames[][2] = {
{ "GetSurfaceMaterialCount", "GetSurfaceOverrideMaterialCount" }, // MeshInstance3D
{ "GetTabDisabled", "IsTabDisabled" }, // Tab
{ "GetTabHidden", "IsTabHidden" }, // Tab
+ { "GetTargetLocation", "GetTargetPosition" }, // NavigationAgent2D, NavigationAgent3D
{ "GetTextAlign", "GetTextAlignment" }, // Button
{ "GetThemeItemTypes", "GetThemeItemTypeList" }, // Theme
{ "GetTimerProcessMode", "GetTimerProcessCallback" }, // Timer
@@ -793,6 +816,7 @@ const char *RenamesMap3To4::csharp_function_renames[][2] = {
{ "GetUnitDb", "GetVolumeDb" }, // AudioStreamPlayer3D
{ "GetUnitOffset", "GetProgressRatio" }, // PathFollow2D, PathFollow3D
{ "GetUseInBakedLight", "IsBakingNavigation" }, // GridMap
+ { "GetVertsPerPoly", "GetVerticesPerPolygon" }, // NavigationMesh
{ "GetVScrollbar", "GetVScrollBar" }, // ScrollContainer
{ "GetVisibleName", "_GetVisibleName" }, // EditorImportPlugin
{ "GetWindowLayout", "_GetWindowLayout" }, // EditorPlugin
@@ -869,6 +893,9 @@ const char *RenamesMap3To4::csharp_function_renames[][2] = {
{ "PropertyListChangedNotify", "NotifyPropertyListChanged" }, // Object
{ "Recognize", "_Recognize" }, // ResourceFormatLoader
{ "RegenNormalmaps", "RegenNormalMaps" }, // ArrayMesh
+ { "RegionBakeNavmesh", "region_bake_navigation_mesh" }, // Navigation3DServer
+ { "RegionSetNavmesh", "RegionSetNavigationMesh" }, // Navigation3DServer
+ { "RegionSetNavpoly", "RegionSetNavigationPolygon" }, // Navigation2DServer
{ "RemoveAnimation", "RemoveAnimationLibrary" }, // AnimationPlayer
{ "RemoveColorOverride", "RemoveThemeColorOverride" }, // Control
{ "RemoveConstantOverride", "RemoveThemeConstantOverride" }, // Control
@@ -920,6 +947,8 @@ const char *RenamesMap3To4::csharp_function_renames[][2] = {
{ "SetInteriorAmbientEnergy", "SetAmbientColorEnergy" }, // ReflectionProbe
{ "SetIsInitialized", "_IsInitialized" }, // XRInterface
{ "SetIsPrimary", "SetPrimary" }, // XRInterface
+ { "SetItemNavmesh", "SetItemNavigationMesh" }, // MeshLibrary
+ { "SetItemNavmeshTransform", "SetItemNavigationMeshTransform" }, // MeshLibrary
{ "SetIterationsPerSecond", "SetPhysicsTicksPerSecond" }, // Engine
{ "SetLayerMaskBit", "SetLayerMaskValue" }, // VisualInstance3D
{ "SetMarginsPreset", "SetOffsetsPreset" }, // Control
@@ -951,6 +980,7 @@ const char *RenamesMap3To4::csharp_function_renames[][2] = {
{ "SetSurfaceMaterial", "SetSurfaceOverrideMaterial" }, // MeshInstance3D -- Breaks ImporterMesh.
{ "SetTabAlign", "SetTabAlignment" }, // TabContainer
{ "SetTangent", "SurfaceSetTangent" }, // ImmediateGeometry -- Breaks SurfaceTool.
+ { "SetTargetLocation", "SetTargetPosition" }, // NavigationAgent2D, NavigationAgent3D
{ "SetTextAlign", "SetTextAlignment" }, // Button
{ "SetTimerProcessMode", "SetTimerProcessCallback" }, // Timer
{ "SetTonemapAutoExposure", "SetTonemapAutoExposureEnabled" }, // Environment
@@ -959,6 +989,7 @@ const char *RenamesMap3To4::csharp_function_renames[][2] = {
{ "SetUnitDb", "SetVolumeDb" }, // AudioStreamPlayer3D
{ "SetUnitOffset", "SetProgressRatio" }, // PathFollow2D, PathFollow3D
{ "SetUv2", "SurfaceSetUv2" }, // ImmediateMesh -- Breaks SurfaceTool.
+ { "SetVertsPerPoly", "SetVerticesPerPolygon" }, // NavigationMesh
{ "SetVDragEnabled", "SetDragVerticalEnabled" }, // Camera2D
{ "SetValign", "SetVerticalAlignment" }, // Label
{ "SetWindowLayout", "_SetWindowLayout" }, // EditorPlugin
@@ -1092,6 +1123,8 @@ const char *RenamesMap3To4::gdscript_properties_renames[][2] = {
{ "margin_right", "offset_right" }, // Control -- Breaks NinePatchRect, StyleBox.
{ "margin_top", "offset_top" }, // Control -- Breaks NinePatchRect, StyleBox.
{ "mid_height", "height" }, // CapsuleMesh
+ { "navpoly", "navigation_polygon" }, // NavigationRegion2D
+ { "navmesh", "navigation_mesh" }, // NavigationRegion3D
{ "neighbor_dist", "neighbor_distance" }, // NavigationAgent2D, NavigationAgent3D
{ "octaves", "fractal_octaves" }, // OpenSimplexNoise -> FastNoiseLite
{ "offset_h", "drag_horizontal_offset" }, // Camera2D
@@ -1102,6 +1135,7 @@ const char *RenamesMap3To4::gdscript_properties_renames[][2] = {
{ "out_of_range_mode", "max_polyphony" }, // AudioStreamPlayer3D
{ "pause_mode", "process_mode" }, // Node
{ "physical_scancode", "physical_keycode" }, // InputEventKey
+ { "polygon_verts_per_poly", "polygon_vertices_per_polyon" }, // NavigationMesh
{ "popup_exclusive", "exclusive" }, // Window
{ "proximity_fade_enable", "proximity_fade_enabled" }, // Material
{ "rect_position", "position" }, // Control
@@ -1132,6 +1166,7 @@ const char *RenamesMap3To4::gdscript_properties_renames[][2] = {
{ "table_hseparation", "table_h_separation" }, // Theme
{ "table_vseparation", "table_v_separation" }, // Theme
{ "tangent", "orthogonal" }, // Vector2
+ { "target_location", "target_position" }, // NavigationAgent2D, NavigationAgent3D
{ "toplevel", "top_level" }, // Node
{ "translation", "position" }, // Node3D
{ "unit_db", "volume_db" }, // AudioStreamPlayer3D
@@ -1187,6 +1222,8 @@ const char *RenamesMap3To4::csharp_properties_renames[][2] = {
{ "MarginRight", "OffsetRight" }, // Control -- Breaks NinePatchRect, StyleBox.
{ "MarginTop", "OffsetTop" }, // Control -- Breaks NinePatchRect, StyleBox.
{ "MidHeight", "Height" }, // CapsuleMesh
+ { "Navpoly", "NavigationPolygon" }, // NavigationRegion2D
+ { "Navmesh", "NavigationMesh" }, // NavigationRegion3D
{ "NeighborDist", "NeighborDistance" }, // NavigationAgent2D, NavigationAgent3D
{ "Octaves", "FractalOctaves" }, // OpenSimplexNoise -> FastNoiseLite
{ "OffsetH", "DragHorizontalOffset" }, // Camera2D
@@ -1228,6 +1265,7 @@ const char *RenamesMap3To4::csharp_properties_renames[][2] = {
{ "TableHseparation", "TableHSeparation" }, // Theme
{ "TableVseparation", "TableVSeparation" }, // Theme
{ "Tangent", "Orthogonal" }, // Vector2
+ { "TargetLocation", "TargetPosition" }, // NavigationAgent2D, NavigationAgent3D
{ "Toplevel", "TopLevel" }, // Node
{ "Translation", "Position" }, // Node3D
{ "UnitDb", "VolumeDb" }, // AudioStreamPlayer3D
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index b6e5ddafe2..2fd783102c 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1278,13 +1278,6 @@ void SceneTreeDock::_notification(int p_what) {
spatial_editor_plugin->get_spatial_editor()->connect("item_lock_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree).bind(false));
spatial_editor_plugin->get_spatial_editor()->connect("item_group_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree).bind(false));
- button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- button_instance->set_icon(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
- button_create_script->set_icon(get_theme_icon(SNAME("ScriptCreate"), SNAME("EditorIcons")));
- button_detach_script->set_icon(get_theme_icon(SNAME("ScriptRemove"), SNAME("EditorIcons")));
- button_tree_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
-
- filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
filter->set_clear_button_enabled(true);
// create_root_dialog
@@ -1366,19 +1359,35 @@ void SceneTreeDock::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
scene_tree->set_auto_expand_selected(EDITOR_GET("docks/scene_tree/auto_expand_to_selected"), false);
+ } break;
+
+ case NOTIFICATION_THEME_CHANGED: {
button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
button_instance->set_icon(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
button_create_script->set_icon(get_theme_icon(SNAME("ScriptCreate"), SNAME("EditorIcons")));
button_detach_script->set_icon(get_theme_icon(SNAME("ScriptRemove"), SNAME("EditorIcons")));
button_tree_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
- button_2d->set_icon(get_theme_icon(SNAME("Node2D"), SNAME("EditorIcons")));
- button_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons")));
- button_ui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons")));
- button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")));
filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
- filter->set_clear_button_enabled(true);
+
+ // These buttons are created on READY, because reasons...
+ if (button_2d) {
+ button_2d->set_icon(get_theme_icon(SNAME("Node2D"), SNAME("EditorIcons")));
+ }
+ if (button_3d) {
+ button_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons")));
+ }
+ if (button_ui) {
+ button_ui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons")));
+ }
+ if (button_custom) {
+ button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ }
+ if (button_clipboard) {
+ button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")));
+ }
+
+ menu_subresources->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")));
} break;
case NOTIFICATION_PROCESS: {
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index 1f02ec4561..8ba6a1e610 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -878,6 +878,8 @@ void SceneTreeEditor::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED: {
+ tree->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")));
+
_update_tree();
} break;
}
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 4dcdbd6446..a7066bc41c 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -593,6 +593,7 @@ void NavMap::set_agent_as_controlled(NavAgent *agent) {
if (!exist) {
ERR_FAIL_COND(!has_agent(agent));
controlled_agents.push_back(agent);
+ agents_dirty = true;
}
}
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index ab5b054bb3..89bb1b5c65 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -2032,8 +2032,8 @@ void CharacterBody3D::_bind_methods() {
ADD_GROUP("Moving Platform", "platform_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_on_leave", PROPERTY_HINT_ENUM, "Add Velocity,Add Upward Velocity,Do Nothing", PROPERTY_USAGE_DEFAULT), "set_platform_on_leave", "get_platform_on_leave");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_floor_layers", "get_platform_floor_layers");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_wall_layers", "get_platform_wall_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_floor_layers", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_platform_floor_layers", "get_platform_floor_layers");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_wall_layers", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_platform_wall_layers", "get_platform_wall_layers");
ADD_GROUP("Collision", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001,suffix:m"), "set_safe_margin", "get_safe_margin");
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 2a8b1cd8c4..70d87e221c 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -83,6 +83,7 @@ void Button::_update_theme_item_cache() {
theme_cache.icon = get_theme_icon(SNAME("icon"));
theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+ theme_cache.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
}
void Button::_notification(int p_what) {
@@ -252,7 +253,6 @@ void Button::_notification(int p_what) {
float icon_ofs_region = 0.0;
Point2 style_offset;
- Size2 icon_size = _icon->get_size();
if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
style_offset.x = style->get_margin(SIDE_LEFT);
if (_internal_margin[SIDE_LEFT] > 0) {
@@ -268,6 +268,7 @@ void Button::_notification(int p_what) {
}
style_offset.y = style->get_margin(SIDE_TOP);
+ Size2 icon_size = _icon->get_size();
if (expand_icon) {
Size2 _size = get_size() - style->get_offset() * 2;
int icon_text_separation = text.is_empty() ? 0 : theme_cache.h_separation;
@@ -285,6 +286,7 @@ void Button::_notification(int p_what) {
icon_size = Size2(icon_width, icon_height);
}
+ icon_size = _fit_icon_size(icon_size);
if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
icon_region = Rect2(style_offset + Point2(icon_ofs_region, Math::floor((valign - icon_size.y) * 0.5)), icon_size);
@@ -365,6 +367,18 @@ void Button::_notification(int p_what) {
}
}
+Size2 Button::_fit_icon_size(const Size2 &p_size) const {
+ int max_width = theme_cache.icon_max_width;
+ Size2 icon_size = p_size;
+
+ if (max_width > 0 && icon_size.width > max_width) {
+ icon_size.height = icon_size.height * max_width / icon_size.width;
+ icon_size.width = max_width;
+ }
+
+ return icon_size;
+}
+
Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Texture2D> p_icon) const {
Ref<TextParagraph> paragraph;
if (p_text.is_empty()) {
@@ -380,15 +394,16 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu
}
if (!expand_icon && p_icon.is_valid()) {
- minsize.height = MAX(minsize.height, p_icon->get_height());
+ Size2 icon_size = _fit_icon_size(p_icon->get_size());
+ minsize.height = MAX(minsize.height, icon_size.height);
if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) {
- minsize.width += p_icon->get_width();
+ minsize.width += icon_size.width;
if (!xl_text.is_empty() || !p_text.is_empty()) {
minsize.width += MAX(0, theme_cache.h_separation);
}
} else {
- minsize.width = MAX(minsize.width, p_icon->get_width());
+ minsize.width = MAX(minsize.width, icon_size.width);
}
}
diff --git a/scene/gui/button.h b/scene/gui/button.h
index af1cde4b1e..3634b5344c 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -89,8 +89,11 @@ private:
Ref<Texture2D> icon;
int h_separation = 0;
+ int icon_max_width = 0;
} theme_cache;
+ Size2 _fit_icon_size(const Size2 &p_size) const;
+
void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = "");
protected:
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 1a6adca121..03860c7449 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -47,6 +47,26 @@ String PopupMenu::_get_accel_text(const Item &p_item) const {
return String();
}
+Size2 PopupMenu::_get_item_icon_size(int p_item) const {
+ const PopupMenu::Item &item = items[p_item];
+ Size2 icon_size = item.get_icon_size();
+
+ int max_width = 0;
+ if (theme_cache.icon_max_width > 0) {
+ max_width = theme_cache.icon_max_width;
+ }
+ if (item.icon_max_width > 0 && (max_width == 0 || item.icon_max_width < max_width)) {
+ max_width = item.icon_max_width;
+ }
+
+ if (max_width > 0 && icon_size.width > max_width) {
+ icon_size.height = icon_size.height * max_width / icon_size.width;
+ icon_size.width = max_width;
+ }
+
+ return icon_size;
+}
+
Size2 PopupMenu::_get_contents_minimum_size() const {
Size2 minsize = theme_cache.panel_style->get_minimum_size(); // Accounts for margin in the margin container
minsize.x += scroll_container->get_v_scroll_bar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
@@ -61,7 +81,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
Size2 item_size;
const_cast<PopupMenu *>(this)->_shape_item(i);
- Size2 icon_size = items[i].get_icon_size();
+ Size2 icon_size = _get_item_icon_size(i);
item_size.height = _get_item_height(i);
icon_w = MAX(icon_size.width, icon_w);
@@ -109,7 +129,8 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
int PopupMenu::_get_item_height(int p_item) const {
ERR_FAIL_INDEX_V(p_item, items.size(), 0);
- int icon_height = items[p_item].get_icon_size().height;
+ Size2 icon_size = _get_item_icon_size(p_item);
+ int icon_height = icon_size.height;
if (items[p_item].checkable_type && !items[p_item].separator) {
icon_height = MAX(icon_height, MAX(theme_cache.checked->get_height(), theme_cache.radio_checked->get_height()));
}
@@ -540,7 +561,8 @@ void PopupMenu::_draw_items() {
continue;
}
- icon_ofs = MAX(items[i].get_icon_size().width, icon_ofs);
+ Size2 icon_size = _get_item_icon_size(i);
+ icon_ofs = MAX(icon_size.width, icon_ofs);
if (items[i].checkable_type) {
has_check = true;
@@ -569,7 +591,7 @@ void PopupMenu::_draw_items() {
_shape_item(i);
Point2 item_ofs = ofs;
- Size2 icon_size = items[i].get_icon_size();
+ Size2 icon_size = _get_item_icon_size(i);
float h = _get_item_height(i);
if (i == mouse_over) {
@@ -631,21 +653,26 @@ void PopupMenu::_draw_items() {
// Icon
if (!items[i].icon.is_null()) {
+ const Point2 icon_offset = Point2(0, Math::floor((h - icon_size.height) / 2.0));
+ Point2 icon_pos;
+
if (items[i].separator) {
separator_ofs -= (icon_size.width + theme_cache.h_separation) / 2;
if (rtl) {
- items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ icon_pos = Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y);
} else {
- items[i].icon->draw(ci, item_ofs + Size2(separator_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ icon_pos = item_ofs + Size2(separator_ofs, 0);
}
} else {
if (rtl) {
- items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ icon_pos = Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y);
} else {
- items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ icon_pos = item_ofs + Size2(check_ofs, 0);
}
}
+
+ items[i].icon->draw_rect(ci, Rect2(icon_pos + icon_offset, icon_size), false, icon_color);
}
// Submenu arrow on right hand side.
@@ -802,6 +829,7 @@ void PopupMenu::_update_theme_item_cache() {
theme_cache.indent = get_theme_constant(SNAME("indent"));
theme_cache.item_start_padding = get_theme_constant(SNAME("item_start_padding"));
theme_cache.item_end_padding = get_theme_constant(SNAME("item_end_padding"));
+ theme_cache.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
theme_cache.checked = get_theme_icon(SNAME("checked"));
theme_cache.checked_disabled = get_theme_icon(SNAME("checked_disabled"));
@@ -946,8 +974,10 @@ void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) {
Item item;
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
notify_property_list_changed();
_menu_changed();
@@ -958,8 +988,10 @@ void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_labe
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.icon = p_icon;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
notify_property_list_changed();
_menu_changed();
@@ -970,8 +1002,10 @@ void PopupMenu::add_check_item(const String &p_label, int p_id, Key p_accel) {
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
_menu_changed();
}
@@ -982,8 +1016,10 @@ void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String &
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
}
@@ -992,8 +1028,10 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_id, Key p_acce
ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
_menu_changed();
}
@@ -1004,8 +1042,10 @@ void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const St
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
_menu_changed();
}
@@ -1016,8 +1056,10 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
item.max_states = p_max_states;
item.state = p_default_state;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
_menu_changed();
}
@@ -1035,8 +1077,10 @@ void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_g
Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
_menu_changed();
}
@@ -1046,8 +1090,10 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortc
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.icon = p_icon;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
_menu_changed();
}
@@ -1057,8 +1103,10 @@ void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bo
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
_menu_changed();
}
@@ -1069,8 +1117,10 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
_menu_changed();
}
@@ -1080,8 +1130,10 @@ void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
_menu_changed();
}
@@ -1092,8 +1144,10 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, cons
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
_menu_changed();
}
@@ -1105,8 +1159,10 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu,
item.id = p_id == -1 ? items.size() : p_id;
item.submenu = p_submenu;
items.push_back(item);
+
_shape_item(items.size() - 1);
control->queue_redraw();
+
child_controls_changed();
_menu_changed();
}
@@ -1176,6 +1232,23 @@ void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
_menu_changed();
}
+void PopupMenu::set_item_icon_max_width(int p_idx, int p_width) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
+ ERR_FAIL_INDEX(p_idx, items.size());
+
+ if (items[p_idx].icon_max_width == p_width) {
+ return;
+ }
+
+ items.write[p_idx].icon_max_width = p_width;
+
+ control->queue_redraw();
+ child_controls_changed();
+ _menu_changed();
+}
+
void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
if (p_idx < 0) {
p_idx += get_item_count();
@@ -1314,6 +1387,11 @@ Ref<Texture2D> PopupMenu::get_item_icon(int p_idx) const {
return items[p_idx].icon;
}
+int PopupMenu::get_item_icon_max_width(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), 0);
+ return items[p_idx].icon_max_width;
+}
+
Key PopupMenu::get_item_accelerator(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), Key::NONE);
return items[p_idx].accel;
@@ -2023,6 +2101,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_text_direction", "index", "direction"), &PopupMenu::set_item_text_direction);
ClassDB::bind_method(D_METHOD("set_item_language", "index", "language"), &PopupMenu::set_item_language);
ClassDB::bind_method(D_METHOD("set_item_icon", "index", "icon"), &PopupMenu::set_item_icon);
+ ClassDB::bind_method(D_METHOD("set_item_icon_max_width", "index", "width"), &PopupMenu::set_item_icon_max_width);
ClassDB::bind_method(D_METHOD("set_item_checked", "index", "checked"), &PopupMenu::set_item_checked);
ClassDB::bind_method(D_METHOD("set_item_id", "index", "id"), &PopupMenu::set_item_id);
ClassDB::bind_method(D_METHOD("set_item_accelerator", "index", "accel"), &PopupMenu::set_item_accelerator);
@@ -2045,6 +2124,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_text_direction", "index"), &PopupMenu::get_item_text_direction);
ClassDB::bind_method(D_METHOD("get_item_language", "index"), &PopupMenu::get_item_language);
ClassDB::bind_method(D_METHOD("get_item_icon", "index"), &PopupMenu::get_item_icon);
+ ClassDB::bind_method(D_METHOD("get_item_icon_max_width", "index"), &PopupMenu::get_item_icon_max_width);
ClassDB::bind_method(D_METHOD("is_item_checked", "index"), &PopupMenu::is_item_checked);
ClassDB::bind_method(D_METHOD("get_item_id", "index"), &PopupMenu::get_item_id);
ClassDB::bind_method(D_METHOD("get_item_index", "id"), &PopupMenu::get_item_index);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index bcc02a890f..74b739ac0f 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -42,6 +42,7 @@ class PopupMenu : public Popup {
struct Item {
Ref<Texture2D> icon;
+ int icon_max_width = 0;
String text;
String xl_text;
Ref<TextLine> text_buf;
@@ -103,6 +104,7 @@ class PopupMenu : public Popup {
int _get_item_height(int p_item) const;
int _get_items_total_height() const;
+ Size2 _get_item_icon_size(int p_item) const;
void _shape_item(int p_item);
@@ -144,6 +146,7 @@ class PopupMenu : public Popup {
int indent = 0;
int item_start_padding = 0;
int item_end_padding = 0;
+ int icon_max_width = 0;
Ref<Texture2D> checked;
Ref<Texture2D> checked_disabled;
@@ -222,6 +225,7 @@ public:
void set_item_text_direction(int p_idx, Control::TextDirection p_text_direction);
void set_item_language(int p_idx, const String &p_language);
void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon);
+ void set_item_icon_max_width(int p_idx, int p_width);
void set_item_checked(int p_idx, bool p_checked);
void set_item_id(int p_idx, int p_id);
void set_item_accelerator(int p_idx, Key p_accel);
@@ -245,6 +249,7 @@ public:
String get_item_language(int p_idx) const;
int get_item_idx_from_text(const String &text) const;
Ref<Texture2D> get_item_icon(int p_idx) const;
+ int get_item_icon_max_width(int p_idx) const;
bool is_item_checked(int p_idx) const;
int get_item_id(int p_idx) const;
int get_item_index(int p_id) const;
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 5e378a0321..5eb5a519ff 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -63,10 +63,10 @@ Size2 TabBar::get_minimum_size() const {
}
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 + y_margin);
- ms.width += tex->get_size().width + theme_cache.h_separation;
+ if (tabs[i].icon.is_valid()) {
+ const Size2 icon_size = _get_tab_icon_size(i);
+ ms.height = MAX(ms.height, icon_size.height + y_margin);
+ ms.width += icon_size.width + theme_cache.h_separation;
}
if (!tabs[i].text.is_empty()) {
@@ -304,6 +304,7 @@ void TabBar::_update_theme_item_cache() {
Control::_update_theme_item_cache();
theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+ theme_cache.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected"));
theme_cache.tab_selected_style = get_theme_stylebox(SNAME("tab_selected"));
@@ -492,9 +493,11 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in
// Draw the icon.
Ref<Texture2D> icon = tabs[p_index].icon;
if (icon.is_valid()) {
- 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));
+ const Size2 icon_size = _get_tab_icon_size(p_index);
+ const Point2 icon_pos = Point2i(rtl ? p_x - icon_size.width : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon_size.height) / 2);
+ icon->draw_rect(ci, Rect2(icon_pos, icon_size));
- p_x = rtl ? p_x - icon->get_width() - theme_cache.h_separation : p_x + icon->get_width() + theme_cache.h_separation;
+ p_x = rtl ? p_x - icon_size.width - theme_cache.h_separation : p_x + icon_size.width + theme_cache.h_separation;
}
// Draw the text.
@@ -719,6 +722,29 @@ Ref<Texture2D> TabBar::get_tab_icon(int p_tab) const {
return tabs[p_tab].icon;
}
+void TabBar::set_tab_icon_max_width(int p_tab, int p_width) {
+ ERR_FAIL_INDEX(p_tab, tabs.size());
+
+ if (tabs[p_tab].icon_max_width == p_width) {
+ return;
+ }
+
+ tabs.write[p_tab].icon_max_width = p_width;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
+ queue_redraw();
+ update_minimum_size();
+}
+
+int TabBar::get_tab_icon_max_width(int p_tab) const {
+ ERR_FAIL_INDEX_V(p_tab, tabs.size(), 0);
+ return tabs[p_tab].icon_max_width;
+}
+
void TabBar::set_tab_disabled(int p_tab, bool p_disabled) {
ERR_FAIL_INDEX(p_tab, tabs.size());
@@ -1023,9 +1049,14 @@ Variant TabBar::get_drag_data(const Point2 &p_point) {
HBoxContainer *drag_preview = memnew(HBoxContainer);
if (!tabs[tab_over].icon.is_null()) {
+ const Size2 icon_size = _get_tab_icon_size(tab_over);
+
TextureRect *tf = memnew(TextureRect);
tf->set_texture(tabs[tab_over].icon);
- tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
+ tf->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
+ tf->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
+ tf->set_custom_minimum_size(icon_size);
+
drag_preview->add_child(tf);
}
@@ -1270,9 +1301,9 @@ int TabBar::get_tab_width(int p_idx) const {
}
int x = style->get_minimum_size().width;
- Ref<Texture2D> tex = tabs[p_idx].icon;
- if (tex.is_valid()) {
- x += tex->get_width() + theme_cache.h_separation;
+ if (tabs[p_idx].icon.is_valid()) {
+ const Size2 icon_size = _get_tab_icon_size(p_idx);
+ x += icon_size.width + theme_cache.h_separation;
}
if (!tabs[p_idx].text.is_empty()) {
@@ -1305,6 +1336,27 @@ int TabBar::get_tab_width(int p_idx) const {
return x;
}
+Size2 TabBar::_get_tab_icon_size(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, tabs.size(), Size2());
+ const TabBar::Tab &tab = tabs[p_index];
+ Size2 icon_size = tab.icon->get_size();
+
+ int icon_max_width = 0;
+ if (theme_cache.icon_max_width > 0) {
+ icon_max_width = theme_cache.icon_max_width;
+ }
+ if (tab.icon_max_width > 0 && (icon_max_width == 0 || tab.icon_max_width < icon_max_width)) {
+ icon_max_width = tab.icon_max_width;
+ }
+
+ if (icon_max_width > 0 && icon_size.width > icon_max_width) {
+ icon_size.height = icon_size.height * icon_max_width / icon_size.width;
+ icon_size.width = icon_max_width;
+ }
+
+ return icon_size;
+}
+
void TabBar::_ensure_no_over_offset() {
if (!is_inside_tree() || !buttons_visible) {
return;
@@ -1547,6 +1599,8 @@ 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_icon_max_width", "tab_idx", "width"), &TabBar::set_tab_icon_max_width);
+ ClassDB::bind_method(D_METHOD("get_tab_icon_max_width", "tab_idx"), &TabBar::get_tab_icon_max_width);
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);
diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h
index dc70a84689..a232061b69 100644
--- a/scene/gui/tab_bar.h
+++ b/scene/gui/tab_bar.h
@@ -62,6 +62,8 @@ private:
Ref<TextLine> text_buf;
Ref<Texture2D> icon;
+ int icon_max_width = 0;
+
bool disabled = false;
bool hidden = false;
int ofs_cache = 0;
@@ -106,6 +108,7 @@ private:
struct ThemeCache {
int h_separation = 0;
+ int icon_max_width = 0;
Ref<StyleBox> tab_unselected_style;
Ref<StyleBox> tab_selected_style;
@@ -133,6 +136,7 @@ private:
} theme_cache;
int get_tab_width(int p_idx) const;
+ Size2 _get_tab_icon_size(int p_idx) const;
void _ensure_no_over_offset();
void _update_hover();
@@ -171,6 +175,9 @@ public:
void set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_tab_icon(int p_tab) const;
+ void set_tab_icon_max_width(int p_tab, int p_width);
+ int get_tab_icon_max_width(int p_tab) const;
+
void set_tab_disabled(int p_tab, bool p_disabled);
bool is_tab_disabled(int p_tab) const;
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 208cb29772..f4f7183475 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -146,6 +146,7 @@ void TabContainer::_update_theme_item_cache() {
// TabBar overrides.
theme_cache.icon_separation = get_theme_constant(SNAME("icon_separation"));
+ theme_cache.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
theme_cache.outline_size = get_theme_constant(SNAME("outline_size"));
theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected"));
@@ -245,6 +246,7 @@ void TabContainer::_on_theme_changed() {
tab_bar->add_theme_font_size_override(SNAME("font_size"), theme_cache.tab_font_size);
tab_bar->add_theme_constant_override(SNAME("h_separation"), theme_cache.icon_separation);
+ tab_bar->add_theme_constant_override(SNAME("icon_max_width"), theme_cache.icon_max_width);
tab_bar->add_theme_constant_override(SNAME("outline_size"), theme_cache.outline_size);
_update_margins();
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 3020e1fada..f64f1fe2a3 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -59,6 +59,7 @@ class TabContainer : public Container {
// TabBar overrides.
int icon_separation = 0;
+ int icon_max_width = 0;
int outline_size = 0;
Ref<StyleBox> tab_unselected_style;
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index 2526ee8215..2464e005ee 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -31,15 +31,10 @@
#include "texture_progress_bar.h"
#include "core/config/engine.h"
+#include "core/core_string_names.h"
void TextureProgressBar::set_under_texture(const Ref<Texture2D> &p_texture) {
- if (under == p_texture) {
- return;
- }
-
- under = p_texture;
- queue_redraw();
- update_minimum_size();
+ _set_texture(&under, p_texture);
}
Ref<Texture2D> TextureProgressBar::get_under_texture() const {
@@ -47,15 +42,7 @@ Ref<Texture2D> TextureProgressBar::get_under_texture() const {
}
void TextureProgressBar::set_over_texture(const Ref<Texture2D> &p_texture) {
- if (over == p_texture) {
- return;
- }
-
- over = p_texture;
- queue_redraw();
- if (under.is_null()) {
- update_minimum_size();
- }
+ _set_texture(&over, p_texture);
}
Ref<Texture2D> TextureProgressBar::get_over_texture() const {
@@ -108,13 +95,7 @@ Size2 TextureProgressBar::get_minimum_size() const {
}
void TextureProgressBar::set_progress_texture(const Ref<Texture2D> &p_texture) {
- if (progress == p_texture) {
- return;
- }
-
- progress = p_texture;
- queue_redraw();
- update_minimum_size();
+ _set_texture(&progress, p_texture);
}
Ref<Texture2D> TextureProgressBar::get_progress_texture() const {
@@ -173,6 +154,28 @@ Color TextureProgressBar::get_tint_over() const {
return tint_over;
}
+void TextureProgressBar::_set_texture(Ref<Texture2D> *p_destination, const Ref<Texture2D> &p_texture) {
+ DEV_ASSERT(p_destination);
+ Ref<Texture2D> &destination = *p_destination;
+ if (destination == p_texture) {
+ return;
+ }
+ if (destination.is_valid()) {
+ destination->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureProgressBar::_texture_changed));
+ }
+ destination = p_texture;
+ if (destination.is_valid()) {
+ // Pass `CONNECT_REFERENCE_COUNTED` to avoid early disconnect in case the same texture is assigned to different "slots".
+ destination->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureProgressBar::_texture_changed), CONNECT_REFERENCE_COUNTED);
+ }
+ _texture_changed();
+}
+
+void TextureProgressBar::_texture_changed() {
+ update_minimum_size();
+ queue_redraw();
+}
+
Point2 TextureProgressBar::unit_val_to_uv(float val) {
if (progress.is_null()) {
return Point2();
diff --git a/scene/gui/texture_progress_bar.h b/scene/gui/texture_progress_bar.h
index 53ec6fa2ae..5999aa986b 100644
--- a/scene/gui/texture_progress_bar.h
+++ b/scene/gui/texture_progress_bar.h
@@ -113,6 +113,8 @@ private:
Color tint_progress = Color(1, 1, 1);
Color tint_over = Color(1, 1, 1);
+ void _set_texture(Ref<Texture2D> *p_destination, const Ref<Texture2D> &p_texture);
+ void _texture_changed();
Point2 unit_val_to_uv(float val);
Point2 get_relative_center();
void draw_nine_patch_stretched(const Ref<Texture2D> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 45a8c48143..4137896b47 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1340,10 +1340,7 @@ Size2 TreeItem::get_minimum_size(int p_column) {
size.width += parent_tree->theme_cache.checked->get_width() + parent_tree->theme_cache.h_separation;
}
if (cell.icon.is_valid()) {
- Size2i icon_size = cell.get_icon_size();
- if (cell.icon_max_w > 0 && icon_size.width > cell.icon_max_w) {
- icon_size.width = cell.icon_max_w;
- }
+ Size2i icon_size = parent_tree->_get_cell_icon_size(cell);
size.width += icon_size.width + parent_tree->theme_cache.h_separation;
size.height = MAX(size.height, icon_size.height);
}
@@ -1628,6 +1625,7 @@ void Tree::_update_theme_item_cache() {
theme_cache.v_separation = get_theme_constant(SNAME("v_separation"));
theme_cache.item_margin = get_theme_constant(SNAME("item_margin"));
theme_cache.button_margin = get_theme_constant(SNAME("button_margin"));
+ theme_cache.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size"));
@@ -1654,6 +1652,25 @@ void Tree::_update_theme_item_cache() {
theme_cache.base_scale = get_theme_default_base_scale();
}
+Size2 Tree::_get_cell_icon_size(const TreeItem::Cell &p_cell) const {
+ Size2i icon_size = p_cell.get_icon_size();
+
+ int max_width = 0;
+ if (theme_cache.icon_max_width > 0) {
+ max_width = theme_cache.icon_max_width;
+ }
+ if (p_cell.icon_max_w > 0 && (max_width == 0 || p_cell.icon_max_w < max_width)) {
+ max_width = p_cell.icon_max_w;
+ }
+
+ if (max_width > 0 && icon_size.width > max_width) {
+ icon_size.height = icon_size.height * max_width / icon_size.width;
+ icon_size.width = max_width;
+ }
+
+ return icon_size;
+}
+
int Tree::compute_item_height(TreeItem *p_item) const {
if ((p_item == root && hide_root) || !p_item->is_visible()) {
return 0;
@@ -1688,10 +1705,7 @@ int Tree::compute_item_height(TreeItem *p_item) const {
case TreeItem::CELL_MODE_ICON: {
Ref<Texture2D> icon = p_item->cells[i].icon;
if (!icon.is_null()) {
- Size2i s = p_item->cells[i].get_icon_size();
- if (p_item->cells[i].icon_max_w > 0 && s.width > p_item->cells[i].icon_max_w) {
- s.height = s.height * p_item->cells[i].icon_max_w / s.width;
- }
+ Size2i s = _get_cell_icon_size(p_item->cells[i]);
if (s.height > height) {
height = s.height;
}
@@ -1745,10 +1759,7 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
int w = 0;
if (!p_cell.icon.is_null()) {
- Size2i bmsize = p_cell.get_icon_size();
- if (p_cell.icon_max_w > 0 && bmsize.width > p_cell.icon_max_w) {
- bmsize.width = p_cell.icon_max_w;
- }
+ Size2i bmsize = _get_cell_icon_size(p_cell);
w += bmsize.width + theme_cache.h_separation;
if (rect.size.width > 0 && (w + ts.width) > rect.size.width) {
ts.width = rect.size.width - w;
@@ -1788,12 +1799,7 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
}
if (!p_cell.icon.is_null()) {
- Size2i bmsize = p_cell.get_icon_size();
-
- if (p_cell.icon_max_w > 0 && bmsize.width > p_cell.icon_max_w) {
- bmsize.height = bmsize.height * p_cell.icon_max_w / bmsize.width;
- bmsize.width = p_cell.icon_max_w;
- }
+ Size2i bmsize = _get_cell_icon_size(p_cell);
p_cell.draw_icon(ci, rect.position + Size2i(0, Math::floor((real_t)(rect.size.y - bmsize.y) / 2)), bmsize, p_icon_color);
rect.position.x += bmsize.x + theme_cache.h_separation;
@@ -2206,12 +2212,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
if (p_item->cells[i].icon.is_null()) {
break;
}
- Size2i icon_size = p_item->cells[i].get_icon_size();
- if (p_item->cells[i].icon_max_w > 0 && icon_size.width > p_item->cells[i].icon_max_w) {
- icon_size.height = icon_size.height * p_item->cells[i].icon_max_w / icon_size.width;
- icon_size.width = p_item->cells[i].icon_max_w;
- }
-
+ Size2i icon_size = _get_cell_icon_size(p_item->cells[i]);
Point2i icon_ofs = (item_rect.size - icon_size) / 2;
icon_ofs += item_rect.position;
@@ -3795,8 +3796,9 @@ bool Tree::edit_selected() {
popup_rect.size = rect.size;
// Account for icon.
- popup_rect.position.x += c.get_icon_size().x;
- popup_rect.size.x -= c.get_icon_size().x;
+ Size2 icon_size = _get_cell_icon_size(c);
+ popup_rect.position.x += icon_size.x;
+ popup_rect.size.x -= icon_size.x;
text_editor->clear();
text_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step)));
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index ec639ce439..3ba9c4ef2a 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -530,21 +530,24 @@ private:
Color font_outline_color;
float base_scale = 1.0;
+ int font_outline_size = 0;
int h_separation = 0;
int v_separation = 0;
int item_margin = 0;
int button_margin = 0;
+ int icon_max_width = 0;
Point2 offset;
+
int draw_relationship_lines = 0;
int relationship_line_width = 0;
int parent_hl_line_width = 0;
int children_hl_line_width = 0;
int parent_hl_line_margin = 0;
int draw_guides = 0;
+
int scroll_border = 0;
int scroll_speed = 0;
- int font_outline_size = 0;
} theme_cache;
struct Cache {
@@ -573,6 +576,7 @@ private:
} cache;
int _get_title_button_height() const;
+ Size2 _get_cell_icon_size(const TreeItem::Cell &p_cell) const;
void _scroll_moved(float p_value);
HScrollBar *h_scroll = nullptr;
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 2e1ba96d11..4c7525c745 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -180,6 +180,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 0.4));
theme->set_constant("h_separation", "Button", 2 * scale);
+ theme->set_constant("icon_max_width", "Button", 0);
// MenuBar
theme->set_stylebox("normal", "MenuBar", button_normal);
@@ -688,6 +689,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("separator_outline_size", "PopupMenu", 0);
theme->set_constant("item_start_padding", "PopupMenu", 2 * scale);
theme->set_constant("item_end_padding", "PopupMenu", 2 * scale);
+ theme->set_constant("icon_max_width", "PopupMenu", 0);
// GraphNode
Ref<StyleBoxFlat> graphnode_normal = make_flat_stylebox(style_normal_color, 18, 42, 18, 12);
@@ -780,6 +782,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("scroll_border", "Tree", 4);
theme->set_constant("scroll_speed", "Tree", 12);
theme->set_constant("outline_size", "Tree", 0);
+ theme->set_constant("icon_max_width", "Tree", 0);
// ItemList
@@ -842,6 +845,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("side_margin", "TabContainer", 8 * scale);
theme->set_constant("icon_separation", "TabContainer", 4 * scale);
+ theme->set_constant("icon_max_width", "TabContainer", 0);
theme->set_constant("outline_size", "TabContainer", 0);
// TabBar
@@ -869,6 +873,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("drop_mark_color", "TabBar", Color(1, 1, 1));
theme->set_constant("h_separation", "TabBar", 4 * scale);
+ theme->set_constant("icon_max_width", "TabBar", 0);
theme->set_constant("outline_size", "TabBar", 0);
// Separators
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 92e0a91596..b015c2bd47 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -686,7 +686,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/Samsung/thorvg
-- Version: 0.8.3 (a0fcf51f80a75f63a066df085f60cdaf715188b6, 2022)
+- Version: 0.8.4 (b0b7f207c6235691d694fc3f76e0b96e4858e606, 2023)
- License: MIT
Files extracted from upstream source:
diff --git a/thirdparty/thorvg/AUTHORS b/thirdparty/thorvg/AUTHORS
index 0f8ba2dd7d..11f3f170a5 100644
--- a/thirdparty/thorvg/AUTHORS
+++ b/thirdparty/thorvg/AUTHORS
@@ -4,7 +4,7 @@ Junsu Choi <jsuya.choi@samsung.com>
Pranay Samanta <pranay.ks@samsung.com>
Mateusz Palkowski <m.palkowski@samsung.com>
Subhransu Mohanty <sub.mohanty@samsung.com>
-Mira Grudzinska <m.grudzinska@samsung.com>
+Mira Grudzinska <veleveta@gmail.com>
Michal Szczecinski <m.szczecinsk@partner.samsung.com>
Shinwoo Kim <cinoo.kim@samsung.com>
Piotr Kalota <p.kalota@samsung.com>
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
index eda302aec0..78522d6d2d 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.8.3"
+#define THORVG_VERSION_STRING "0.8.4"
#endif
diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h
index 7e8988a65e..b1f2e9e286 100644
--- a/thirdparty/thorvg/inc/thorvg.h
+++ b/thirdparty/thorvg/inc/thorvg.h
@@ -336,6 +336,20 @@ public:
CompositeMethod composite(const Paint** target) const noexcept;
/**
+ * @brief Gets the composition source object and the composition method.
+ *
+ * @param[out] source The paint of the composition source object.
+ * @param[out] method The method used to composite the source object with the target.
+ *
+ * @return Result::Success when the paint object used as a composition target, Result::InsufficientCondition otherwise.
+ *
+ * @warning Please do not use it, this API is not official one. It could be modified in the next version.
+ *
+ * @BETA_API
+ */
+ Result composite(const Paint** source, CompositeMethod* method) const noexcept;
+
+ /**
* @brief Return the unique id value of the paint instance.
*
* This method can be called for checking the current concrete instance type.
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
index c3872f3207..a1c0032a2e 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
@@ -76,7 +76,9 @@ struct SwShapeTask : SwTask
void run(unsigned tid) override
{
- if (opacity == 0) return; //Invisible
+ auto compMethod = CompositeMethod::None;
+ auto usedAsClip = (sdata->composite(nullptr, &compMethod) == Result::Success) && (compMethod == CompositeMethod::ClipPath);
+ if (opacity == 0 && !usedAsClip) return; //Invisible
uint8_t strokeAlpha = 0;
auto visibleStroke = false;
@@ -98,7 +100,7 @@ struct SwShapeTask : SwTask
sdata->fillColor(nullptr, nullptr, nullptr, &alpha);
alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
visibleFill = (alpha > 0 || sdata->fill());
- if (visibleFill || visibleStroke) {
+ if (visibleFill || visibleStroke || usedAsClip) {
shapeReset(&shape);
if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
}
@@ -110,7 +112,7 @@ struct SwShapeTask : SwTask
//Fill
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
- if (visibleFill) {
+ if (visibleFill || usedAsClip) {
/* We assume that if stroke width is bigger than 2,
shape outline below stroke could be full covered by stroke drawing.
Thus it turns off antialising in that condition.
@@ -291,7 +293,7 @@ bool SwRenderer::viewport(const RenderRegion& vp)
}
-bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs)
+bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t colorSpace)
{
if (!buffer || stride == 0 || w == 0 || h == 0 || w > stride) return false;
@@ -301,7 +303,7 @@ bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
surface->stride = stride;
surface->w = w;
surface->h = h;
- surface->cs = cs;
+ surface->cs = colorSpace;
vport.x = vport.y = 0;
vport.w = surface->w;
@@ -644,6 +646,13 @@ SwRenderer::SwRenderer():mpool(globalMpool)
}
+uint32_t SwRenderer::colorSpace()
+{
+ if (surface) return surface->cs;
+ return tvg::SwCanvas::ARGB8888;
+}
+
+
bool SwRenderer::init(uint32_t threads)
{
if ((initEngineCnt++) > 0) return true;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h
index 574d9d488f..cab93f9e1c 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h
@@ -48,7 +48,7 @@ public:
bool clear() override;
bool sync() override;
- bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
+ bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t colorSpace);
bool mempool(bool shared);
Compositor* target(const RenderRegion& region) override;
@@ -56,6 +56,8 @@ public:
bool endComposite(Compositor* cmp) override;
void clearCompositors();
+ uint32_t colorSpace() override;
+
static SwRenderer* gen();
static bool init(uint32_t threads);
static int32_t init();
diff --git a/thirdparty/thorvg/src/lib/tvgAccessor.cpp b/thirdparty/thorvg/src/lib/tvgAccessor.cpp
index 092c8b0731..5ad24f4acf 100644
--- a/thirdparty/thorvg/src/lib/tvgAccessor.cpp
+++ b/thirdparty/thorvg/src/lib/tvgAccessor.cpp
@@ -72,7 +72,7 @@ Accessor::~Accessor()
}
-Accessor::Accessor()
+Accessor::Accessor() : pImpl(nullptr)
{
}
diff --git a/thirdparty/thorvg/src/lib/tvgLoadModule.h b/thirdparty/thorvg/src/lib/tvgLoadModule.h
index 004983152b..0a154aa47d 100644
--- a/thirdparty/thorvg/src/lib/tvgLoadModule.h
+++ b/thirdparty/thorvg/src/lib/tvgLoadModule.h
@@ -36,6 +36,7 @@ public:
float vw = 0;
float vh = 0;
float w = 0, h = 0; //default image size
+ uint32_t colorSpace = SwCanvas::ARGB8888;
virtual ~LoadModule() {}
@@ -48,7 +49,7 @@ public:
virtual bool read() = 0;
virtual bool close() = 0;
- virtual unique_ptr<Surface> bitmap() { return nullptr; }
+ virtual unique_ptr<Surface> bitmap(uint32_t colorSpace) { return nullptr; }
virtual unique_ptr<Paint> paint() { return nullptr; }
};
diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/lib/tvgMath.h
index 6120216d74..74f34fb744 100644
--- a/thirdparty/thorvg/src/lib/tvgMath.h
+++ b/thirdparty/thorvg/src/lib/tvgMath.h
@@ -53,6 +53,12 @@ static inline bool mathRightAngle(const Matrix* m)
}
+static inline bool mathSkewed(const Matrix* m)
+{
+ return (fabsf(m->e21 + m->e12) > FLT_EPSILON);
+}
+
+
static inline bool mathIdentity(const Matrix* m)
{
if (!mathEqual(m->e11, 1.0f) || !mathZero(m->e12) || !mathZero(m->e13) ||
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.cpp b/thirdparty/thorvg/src/lib/tvgPaint.cpp
index d0e908ddf2..c90e95cd33 100644
--- a/thirdparty/thorvg/src/lib/tvgPaint.cpp
+++ b/thirdparty/thorvg/src/lib/tvgPaint.cpp
@@ -38,9 +38,9 @@ static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform,
if (rTransform) rTransform->update();
- //No rotational.
- if (pTransform && !mathRightAngle(&pTransform->m)) return false;
- if (rTransform && !mathRightAngle(&rTransform->m)) return false;
+ //No rotation and no skewing
+ if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) return false;
+ if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&rTransform->m))) return false;
//Perpendicular Rectangle?
auto pt1 = pts + 0;
@@ -384,6 +384,19 @@ CompositeMethod Paint::composite(const Paint** target) const noexcept
}
+Result Paint::composite(const Paint** source, CompositeMethod* method) const noexcept
+{
+ if (source) *source = pImpl->compSource;
+ auto met = (pImpl->compSource && pImpl->compSource->pImpl->compData ?
+ pImpl->compSource->pImpl->compData->method : CompositeMethod::None);
+ if (method) *method = met;
+
+ if (pImpl->compSource != nullptr && met != CompositeMethod::None)
+ return Result::Success;
+ return Result::InsufficientCondition;
+}
+
+
Result Paint::opacity(uint8_t o) noexcept
{
if (pImpl->opacity == o) return Result::Success;
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.h b/thirdparty/thorvg/src/lib/tvgPaint.h
index c170559a37..94239c30a1 100644
--- a/thirdparty/thorvg/src/lib/tvgPaint.h
+++ b/thirdparty/thorvg/src/lib/tvgPaint.h
@@ -62,6 +62,7 @@ namespace tvg
StrategyMethod* smethod = nullptr;
RenderTransform* rTransform = nullptr;
Composite* compData = nullptr;
+ Paint* compSource = nullptr;
uint32_t renderFlag = RenderUpdateFlag::None;
uint32_t ctxFlag = ContextFlag::Invalid;
uint32_t id;
@@ -136,6 +137,7 @@ namespace tvg
if (!target && method == CompositeMethod::None) return true;
compData = static_cast<Composite*>(calloc(1, sizeof(Composite)));
}
+ target->pImpl->compSource = source;
compData->target = target;
compData->source = source;
compData->method = method;
diff --git a/thirdparty/thorvg/src/lib/tvgPictureImpl.h b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
index b2e097d400..b6bc19fa9c 100644
--- a/thirdparty/thorvg/src/lib/tvgPictureImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
@@ -66,6 +66,7 @@ struct Picture::Impl
void* rdata = nullptr; //engine data
float w = 0, h = 0;
bool resizing = false;
+ uint32_t rendererColorSpace = 0;
~Impl()
{
@@ -100,7 +101,7 @@ struct Picture::Impl
}
}
free(surface);
- if ((surface = loader->bitmap().release())) {
+ if ((surface = loader->bitmap(rendererColorSpace).release())) {
loader->close();
return RenderUpdateFlag::Image;
}
@@ -124,6 +125,7 @@ struct Picture::Impl
void* update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag)
{
+ rendererColorSpace = renderer.colorSpace();
auto flag = reload();
if (surface) {
diff --git a/thirdparty/thorvg/src/lib/tvgRender.h b/thirdparty/thorvg/src/lib/tvgRender.h
index ed66f393da..f474d87895 100644
--- a/thirdparty/thorvg/src/lib/tvgRender.h
+++ b/thirdparty/thorvg/src/lib/tvgRender.h
@@ -106,6 +106,8 @@ public:
virtual Compositor* target(const RenderRegion& region) = 0;
virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) = 0;
virtual bool endComposite(Compositor* cmp) = 0;
+
+ virtual uint32_t colorSpace() = 0;
};
}
diff --git a/thirdparty/thorvg/src/lib/tvgScene.cpp b/thirdparty/thorvg/src/lib/tvgScene.cpp
index 0beec47b38..9ed7d45d5c 100644
--- a/thirdparty/thorvg/src/lib/tvgScene.cpp
+++ b/thirdparty/thorvg/src/lib/tvgScene.cpp
@@ -25,7 +25,7 @@
/* External Class Implementation */
/************************************************************************/
-Scene::Scene() : pImpl(new Impl())
+Scene::Scene() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_SCENE;
Paint::pImpl->method(new PaintMethod<Scene::Impl>(pImpl));
diff --git a/thirdparty/thorvg/src/lib/tvgSceneImpl.h b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
index 6a7614c667..b6c68262c6 100644
--- a/thirdparty/thorvg/src/lib/tvgSceneImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
@@ -60,6 +60,11 @@ struct Scene::Impl
Array<Paint*> paints;
uint8_t opacity; //for composition
RenderMethod* renderer = nullptr; //keep it for explicit clear
+ Scene* scene = nullptr;
+
+ Impl(Scene* s) : scene(s)
+ {
+ }
~Impl()
{
@@ -81,8 +86,14 @@ struct Scene::Impl
bool needComposition(uint32_t opacity)
{
+ if (opacity == 0 || paints.count == 0) return false;
+
+ //Masking may require composition (even if opacity == 255)
+ auto compMethod = scene->composite(nullptr);
+ if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true;
+
//Half translucent requires intermediate composition.
- if (opacity == 255 || opacity == 0) return false;
+ if (opacity == 255) return false;
//If scene has several children or only scene, it may require composition.
if (paints.count > 1) return true;
diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
index 05db65cb23..1fb0681814 100644
--- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
@@ -42,6 +42,24 @@ static void _premultiply(uint32_t* data, uint32_t w, uint32_t h)
}
+static inline uint32_t CHANGE_COLORSPACE(uint32_t c)
+{
+ return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16);
+}
+
+
+static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h)
+{
+ auto buffer = data;
+ for (uint32_t y = 0; y < h; ++y, buffer += w) {
+ auto src = buffer;
+ for (uint32_t x = 0; x < w; ++x, ++src) {
+ *src = CHANGE_COLORSPACE(*src);
+ }
+ }
+}
+
+
PngLoader::PngLoader()
{
image = static_cast<png_imagep>(calloc(1, sizeof(png_image)));
@@ -110,16 +128,21 @@ bool PngLoader::close()
return true;
}
-unique_ptr<Surface> PngLoader::bitmap()
+unique_ptr<Surface> PngLoader::bitmap(uint32_t colorSpace)
{
if (!content) return nullptr;
+ if (this->colorSpace != colorSpace) {
+ this->colorSpace = colorSpace;
+ _changeColorSpace(content, w, h);
+ }
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
- surface->buffer = (uint32_t*)(content);
+ surface->buffer = content;
surface->stride = w;
surface->w = w;
surface->h = h;
- surface->cs = SwCanvas::ARGB8888;
+ surface->cs = colorSpace;
return unique_ptr<Surface>(surface);
}
+
diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
index 8beff0a3b3..f8c0daa61c 100644
--- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
+++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
@@ -36,11 +36,11 @@ public:
bool read() override;
bool close() override;
- unique_ptr<Surface> bitmap() override;
+ unique_ptr<Surface> bitmap(uint32_t colorSpace) override;
private:
png_imagep image = nullptr;
- const uint32_t* content = nullptr;
+ uint32_t* content = nullptr;
};
#endif //_TVG_PNG_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
index d11dfc1e7c..f64b7110fe 100644
--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
@@ -28,6 +28,24 @@
/* Internal Class Implementation */
/************************************************************************/
+static inline uint32_t CHANGE_COLORSPACE(uint32_t c)
+{
+ return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16);
+}
+
+
+static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h)
+{
+ auto buffer = data;
+ for (uint32_t y = 0; y < h; ++y, buffer += w) {
+ auto src = buffer;
+ for (uint32_t x = 0; x < w; ++x, ++src) {
+ *src = CHANGE_COLORSPACE(*src);
+ }
+ }
+}
+
+
void JpgLoader::clear()
{
jpgdDelete(decoder);
@@ -110,18 +128,22 @@ bool JpgLoader::close()
}
-unique_ptr<Surface> JpgLoader::bitmap()
+unique_ptr<Surface> JpgLoader::bitmap(uint32_t colorSpace)
{
this->done();
if (!image) return nullptr;
+ if (this->colorSpace != colorSpace) {
+ this->colorSpace = colorSpace;
+ _changeColorSpace(reinterpret_cast<uint32_t*>(image), w, h);
+ }
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
- surface->buffer = (uint32_t*)(image);
+ surface->buffer = reinterpret_cast<uint32_t*>(image);
surface->stride = static_cast<uint32_t>(w);
surface->w = static_cast<uint32_t>(w);
surface->h = static_cast<uint32_t>(h);
- surface->cs = SwCanvas::ARGB8888;
+ surface->cs = colorSpace;
return unique_ptr<Surface>(surface);
}
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
index d6b886cb29..c47cb6704f 100644
--- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
@@ -44,7 +44,7 @@ public:
bool read() override;
bool close() override;
- unique_ptr<Surface> bitmap() override;
+ unique_ptr<Surface> bitmap(uint32_t colorSpace) override;
void run(unsigned tid) override;
};
diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
index 889f130ce9..19c1dd6668 100644
--- a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
@@ -28,6 +28,23 @@
/* Internal Class Implementation */
/************************************************************************/
+static inline uint32_t CHANGE_COLORSPACE(uint32_t c)
+{
+ return (c & 0xff000000) + ((c & 0x00ff0000)>>16) + (c & 0x0000ff00) + ((c & 0x000000ff)<<16);
+}
+
+
+static void _changeColorSpace(uint32_t* data, uint32_t w, uint32_t h)
+{
+ auto buffer = data;
+ for (uint32_t y = 0; y < h; ++y, buffer += w) {
+ auto src = buffer;
+ for (uint32_t x = 0; x < w; ++x, ++src) {
+ *src = CHANGE_COLORSPACE(*src);
+ }
+ }
+}
+
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
@@ -54,7 +71,7 @@ bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy)
if (!content) return false;
memcpy((void*)content, data, sizeof(uint32_t) * w * h);
}
- else content = data;
+ else content = const_cast<uint32_t*>(data);
return true;
}
@@ -72,16 +89,20 @@ bool RawLoader::close()
}
-unique_ptr<Surface> RawLoader::bitmap()
+unique_ptr<Surface> RawLoader::bitmap(uint32_t colorSpace)
{
if (!content) return nullptr;
+ if (this->colorSpace != colorSpace) {
+ this->colorSpace = colorSpace;
+ _changeColorSpace(content, w, h);
+ }
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
- surface->buffer = (uint32_t*)(content);
- surface->stride = (uint32_t)w;
- surface->w = (uint32_t)w;
- surface->h = (uint32_t)h;
- surface->cs = SwCanvas::ARGB8888;
+ surface->buffer = content;
+ surface->stride = static_cast<uint32_t>(w);
+ surface->w = static_cast<uint32_t>(w);
+ surface->h = static_cast<uint32_t>(h);
+ surface->cs = colorSpace;
return unique_ptr<Surface>(surface);
}
diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
index e4f3423b41..8789b0cf51 100644
--- a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
+++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
@@ -25,7 +25,7 @@
class RawLoader : public LoadModule
{
public:
- const uint32_t* content = nullptr;
+ uint32_t* content = nullptr;
bool copy = false;
~RawLoader();
@@ -35,7 +35,7 @@ public:
bool read() override;
bool close() override;
- unique_ptr<Surface> bitmap() override;
+ unique_ptr<Surface> bitmap(uint32_t colorSpace) override;
};
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index 737fd96455..bc350a0eb8 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -180,9 +180,9 @@ static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengt
else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0) * svgParse->global.w;
else //if other then it's radius
{
- float max = (float)svgParse->global.w;
+ float max = svgParse->global.w;
if (max < svgParse->global.h)
- max = (float)svgParse->global.h;
+ max = svgParse->global.h;
parsedValue = (parsedValue / 100.0) * max;
}
}
@@ -341,7 +341,7 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
++end;
//Refers to the diagonal length of the viewport.
//https://www.w3.org/TR/SVG2/coords.html#Units
- parsedValue = (sqrtf(pow(loader->svgParse->global.w, 2) + pow(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
+ parsedValue = (sqrtf(powf(loader->svgParse->global.w, 2) + powf(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
}
(*dash).array.push(parsedValue);
str = end;
@@ -376,7 +376,7 @@ static char* _idFromUrl(const char* url)
}
-static unsigned char _parserColor(const char* value, char** end)
+static unsigned char _parseColor(const char* value, char** end)
{
float r;
@@ -586,11 +586,11 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
*b = strtol(tmp, nullptr, 16);
}
} else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') {
- tr = _parserColor(str + 4, &red);
+ tr = _parseColor(str + 4, &red);
if (red && *red == ',') {
- tg = _parserColor(red + 1, &green);
+ tg = _parseColor(red + 1, &green);
if (green && *green == ',') {
- tb = _parserColor(green + 1, &blue);
+ tb = _parseColor(green + 1, &blue);
if (blue && blue[0] == ')' && blue[1] == '\0') {
*r = tr;
*g = tg;
@@ -840,13 +840,13 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
if (_parseNumber(&value, &doc->vy)) {
if (_parseNumber(&value, &doc->vw)) {
_parseNumber(&value, &doc->vh);
- loader->svgParse->global.h = (uint32_t)doc->vh;
+ loader->svgParse->global.h = doc->vh;
}
- loader->svgParse->global.w = (uint32_t)doc->vw;
+ loader->svgParse->global.w = doc->vw;
}
- loader->svgParse->global.y = (int)doc->vy;
+ loader->svgParse->global.y = doc->vy;
}
- loader->svgParse->global.x = (int)doc->vx;
+ loader->svgParse->global.x = doc->vx;
} else if (!strcmp(key, "preserveAspectRatio")) {
_parseAspectRatio(&value, &doc->align, &doc->meetOrSlice);
} else if (!strcmp(key, "style")) {
@@ -1300,11 +1300,11 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha
if (loader->svgParse->global.w == 0) {
if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1;
- else loader->svgParse->global.w = (uint32_t)doc->w;
+ else loader->svgParse->global.w = doc->w;
}
if (loader->svgParse->global.h == 0) {
if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1;
- else loader->svgParse->global.h = (uint32_t)doc->h;
+ else loader->svgParse->global.h = doc->h;
}
return loader->svgParse->node;
@@ -2375,7 +2375,7 @@ static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial
static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
{
// scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
- if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(pow(loader->svgParse->global.h, 2) + pow(loader->svgParse->global.w, 2)) / sqrtf(2.0));
+ if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0));
}
@@ -3180,6 +3180,12 @@ SvgLoader::~SvgLoader()
void SvgLoader::run(unsigned tid)
{
+ //According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering
+ if (renderingDisabled) {
+ root = Scene::gen();
+ return;
+ }
+
if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return;
if (loaderData.doc) {
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
index f224d1a4ac..c6fdde55af 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
@@ -52,6 +52,7 @@ public:
private:
AspectRatioAlign align = AspectRatioAlign::XMidYMid;
AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet;
+ bool renderingDisabled = false;
bool header();
void clear();
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
index c657c0e21a..3588cabf0b 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
@@ -425,8 +425,7 @@ struct SvgParser
SvgStopStyleFlags flags;
struct
{
- int x, y;
- uint32_t w, h;
+ float x, y, w, h;
} global;
struct
{
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
index 4cb4ffdaeb..254ee2d008 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
@@ -255,7 +255,6 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
node->style->clipPath.applying = true;
auto comp = Shape::gen();
- comp->fill(255, 255, 255, 255);
if (node->transform) comp->transform(*node->transform);
auto child = compNode->child.data;
@@ -348,7 +347,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri
//If stroke property is nullptr then do nothing
if (style->stroke.paint.none) {
- //Do nothing
+ vg->stroke(0.0f);
} else if (style->stroke.paint.gradient) {
Box bBox = vBox;
if (!style->stroke.paint.gradient->userSpace) bBox = _boundingBox(vg);
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
index 1f1fe2a718..7fb108bc21 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
@@ -137,7 +137,11 @@ float svgUtilStrtof(const char *nPtr, char **endPtr)
pow10 *= 10ULL;
}
}
+ } else if (isspace(*iter)) { //skip if there is a space after the dot.
+ a = iter;
+ goto success;
}
+
val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
a = iter;
}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
index 231badd27d..0e2c3fa141 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
@@ -304,38 +304,38 @@ bool isIgnoreUnsupportedLogElements(TVG_UNUSED const char* tagName)
bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
{
const char *itr = buf, *itrEnd = buf + bufLength;
- char* tmpBuf = (char*)alloca(bufLength + 1);
+ char* tmpBuf = (char*)malloc(bufLength + 1);
- if (!buf || !func) return false;
+ if (!buf || !func || !tmpBuf) goto error;
while (itr < itrEnd) {
const char* p = _skipWhiteSpacesAndXmlEntities(itr, itrEnd);
const char *key, *keyEnd, *value, *valueEnd;
char* tval;
- if (p == itrEnd) return true;
+ if (p == itrEnd) goto success;
key = p;
for (keyEnd = key; keyEnd < itrEnd; keyEnd++) {
if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
}
- if (keyEnd == itrEnd) return false;
+ if (keyEnd == itrEnd) goto error;
if (keyEnd == key) continue;
if (*keyEnd == '=') value = keyEnd + 1;
else {
value = (const char*)memchr(keyEnd, '=', itrEnd - keyEnd);
- if (!value) return false;
+ if (!value) goto error;
value++;
}
keyEnd = _simpleXmlUnskipXmlEntities(keyEnd, key);
value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
- if (value == itrEnd) return false;
+ if (value == itrEnd) goto error;
if ((*value == '"') || (*value == '\'')) {
valueEnd = (const char*)memchr(value + 1, *value, itrEnd - value);
- if (!valueEnd) return false;
+ if (!valueEnd) goto error;
value++;
} else {
valueEnd = _simpleXmlFindWhiteSpace(value, itrEnd);
@@ -364,7 +364,14 @@ bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttr
}
}
}
+
+success:
+ free(tmpBuf);
return true;
+
+error:
+ free(tmpBuf);
+ return false;
}
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
index 8cccc947ce..34bb3b8e59 100755
--- a/thirdparty/thorvg/update-thorvg.sh
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -1,6 +1,6 @@
-VERSION=0.8.3
+VERSION=0.8.4
rm -rf AUTHORS inc LICENSE src *.zip
-curl -L -O https://github.com/Samsung/thorvg/archive/v$VERSION.zip
+curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.zip
bsdtar --strip-components=1 -xvf *.zip
rm *.zip
rm -rf .github docs pc res test tools tvgcompat .git* *.md *.txt wasm_build.sh