summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/config/project_settings.cpp17
-rw-r--r--core/core_bind.h1
-rw-r--r--core/extension/gdextension_interface.cpp1
-rw-r--r--core/input/input_event.cpp3
-rw-r--r--core/input/input_event.h5
-rw-r--r--core/input/input_map.cpp4
-rw-r--r--core/input/input_map.h5
-rw-r--r--core/io/json.cpp2
-rw-r--r--core/os/os.h1
-rw-r--r--core/string/translation_server.cpp35
-rw-r--r--core/string/translation_server.h2
-rw-r--r--core/variant/variant.h13
-rw-r--r--core/variant/variant_parser.cpp2
-rw-r--r--doc/classes/Control.xml10
-rw-r--r--doc/classes/Material.xml2
-rw-r--r--doc/classes/Shader.xml6
-rw-r--r--doc/classes/Tree.xml15
-rw-r--r--doc/classes/Window.xml10
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp1
-rw-r--r--drivers/gles3/storage/texture_storage.h1
-rw-r--r--drivers/metal/metal_objects.h53
-rw-r--r--drivers/metal/metal_objects.mm130
-rw-r--r--drivers/metal/rendering_device_driver_metal.mm6
-rw-r--r--editor/debugger/editor_profiler.cpp1
-rw-r--r--editor/debugger/editor_visual_profiler.cpp1
-rw-r--r--editor/editor_node.cpp1
-rw-r--r--editor/event_listener_line_edit.cpp2
-rw-r--r--editor/gui/editor_file_dialog.cpp7
-rw-r--r--editor/input_event_configuration_dialog.cpp15
-rw-r--r--editor/plugins/editor_preview_plugins.cpp1
-rw-r--r--editor/plugins/shader_editor_plugin.cpp8
-rw-r--r--editor/plugins/shader_editor_plugin.h1
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp9
-rw-r--r--editor/project_manager/project_list.cpp2
-rw-r--r--editor/themes/editor_theme_manager.cpp10
-rw-r--r--main/main.cpp1
-rw-r--r--modules/basis_universal/image_compress_basisu.cpp1
-rw-r--r--modules/gdscript/editor/gdscript_docgen.cpp2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs4
-rw-r--r--modules/mono/editor/bindings_generator.cpp4
-rw-r--r--modules/tga/image_loader_tga.cpp1
-rw-r--r--modules/theora/video_stream_theora.cpp1
-rw-r--r--modules/tinyexr/image_saver_tinyexr.cpp1
-rw-r--r--modules/tinyexr/image_saver_tinyexr.h2
-rw-r--r--platform/android/export/export_plugin.h1
-rw-r--r--platform/macos/export/export_plugin.h1
-rw-r--r--platform/windows/display_server_windows.h1
-rw-r--r--platform/windows/native_menu_windows.h1
-rw-r--r--scene/3d/lightmap_gi.cpp15
-rw-r--r--scene/gui/color_picker.cpp20
-rw-r--r--scene/gui/control.cpp20
-rw-r--r--scene/gui/control.h9
-rw-r--r--scene/gui/tree.cpp395
-rw-r--r--scene/gui/tree.h18
-rw-r--r--scene/main/window.cpp18
-rw-r--r--scene/main/window.h9
-rw-r--r--scene/resources/shader.cpp12
-rw-r--r--scene/resources/shader.h2
-rw-r--r--scene/theme/default_theme.cpp6
-rw-r--r--servers/display_server.h2
-rw-r--r--servers/movie_writer/movie_writer.h1
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.h1
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp1
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl1
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.cpp10
-rw-r--r--servers/rendering/renderer_scene_cull.cpp3
-rw-r--r--servers/text_server.h1
-rw-r--r--tests/core/variant/test_variant.h8
72 files changed, 723 insertions, 241 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 01f15f9c8e..d9cf65b63c 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -494,6 +494,7 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_f
}
void ProjectSettings::_convert_to_last_version(int p_from_version) {
+#ifndef DISABLE_DEPRECATED
if (p_from_version <= 3) {
// Converts the actions from array to dictionary (array of events to dictionary with deadzone + events)
for (KeyValue<StringName, ProjectSettings::VariantContainer> &E : props) {
@@ -507,6 +508,22 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) {
}
}
}
+ if (p_from_version <= 5) {
+ // Converts the device in events from -1 (emulated events) to -3 (all events).
+ for (KeyValue<StringName, ProjectSettings::VariantContainer> &E : props) {
+ if (String(E.key).begins_with("input/")) {
+ Dictionary action = E.value.variant;
+ Array events = action["events"];
+ for (int i = 0; i < events.size(); i++) {
+ Ref<InputEvent> x = events[i];
+ if (x->get_device() == -1) { // -1 was the previous value (GH-97707).
+ x->set_device(InputEvent::DEVICE_ID_ALL_DEVICES);
+ }
+ }
+ }
+ }
+ }
+#endif // DISABLE_DEPRECATED
}
/*
diff --git a/core/core_bind.h b/core/core_bind.h
index ce0bde3c05..d59a2c55f1 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -32,7 +32,6 @@
#define CORE_BIND_H
#include "core/debugger/engine_profiler.h"
-#include "core/io/image.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/script_language.h"
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 66b0161160..91f0b4a2de 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -34,6 +34,7 @@
#include "core/extension/gdextension.h"
#include "core/extension/gdextension_compat_hashes.h"
#include "core/io/file_access.h"
+#include "core/io/image.h"
#include "core/io/xml_parser.h"
#include "core/object/class_db.h"
#include "core/object/script_language_extension.h"
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index 905526bbbd..d125bad252 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -35,9 +35,6 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
-const int InputEvent::DEVICE_ID_EMULATION = -1;
-const int InputEvent::DEVICE_ID_INTERNAL = -2;
-
void InputEvent::set_device(int p_device) {
device = p_device;
emit_changed();
diff --git a/core/input/input_event.h b/core/input/input_event.h
index 19176f748e..80bca28fbf 100644
--- a/core/input/input_event.h
+++ b/core/input/input_event.h
@@ -62,8 +62,9 @@ protected:
static void _bind_methods();
public:
- static const int DEVICE_ID_EMULATION;
- static const int DEVICE_ID_INTERNAL;
+ inline static constexpr int DEVICE_ID_EMULATION = -1;
+ inline static constexpr int DEVICE_ID_INTERNAL = -2;
+ inline static constexpr int DEVICE_ID_ALL_DEVICES = -3; // Signify that a given Action can be triggered by any device.
void set_device(int p_device);
int get_device() const;
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 27a50c79f6..5b9377fe59 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -39,8 +39,6 @@
InputMap *InputMap::singleton = nullptr;
-int InputMap::ALL_DEVICES = -1;
-
void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action);
ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::_get_actions);
@@ -163,7 +161,7 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re
int i = 0;
for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) {
int device = E->get()->get_device();
- if (device == ALL_DEVICES || device == p_event->get_device()) {
+ if (device == InputEvent::DEVICE_ID_ALL_DEVICES || device == p_event->get_device()) {
if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) {
if (r_event_index) {
*r_event_index = i;
diff --git a/core/input/input_map.h b/core/input/input_map.h
index b29687d144..45798490f7 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -43,11 +43,6 @@ class InputMap : public Object {
GDCLASS(InputMap, Object);
public:
- /**
- * A special value used to signify that a given Action can be triggered by any device
- */
- static int ALL_DEVICES;
-
struct Action {
int id;
float deadzone;
diff --git a/core/io/json.cpp b/core/io/json.cpp
index 664ff7857b..22219fca29 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -121,7 +121,7 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
d.get_key_list(&keys);
if (p_sort_keys) {
- keys.sort();
+ keys.sort_custom<StringLikeVariantOrder>();
}
bool first_key = true;
diff --git a/core/os/os.h b/core/os/os.h
index 30d2a4266f..c42a39e0a4 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -32,7 +32,6 @@
#define OS_H
#include "core/config/engine.h"
-#include "core/io/image.h"
#include "core/io/logger.h"
#include "core/io/remote_filesystem_client.h"
#include "core/os/time_enums.h"
diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp
index 89b37d0b8a..92b473b61f 100644
--- a/core/string/translation_server.cpp
+++ b/core/string/translation_server.cpp
@@ -228,32 +228,41 @@ int TranslationServer::compare_locales(const String &p_locale_a, const String &p
return 10;
}
+ const String cache_key = p_locale_a + "|" + p_locale_b;
+ const int *cached_result = locale_compare_cache.getptr(cache_key);
+ if (cached_result) {
+ return *cached_result;
+ }
+
String locale_a = _standardize_locale(p_locale_a, true);
String locale_b = _standardize_locale(p_locale_b, true);
if (locale_a == locale_b) {
// Exact match.
+ locale_compare_cache.insert(cache_key, 10);
return 10;
}
Vector<String> locale_a_elements = locale_a.split("_");
Vector<String> locale_b_elements = locale_b.split("_");
- if (locale_a_elements[0] == locale_b_elements[0]) {
- // Matching language, both locales have extra parts.
- // Return number of matching elements.
- int matching_elements = 1;
- for (int i = 1; i < locale_a_elements.size(); i++) {
- for (int j = 1; j < locale_b_elements.size(); j++) {
- if (locale_a_elements[i] == locale_b_elements[j]) {
- matching_elements++;
- }
- }
- }
- return matching_elements;
- } else {
+ if (locale_a_elements[0] != locale_b_elements[0]) {
// No match.
+ locale_compare_cache.insert(cache_key, 0);
return 0;
}
+
+ // Matching language, both locales have extra parts.
+ // Return number of matching elements.
+ int matching_elements = 1;
+ for (int i = 1; i < locale_a_elements.size(); i++) {
+ for (int j = 1; j < locale_b_elements.size(); j++) {
+ if (locale_a_elements[i] == locale_b_elements[j]) {
+ matching_elements++;
+ }
+ }
+ }
+ locale_compare_cache.insert(cache_key, matching_elements);
+ return matching_elements;
}
String TranslationServer::get_locale_name(const String &p_locale) const {
diff --git a/core/string/translation_server.h b/core/string/translation_server.h
index a09230c019..2438349a69 100644
--- a/core/string/translation_server.h
+++ b/core/string/translation_server.h
@@ -46,6 +46,8 @@ class TranslationServer : public Object {
Ref<TranslationDomain> doc_domain;
HashMap<StringName, Ref<TranslationDomain>> custom_domains;
+ mutable HashMap<String, int> locale_compare_cache;
+
bool enabled = true;
static TranslationServer *singleton;
diff --git a/core/variant/variant.h b/core/variant/variant.h
index c76b849abd..3b1924e8ea 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -854,6 +854,19 @@ struct StringLikeVariantComparator {
static bool compare(const Variant &p_lhs, const Variant &p_rhs);
};
+struct StringLikeVariantOrder {
+ static _ALWAYS_INLINE_ bool compare(const Variant &p_lhs, const Variant &p_rhs) {
+ if (p_lhs.is_string() && p_rhs.is_string()) {
+ return p_lhs.operator String() < p_rhs.operator String();
+ }
+ return p_lhs < p_rhs;
+ }
+
+ _ALWAYS_INLINE_ bool operator()(const Variant &p_lhs, const Variant &p_rhs) const {
+ return compare(p_lhs, p_rhs);
+ }
+};
+
Variant::ObjData &Variant::_get_obj() {
return *reinterpret_cast<ObjData *>(&_data._mem[0]);
}
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index f5f96456d3..f05b9cd83a 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -2245,7 +2245,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} else {
List<Variant> keys;
dict.get_key_list(&keys);
- keys.sort();
+ keys.sort_custom<StringLikeVariantOrder>();
if (keys.is_empty()) {
// Avoid unnecessary line break.
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 9d36bc657b..516b01bd7d 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -1365,7 +1365,7 @@
<constant name="LAYOUT_DIRECTION_INHERITED" value="0" enum="LayoutDirection">
Automatic layout direction, determined from the parent control layout direction.
</constant>
- <constant name="LAYOUT_DIRECTION_LOCALE" value="1" enum="LayoutDirection">
+ <constant name="LAYOUT_DIRECTION_APPLICATION_LOCALE" value="1" enum="LayoutDirection">
Automatic layout direction, determined from the current locale.
</constant>
<constant name="LAYOUT_DIRECTION_LTR" value="2" enum="LayoutDirection">
@@ -1374,6 +1374,14 @@
<constant name="LAYOUT_DIRECTION_RTL" value="3" enum="LayoutDirection">
Right-to-left layout direction.
</constant>
+ <constant name="LAYOUT_DIRECTION_SYSTEM_LOCALE" value="4" enum="LayoutDirection">
+ Automatic layout direction, determined from the system locale.
+ </constant>
+ <constant name="LAYOUT_DIRECTION_MAX" value="5" enum="LayoutDirection">
+ Represents the size of the [enum LayoutDirection] enum.
+ </constant>
+ <constant name="LAYOUT_DIRECTION_LOCALE" value="1" enum="LayoutDirection" deprecated="Use [constant LAYOUT_DIRECTION_APPLICATION_LOCALE] instead.">
+ </constant>
<constant name="TEXT_DIRECTION_INHERITED" value="3" enum="TextDirection">
Text writing direction is the same as layout direction.
</constant>
diff --git a/doc/classes/Material.xml b/doc/classes/Material.xml
index 760773d5d9..94d12018ca 100644
--- a/doc/classes/Material.xml
+++ b/doc/classes/Material.xml
@@ -45,7 +45,7 @@
<method name="inspect_native_shader_code">
<return type="void" />
<description>
- Only available when running in the editor. Opens a popup that visualizes the generated shader code, including all variants and internal shader code.
+ Only available when running in the editor. Opens a popup that visualizes the generated shader code, including all variants and internal shader code. See also [method Shader.inspect_native_shader_code].
</description>
</method>
</methods>
diff --git a/doc/classes/Shader.xml b/doc/classes/Shader.xml
index 68176dea14..1e7f9e5ee5 100644
--- a/doc/classes/Shader.xml
+++ b/doc/classes/Shader.xml
@@ -35,6 +35,12 @@
If argument [param get_groups] is true, parameter grouping hints will be provided.
</description>
</method>
+ <method name="inspect_native_shader_code">
+ <return type="void" />
+ <description>
+ Only available when running in the editor. Opens a popup that visualizes the generated shader code, including all variants and internal shader code. See also [method Material.inspect_native_shader_code].
+ </description>
+ </method>
<method name="set_default_texture_parameter">
<return type="void" />
<param index="0" name="name" type="StringName" />
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index b0cb25fafd..9510d237da 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -509,6 +509,12 @@
<theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
Text [Color] for a [constant TreeItem.CELL_MODE_CHECK] mode cell when it's non-editable (see [method TreeItem.set_editable]).
</theme_item>
+ <theme_item name="font_hovered_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
+ Text [Color] used when the item is hovered.
+ </theme_item>
+ <theme_item name="font_hovered_dimmed_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
+ Text [Color] used when the item is hovered, while a button of the same item is hovered as the same time.
+ </theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
The tint of text outline of the item.
</theme_item>
@@ -645,6 +651,9 @@
<theme_item name="updown" data_type="icon" type="Texture2D">
The updown arrow icon to display for the [constant TreeItem.CELL_MODE_RANGE] mode cell.
</theme_item>
+ <theme_item name="button_hover" data_type="style" type="StyleBox">
+ [StyleBox] used when a button in the tree is hovered.
+ </theme_item>
<theme_item name="button_pressed" data_type="style" type="StyleBox">
[StyleBox] used when a button in the tree is pressed.
</theme_item>
@@ -666,6 +675,12 @@
<theme_item name="focus" data_type="style" type="StyleBox">
The focused style for the [Tree], drawn on top of everything.
</theme_item>
+ <theme_item name="hovered" data_type="style" type="StyleBox">
+ [StyleBox] for the item being hovered.
+ </theme_item>
+ <theme_item name="hovered_dimmed" data_type="style" type="StyleBox">
+ [StyleBox] for the item being hovered, while a button of the same item is hovered as the same time.
+ </theme_item>
<theme_item name="panel" data_type="style" type="StyleBox">
The background style for the [Tree].
</theme_item>
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index ca155881c8..a37e64ee12 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -878,7 +878,7 @@
<constant name="LAYOUT_DIRECTION_INHERITED" value="0" enum="LayoutDirection">
Automatic layout direction, determined from the parent window layout direction.
</constant>
- <constant name="LAYOUT_DIRECTION_LOCALE" value="1" enum="LayoutDirection">
+ <constant name="LAYOUT_DIRECTION_APPLICATION_LOCALE" value="1" enum="LayoutDirection">
Automatic layout direction, determined from the current locale.
</constant>
<constant name="LAYOUT_DIRECTION_LTR" value="2" enum="LayoutDirection">
@@ -887,6 +887,14 @@
<constant name="LAYOUT_DIRECTION_RTL" value="3" enum="LayoutDirection">
Right-to-left layout direction.
</constant>
+ <constant name="LAYOUT_DIRECTION_SYSTEM_LOCALE" value="4" enum="LayoutDirection">
+ Automatic layout direction, determined from the system locale.
+ </constant>
+ <constant name="LAYOUT_DIRECTION_MAX" value="5" enum="LayoutDirection">
+ Represents the size of the [enum LayoutDirection] enum.
+ </constant>
+ <constant name="LAYOUT_DIRECTION_LOCALE" value="1" enum="LayoutDirection" deprecated="Use [constant LAYOUT_DIRECTION_APPLICATION_LOCALE] instead.">
+ </constant>
<constant name="WINDOW_INITIAL_POSITION_ABSOLUTE" value="0" enum="WindowInitialPosition">
Initial window position is determined by [member position].
</constant>
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index 6e508c6ebf..843b6eac05 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -35,6 +35,7 @@
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "storage/texture_storage.h"
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index 3786c8c690..544e13f9bc 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -36,6 +36,7 @@
#include "platform_gl.h"
#include "config.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "core/templates/rid_owner.h"
#include "servers/rendering/renderer_compositor.h"
diff --git a/drivers/metal/metal_objects.h b/drivers/metal/metal_objects.h
index 030b353ee8..38d5b53ffa 100644
--- a/drivers/metal/metal_objects.h
+++ b/drivers/metal/metal_objects.h
@@ -96,6 +96,22 @@ _FORCE_INLINE_ ShaderStageUsage &operator|=(ShaderStageUsage &p_a, int p_b) {
return p_a;
}
+enum StageResourceUsage : uint32_t {
+ VertexRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_VERTEX * 2),
+ VertexWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_VERTEX * 2),
+ FragmentRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_FRAGMENT * 2),
+ FragmentWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_FRAGMENT * 2),
+ TesselationControlRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
+ TesselationControlWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
+ TesselationEvaluationRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
+ TesselationEvaluationWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
+ ComputeRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_COMPUTE * 2),
+ ComputeWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_COMPUTE * 2),
+};
+
+typedef LocalVector<__unsafe_unretained id<MTLResource>> ResourceVector;
+typedef HashMap<StageResourceUsage, ResourceVector> ResourceUsageMap;
+
enum class MDCommandBufferStateType {
None,
Render,
@@ -230,6 +246,7 @@ public:
uint32_t index_offset = 0;
LocalVector<id<MTLBuffer> __unsafe_unretained> vertex_buffers;
LocalVector<NSUInteger> vertex_offsets;
+ ResourceUsageMap resource_usage;
// clang-format off
enum DirtyFlag: uint8_t {
DIRTY_NONE = 0b0000'0000,
@@ -271,8 +288,14 @@ public:
blend_constants.reset();
vertex_buffers.clear();
vertex_offsets.clear();
+ // Keep the keys, as they are likely to be used again.
+ for (KeyValue<StageResourceUsage, LocalVector<__unsafe_unretained id<MTLResource>>> &kv : resource_usage) {
+ kv.value.clear();
+ }
}
+ void end_encoding();
+
_FORCE_INLINE_ void mark_viewport_dirty() {
if (viewports.is_empty()) {
return;
@@ -356,13 +379,20 @@ public:
} render;
// State specific for a compute pass.
- struct {
+ struct ComputeState {
MDComputePipeline *pipeline = nullptr;
id<MTLComputeCommandEncoder> encoder = nil;
+ ResourceUsageMap resource_usage;
_FORCE_INLINE_ void reset() {
pipeline = nil;
encoder = nil;
+ // Keep the keys, as they are likely to be used again.
+ for (KeyValue<StageResourceUsage, LocalVector<__unsafe_unretained id<MTLResource>>> &kv : resource_usage) {
+ kv.value.clear();
+ }
}
+
+ void end_encoding();
} compute;
// State specific to a blit pass.
@@ -632,19 +662,6 @@ public:
MDRenderShader(CharString p_name, Vector<UniformSet> p_sets, MDLibrary *p_vert, MDLibrary *p_frag);
};
-enum StageResourceUsage : uint32_t {
- VertexRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_VERTEX * 2),
- VertexWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_VERTEX * 2),
- FragmentRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_FRAGMENT * 2),
- FragmentWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_FRAGMENT * 2),
- TesselationControlRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
- TesselationControlWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
- TesselationEvaluationRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
- TesselationEvaluationWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
- ComputeRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_COMPUTE * 2),
- ComputeWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_COMPUTE * 2),
-};
-
_FORCE_INLINE_ StageResourceUsage &operator|=(StageResourceUsage &p_a, uint32_t p_b) {
p_a = StageResourceUsage(uint32_t(p_a) | p_b);
return p_a;
@@ -667,7 +684,13 @@ struct HashMapComparatorDefault<RDD::ShaderID> {
struct BoundUniformSet {
id<MTLBuffer> buffer;
- HashMap<id<MTLResource>, StageResourceUsage> bound_resources;
+ ResourceUsageMap usage_to_resources;
+
+ /// Perform a 2-way merge each key of `ResourceVector` resources from this set into the
+ /// destination set.
+ ///
+ /// Assumes the vectors of resources are sorted.
+ void merge_into(ResourceUsageMap &p_dst) const;
};
class API_AVAILABLE(macos(11.0), ios(14.0)) MDUniformSet {
diff --git a/drivers/metal/metal_objects.mm b/drivers/metal/metal_objects.mm
index 596728212a..c3906af159 100644
--- a/drivers/metal/metal_objects.mm
+++ b/drivers/metal/metal_objects.mm
@@ -58,7 +58,7 @@
void MDCommandBuffer::begin() {
DEV_ASSERT(commandBuffer == nil);
- commandBuffer = queue.commandBuffer;
+ commandBuffer = queue.commandBufferWithUnretainedReferences;
}
void MDCommandBuffer::end() {
@@ -390,6 +390,38 @@ void MDCommandBuffer::render_set_blend_constants(const Color &p_constants) {
}
}
+void BoundUniformSet::merge_into(ResourceUsageMap &p_dst) const {
+ for (KeyValue<StageResourceUsage, ResourceVector> const &keyval : usage_to_resources) {
+ ResourceVector *resources = p_dst.getptr(keyval.key);
+ if (resources == nullptr) {
+ resources = &p_dst.insert(keyval.key, ResourceVector())->value;
+ }
+ // Reserve space for the new resources, assuming they are all added.
+ resources->reserve(resources->size() + keyval.value.size());
+
+ uint32_t i = 0, j = 0;
+ __unsafe_unretained id<MTLResource> *resources_ptr = resources->ptr();
+ const __unsafe_unretained id<MTLResource> *keyval_ptr = keyval.value.ptr();
+ // 2-way merge.
+ while (i < resources->size() && j < keyval.value.size()) {
+ if (resources_ptr[i] < keyval_ptr[j]) {
+ i++;
+ } else if (resources_ptr[i] > keyval_ptr[j]) {
+ resources->insert(i, keyval_ptr[j]);
+ i++;
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ // Append the remaining resources.
+ for (; j < keyval.value.size(); j++) {
+ resources->push_back(keyval_ptr[j]);
+ }
+ }
+}
+
void MDCommandBuffer::_render_bind_uniform_sets() {
DEV_ASSERT(type == MDCommandBufferStateType::Render);
if (!render.dirty.has_flag(RenderState::DIRTY_UNIFORMS)) {
@@ -408,7 +440,7 @@ void MDCommandBuffer::_render_bind_uniform_sets() {
// Find the index of the next set bit.
int index = __builtin_ctzll(set_uniforms);
// Clear the set bit.
- set_uniforms &= ~(1ULL << index);
+ set_uniforms &= (set_uniforms - 1);
MDUniformSet *set = render.uniform_sets[index];
if (set == nullptr || set->index >= (uint32_t)shader->sets.size()) {
continue;
@@ -416,17 +448,7 @@ void MDCommandBuffer::_render_bind_uniform_sets() {
UniformSet const &set_info = shader->sets[set->index];
BoundUniformSet &bus = set->boundUniformSetForShader(shader, device);
-
- for (KeyValue<id<MTLResource>, StageResourceUsage> const &keyval : bus.bound_resources) {
- MTLResourceUsage usage = resource_usage_for_stage(keyval.value, RDD::ShaderStage::SHADER_STAGE_VERTEX);
- if (usage != 0) {
- [enc useResource:keyval.key usage:usage stages:MTLRenderStageVertex];
- }
- usage = resource_usage_for_stage(keyval.value, RDD::ShaderStage::SHADER_STAGE_FRAGMENT);
- if (usage != 0) {
- [enc useResource:keyval.key usage:usage stages:MTLRenderStageFragment];
- }
- }
+ bus.merge_into(render.resource_usage);
// Set the buffer for the vertex stage.
{
@@ -545,8 +567,7 @@ void MDCommandBuffer::_end_render_pass() {
// see: https://github.com/KhronosGroup/MoltenVK/blob/d20d13fe2735adb845636a81522df1b9d89c0fba/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm#L407
}
- [render.encoder endEncoding];
- render.encoder = nil;
+ render.end_encoding();
}
void MDCommandBuffer::_render_clear_render_area() {
@@ -792,10 +813,59 @@ void MDCommandBuffer::render_draw_indirect_count(RDD::BufferID p_indirect_buffer
ERR_FAIL_MSG("not implemented");
}
+void MDCommandBuffer::RenderState::end_encoding() {
+ if (encoder == nil) {
+ return;
+ }
+
+ // Bind all resources.
+ for (KeyValue<StageResourceUsage, ResourceVector> const &keyval : resource_usage) {
+ if (keyval.value.is_empty()) {
+ continue;
+ }
+
+ MTLResourceUsage vert_usage = resource_usage_for_stage(keyval.key, RDD::ShaderStage::SHADER_STAGE_VERTEX);
+ MTLResourceUsage frag_usage = resource_usage_for_stage(keyval.key, RDD::ShaderStage::SHADER_STAGE_FRAGMENT);
+ if (vert_usage == frag_usage) {
+ [encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:vert_usage stages:MTLRenderStageVertex | MTLRenderStageFragment];
+ } else {
+ if (vert_usage != 0) {
+ [encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:vert_usage stages:MTLRenderStageVertex];
+ }
+ if (frag_usage != 0) {
+ [encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:frag_usage stages:MTLRenderStageFragment];
+ }
+ }
+ }
+
+ [encoder endEncoding];
+ encoder = nil;
+}
+
+void MDCommandBuffer::ComputeState::end_encoding() {
+ if (encoder == nil) {
+ return;
+ }
+
+ // Bind all resources.
+ for (KeyValue<StageResourceUsage, ResourceVector> const &keyval : resource_usage) {
+ if (keyval.value.is_empty()) {
+ continue;
+ }
+ MTLResourceUsage usage = resource_usage_for_stage(keyval.key, RDD::ShaderStage::SHADER_STAGE_COMPUTE);
+ if (usage != 0) {
+ [encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:usage];
+ }
+ }
+
+ [encoder endEncoding];
+ encoder = nil;
+}
+
void MDCommandBuffer::render_end_pass() {
DEV_ASSERT(type == MDCommandBufferStateType::Render);
- [render.encoder endEncoding];
+ render.end_encoding();
render.reset();
type = MDCommandBufferStateType::None;
}
@@ -813,13 +883,7 @@ void MDCommandBuffer::compute_bind_uniform_set(RDD::UniformSetID p_uniform_set,
MDUniformSet *set = (MDUniformSet *)(p_uniform_set.id);
BoundUniformSet &bus = set->boundUniformSetForShader(shader, device);
-
- for (KeyValue<id<MTLResource>, StageResourceUsage> &keyval : bus.bound_resources) {
- MTLResourceUsage usage = resource_usage_for_stage(keyval.value, RDD::ShaderStage::SHADER_STAGE_COMPUTE);
- if (usage != 0) {
- [enc useResource:keyval.key usage:usage];
- }
- }
+ bus.merge_into(compute.resource_usage);
uint32_t const *offset = set_info.offsets.getptr(RDD::SHADER_STAGE_COMPUTE);
if (offset) {
@@ -848,7 +912,7 @@ void MDCommandBuffer::compute_dispatch_indirect(RDD::BufferID p_indirect_buffer,
void MDCommandBuffer::_end_compute_dispatch() {
DEV_ASSERT(type == MDCommandBufferStateType::Compute);
- [compute.encoder endEncoding];
+ compute.end_encoding();
compute.reset();
type = MDCommandBufferStateType::None;
}
@@ -1052,7 +1116,20 @@ BoundUniformSet &MDUniformSet::boundUniformSetForShader(MDShader *p_shader, id<M
}
}
- BoundUniformSet bs = { .buffer = enc_buffer, .bound_resources = bound_resources };
+ SearchArray<__unsafe_unretained id<MTLResource>> search;
+ ResourceUsageMap usage_to_resources;
+ for (KeyValue<id<MTLResource>, StageResourceUsage> const &keyval : bound_resources) {
+ ResourceVector *resources = usage_to_resources.getptr(keyval.value);
+ if (resources == nullptr) {
+ resources = &usage_to_resources.insert(keyval.value, ResourceVector())->value;
+ }
+ int64_t pos = search.bisect(resources->ptr(), resources->size(), keyval.key, true);
+ if (pos == resources->size() || (*resources)[pos] != keyval.key) {
+ resources->insert(pos, keyval.key);
+ }
+ }
+
+ BoundUniformSet bs = { .buffer = enc_buffer, .usage_to_resources = usage_to_resources };
bound_uniforms.insert(p_shader, bs);
return bound_uniforms.get(p_shader);
}
@@ -1211,8 +1288,7 @@ vertex VaryingsPos vertClear(AttributesPos attributes [[stage_in]], constant Cle
varyings.layer = uint(attributes.a_position.w);
return varyings;
}
-)",
- ClearAttKey::DEPTH_INDEX];
+)", ClearAttKey::DEPTH_INDEX];
return new_func(msl, @"vertClear", nil);
}
diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm
index a4a408356a..4da11ecd21 100644
--- a/drivers/metal/rendering_device_driver_metal.mm
+++ b/drivers/metal/rendering_device_driver_metal.mm
@@ -2060,6 +2060,10 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
case BT::Sampler: {
primary.dataType = MTLDataTypeSampler;
+ primary.arrayLength = 1;
+ for (uint32_t const &a : a_type.array) {
+ primary.arrayLength *= a;
+ }
} break;
default: {
@@ -2067,7 +2071,7 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
} break;
}
- // Find array length.
+ // Find array length of image.
if (basetype == BT::Image || basetype == BT::SampledImage) {
primary.arrayLength = 1;
for (uint32_t const &a : a_type.array) {
diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp
index d244b6b4cd..8b253f36e4 100644
--- a/editor/debugger/editor_profiler.cpp
+++ b/editor/debugger/editor_profiler.cpp
@@ -30,6 +30,7 @@
#include "editor_profiler.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp
index 7b831a1c8b..b949df4518 100644
--- a/editor/debugger/editor_visual_profiler.cpp
+++ b/editor/debugger/editor_visual_profiler.cpp
@@ -30,6 +30,7 @@
#include "editor_visual_profiler.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index dd6c88ef25..f328b9fc91 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -35,6 +35,7 @@
#include "core/input/input.h"
#include "core/io/config_file.h"
#include "core/io/file_access.h"
+#include "core/io/image.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/class_db.h"
diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp
index a6b30233fc..8fde728027 100644
--- a/editor/event_listener_line_edit.cpp
+++ b/editor/event_listener_line_edit.cpp
@@ -121,7 +121,7 @@ String EventListenerLineEdit::get_event_text(const Ref<InputEvent> &p_event, boo
}
String EventListenerLineEdit::get_device_string(int p_device) {
- if (p_device == InputMap::ALL_DEVICES) {
+ if (p_device == InputEvent::DEVICE_ID_ALL_DEVICES) {
return TTR("All Devices");
}
return TTR("Device") + " " + itos(p_device);
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index 91b7810f77..7600748685 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -1355,6 +1355,13 @@ EditorFileDialog::Access EditorFileDialog::get_access() const {
void EditorFileDialog::_make_dir_confirm() {
const String stripped_dirname = makedirname->get_text().strip_edges();
+ if (stripped_dirname.is_empty()) {
+ error_dialog->set_text(TTR("The path specified is invalid."));
+ error_dialog->popup_centered(Size2(250, 50) * EDSCALE);
+ makedirname->set_text(""); // Reset label.
+ return;
+ }
+
if (dir_access->dir_exists(stripped_dirname)) {
error_dialog->set_text(TTR("Could not create folder. File with that name already exists."));
error_dialog->popup_centered(Size2(250, 50) * EDSCALE);
diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp
index c60197b96b..a2aeeb11bd 100644
--- a/editor/input_event_configuration_dialog.cpp
+++ b/editor/input_event_configuration_dialog.cpp
@@ -551,18 +551,18 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
}
void InputEventConfigurationDialog::_device_selection_changed(int p_option_button_index) {
- // Subtract 1 as option index 0 corresponds to "All Devices" (value of -1)
- // and option index 1 corresponds to device 0, etc...
- event->set_device(p_option_button_index - 1);
+ // Option index 0 corresponds to "All Devices" (value of -3).
+ // Otherwise subtract 1 as option index 1 corresponds to device 0, etc...
+ event->set_device(p_option_button_index == 0 ? InputEvent::DEVICE_ID_ALL_DEVICES : p_option_button_index - 1);
event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));
}
void InputEventConfigurationDialog::_set_current_device(int p_device) {
- device_id_option->select(p_device + 1);
+ device_id_option->select(p_device == InputEvent::DEVICE_ID_ALL_DEVICES ? 0 : p_device + 1);
}
int InputEventConfigurationDialog::_get_current_device() const {
- return device_id_option->get_selected() - 1;
+ return device_id_option->get_selected() == 0 ? InputEvent::DEVICE_ID_ALL_DEVICES : device_id_option->get_selected() - 1;
}
void InputEventConfigurationDialog::_notification(int p_what) {
@@ -705,11 +705,12 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
device_id_option = memnew(OptionButton);
device_id_option->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- for (int i = -1; i < 8; i++) {
+ device_id_option->add_item(EventListenerLineEdit::get_device_string(InputEvent::DEVICE_ID_ALL_DEVICES));
+ for (int i = 0; i < 8; i++) {
device_id_option->add_item(EventListenerLineEdit::get_device_string(i));
}
device_id_option->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_device_selection_changed));
- _set_current_device(InputMap::ALL_DEVICES);
+ _set_current_device(InputEvent::DEVICE_ID_ALL_DEVICES);
device_container->add_child(device_id_option);
device_container->hide();
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 9a53f07a3f..3618c0e6d3 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/io/file_access_memory.h"
+#include "core/io/image.h"
#include "core/io/resource_loader.h"
#include "core/object/script_language.h"
#include "core/os/os.h"
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 6b00a2c9c5..5166619f90 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -396,6 +396,7 @@ void ShaderEditorPlugin::_setup_popup_menu(PopupMenuType p_type, PopupMenu *p_me
if (p_type == FILE) {
p_menu->add_separator();
p_menu->add_item(TTR("Open File in Inspector"), FILE_INSPECT);
+ p_menu->add_item(TTR("Inspect Native Shader Code..."), FILE_INSPECT_NATIVE_SHADER_CODE);
p_menu->add_separator();
p_menu->add_shortcut(ED_SHORTCUT("shader_editor/close_file", TTR("Close File"), KeyModifierMask::CMD_OR_CTRL | Key::W), FILE_CLOSE);
} else {
@@ -554,6 +555,12 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) {
EditorNode::get_singleton()->push_item(edited_shaders[index].shader_inc.ptr());
}
} break;
+ case FILE_INSPECT_NATIVE_SHADER_CODE: {
+ int index = shader_tabs->get_current_tab();
+ if (edited_shaders[index].shader.is_valid()) {
+ edited_shaders[index].shader->inspect_native_shader_code();
+ }
+ } break;
case FILE_CLOSE: {
_close_shader(shader_tabs->get_current_tab());
} break;
@@ -754,6 +761,7 @@ void ShaderEditorPlugin::_set_file_specific_items_disabled(bool p_disabled) {
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_SAVE), p_disabled);
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_SAVE_AS), p_disabled);
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_INSPECT), p_disabled);
+ file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_INSPECT_NATIVE_SHADER_CODE), p_disabled);
file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_CLOSE), p_disabled);
}
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index 43e6af79fa..19e43921c3 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -69,6 +69,7 @@ class ShaderEditorPlugin : public EditorPlugin {
FILE_SAVE,
FILE_SAVE_AS,
FILE_INSPECT,
+ FILE_INSPECT_NATIVE_SHADER_CODE,
FILE_CLOSE,
CLOSE_ALL,
CLOSE_OTHER_TABS,
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index ede8351e41..a5df9edcf0 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -2128,12 +2128,11 @@ void VisualShaderEditor::_update_nodes() {
}
}
- Array keys = added.keys();
- keys.sort();
-
- for (int i = 0; i < keys.size(); i++) {
- const Variant &key = keys.get(i);
+ List<Variant> keys;
+ added.get_key_list(&keys);
+ keys.sort_custom<StringLikeVariantOrder>();
+ for (const Variant &key : keys) {
const Dictionary &value = (Dictionary)added[key];
add_custom_type(value["name"], value["type"], value["script"], value["description"], value["return_icon_type"], value["category"], value["highend"]);
diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp
index 541ab01e62..39c1a78c4a 100644
--- a/editor/project_manager/project_list.cpp
+++ b/editor/project_manager/project_list.cpp
@@ -88,7 +88,7 @@ void ProjectListItemControl::_notification(int p_what) {
draw_style_box(get_theme_stylebox(SNAME("selected"), SNAME("Tree")), Rect2(Point2(), get_size()));
}
if (is_hovering) {
- draw_style_box(get_theme_stylebox(SNAME("hover"), SNAME("Tree")), Rect2(Point2(), get_size()));
+ draw_style_box(get_theme_stylebox(SNAME("hovered"), SNAME("Tree")), Rect2(Point2(), get_size()));
}
draw_line(Point2(0, get_size().y + 1), Point2(get_size().x, get_size().y + 1), get_theme_color(SNAME("guide_color"), SNAME("Tree")));
diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp
index 17bcbacfc2..cdc4087142 100644
--- a/editor/themes/editor_theme_manager.cpp
+++ b/editor/themes/editor_theme_manager.cpp
@@ -945,6 +945,8 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
p_theme->set_color("custom_button_font_highlight", "Tree", p_config.font_hover_color);
p_theme->set_color(SceneStringName(font_color), "Tree", p_config.font_color);
+ p_theme->set_color("font_hovered_color", "Tree", p_config.mono_color);
+ p_theme->set_color("font_hovered_dimmed_color", "Tree", p_config.font_color);
p_theme->set_color("font_selected_color", "Tree", p_config.mono_color);
p_theme->set_color("font_disabled_color", "Tree", p_config.font_disabled_color);
p_theme->set_color("font_outline_color", "Tree", p_config.font_outline_color);
@@ -997,7 +999,13 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
Ref<StyleBoxFlat> style_tree_hover = p_config.base_style->duplicate();
style_tree_hover->set_bg_color(p_config.highlight_color * Color(1, 1, 1, 0.4));
style_tree_hover->set_border_width_all(0);
- p_theme->set_stylebox("hover", "Tree", style_tree_hover);
+ p_theme->set_stylebox("hovered", "Tree", style_tree_hover);
+ p_theme->set_stylebox("button_hover", "Tree", style_tree_hover);
+
+ Ref<StyleBoxFlat> style_tree_hover_dimmed = p_config.base_style->duplicate();
+ style_tree_hover_dimmed->set_bg_color(p_config.highlight_color * Color(1, 1, 1, 0.2));
+ style_tree_hover_dimmed->set_border_width_all(0);
+ p_theme->set_stylebox("hovered_dimmed", "Tree", style_tree_hover_dimmed);
p_theme->set_stylebox("selected_focus", "Tree", style_tree_focus);
p_theme->set_stylebox("selected", "Tree", style_tree_selected);
diff --git a/main/main.cpp b/main/main.cpp
index 5206e9b84c..f7ec817bea 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -42,6 +42,7 @@
#include "core/io/dir_access.h"
#include "core/io/file_access_pack.h"
#include "core/io/file_access_zip.h"
+#include "core/io/image.h"
#include "core/io/image_loader.h"
#include "core/io/ip.h"
#include "core/io/resource_loader.h"
diff --git a/modules/basis_universal/image_compress_basisu.cpp b/modules/basis_universal/image_compress_basisu.cpp
index d48ea363a7..8ca5dba225 100644
--- a/modules/basis_universal/image_compress_basisu.cpp
+++ b/modules/basis_universal/image_compress_basisu.cpp
@@ -30,6 +30,7 @@
#include "image_compress_basisu.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
#include "servers/rendering_server.h"
diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp
index 32ef429b0d..758887a723 100644
--- a/modules/gdscript/editor/gdscript_docgen.cpp
+++ b/modules/gdscript/editor/gdscript_docgen.cpp
@@ -217,7 +217,7 @@ String GDScriptDocGen::_docvalue_from_variant(const Variant &p_variant, int p_re
List<Variant> keys;
dict.get_key_list(&keys);
- keys.sort();
+ keys.sort_custom<StringLikeVariantOrder>();
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
if (E->prev()) {
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
index 188972e6fe..f54058b0d9 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
@@ -32,7 +32,7 @@ partial class EventSignals
add => backing_MySignal += value;
remove => backing_MySignal -= value;
}
- protected void OnMySignal(string str, int num)
+ protected void EmitSignalMySignal(string str, int num)
{
EmitSignal(SignalName.MySignal, str, num);
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs
index c734dc7be1..ffde135930 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs
@@ -807,7 +807,7 @@ partial class ExportedFields
properties.Add(new(type: (global::Godot.Variant.Type)23, name: PropertyName.@_fieldRid, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@_fieldGodotDictionary, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@_fieldGodotArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
- properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@_fieldGodotGenericDictionary, hint: (global::Godot.PropertyHint)38, hintString: "4/0:;1/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
+ properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@_fieldGodotGenericDictionary, hint: (global::Godot.PropertyHint)23, hintString: "4/0:;1/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@_fieldGodotGenericArray, hint: (global::Godot.PropertyHint)23, hintString: "2/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)31, name: PropertyName.@_fieldEmptyInt64Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
return properties;
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs
index 0de840aa34..94d447f61a 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs
@@ -925,7 +925,7 @@ partial class ExportedProperties
properties.Add(new(type: (global::Godot.Variant.Type)23, name: PropertyName.@PropertyRid, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@PropertyGodotDictionary, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@PropertyGodotArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
- properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@PropertyGodotGenericDictionary, hint: (global::Godot.PropertyHint)38, hintString: "4/0:;1/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
+ properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@PropertyGodotGenericDictionary, hint: (global::Godot.PropertyHint)23, hintString: "4/0:;1/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@PropertyGodotGenericArray, hint: (global::Godot.PropertyHint)23, hintString: "2/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
return properties;
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
index 0f86b3b91c..fc67e4f592 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -791,7 +791,7 @@ namespace Godot.SourceGenerators
}
}
- hint = PropertyHint.DictionaryType;
+ hint = PropertyHint.TypeString;
hintString = keyHintString != null && valueHintString != null ? $"{keyHintString};{valueHintString}" : null;
return hintString != null;
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 0dda43ab4c..702c50d461 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -282,7 +282,7 @@ namespace Godot.SourceGenerators
.Append(" -= value;\n")
.Append("}\n");
- // Generate On{EventName} method to raise the event
+ // Generate EmitSignal{EventName} method to raise the event
var invokeMethodSymbol = signalDelegate.InvokeMethodData.Method;
int paramCount = invokeMethodSymbol.Parameters.Length;
@@ -291,7 +291,7 @@ namespace Godot.SourceGenerators
"private" :
"protected";
- source.Append($" {raiseMethodModifiers} void On{signalName}(");
+ source.Append($" {raiseMethodModifiers} void EmitSignal{signalName}(");
for (int i = 0; i < paramCount; i++)
{
var paramSymbol = invokeMethodSymbol.Parameters[i];
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 946e997c1b..e97229c621 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -3275,10 +3275,10 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output.append(CLOSE_BLOCK_L1);
- // Generate On{EventName} method to raise the event.
+ // Generate EmitSignal{EventName} method to raise the event.
if (!p_itype.is_singleton) {
p_output.append(MEMBER_BEGIN "protected void ");
- p_output << "On" << p_isignal.proxy_name;
+ p_output << "EmitSignal" << p_isignal.proxy_name;
if (is_parameterless) {
p_output.append("()\n" OPEN_BLOCK_L1 INDENT2);
p_output << "EmitSignal(SignalName." << p_isignal.proxy_name << ");\n";
diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp
index e2bb89811c..b205dbbbf2 100644
--- a/modules/tga/image_loader_tga.cpp
+++ b/modules/tga/image_loader_tga.cpp
@@ -32,6 +32,7 @@
#include "core/error/error_macros.h"
#include "core/io/file_access_memory.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index d964fd7627..2725a61a82 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -31,6 +31,7 @@
#include "video_stream_theora.h"
#include "core/config/project_settings.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "scene/resources/image_texture.h"
diff --git a/modules/tinyexr/image_saver_tinyexr.cpp b/modules/tinyexr/image_saver_tinyexr.cpp
index 31c1e06dee..b0a977f647 100644
--- a/modules/tinyexr/image_saver_tinyexr.cpp
+++ b/modules/tinyexr/image_saver_tinyexr.cpp
@@ -31,6 +31,7 @@
#include "image_saver_tinyexr.h"
#include "core/math/math_funcs.h"
+#include "core/os/os.h"
#include <zlib.h> // Should come before including tinyexr.
diff --git a/modules/tinyexr/image_saver_tinyexr.h b/modules/tinyexr/image_saver_tinyexr.h
index 058eeae58e..014c2f2e19 100644
--- a/modules/tinyexr/image_saver_tinyexr.h
+++ b/modules/tinyexr/image_saver_tinyexr.h
@@ -31,7 +31,7 @@
#ifndef IMAGE_SAVER_TINYEXR_H
#define IMAGE_SAVER_TINYEXR_H
-#include "core/os/os.h"
+#include "core/io/image.h"
Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale);
Vector<uint8_t> save_exr_buffer(const Ref<Image> &p_img, bool p_grayscale);
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index 7e1d626486..15e80f824d 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -35,6 +35,7 @@
#include "godot_plugin_config.h"
#endif // DISABLE_DEPRECATED
+#include "core/io/image.h"
#include "core/io/zip_io.h"
#include "core/os/os.h"
#include "editor/export/editor_export_platform.h"
diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h
index d88d347359..4ded2f3301 100644
--- a/platform/macos/export/export_plugin.h
+++ b/platform/macos/export/export_plugin.h
@@ -34,6 +34,7 @@
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
+#include "core/io/image.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
#include "core/os/os.h"
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 7d6a3e96a6..0f0faf892c 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -38,6 +38,7 @@
#include "core/config/project_settings.h"
#include "core/input/input.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "drivers/unix/ip_unix.h"
#include "drivers/wasapi/audio_driver_wasapi.h"
diff --git a/platform/windows/native_menu_windows.h b/platform/windows/native_menu_windows.h
index 235a4b332a..09e4640b40 100644
--- a/platform/windows/native_menu_windows.h
+++ b/platform/windows/native_menu_windows.h
@@ -31,6 +31,7 @@
#ifndef NATIVE_MENU_WINDOWS_H
#define NATIVE_MENU_WINDOWS_H
+#include "core/io/image.h"
#include "core/templates/hash_map.h"
#include "core/templates/rid_owner.h"
#include "servers/display/native_menu.h"
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index a1f32fccbe..012ef7860d 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -1602,19 +1602,16 @@ Ref<CameraAttributes> LightmapGI::get_camera_attributes() const {
PackedStringArray LightmapGI::get_configuration_warnings() const {
PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
-#ifndef MODULE_LIGHTMAPPER_RD_ENABLED
-#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
- warnings.push_back(vformat(RTR("Lightmaps cannot be baked on %s. Rendering existing baked lightmaps will still work."), OS::get_singleton()->get_name()));
-#else
- warnings.push_back(RTR("Lightmaps cannot be baked, as the `lightmapper_rd` module was disabled at compile-time. Rendering existing baked lightmaps will still work."));
-#endif
- return warnings;
-#endif
-
+#ifdef MODULE_LIGHTMAPPER_RD_ENABLED
if (!DisplayServer::get_singleton()->can_create_rendering_device()) {
warnings.push_back(vformat(RTR("Lightmaps can only be baked from a GPU that supports the RenderingDevice backends.\nYour GPU (%s) does not support RenderingDevice, as it does not support Vulkan, Direct3D 12, or Metal.\nLightmap baking will not be available on this device, although rendering existing baked lightmaps will work."), RenderingServer::get_singleton()->get_video_adapter_name()));
return warnings;
}
+#elif defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
+ warnings.push_back(vformat(RTR("Lightmaps cannot be baked on %s. Rendering existing baked lightmaps will still work."), OS::get_singleton()->get_name()));
+#else
+ warnings.push_back(RTR("Lightmaps cannot be baked, as the `lightmapper_rd` module was disabled at compile-time. Rendering existing baked lightmaps will still work."));
+#endif
return warnings;
}
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index fe4c91cb56..e92f979c32 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -245,21 +245,7 @@ void ColorPicker::finish_shaders() {
}
void ColorPicker::set_focus_on_line_edit() {
- bool has_hardware_keyboard = true;
-#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
- has_hardware_keyboard = DisplayServer::get_singleton()->has_hardware_keyboard();
-#endif // ANDROID_ENABLED || IOS_ENABLED
- if (has_hardware_keyboard) {
- callable_mp((Control *)c_text, &Control::grab_focus).call_deferred();
- } else {
- // A hack to avoid showing the virtual keyboard when the ColorPicker window popups and
- // no hardware keyboard is detected on Android and IOS.
- // This will only focus the LineEdit without editing, the virtual keyboard will only be visible when
- // we touch the LineEdit to enter edit mode.
- callable_mp(c_text, &LineEdit::set_editable).call_deferred(false);
- callable_mp((Control *)c_text, &Control::grab_focus).call_deferred();
- callable_mp(c_text, &LineEdit::set_editable).call_deferred(true);
- }
+ callable_mp((Control *)c_text, &Control::grab_focus).call_deferred();
}
void ColorPicker::_update_controls() {
@@ -2103,7 +2089,9 @@ void ColorPickerButton::pressed() {
float v_offset = show_above ? -minsize.y : get_size().y;
popup->set_position(get_screen_position() + Vector2(h_offset, v_offset));
popup->popup();
- picker->set_focus_on_line_edit();
+ if (DisplayServer::get_singleton()->has_hardware_keyboard()) {
+ picker->set_focus_on_line_edit();
+ }
}
void ColorPickerButton::_notification(int p_what) {
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index c1d197ea9b..5052deb65a 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -3042,7 +3042,7 @@ void Control::set_layout_direction(Control::LayoutDirection p_direction) {
if (data.layout_dir == p_direction) {
return;
}
- ERR_FAIL_INDEX((int)p_direction, 4);
+ ERR_FAIL_INDEX(p_direction, LAYOUT_DIRECTION_MAX);
data.layout_dir = p_direction;
@@ -3115,13 +3115,20 @@ bool Control::is_layout_rtl() const {
String locale = TranslationServer::get_singleton()->get_tool_locale();
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
}
- } else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) {
+ } else if (data.layout_dir == LAYOUT_DIRECTION_APPLICATION_LOCALE) {
if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
const_cast<Control *>(this)->data.is_rtl = true;
} else {
String locale = TranslationServer::get_singleton()->get_tool_locale();
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
}
+ } else if (data.layout_dir == LAYOUT_DIRECTION_SYSTEM_LOCALE) {
+ if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
+ const_cast<Control *>(this)->data.is_rtl = true;
+ } else {
+ String locale = OS::get_singleton()->get_locale();
+ const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
+ }
} else {
const_cast<Control *>(this)->data.is_rtl = (data.layout_dir == LAYOUT_DIRECTION_RTL);
}
@@ -3574,7 +3581,7 @@ void Control::_bind_methods() {
ADD_GROUP("Layout", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_contents"), "set_clip_contents", "is_clipping_contents");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "custom_minimum_size", PROPERTY_HINT_NONE, "suffix:px"), "set_custom_minimum_size", "get_custom_minimum_size");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Based on Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale"), "set_layout_direction", "get_layout_direction");
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode");
ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION);
@@ -3723,9 +3730,14 @@ void Control::_bind_methods() {
BIND_ENUM_CONSTANT(ANCHOR_END);
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_INHERITED);
- BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE);
+ BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_APPLICATION_LOCALE);
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LTR);
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_RTL);
+ BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_SYSTEM_LOCALE);
+ BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_MAX);
+#ifndef DISABLE_DEPRECATED
+ BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE);
+#endif // DISABLE_DEPRECATED
BIND_ENUM_CONSTANT(TEXT_DIRECTION_INHERITED);
BIND_ENUM_CONSTANT(TEXT_DIRECTION_AUTO);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 6e1621ce58..6cabf10971 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -140,9 +140,14 @@ public:
enum LayoutDirection {
LAYOUT_DIRECTION_INHERITED,
- LAYOUT_DIRECTION_LOCALE,
+ LAYOUT_DIRECTION_APPLICATION_LOCALE,
LAYOUT_DIRECTION_LTR,
- LAYOUT_DIRECTION_RTL
+ LAYOUT_DIRECTION_RTL,
+ LAYOUT_DIRECTION_SYSTEM_LOCALE,
+ LAYOUT_DIRECTION_MAX,
+#ifndef DISABLE_DEPRECATED
+ LAYOUT_DIRECTION_LOCALE = LAYOUT_DIRECTION_APPLICATION_LOCALE,
+#endif // DISABLE_DEPRECATED
};
enum TextDirection {
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 646cd9c70e..c2a9e80287 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -2165,12 +2165,18 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
int ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.h_separation : theme_cache.item_margin);
int skip2 = 0;
+
+ bool is_row_hovered = (!cache.hover_header_row && cache.hover_item == p_item);
+
for (int i = 0; i < columns.size(); i++) {
if (skip2) {
skip2--;
continue;
}
+ bool is_col_hovered = cache.hover_column == i;
+ bool is_cell_hovered = is_row_hovered && is_col_hovered;
+ bool is_cell_button_hovered = is_cell_hovered && cache.hover_button_index_in_column != -1;
int item_width = get_column_width(i);
if (i == 0) {
@@ -2203,6 +2209,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
int total_ofs = ofs - theme_cache.offset.x;
+ // If part of the column is beyond the right side of the control due to scrolling, clamp the label width
+ // so that all buttons attached to the cell remain within view.
if (total_ofs + item_width > p_draw_size.width) {
item_width = MAX(buttons_width, p_draw_size.width - total_ofs);
}
@@ -2247,17 +2255,42 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(r.position.x, r.position.y + r.size.height), r.position + r.size, theme_cache.guide_color, 1);
}
- if (i == 0) {
- if (p_item->cells[0].selected && select_mode == SELECT_ROW) {
+ if (i == 0 && select_mode == SELECT_ROW) {
+ if (p_item->cells[0].selected || is_row_hovered) {
const Rect2 content_rect = _get_content_rect();
Rect2i row_rect = Rect2i(Point2i(content_rect.position.x, item_rect.position.y), Size2i(content_rect.size.x, item_rect.size.y));
if (rtl) {
row_rect.position.x = get_size().width - row_rect.position.x - row_rect.size.x;
}
- if (has_focus()) {
- theme_cache.selected_focus->draw(ci, row_rect);
+
+ if (p_item->cells[0].selected) {
+ if (has_focus()) {
+ theme_cache.selected_focus->draw(ci, row_rect);
+ } else {
+ theme_cache.selected->draw(ci, row_rect);
+ }
+ } else if (!drop_mode_flags) {
+ if (is_cell_button_hovered) {
+ theme_cache.hovered_dimmed->draw(ci, row_rect);
+ } else {
+ theme_cache.hovered->draw(ci, row_rect);
+ }
+ }
+ }
+ }
+
+ if (select_mode != SELECT_ROW) {
+ Rect2i r = cell_rect;
+ if (rtl) {
+ r.position.x = get_size().width - r.position.x - r.size.x;
+ }
+
+ // Cell hover.
+ if (is_cell_hovered && !p_item->cells[i].selected && !drop_mode_flags) {
+ if (is_cell_button_hovered) {
+ theme_cache.hovered_dimmed->draw(ci, r);
} else {
- theme_cache.selected->draw(ci, row_rect);
+ theme_cache.hovered->draw(ci, r);
}
}
}
@@ -2335,7 +2368,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
if (p_item->cells[i].custom_color) {
cell_color = p_item->cells[i].color;
} else {
- cell_color = p_item->cells[i].selected ? theme_cache.font_selected_color : theme_cache.font_color;
+ bool draw_as_hover = !drop_mode_flags && (select_mode == SELECT_ROW ? is_row_hovered : is_cell_hovered);
+ bool draw_as_hover_dim = draw_as_hover && is_cell_button_hovered;
+ cell_color = p_item->cells[i].selected ? theme_cache.font_selected_color : (draw_as_hover_dim ? theme_cache.font_hovered_dimmed_color : (draw_as_hover ? theme_cache.font_hovered_color : theme_cache.font_color));
}
Color font_outline_color = theme_cache.font_outline_color;
@@ -2487,7 +2522,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
ir.size.width -= downarrow->get_width();
if (p_item->cells[i].custom_button) {
- if (cache.hover_item == p_item && cache.hover_cell == i) {
+ if (cache.hover_item == p_item && cache.hover_column == i) {
if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
draw_style_box(theme_cache.custom_button_pressed, ir);
} else {
@@ -2508,19 +2543,26 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
} break;
}
+ // Draw the buttons inside the cell.
for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) {
Ref<Texture2D> button_texture = p_item->cells[i].buttons[j].texture;
Size2 button_size = button_texture->get_size() + theme_cache.button_pressed->get_minimum_size();
Point2i button_ofs = Point2i(ofs + item_width_with_buttons - button_size.width, p_pos.y) - theme_cache.offset + p_draw_ofs;
- if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item == p_item && cache.click_column == i && cache.click_index == j && !p_item->cells[i].buttons[j].disabled) {
- // Being pressed.
+ bool should_draw_pressed = cache.click_type == Cache::CLICK_BUTTON && cache.click_item == p_item && cache.click_column == i && cache.click_index == j && !p_item->cells[i].buttons[j].disabled;
+ bool should_draw_hovered = !should_draw_pressed && !drop_mode_flags && cache.hover_item == p_item && cache.hover_column == i && cache.hover_button_index_in_column == j && !p_item->cells[i].buttons[j].disabled;
+
+ if (should_draw_pressed || should_draw_hovered) {
Point2 od = button_ofs;
if (rtl) {
od.x = get_size().width - od.x - button_size.x;
}
- theme_cache.button_pressed->draw(get_canvas_item(), Rect2(od.x, od.y, button_size.width, MAX(button_size.height, label_h)));
+ if (should_draw_pressed) {
+ theme_cache.button_pressed->draw(get_canvas_item(), Rect2(od.x, od.y, button_size.width, MAX(button_size.height, label_h)));
+ } else {
+ theme_cache.button_hover->draw(get_canvas_item(), Rect2(od.x, od.y, button_size.width, MAX(button_size.height, label_h)));
+ }
}
button_ofs.y += (label_h - button_size.height) / 2;
@@ -2551,6 +2593,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
}
}
+ // Draw the folding arrow.
if (!p_item->disable_folding && !hide_folding && p_item->first_child && p_item->get_visible_child_count() != 0) { //has visible children, draw the guide box
Ref<Texture2D> arrow;
@@ -2966,6 +3009,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
col_width = MAX(button_w, MIN(limit_w, col_width));
}
+ // Cell button detection code.
for (int j = c.buttons.size() - 1; j >= 0; j--) {
Ref<Texture2D> b = c.buttons[j].texture;
int w = b->get_size().width + theme_cache.button_pressed->get_minimum_size().width;
@@ -3485,7 +3529,11 @@ bool Tree::_scroll(bool p_horizontal, float p_pages) {
double prev_value = scroll->get_value();
scroll->set_value(scroll->get_value() + scroll->get_page() * p_pages);
- return scroll->get_value() != prev_value;
+ bool scroll_happened = scroll->get_value() != prev_value;
+ if (scroll_happened) {
+ _determine_hovered_item();
+ }
+ return scroll_happened;
}
Rect2 Tree::_get_scrollbar_layout_rect() const {
@@ -3710,92 +3758,10 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
- Ref<StyleBox> bg = theme_cache.panel_style;
- bool rtl = is_layout_rtl();
-
- Point2 pos = mm->get_position();
- if (rtl) {
- pos.x = get_size().width - pos.x;
- }
- pos -= theme_cache.panel_style->get_offset();
-
- Cache::ClickType old_hover = cache.hover_type;
- int old_index = cache.hover_index;
-
- cache.hover_type = Cache::CLICK_NONE;
- cache.hover_index = 0;
- if (show_column_titles) {
- pos.y -= _get_title_button_height();
- if (pos.y < 0) {
- pos.x += theme_cache.offset.x;
- int len = 0;
- for (int i = 0; i < columns.size(); i++) {
- len += get_column_width(i);
- if (pos.x < len) {
- cache.hover_type = Cache::CLICK_TITLE;
- cache.hover_index = i;
- break;
- }
- }
- }
- }
-
- if (root) {
- Point2 mpos = mm->get_position();
- if (rtl) {
- mpos.x = get_size().width - mpos.x;
- }
- mpos -= theme_cache.panel_style->get_offset();
- mpos.y -= _get_title_button_height();
- if (mpos.y >= 0) {
- if (h_scroll->is_visible_in_tree()) {
- mpos.x += h_scroll->get_value();
- }
- if (v_scroll->is_visible_in_tree()) {
- mpos.y += v_scroll->get_value();
- }
-
- TreeItem *old_it = cache.hover_item;
- int old_col = cache.hover_cell;
-
- int col, h, section;
- TreeItem *it = _find_item_at_pos(root, mpos, col, h, section);
-
- if (drop_mode_flags) {
- if (it != drop_mode_over) {
- drop_mode_over = it;
- queue_redraw();
- }
- if (it && section != drop_mode_section) {
- drop_mode_section = section;
- queue_redraw();
- }
- }
-
- cache.hover_item = it;
- cache.hover_cell = col;
-
- if (it != old_it || col != old_col) {
- if (old_it && old_col >= old_it->cells.size()) {
- // Columns may have changed since last redraw().
- queue_redraw();
- } else {
- // Only need to update if mouse enters/exits a button
- bool was_over_button = old_it && old_it->cells[old_col].custom_button;
- bool is_over_button = it && it->cells[col].custom_button;
- if (was_over_button || is_over_button) {
- queue_redraw();
- }
- }
- }
- }
- }
-
- // Update if mouse enters/exits columns
- if (cache.hover_type != old_hover || cache.hover_index != old_index) {
- queue_redraw();
- }
+ hovered_pos = mm->get_position();
+ _determine_hovered_item();
+ bool rtl = is_layout_rtl();
if (pressing_for_editor && popup_pressing_edited_item && (popup_pressing_edited_item->get_cell_mode(popup_pressing_edited_item_column) == TreeItem::CELL_MODE_RANGE)) {
/* This needs to happen now, because the popup can be closed when pressing another item, and must remain the popup edited item until it actually closes */
popup_edited_item = popup_pressing_edited_item;
@@ -4080,6 +4046,174 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
}
}
+void Tree::_determine_hovered_item() {
+ Ref<StyleBox> bg = theme_cache.panel_style;
+ bool rtl = is_layout_rtl();
+
+ Point2 pos = hovered_pos;
+ if (rtl) {
+ pos.x = get_size().width - pos.x;
+ }
+ pos -= theme_cache.panel_style->get_offset();
+
+ bool old_header_row = cache.hover_header_row;
+ int old_header_column = cache.hover_header_column;
+ TreeItem *old_item = cache.hover_item;
+ int old_column = cache.hover_column;
+ int old_button_index_in_column = cache.hover_button_index_in_column;
+
+ // Determine hover on column headers.
+ cache.hover_header_row = false;
+ cache.hover_header_column = 0;
+ if (show_column_titles && is_mouse_hovering) {
+ pos.y -= _get_title_button_height();
+ if (pos.y < 0) {
+ pos.x += theme_cache.offset.x;
+ int len = 0;
+ for (int i = 0; i < columns.size(); i++) {
+ len += get_column_width(i);
+ if (pos.x < len) {
+ cache.hover_header_row = true;
+ cache.hover_header_column = i;
+ cache.hover_button_index_in_column = -1;
+ break;
+ }
+ }
+ }
+ }
+
+ // Determine hover on rows and items.
+ if (root && is_mouse_hovering) {
+ Point2 mpos = hovered_pos;
+ if (rtl) {
+ mpos.x = get_size().width - mpos.x;
+ }
+ mpos -= theme_cache.panel_style->get_offset();
+ mpos.y -= _get_title_button_height();
+ if (mpos.y >= 0) {
+ if (h_scroll->is_visible_in_tree()) {
+ mpos.x += h_scroll->get_value();
+ }
+ if (v_scroll->is_visible_in_tree()) {
+ mpos.y += v_scroll->get_value();
+ }
+
+ int col, h, section;
+ TreeItem *it = _find_item_at_pos(root, mpos, col, h, section);
+
+ // Find possible hovered button in cell.
+ int col_button_index = -1;
+
+ Point2 cpos = mpos;
+
+ if (it) {
+ const TreeItem::Cell &c = it->cells[col];
+ int col_width = get_column_width(col);
+
+ // In the first column, tree nesting indent impacts the leftmost possible buttons position
+ // and the clickable area of the folding arrow.
+ int col_indent = 0;
+ if (col == 0) {
+ col_indent = _get_item_h_offset(it);
+ }
+
+ // Compute total width of buttons block including spacings.
+ int buttons_width = 0;
+ for (int j = c.buttons.size() - 1; j >= 0; j--) {
+ Ref<Texture2D> b = c.buttons[j].texture;
+ Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size();
+ buttons_width += size.width + theme_cache.button_margin;
+ }
+
+ // Adjust when buttons are shifted left into view so that they remain visible even
+ // if part of the cell is beyond the right border due to horizontal scrolling and
+ // a long string in one of the items. This matches the drawing & click handling algorithms
+ // that are based on recursion.
+ int clamped_column_offset = 0;
+ int col_left = 0;
+
+ for (int i = 0; i < col; i++) {
+ int i_col_w = get_column_width(i);
+ cpos.x -= i_col_w;
+ col_left += i_col_w;
+ }
+ col_left -= theme_cache.offset.x;
+
+ // Compute buttons offset that makes them visible, in comparison to what would be their
+ // natural position that would cut them off.
+ if (!rtl) {
+ const Rect2 content_rect = _get_content_rect();
+ int cw = content_rect.size.width;
+ int col_right = col_left + col_width;
+ if (col_right > cw) {
+ clamped_column_offset = col_right - cw - theme_cache.scrollbar_h_separation;
+ int max_clamp_offset = col_width - col_indent - buttons_width;
+ if (clamped_column_offset > max_clamp_offset) {
+ clamped_column_offset = max_clamp_offset;
+ }
+ }
+ }
+ col_width -= clamped_column_offset;
+
+ // Find the actual button under coordinates.
+ for (int j = c.buttons.size() - 1; j >= 0; j--) {
+ Ref<Texture2D> b = c.buttons[j].texture;
+ Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size();
+ if (cpos.x > col_width - size.width && col_button_index == -1) {
+ col_button_index = j;
+ }
+ col_width -= size.width + theme_cache.button_margin;
+ }
+ }
+
+ if (drop_mode_flags) {
+ if (it != drop_mode_over) {
+ drop_mode_over = it;
+ queue_redraw();
+ }
+ if (it && section != drop_mode_section) {
+ drop_mode_section = section;
+ queue_redraw();
+ }
+ }
+
+ cache.hover_item = it;
+ cache.hover_column = col;
+ cache.hover_button_index_in_column = col_button_index;
+
+ if (it != old_item || col != old_column) {
+ if (old_item && old_column >= old_item->cells.size()) {
+ // Columns may have changed since last redraw().
+ queue_redraw();
+ } else {
+ // Only need to update if mouse enters/exits a button.
+ bool was_over_button = old_item && old_item->cells[old_column].custom_button;
+ bool is_over_button = it && it->cells[col].custom_button;
+ if (was_over_button || is_over_button) {
+ queue_redraw();
+ }
+ }
+ }
+ }
+ }
+
+ // Reduce useless redraw calls.
+
+ bool hovered_cell_button_changed = (cache.hover_button_index_in_column != old_button_index_in_column);
+ bool hovered_column_changed = (cache.hover_column != old_column);
+
+ // Mouse has moved from row to row, or from cell to cell within same row unless selection mode is full row which saves a useless redraw.
+ bool item_hover_needs_redraw = !cache.hover_header_row && (cache.hover_item != old_item || hovered_cell_button_changed || (select_mode != SELECT_ROW && hovered_column_changed));
+ // Mouse has moved between two different column header sections.
+ bool header_hover_needs_redraw = cache.hover_header_row && cache.hover_header_column != old_header_column;
+ // Mouse has moved between header and "main" areas.
+ bool whole_needs_redraw = cache.hover_header_row != old_header_row;
+
+ if (whole_needs_redraw || header_hover_needs_redraw || item_hover_needs_redraw) {
+ queue_redraw();
+ }
+}
+
bool Tree::edit_selected(bool p_force_edit) {
TreeItem *s = get_selected();
ERR_FAIL_NULL_V_MSG(s, false, "No item selected.");
@@ -4304,9 +4438,20 @@ void Tree::_notification(int p_what) {
}
} break;
+ case NOTIFICATION_MOUSE_ENTER: {
+ is_mouse_hovering = true;
+ _determine_hovered_item();
+ } break;
+
case NOTIFICATION_MOUSE_EXIT: {
- if (cache.hover_type != Cache::CLICK_NONE) {
- cache.hover_type = Cache::CLICK_NONE;
+ is_mouse_hovering = false;
+ // Clear hovered item cache.
+ if (cache.hover_header_row || cache.hover_item != nullptr) {
+ cache.hover_header_row = false;
+ cache.hover_header_column = -1;
+ cache.hover_item = nullptr;
+ cache.hover_column = -1;
+ cache.hover_button_index_in_column = -1;
queue_redraw();
}
} break;
@@ -4420,7 +4565,7 @@ void Tree::_notification(int p_what) {
//title buttons
int ofs2 = theme_cache.panel_style->get_margin(SIDE_LEFT);
for (int i = 0; i < columns.size(); i++) {
- Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? theme_cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? theme_cache.title_button_hover : theme_cache.title_button);
+ Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? theme_cache.title_button_pressed : ((cache.hover_header_row && cache.hover_header_column == i) ? theme_cache.title_button_hover : theme_cache.title_button);
Rect2 tbrect = Rect2(ofs2 - theme_cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh);
if (cache.rtl) {
tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x;
@@ -4536,6 +4681,8 @@ TreeItem *Tree::create_item(TreeItem *p_parent, int p_index) {
}
}
+ _determine_hovered_item();
+
return ti;
}
@@ -4679,6 +4826,8 @@ void Tree::clear() {
popup_edited_item = nullptr;
popup_pressing_edited_item = nullptr;
+ _determine_hovered_item();
+
queue_redraw();
};
@@ -4931,6 +5080,7 @@ int Tree::get_columns() const {
}
void Tree::_scroll_moved(float) {
+ _determine_hovered_item();
queue_redraw();
}
@@ -4972,7 +5122,47 @@ int Tree::get_item_offset(TreeItem *p_item) const {
}
}
- return -1; //not found
+ return -1; // Not found.
+}
+
+int Tree::_get_item_h_offset(TreeItem *p_item) const {
+ TreeItem *it = root;
+ int nesting_level = 0;
+ if (!it) {
+ return 0;
+ }
+
+ while (true) {
+ if (it == p_item) {
+ if (!hide_root) {
+ nesting_level += 1;
+ }
+ if (hide_folding) {
+ nesting_level -= 1;
+ }
+ return nesting_level * theme_cache.item_margin;
+ }
+
+ if (it->first_child && !it->collapsed) {
+ it = it->first_child;
+ nesting_level += 1;
+
+ } else if (it->next) {
+ it = it->next;
+ } else {
+ while (!it->next) {
+ it = it->parent;
+ nesting_level -= 1;
+ if (it == nullptr) {
+ return 0;
+ }
+ }
+
+ it = it->next;
+ }
+ }
+
+ return -1; // Not found.
}
void Tree::ensure_cursor_is_visible() {
@@ -5803,10 +5993,13 @@ void Tree::_bind_methods() {
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_FONT, Tree, tb_font, "title_button_font");
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_FONT_SIZE, Tree, tb_font_size, "title_button_font_size");
+ BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, hovered);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, hovered_dimmed);
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, selected);
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, selected_focus);
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, cursor);
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, Tree, cursor_unfocus, "cursor_unfocused");
+ BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, button_hover);
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, button_pressed);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, checked);
@@ -5827,6 +6020,8 @@ void Tree::_bind_methods() {
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, custom_button_font_highlight);
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_color);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_hovered_color);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_hovered_dimmed_color);
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_selected_color);
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_disabled_color);
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, drop_position_color);
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 86efdfec52..b417b7ee31 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -450,6 +450,9 @@ private:
Vector2 pressing_pos;
Rect2 pressing_item_rect;
+ Vector2 hovered_pos;
+ bool is_mouse_hovering = false;
+
float range_drag_base = 0.0;
bool range_drag_enabled = false;
Vector2 range_drag_capture_pos;
@@ -545,10 +548,13 @@ private:
int font_size = 0;
int tb_font_size = 0;
+ Ref<StyleBox> hovered;
+ Ref<StyleBox> hovered_dimmed;
Ref<StyleBox> selected;
Ref<StyleBox> selected_focus;
Ref<StyleBox> cursor;
Ref<StyleBox> cursor_unfocus;
+ Ref<StyleBox> button_hover;
Ref<StyleBox> button_pressed;
Ref<StyleBox> title_button;
Ref<StyleBox> title_button_hover;
@@ -572,6 +578,8 @@ private:
Ref<Texture2D> updown;
Color font_color;
+ Color font_hovered_color;
+ Color font_hovered_dimmed_color;
Color font_selected_color;
Color font_disabled_color;
Color guide_color;
@@ -623,16 +631,17 @@ private:
};
ClickType click_type = Cache::CLICK_NONE;
- ClickType hover_type = Cache::CLICK_NONE;
int click_index = -1;
int click_id = -1;
TreeItem *click_item = nullptr;
int click_column = 0;
- int hover_index = -1;
+ int hover_header_column = -1;
+ bool hover_header_row = false;
Point2 click_pos;
TreeItem *hover_item = nullptr;
- int hover_cell = -1;
+ int hover_column = -1;
+ int hover_button_index_in_column = -1;
bool rtl = false;
} cache;
@@ -660,6 +669,7 @@ private:
TreeItem *_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards = false);
TreeItem *_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_column, int &h, int &section) const;
+ int _get_item_h_offset(TreeItem *p_item) const;
void _find_button_at_pos(const Point2 &p_pos, TreeItem *&r_item, int &r_column, int &r_index) const;
@@ -689,6 +699,8 @@ private:
bool enable_recursive_folding = true;
+ void _determine_hovered_item();
+
int _count_selected_items(TreeItem *p_from) const;
bool _is_branch_selected(TreeItem *p_from) const;
bool _is_sibling_branch_selected(TreeItem *p_from) const;
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 803ce89bc9..1b2025ca78 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -2635,7 +2635,7 @@ void Window::set_unparent_when_invisible(bool p_unparent) {
void Window::set_layout_direction(Window::LayoutDirection p_direction) {
ERR_MAIN_THREAD_GUARD;
- ERR_FAIL_INDEX((int)p_direction, 4);
+ ERR_FAIL_INDEX(p_direction, LAYOUT_DIRECTION_MAX);
layout_dir = p_direction;
propagate_notification(Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED);
@@ -2700,13 +2700,20 @@ bool Window::is_layout_rtl() const {
String locale = TranslationServer::get_singleton()->get_tool_locale();
return TS->is_locale_right_to_left(locale);
}
- } else if (layout_dir == LAYOUT_DIRECTION_LOCALE) {
+ } else if (layout_dir == LAYOUT_DIRECTION_APPLICATION_LOCALE) {
if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
return true;
} else {
String locale = TranslationServer::get_singleton()->get_tool_locale();
return TS->is_locale_right_to_left(locale);
}
+ } else if (layout_dir == LAYOUT_DIRECTION_SYSTEM_LOCALE) {
+ if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
+ return true;
+ } else {
+ String locale = OS::get_singleton()->get_locale();
+ return TS->is_locale_right_to_left(locale);
+ }
} else {
return (layout_dir == LAYOUT_DIRECTION_RTL);
}
@@ -3070,9 +3077,14 @@ void Window::_bind_methods() {
BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_INTEGER);
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_INHERITED);
- BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE);
+ BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_APPLICATION_LOCALE);
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LTR);
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_RTL);
+ BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_SYSTEM_LOCALE);
+ BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_MAX);
+#ifndef DISABLE_DEPRECATED
+ BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE);
+#endif // DISABLE_DEPRECATED
BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_ABSOLUTE);
BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_PRIMARY_SCREEN);
diff --git a/scene/main/window.h b/scene/main/window.h
index 47aaf73728..c2937d6168 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -86,9 +86,14 @@ public:
enum LayoutDirection {
LAYOUT_DIRECTION_INHERITED,
- LAYOUT_DIRECTION_LOCALE,
+ LAYOUT_DIRECTION_APPLICATION_LOCALE,
LAYOUT_DIRECTION_LTR,
- LAYOUT_DIRECTION_RTL
+ LAYOUT_DIRECTION_RTL,
+ LAYOUT_DIRECTION_SYSTEM_LOCALE,
+ LAYOUT_DIRECTION_MAX,
+#ifndef DISABLE_DEPRECATED
+ LAYOUT_DIRECTION_LOCALE = LAYOUT_DIRECTION_APPLICATION_LOCALE,
+#endif // DISABLE_DEPRECATED
};
enum {
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 24d17108d5..d163a42fa9 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -32,6 +32,7 @@
#include "shader.compat.inc"
#include "core/io/file_access.h"
+#include "scene/main/scene_tree.h"
#include "servers/rendering/shader_language.h"
#include "servers/rendering/shader_preprocessor.h"
#include "servers/rendering_server.h"
@@ -138,6 +139,14 @@ String Shader::get_code() const {
return code;
}
+void Shader::inspect_native_shader_code() {
+ SceneTree *st = SceneTree::get_singleton();
+ RID _shader = get_rid();
+ if (st && _shader.is_valid()) {
+ st->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_native_shader_source_visualizer", "_inspect_shader", _shader);
+ }
+}
+
void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_groups) const {
_update_shader();
_check_shader_rid();
@@ -267,6 +276,9 @@ void Shader::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_shader_uniform_list", "get_groups"), &Shader::_get_shader_uniform_list, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("inspect_native_shader_code"), &Shader::inspect_native_shader_code);
+ ClassDB::set_method_flags(get_class_static(), _scs_create("inspect_native_shader_code"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_code", "get_code");
BIND_ENUM_CONSTANT(MODE_SPATIAL);
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 18197419f3..7234d37579 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -88,6 +88,8 @@ public:
void set_code(const String &p_code);
String get_code() const;
+ void inspect_native_shader_code();
+
void get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_groups = false) const;
void set_default_texture_parameter(const StringName &p_name, const Ref<Texture> &p_texture, int p_index = 0);
diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp
index caf44ac392..f5065e8de1 100644
--- a/scene/theme/default_theme.cpp
+++ b/scene/theme/default_theme.cpp
@@ -30,6 +30,7 @@
#include "default_theme.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "default_font.gen.h"
#include "default_theme_icons.gen.h"
@@ -832,10 +833,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox(SceneStringName(panel), "Tree", make_flat_stylebox(style_normal_color, 4, 4, 4, 5));
theme->set_stylebox("focus", "Tree", focus);
+ theme->set_stylebox("hovered", "Tree", make_flat_stylebox(Color(1, 1, 1, 0.07)));
+ theme->set_stylebox("hovered_dimmed", "Tree", make_flat_stylebox(Color(1, 1, 1, 0.03)));
theme->set_stylebox("selected", "Tree", make_flat_stylebox(style_selected_color));
theme->set_stylebox("selected_focus", "Tree", make_flat_stylebox(style_selected_color));
theme->set_stylebox("cursor", "Tree", focus);
theme->set_stylebox("cursor_unfocused", "Tree", focus);
+ theme->set_stylebox("button_hover", "Tree", make_flat_stylebox(Color(1, 1, 1, 0.07)));
theme->set_stylebox("button_pressed", "Tree", button_pressed);
theme->set_stylebox("title_button_normal", "Tree", make_flat_stylebox(style_pressed_color, 4, 4, 4, 4));
theme->set_stylebox("title_button_pressed", "Tree", make_flat_stylebox(style_hover_color, 4, 4, 4, 4));
@@ -863,6 +867,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("title_button_color", "Tree", control_font_color);
theme->set_color(SceneStringName(font_color), "Tree", control_font_low_color);
+ theme->set_color("font_hovered_color", "Tree", control_font_hover_color);
+ theme->set_color("font_hovered_dimmed_color", "Tree", control_font_color);
theme->set_color("font_selected_color", "Tree", control_font_pressed_color);
theme->set_color("font_disabled_color", "Tree", control_font_disabled_color);
theme->set_color("font_outline_color", "Tree", Color(0, 0, 0));
diff --git a/servers/display_server.h b/servers/display_server.h
index 36798bd011..f4057c18a8 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -32,6 +32,7 @@
#define DISPLAY_SERVER_H
#include "core/input/input.h"
+#include "core/io/image.h"
#include "core/io/resource.h"
#include "core/os/os.h"
#include "core/variant/callable.h"
@@ -39,7 +40,6 @@
#include "display/native_menu.h"
class Texture2D;
-class Image;
class DisplayServer : public Object {
GDCLASS(DisplayServer, Object)
diff --git a/servers/movie_writer/movie_writer.h b/servers/movie_writer/movie_writer.h
index e1dc8ef8cf..69d6b1ba2b 100644
--- a/servers/movie_writer/movie_writer.h
+++ b/servers/movie_writer/movie_writer.h
@@ -31,6 +31,7 @@
#ifndef MOVIE_WRITER_H
#define MOVIE_WRITER_H
+#include "core/io/image.h"
#include "core/templates/local_vector.h"
#include "servers/audio/audio_driver_dummy.h"
#include "servers/audio_server.h"
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h
index dcd3e90e1b..2547f08715 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.h
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h
@@ -31,6 +31,7 @@
#ifndef RENDERER_COMPOSITOR_RD_H
#define RENDERER_COMPOSITOR_RD_H
+#include "core/io/image.h"
#include "core/os/os.h"
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering/renderer_rd/environment/fog.h"
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 0c21fec282..dc2605b670 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -31,6 +31,7 @@
#include "renderer_scene_render_rd.h"
#include "core/config/project_settings.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "renderer_compositor_rd.h"
#include "servers/rendering/renderer_rd/environment/fog.h"
diff --git a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl
index a797891ab6..90bbdfe685 100644
--- a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl
@@ -513,6 +513,7 @@ void main() {
shadow_sample.z = 1.0 + abs(shadow_sample.z);
vec3 pos = vec3(shadow_sample.xy / shadow_sample.z, shadow_len - omni_lights.data[light_index].shadow_bias);
pos.z *= omni_lights.data[light_index].inv_radius;
+ pos.z = 1.0 - pos.z;
pos.xy = pos.xy * 0.5 + 0.5;
pos.xy = uv_rect.xy + pos.xy * uv_rect.zw;
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index 9dc606620b..0b133ec1ca 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -2269,6 +2269,16 @@ void TextureStorage::_texture_format_from_rd(RD::DataFormat p_rd_format, Texture
r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
} break;
+ case RD::DATA_FORMAT_B8G8R8A8_UNORM:
+ case RD::DATA_FORMAT_B8G8R8A8_SRGB: {
+ r_format.image_format = Image::FORMAT_RGBA8;
+ r_format.rd_format = RD::DATA_FORMAT_B8G8R8A8_UNORM;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_B8G8R8A8_SRGB;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+ } break;
case RD::DATA_FORMAT_B4G4R4A4_UNORM_PACK16: {
r_format.image_format = Image::FORMAT_RGBA4444;
r_format.rd_format = RD::DATA_FORMAT_B4G4R4A4_UNORM_PACK16;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 8b7ec08868..ca07444465 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -1998,6 +1998,9 @@ void RendererSceneCull::_update_instance(Instance *p_instance) {
pair.bvh2 = &p_instance->scenario->indexers[Scenario::INDEXER_VOLUMES];
}
pair.cull_mask = RSG::light_storage->light_get_cull_mask(p_instance->base);
+ } else if (p_instance->base_type == RS::INSTANCE_LIGHTMAP) {
+ pair.pair_mask = RS::INSTANCE_GEOMETRY_MASK;
+ pair.bvh = &p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY];
} else if (geometry_instance_pair_mask & (1 << RS::INSTANCE_REFLECTION_PROBE) && (p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE)) {
pair.pair_mask = RS::INSTANCE_GEOMETRY_MASK;
pair.bvh = &p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY];
diff --git a/servers/text_server.h b/servers/text_server.h
index ba3fdaa35e..7dd9669818 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -31,6 +31,7 @@
#ifndef TEXT_SERVER_H
#define TEXT_SERVER_H
+#include "core/io/image.h"
#include "core/object/ref_counted.h"
#include "core/os/os.h"
#include "core/templates/rid.h"
diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h
index be615975f8..599a282b20 100644
--- a/tests/core/variant/test_variant.h
+++ b/tests/core/variant/test_variant.h
@@ -1806,6 +1806,14 @@ TEST_CASE("[Variant] Writer and parser dictionary") {
CHECK_MESSAGE(d_parsed == Variant(d), "Should parse back.");
}
+TEST_CASE("[Variant] Writer key sorting") {
+ Dictionary d = build_dictionary(StringName("C"), 3, "A", 1, StringName("B"), 2, "D", 4);
+ String d_str;
+ VariantWriter::write_to_string(d, d_str);
+
+ CHECK_EQ(d_str, "{\n\"A\": 1,\n&\"B\": 2,\n&\"C\": 3,\n\"D\": 4\n}");
+}
+
TEST_CASE("[Variant] Writer recursive dictionary") {
// There is no way to accurately represent a recursive dictionary,
// the only thing we can do is make sure the writer doesn't blow up