summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/config/project_settings.cpp2
-rw-r--r--core/io/resource.cpp6
-rw-r--r--doc/classes/@GlobalScope.xml2
-rw-r--r--doc/classes/DisplayServer.xml4
-rw-r--r--doc/classes/EditorSettings.xml3
-rw-r--r--doc/classes/MultiplayerPeer.xml2
-rw-r--r--doc/classes/MultiplayerPeerExtension.xml2
-rw-r--r--doc/classes/RenderingServer.xml2
-rw-r--r--doc/classes/TextureProgressBar.xml14
-rw-r--r--doc/classes/TileSetAtlasSource.xml4
-rw-r--r--doc/classes/Tree.xml18
-rw-r--r--doc/classes/Viewport.xml2
-rw-r--r--doc/classes/XRInterface.xml2
-rw-r--r--doc/classes/XRInterfaceExtension.xml2
-rw-r--r--editor/editor_node.cpp4
-rw-r--r--editor/editor_properties.cpp227
-rw-r--r--editor/editor_properties.h12
-rw-r--r--editor/editor_settings.cpp41
-rw-r--r--editor/editor_settings.h2
-rw-r--r--editor/editor_themes.cpp4
-rw-r--r--editor/icons/GuiIndeterminateDisabled.svg1
-rw-r--r--editor/import/resource_importer_scene.cpp2
-rw-r--r--editor/plugins/script_text_editor.cpp3
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp2
-rw-r--r--editor/project_manager.cpp4
-rw-r--r--main/main.cpp2
-rw-r--r--modules/csg/doc_classes/CSGPolygon3D.xml2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp54
-rw-r--r--modules/gdscript/gdscript_parser.cpp74
-rw-r--r--modules/gdscript/gdscript_parser.h4
-rw-r--r--modules/gridmap/grid_map.h1
-rw-r--r--modules/mono/editor/bindings_generator.cpp14
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs9
-rw-r--r--modules/navigation/nav_map.cpp2
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml2
-rw-r--r--platform/windows/display_server_windows.cpp32
-rw-r--r--scene/2d/cpu_particles_2d.cpp2
-rw-r--r--scene/3d/light_3d.cpp2
-rw-r--r--scene/3d/visual_instance_3d.cpp2
-rw-r--r--scene/gui/control.cpp39
-rw-r--r--scene/gui/control.h4
-rw-r--r--scene/gui/dialogs.cpp3
-rw-r--r--scene/gui/dialogs.h2
-rw-r--r--scene/gui/popup.cpp3
-rw-r--r--scene/gui/popup.h3
-rw-r--r--scene/gui/popup_menu.cpp13
-rw-r--r--scene/gui/popup_menu.h3
-rw-r--r--scene/gui/texture_progress_bar.cpp24
-rw-r--r--scene/gui/texture_progress_bar.h1
-rw-r--r--scene/gui/tree.cpp35
-rw-r--r--scene/gui/tree.h4
-rw-r--r--scene/gui/view_panner.cpp18
-rw-r--r--scene/main/window.cpp46
-rw-r--r--scene/main/window.h5
-rw-r--r--scene/register_scene_types.cpp4
-rw-r--r--scene/theme/default_theme.cpp4
-rw-r--r--scene/theme/icons/indeterminate.svg2
-rw-r--r--scene/theme/icons/indeterminate_disabled.svg1
-rw-r--r--tests/scene/test_camera_3d.h370
-rw-r--r--tests/test_main.cpp1
60 files changed, 840 insertions, 314 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index bf1595b41b..4e3196dec3 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1387,7 +1387,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/occlusion_culling/bvh_build_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), 2);
GLOBAL_DEF(PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1"), 60); // No negative and limit to 500 due to crashes.
GLOBAL_DEF_RST("internationalization/rendering/force_right_to_left_layout_direction", false);
- GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Locale,Left-to-Right,Right-to-Left"), 0);
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale"), 0);
GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 2000);
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index 64fa597a67..04ecabaf27 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -489,12 +489,14 @@ RWLock ResourceCache::path_cache_lock;
#endif
void ResourceCache::clear() {
- if (resources.size()) {
- ERR_PRINT("Resources still in use at exit (run with --verbose for details).");
+ if (!resources.is_empty()) {
if (OS::get_singleton()->is_stdout_verbose()) {
+ ERR_PRINT(vformat("%d resources still in use at exit.", resources.size()));
for (const KeyValue<String, Resource *> &E : resources) {
print_line(vformat("Resource still in use: %s (%s)", E.key, E.value->get_class()));
}
+ } else {
+ ERR_PRINT(vformat("%d resources still in use at exit (run with --verbose for details).", resources.size()));
}
}
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 193284896a..77c5a168f3 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -1393,7 +1393,7 @@
<description>
Converts the given [param variant] to the given [param type], using the [enum Variant.Type] values. This method is generous with how it handles types, it can automatically convert between array types, convert numeric [String]s to [int], and converting most things to [String].
If the type conversion cannot be done, this method will return the default value for that type, for example converting [Rect2] to [Vector2] will always return [constant Vector2.ZERO]. This method will never show error messages as long as [param type] is a valid Variant type.
- The returned value is a [Variant], but the data inside and the [enum Variant.Type] will be the same as the requested type.
+ The returned value is a [Variant], but the data inside and its type will be the same as the requested type.
[codeblock]
type_convert("Hi!", TYPE_INT) # Returns 0
type_convert("123", TYPE_INT) # Returns 123
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index b8f82accfc..8436cbf6ee 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -1122,6 +1122,10 @@
<param index="0" name="name" type="String" />
<description>
Set active tablet driver name.
+ Supported drivers:
+ - [code]winink[/code]: Windows Ink API, default (Windows 8.1+ required).
+ - [code]wintab[/code]: Wacom Wintab API (compatible device driver required).
+ - [code]dummy[/code]: Dummy driver, tablet input is disabled.
[b]Note:[/b] This method is implemented only on Windows.
</description>
</method>
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index ec051c0545..98b4920953 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -599,6 +599,9 @@
<member name="interface/editor/single_window_mode" type="bool" setter="" getter="">
If [code]true[/code], embed modal windows such as docks inside the main editor window. When single-window mode is enabled, tooltips will also be embedded inside the main editor window, which means they can't be displayed outside of the editor window.
</member>
+ <member name="interface/editor/ui_layout_direction" type="int" setter="" getter="">
+ Editor UI default layout direction.
+ </member>
<member name="interface/editor/unfocused_low_processor_mode_sleep_usec" type="float" setter="" getter="">
When the editor window is unfocused, the amount of sleeping between frames when the low-processor usage mode is enabled (in microseconds). Higher values will result in lower CPU/GPU usage, which can improve battery life on laptops (in addition to improving the running project's performance if the editor has to redraw continuously). However, higher values will result in a less responsive editor. The default value is set to limit the editor to 20 FPS when the editor window is unfocused. See also [member interface/editor/low_processor_mode_sleep_usec].
</member>
diff --git a/doc/classes/MultiplayerPeer.xml b/doc/classes/MultiplayerPeer.xml
index 350fffed32..39980a05e1 100644
--- a/doc/classes/MultiplayerPeer.xml
+++ b/doc/classes/MultiplayerPeer.xml
@@ -48,7 +48,7 @@
<method name="get_packet_mode" qualifiers="const">
<return type="int" enum="MultiplayerPeer.TransferMode" />
<description>
- Returns the [enum MultiplayerPeer.TransferMode] the remote peer used to send the next available packet. See [method PacketPeer.get_available_packet_count].
+ Returns the transfer mode the remote peer used to send the next available packet. See [method PacketPeer.get_available_packet_count].
</description>
</method>
<method name="get_packet_peer" qualifiers="const">
diff --git a/doc/classes/MultiplayerPeerExtension.xml b/doc/classes/MultiplayerPeerExtension.xml
index 9b2b5dbc94..8fd6755a7b 100644
--- a/doc/classes/MultiplayerPeerExtension.xml
+++ b/doc/classes/MultiplayerPeerExtension.xml
@@ -58,7 +58,7 @@
<method name="_get_packet_mode" qualifiers="virtual const">
<return type="int" enum="MultiplayerPeer.TransferMode" />
<description>
- Called to get the [enum MultiplayerPeer.TransferMode] the remote peer used to send the next available packet. See [method MultiplayerPeer.get_packet_mode].
+ Called to get the transfer mode the remote peer used to send the next available packet. See [method MultiplayerPeer.get_packet_mode].
</description>
</method>
<method name="_get_packet_peer" qualifiers="virtual const">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 6bbef47763..a3ec4d25f1 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3337,7 +3337,7 @@
<return type="int" enum="Image.Format" />
<param index="0" name="texture" type="RID" />
<description>
- Returns the [enum Image.Format] for the texture.
+ Returns the format for the texture.
</description>
</method>
<method name="texture_get_native_handle" qualifiers="const">
diff --git a/doc/classes/TextureProgressBar.xml b/doc/classes/TextureProgressBar.xml
index 0c1e9adf71..6ab1b86e5c 100644
--- a/doc/classes/TextureProgressBar.xml
+++ b/doc/classes/TextureProgressBar.xml
@@ -34,28 +34,28 @@
If [code]true[/code], Godot treats the bar's textures like in [NinePatchRect]. Use the [code]stretch_margin_*[/code] properties like [member stretch_margin_bottom] to set up the nine patch's 3×3 grid. When using a radial [member fill_mode], this setting will enable stretching.
</member>
<member name="radial_center_offset" type="Vector2" setter="set_radial_center_offset" getter="get_radial_center_offset" default="Vector2(0, 0)">
- Offsets [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE] or [constant FILL_COUNTER_CLOCKWISE].
+ Offsets [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE], [constant FILL_COUNTER_CLOCKWISE], or [constant FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE].
</member>
<member name="radial_fill_degrees" type="float" setter="set_fill_degrees" getter="get_fill_degrees" default="360.0">
- Upper limit for the fill of [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE] or [constant FILL_COUNTER_CLOCKWISE]. When the node's [code]value[/code] is equal to its [code]max_value[/code], the texture fills up to this angle.
+ Upper limit for the fill of [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE], [constant FILL_COUNTER_CLOCKWISE], or [constant FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE]. When the node's [code]value[/code] is equal to its [code]max_value[/code], the texture fills up to this angle.
See [member Range.value], [member Range.max_value].
</member>
<member name="radial_initial_angle" type="float" setter="set_radial_initial_angle" getter="get_radial_initial_angle" default="0.0">
- Starting angle for the fill of [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE] or [constant FILL_COUNTER_CLOCKWISE]. When the node's [code]value[/code] is equal to its [code]min_value[/code], the texture doesn't show up at all. When the [code]value[/code] increases, the texture fills and tends towards [member radial_fill_degrees].
+ Starting angle for the fill of [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE], [constant FILL_COUNTER_CLOCKWISE], or [constant FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE]. When the node's [code]value[/code] is equal to its [code]min_value[/code], the texture doesn't show up at all. When the [code]value[/code] increases, the texture fills and tends towards [member radial_fill_degrees].
</member>
<member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" is_bitfield="true" default="1" />
<member name="step" type="float" setter="set_step" getter="get_step" overrides="Range" default="1.0" />
<member name="stretch_margin_bottom" type="int" setter="set_stretch_margin" getter="get_stretch_margin" default="0">
- The height of the 9-patch's bottom row. A margin of 16 means the 9-slice's bottom corners and side will have a height of 16 pixels. You can set all 4 margin values individually to create panels with non-uniform borders.
+ The height of the 9-patch's bottom row. A margin of 16 means the 9-slice's bottom corners and side will have a height of 16 pixels. You can set all 4 margin values individually to create panels with non-uniform borders. Only effective if [member nine_patch_stretch] is [code]true[/code].
</member>
<member name="stretch_margin_left" type="int" setter="set_stretch_margin" getter="get_stretch_margin" default="0">
- The width of the 9-patch's left column.
+ The width of the 9-patch's left column. Only effective if [member nine_patch_stretch] is [code]true[/code].
</member>
<member name="stretch_margin_right" type="int" setter="set_stretch_margin" getter="get_stretch_margin" default="0">
- The width of the 9-patch's right column.
+ The width of the 9-patch's right column. Only effective if [member nine_patch_stretch] is [code]true[/code].
</member>
<member name="stretch_margin_top" type="int" setter="set_stretch_margin" getter="get_stretch_margin" default="0">
- The height of the 9-patch's top row.
+ The height of the 9-patch's top row. Only effective if [member nine_patch_stretch] is [code]true[/code].
</member>
<member name="texture_over" type="Texture2D" setter="set_over_texture" getter="get_over_texture">
[Texture2D] that draws over the progress bar. Use it to add highlights or an upper-frame that hides part of [member texture_progress].
diff --git a/doc/classes/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml
index 755c266cd9..6f212274f8 100644
--- a/doc/classes/TileSetAtlasSource.xml
+++ b/doc/classes/TileSetAtlasSource.xml
@@ -90,7 +90,7 @@
<return type="int" enum="TileSetAtlasSource.TileAnimationMode" />
<param index="0" name="atlas_coords" type="Vector2i" />
<description>
- Returns the [enum TileAnimationMode] of the tile at [param atlas_coords]. See also [method set_tile_animation_mode].
+ Returns the tile animation mode of the tile at [param atlas_coords]. See also [method set_tile_animation_mode].
</description>
</method>
<method name="get_tile_animation_separation" qualifiers="const">
@@ -239,7 +239,7 @@
<param index="0" name="atlas_coords" type="Vector2i" />
<param index="1" name="mode" type="int" enum="TileSetAtlasSource.TileAnimationMode" />
<description>
- Sets the [enum TileAnimationMode] of the tile at [param atlas_coords] to [param mode]. See also [method get_tile_animation_mode].
+ Sets the tile animation mode of the tile at [param atlas_coords] to [param mode]. See also [method get_tile_animation_mode].
</description>
</method>
<method name="set_tile_animation_separation">
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index e322e3adc0..bf5a504aba 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -506,6 +506,9 @@
<theme_item name="font_color" data_type="color" type="Color" default="Color(0.7, 0.7, 0.7, 1)">
Default text [Color] of the item.
</theme_item>
+ <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_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the item.
</theme_item>
@@ -619,16 +622,25 @@
The arrow icon used when a foldable item is collapsed (for right-to-left layouts).
</theme_item>
<theme_item name="checked" data_type="icon" type="Texture2D">
- The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is checked.
+ The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is checked and editable (see [method TreeItem.set_editable]).
+ </theme_item>
+ <theme_item name="checked_disabled" data_type="icon" type="Texture2D">
+ The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is checked and non-editable (see [method TreeItem.set_editable]).
</theme_item>
<theme_item name="indeterminate" data_type="icon" type="Texture2D">
- The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is indeterminate.
+ The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is indeterminate and editable (see [method TreeItem.set_editable]).
+ </theme_item>
+ <theme_item name="indeterminate_disabled" data_type="icon" type="Texture2D">
+ The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is indeterminate and non-editable (see [method TreeItem.set_editable]).
</theme_item>
<theme_item name="select_arrow" data_type="icon" type="Texture2D">
The arrow icon to display for the [constant TreeItem.CELL_MODE_RANGE] mode cell.
</theme_item>
<theme_item name="unchecked" data_type="icon" type="Texture2D">
- The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is unchecked.
+ The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is unchecked and editable (see [method TreeItem.set_editable]).
+ </theme_item>
+ <theme_item name="unchecked_disabled" data_type="icon" type="Texture2D">
+ The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is unchecked and non-editable (see [method TreeItem.set_editable]).
</theme_item>
<theme_item name="updown" data_type="icon" type="Texture2D">
The updown arrow icon to display for the [constant TreeItem.CELL_MODE_RANGE] mode cell.
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 4d0f453fe0..6e521c9e14 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -75,7 +75,7 @@
<return type="int" enum="Viewport.PositionalShadowAtlasQuadrantSubdiv" />
<param index="0" name="quadrant" type="int" />
<description>
- Returns the [enum PositionalShadowAtlasQuadrantSubdiv] of the specified quadrant.
+ Returns the positional shadow atlas quadrant subdivision of the specified quadrant.
</description>
</method>
<method name="get_render_info">
diff --git a/doc/classes/XRInterface.xml b/doc/classes/XRInterface.xml
index 99d6e67e51..a7878378dd 100644
--- a/doc/classes/XRInterface.xml
+++ b/doc/classes/XRInterface.xml
@@ -119,7 +119,7 @@
<param index="0" name="mode" type="int" enum="XRInterface.EnvironmentBlendMode" />
<description>
Sets the active environment blend mode.
- [param mode] is the [enum XRInterface.EnvironmentBlendMode] starting with the next frame.
+ [param mode] is the environment blend mode starting with the next frame.
[b]Note:[/b] Not all runtimes support all environment blend modes, so it is important to check this at startup. For example:
[codeblock]
func _ready():
diff --git a/doc/classes/XRInterfaceExtension.xml b/doc/classes/XRInterfaceExtension.xml
index ea2bbf4cfb..b2c27bedf3 100644
--- a/doc/classes/XRInterfaceExtension.xml
+++ b/doc/classes/XRInterfaceExtension.xml
@@ -67,7 +67,7 @@
<method name="_get_play_area_mode" qualifiers="virtual const">
<return type="int" enum="XRInterface.PlayAreaMode" />
<description>
- Returns the [enum XRInterface.PlayAreaMode] that sets up our play area.
+ Returns the play area mode that sets up our play area.
</description>
</method>
<method name="_get_projection_for_view" qualifiers="virtual">
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index c514ca7ba7..cdaa0c036d 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -6864,6 +6864,10 @@ EditorNode::EditorNode() {
AcceptDialog::set_swap_cancel_ok(swap_cancel_ok == 2);
}
+ int ed_root_dir = EDITOR_GET("interface/editor/ui_layout_direction");
+ Control::set_root_layout_direction(ed_root_dir);
+ Window::set_root_layout_direction(ed_root_dir);
+
ResourceLoader::set_abort_on_missing_resources(false);
ResourceLoader::set_error_notify_func(&EditorNode::add_io_error);
ResourceLoader::set_dependency_error_notify_func(&EditorNode::_dependency_error_report);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index f891bfbff7..27530dc641 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -1312,17 +1312,12 @@ void EditorPropertyInteger::_set_read_only(bool p_read_only) {
}
void EditorPropertyInteger::_value_changed(int64_t val) {
- if (setting) {
- return;
- }
emit_changed(get_edited_property(), val);
}
void EditorPropertyInteger::update_property() {
int64_t val = get_edited_property_value();
- setting = true;
- spin->set_value(val);
- setting = false;
+ spin->set_value_no_signal(val);
#ifdef DEBUG_ENABLED
// If spin (currently EditorSplinSlider : Range) is changed so that it can use int64_t, then the below warning wouldn't be a problem.
if (val != (int64_t)(double)(val)) {
@@ -1452,10 +1447,6 @@ void EditorPropertyFloat::_set_read_only(bool p_read_only) {
}
void EditorPropertyFloat::_value_changed(double val) {
- if (setting) {
- return;
- }
-
if (radians_as_degrees) {
val = Math::deg_to_rad(val);
}
@@ -1467,9 +1458,7 @@ void EditorPropertyFloat::update_property() {
if (radians_as_degrees) {
val = Math::rad_to_deg(val);
}
- setting = true;
- spin->set_value(val);
- setting = false;
+ spin->set_value_no_signal(val);
}
void EditorPropertyFloat::_bind_methods() {
@@ -1627,18 +1616,12 @@ void EditorPropertyEasing::_set_preset(int p_preset) {
}
void EditorPropertyEasing::_setup_spin() {
- setting = true;
spin->setup_and_show();
spin->get_line_edit()->set_text(TS->format_number(rtos(get_edited_property_value())));
- setting = false;
spin->show();
}
void EditorPropertyEasing::_spin_value_changed(double p_value) {
- if (setting) {
- return;
- }
-
// 0 is a singularity, but both positive and negative values
// are otherwise allowed. Enforce 0+ as workaround.
if (Math::is_zero_approx(p_value)) {
@@ -1725,10 +1708,6 @@ void EditorPropertyRect2::_set_read_only(bool p_read_only) {
}
void EditorPropertyRect2::_value_changed(double val, const String &p_name) {
- if (setting) {
- return;
- }
-
Rect2 r2;
r2.position.x = spin[0]->get_value();
r2.position.y = spin[1]->get_value();
@@ -1739,12 +1718,10 @@ void EditorPropertyRect2::_value_changed(double val, const String &p_name) {
void EditorPropertyRect2::update_property() {
Rect2 val = get_edited_property_value();
- setting = true;
- spin[0]->set_value(val.position.x);
- spin[1]->set_value(val.position.y);
- spin[2]->set_value(val.size.x);
- spin[3]->set_value(val.size.y);
- setting = false;
+ spin[0]->set_value_no_signal(val.position.x);
+ spin[1]->set_value_no_signal(val.position.y);
+ spin[2]->set_value_no_signal(val.size.x);
+ spin[3]->set_value_no_signal(val.size.y);
}
void EditorPropertyRect2::_notification(int p_what) {
@@ -1828,10 +1805,6 @@ void EditorPropertyRect2i::_set_read_only(bool p_read_only) {
}
void EditorPropertyRect2i::_value_changed(double val, const String &p_name) {
- if (setting) {
- return;
- }
-
Rect2i r2;
r2.position.x = spin[0]->get_value();
r2.position.y = spin[1]->get_value();
@@ -1842,12 +1815,10 @@ void EditorPropertyRect2i::_value_changed(double val, const String &p_name) {
void EditorPropertyRect2i::update_property() {
Rect2i val = get_edited_property_value();
- setting = true;
- spin[0]->set_value(val.position.x);
- spin[1]->set_value(val.position.y);
- spin[2]->set_value(val.size.x);
- spin[3]->set_value(val.size.y);
- setting = false;
+ spin[0]->set_value_no_signal(val.position.x);
+ spin[1]->set_value_no_signal(val.position.y);
+ spin[2]->set_value_no_signal(val.size.x);
+ spin[3]->set_value_no_signal(val.size.y);
}
void EditorPropertyRect2i::_notification(int p_what) {
@@ -1930,10 +1901,6 @@ void EditorPropertyPlane::_set_read_only(bool p_read_only) {
}
void EditorPropertyPlane::_value_changed(double val, const String &p_name) {
- if (setting) {
- return;
- }
-
Plane p;
p.normal.x = spin[0]->get_value();
p.normal.y = spin[1]->get_value();
@@ -1944,12 +1911,10 @@ void EditorPropertyPlane::_value_changed(double val, const String &p_name) {
void EditorPropertyPlane::update_property() {
Plane val = get_edited_property_value();
- setting = true;
- spin[0]->set_value(val.normal.x);
- spin[1]->set_value(val.normal.y);
- spin[2]->set_value(val.normal.z);
- spin[3]->set_value(val.d);
- setting = false;
+ spin[0]->set_value_no_signal(val.normal.x);
+ spin[1]->set_value_no_signal(val.normal.y);
+ spin[2]->set_value_no_signal(val.normal.z);
+ spin[3]->set_value_no_signal(val.d);
}
void EditorPropertyPlane::_notification(int p_what) {
@@ -2041,10 +2006,6 @@ void EditorPropertyQuaternion::_edit_custom_value() {
}
void EditorPropertyQuaternion::_custom_value_changed(double val) {
- if (setting) {
- return;
- }
-
edit_euler.x = euler[0]->get_value();
edit_euler.y = euler[1]->get_value();
edit_euler.z = euler[2]->get_value();
@@ -2055,17 +2016,13 @@ void EditorPropertyQuaternion::_custom_value_changed(double val) {
v.z = Math::deg_to_rad(edit_euler.z);
Quaternion temp_q = Quaternion::from_euler(v);
- spin[0]->set_value(temp_q.x);
- spin[1]->set_value(temp_q.y);
- spin[2]->set_value(temp_q.z);
- spin[3]->set_value(temp_q.w);
+ spin[0]->set_value_no_signal(temp_q.x);
+ spin[1]->set_value_no_signal(temp_q.y);
+ spin[2]->set_value_no_signal(temp_q.z);
+ spin[3]->set_value_no_signal(temp_q.w);
}
void EditorPropertyQuaternion::_value_changed(double val, const String &p_name) {
- if (setting) {
- return;
- }
-
Quaternion p;
p.x = spin[0]->get_value();
p.y = spin[1]->get_value();
@@ -2085,21 +2042,19 @@ bool EditorPropertyQuaternion::is_grabbing_euler() {
void EditorPropertyQuaternion::update_property() {
Quaternion val = get_edited_property_value();
- setting = true;
- spin[0]->set_value(val.x);
- spin[1]->set_value(val.y);
- spin[2]->set_value(val.z);
- spin[3]->set_value(val.w);
+ spin[0]->set_value_no_signal(val.x);
+ spin[1]->set_value_no_signal(val.y);
+ spin[2]->set_value_no_signal(val.z);
+ spin[3]->set_value_no_signal(val.w);
if (!is_grabbing_euler()) {
Vector3 v = val.normalized().get_euler();
edit_euler.x = Math::rad_to_deg(v.x);
edit_euler.y = Math::rad_to_deg(v.y);
edit_euler.z = Math::rad_to_deg(v.z);
- euler[0]->set_value(edit_euler.x);
- euler[1]->set_value(edit_euler.y);
- euler[2]->set_value(edit_euler.z);
+ euler[0]->set_value_no_signal(edit_euler.x);
+ euler[1]->set_value_no_signal(edit_euler.y);
+ euler[2]->set_value_no_signal(edit_euler.z);
}
- setting = false;
}
void EditorPropertyQuaternion::_warning_pressed() {
@@ -2240,10 +2195,6 @@ void EditorPropertyAABB::_set_read_only(bool p_read_only) {
}
void EditorPropertyAABB::_value_changed(double val, const String &p_name) {
- if (setting) {
- return;
- }
-
AABB p;
p.position.x = spin[0]->get_value();
p.position.y = spin[1]->get_value();
@@ -2251,21 +2202,17 @@ void EditorPropertyAABB::_value_changed(double val, const String &p_name) {
p.size.x = spin[3]->get_value();
p.size.y = spin[4]->get_value();
p.size.z = spin[5]->get_value();
-
emit_changed(get_edited_property(), p, p_name);
}
void EditorPropertyAABB::update_property() {
AABB val = get_edited_property_value();
- setting = true;
- spin[0]->set_value(val.position.x);
- spin[1]->set_value(val.position.y);
- spin[2]->set_value(val.position.z);
- spin[3]->set_value(val.size.x);
- spin[4]->set_value(val.size.y);
- spin[5]->set_value(val.size.z);
-
- setting = false;
+ spin[0]->set_value_no_signal(val.position.x);
+ spin[1]->set_value_no_signal(val.position.y);
+ spin[2]->set_value_no_signal(val.position.z);
+ spin[3]->set_value_no_signal(val.size.x);
+ spin[4]->set_value_no_signal(val.size.y);
+ spin[5]->set_value_no_signal(val.size.z);
}
void EditorPropertyAABB::_notification(int p_what) {
@@ -2323,10 +2270,6 @@ void EditorPropertyTransform2D::_set_read_only(bool p_read_only) {
}
void EditorPropertyTransform2D::_value_changed(double val, const String &p_name) {
- if (setting) {
- return;
- }
-
Transform2D p;
p[0][0] = spin[0]->get_value();
p[1][0] = spin[1]->get_value();
@@ -2340,15 +2283,12 @@ void EditorPropertyTransform2D::_value_changed(double val, const String &p_name)
void EditorPropertyTransform2D::update_property() {
Transform2D val = get_edited_property_value();
- setting = true;
- spin[0]->set_value(val[0][0]);
- spin[1]->set_value(val[1][0]);
- spin[2]->set_value(val[2][0]);
- spin[3]->set_value(val[0][1]);
- spin[4]->set_value(val[1][1]);
- spin[5]->set_value(val[2][1]);
-
- setting = false;
+ spin[0]->set_value_no_signal(val[0][0]);
+ spin[1]->set_value_no_signal(val[1][0]);
+ spin[2]->set_value_no_signal(val[2][0]);
+ spin[3]->set_value_no_signal(val[0][1]);
+ spin[4]->set_value_no_signal(val[1][1]);
+ spin[5]->set_value_no_signal(val[2][1]);
}
void EditorPropertyTransform2D::_notification(int p_what) {
@@ -2414,10 +2354,6 @@ void EditorPropertyBasis::_set_read_only(bool p_read_only) {
}
void EditorPropertyBasis::_value_changed(double val, const String &p_name) {
- if (setting) {
- return;
- }
-
Basis p;
p[0][0] = spin[0]->get_value();
p[0][1] = spin[1]->get_value();
@@ -2434,18 +2370,15 @@ void EditorPropertyBasis::_value_changed(double val, const String &p_name) {
void EditorPropertyBasis::update_property() {
Basis val = get_edited_property_value();
- setting = true;
- spin[0]->set_value(val[0][0]);
- spin[1]->set_value(val[0][1]);
- spin[2]->set_value(val[0][2]);
- spin[3]->set_value(val[1][0]);
- spin[4]->set_value(val[1][1]);
- spin[5]->set_value(val[1][2]);
- spin[6]->set_value(val[2][0]);
- spin[7]->set_value(val[2][1]);
- spin[8]->set_value(val[2][2]);
-
- setting = false;
+ spin[0]->set_value_no_signal(val[0][0]);
+ spin[1]->set_value_no_signal(val[0][1]);
+ spin[2]->set_value_no_signal(val[0][2]);
+ spin[3]->set_value_no_signal(val[1][0]);
+ spin[4]->set_value_no_signal(val[1][1]);
+ spin[5]->set_value_no_signal(val[1][2]);
+ spin[6]->set_value_no_signal(val[2][0]);
+ spin[7]->set_value_no_signal(val[2][1]);
+ spin[8]->set_value_no_signal(val[2][2]);
}
void EditorPropertyBasis::_notification(int p_what) {
@@ -2504,10 +2437,6 @@ void EditorPropertyTransform3D::_set_read_only(bool p_read_only) {
}
void EditorPropertyTransform3D::_value_changed(double val, const String &p_name) {
- if (setting) {
- return;
- }
-
Transform3D p;
p.basis[0][0] = spin[0]->get_value();
p.basis[0][1] = spin[1]->get_value();
@@ -2530,20 +2459,18 @@ void EditorPropertyTransform3D::update_property() {
}
void EditorPropertyTransform3D::update_using_transform(Transform3D p_transform) {
- setting = true;
- spin[0]->set_value(p_transform.basis[0][0]);
- spin[1]->set_value(p_transform.basis[0][1]);
- spin[2]->set_value(p_transform.basis[0][2]);
- spin[3]->set_value(p_transform.origin[0]);
- spin[4]->set_value(p_transform.basis[1][0]);
- spin[5]->set_value(p_transform.basis[1][1]);
- spin[6]->set_value(p_transform.basis[1][2]);
- spin[7]->set_value(p_transform.origin[1]);
- spin[8]->set_value(p_transform.basis[2][0]);
- spin[9]->set_value(p_transform.basis[2][1]);
- spin[10]->set_value(p_transform.basis[2][2]);
- spin[11]->set_value(p_transform.origin[2]);
- setting = false;
+ spin[0]->set_value_no_signal(p_transform.basis[0][0]);
+ spin[1]->set_value_no_signal(p_transform.basis[0][1]);
+ spin[2]->set_value_no_signal(p_transform.basis[0][2]);
+ spin[3]->set_value_no_signal(p_transform.origin[0]);
+ spin[4]->set_value_no_signal(p_transform.basis[1][0]);
+ spin[5]->set_value_no_signal(p_transform.basis[1][1]);
+ spin[6]->set_value_no_signal(p_transform.basis[1][2]);
+ spin[7]->set_value_no_signal(p_transform.origin[1]);
+ spin[8]->set_value_no_signal(p_transform.basis[2][0]);
+ spin[9]->set_value_no_signal(p_transform.basis[2][1]);
+ spin[10]->set_value_no_signal(p_transform.basis[2][2]);
+ spin[11]->set_value_no_signal(p_transform.origin[2]);
}
void EditorPropertyTransform3D::_notification(int p_what) {
@@ -2602,10 +2529,6 @@ void EditorPropertyProjection::_set_read_only(bool p_read_only) {
}
void EditorPropertyProjection::_value_changed(double val, const String &p_name) {
- if (setting) {
- return;
- }
-
Projection p;
p.columns[0][0] = spin[0]->get_value();
p.columns[0][1] = spin[1]->get_value();
@@ -2632,24 +2555,22 @@ void EditorPropertyProjection::update_property() {
}
void EditorPropertyProjection::update_using_transform(Projection p_transform) {
- setting = true;
- spin[0]->set_value(p_transform.columns[0][0]);
- spin[1]->set_value(p_transform.columns[0][1]);
- spin[2]->set_value(p_transform.columns[0][2]);
- spin[3]->set_value(p_transform.columns[0][3]);
- spin[4]->set_value(p_transform.columns[1][0]);
- spin[5]->set_value(p_transform.columns[1][1]);
- spin[6]->set_value(p_transform.columns[1][2]);
- spin[7]->set_value(p_transform.columns[1][3]);
- spin[8]->set_value(p_transform.columns[2][0]);
- spin[9]->set_value(p_transform.columns[2][1]);
- spin[10]->set_value(p_transform.columns[2][2]);
- spin[11]->set_value(p_transform.columns[2][3]);
- spin[12]->set_value(p_transform.columns[3][0]);
- spin[13]->set_value(p_transform.columns[3][1]);
- spin[14]->set_value(p_transform.columns[3][2]);
- spin[15]->set_value(p_transform.columns[3][3]);
- setting = false;
+ spin[0]->set_value_no_signal(p_transform.columns[0][0]);
+ spin[1]->set_value_no_signal(p_transform.columns[0][1]);
+ spin[2]->set_value_no_signal(p_transform.columns[0][2]);
+ spin[3]->set_value_no_signal(p_transform.columns[0][3]);
+ spin[4]->set_value_no_signal(p_transform.columns[1][0]);
+ spin[5]->set_value_no_signal(p_transform.columns[1][1]);
+ spin[6]->set_value_no_signal(p_transform.columns[1][2]);
+ spin[7]->set_value_no_signal(p_transform.columns[1][3]);
+ spin[8]->set_value_no_signal(p_transform.columns[2][0]);
+ spin[9]->set_value_no_signal(p_transform.columns[2][1]);
+ spin[10]->set_value_no_signal(p_transform.columns[2][2]);
+ spin[11]->set_value_no_signal(p_transform.columns[2][3]);
+ spin[12]->set_value_no_signal(p_transform.columns[3][0]);
+ spin[13]->set_value_no_signal(p_transform.columns[3][1]);
+ spin[14]->set_value_no_signal(p_transform.columns[3][2]);
+ spin[15]->set_value_no_signal(p_transform.columns[3][3]);
}
void EditorPropertyProjection::_notification(int p_what) {
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index ff9d47627a..b7ae4bd1ca 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -340,7 +340,6 @@ public:
class EditorPropertyInteger : public EditorProperty {
GDCLASS(EditorPropertyInteger, EditorProperty);
EditorSpinSlider *spin = nullptr;
- bool setting = false;
void _value_changed(int64_t p_val);
protected:
@@ -399,7 +398,6 @@ public:
class EditorPropertyFloat : public EditorProperty {
GDCLASS(EditorPropertyFloat, EditorProperty);
EditorSpinSlider *spin = nullptr;
- bool setting = false;
bool radians_as_degrees = false;
void _value_changed(double p_val);
@@ -418,7 +416,6 @@ class EditorPropertyEasing : public EditorProperty {
Control *easing_draw = nullptr;
PopupMenu *preset = nullptr;
EditorSpinSlider *spin = nullptr;
- bool setting = false;
bool dragging = false;
bool full = false;
@@ -459,7 +456,6 @@ public:
class EditorPropertyRect2 : public EditorProperty {
GDCLASS(EditorPropertyRect2, EditorProperty);
EditorSpinSlider *spin[4];
- bool setting = false;
void _value_changed(double p_val, const String &p_name);
protected:
@@ -476,7 +472,6 @@ public:
class EditorPropertyRect2i : public EditorProperty {
GDCLASS(EditorPropertyRect2i, EditorProperty);
EditorSpinSlider *spin[4];
- bool setting = false;
void _value_changed(double p_val, const String &p_name);
protected:
@@ -493,7 +488,6 @@ public:
class EditorPropertyPlane : public EditorProperty {
GDCLASS(EditorPropertyPlane, EditorProperty);
EditorSpinSlider *spin[4];
- bool setting = false;
void _value_changed(double p_val, const String &p_name);
protected:
@@ -511,7 +505,6 @@ class EditorPropertyQuaternion : public EditorProperty {
GDCLASS(EditorPropertyQuaternion, EditorProperty);
BoxContainer *default_layout = nullptr;
EditorSpinSlider *spin[4];
- bool setting = false;
Button *warning = nullptr;
AcceptDialog *warning_dialog = nullptr;
@@ -544,7 +537,6 @@ public:
class EditorPropertyAABB : public EditorProperty {
GDCLASS(EditorPropertyAABB, EditorProperty);
EditorSpinSlider *spin[6];
- bool setting = false;
void _value_changed(double p_val, const String &p_name);
protected:
@@ -561,7 +553,6 @@ public:
class EditorPropertyTransform2D : public EditorProperty {
GDCLASS(EditorPropertyTransform2D, EditorProperty);
EditorSpinSlider *spin[6];
- bool setting = false;
void _value_changed(double p_val, const String &p_name);
protected:
@@ -578,7 +569,6 @@ public:
class EditorPropertyBasis : public EditorProperty {
GDCLASS(EditorPropertyBasis, EditorProperty);
EditorSpinSlider *spin[9];
- bool setting = false;
void _value_changed(double p_val, const String &p_name);
protected:
@@ -595,7 +585,6 @@ public:
class EditorPropertyTransform3D : public EditorProperty {
GDCLASS(EditorPropertyTransform3D, EditorProperty);
EditorSpinSlider *spin[12];
- bool setting = false;
void _value_changed(double p_val, const String &p_name);
protected:
@@ -613,7 +602,6 @@ public:
class EditorPropertyProjection : public EditorProperty {
GDCLASS(EditorPropertyProjection, EditorProperty);
EditorSpinSlider *spin[16];
- bool setting = false;
void _value_changed(double p_val, const String &p_name);
protected:
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 1eaeee97a5..7fc870d174 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -400,6 +400,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
const String display_scale_hint_string = vformat("Auto (%d%%),75%%,100%%,125%%,150%%,175%%,200%%,Custom", Math::round(get_auto_display_scale() * 100));
EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/display_scale", 0, display_scale_hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
+ EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/ui_layout_direction", 0, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
+
String ed_screen_hints = "Screen With Mouse Pointer:-4,Screen With Keyboard Focus:-3,Primary Screen:-2"; // Note: Main Window Screen:-1 is not used for the main window.
for (int i = 0; i < DisplayServer::get_singleton()->get_screen_count(); i++) {
ed_screen_hints += ",Screen " + itos(i + 1) + ":" + itos(i);
@@ -884,6 +886,10 @@ bool EditorSettings::_is_default_text_editor_theme(String p_theme_name) {
return p_theme_name == "default" || p_theme_name == "godot 2" || p_theme_name == "custom";
}
+const String EditorSettings::_get_project_metadata_path() const {
+ return EditorPaths::get_singleton()->get_project_settings_dir().path_join("project_metadata.cfg");
+}
+
// PUBLIC METHODS
EditorSettings *EditorSettings::get_singleton() {
@@ -1171,24 +1177,31 @@ void EditorSettings::add_property_hint(const PropertyInfo &p_hint) {
// Metadata
void EditorSettings::set_project_metadata(const String &p_section, const String &p_key, Variant p_data) {
- Ref<ConfigFile> cf = memnew(ConfigFile);
- String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("project_metadata.cfg");
- Error err;
- err = cf->load(path);
- ERR_FAIL_COND_MSG(err != OK && err != ERR_FILE_NOT_FOUND, "Cannot load editor settings from file '" + path + "'.");
- cf->set_value(p_section, p_key, p_data);
- err = cf->save(path);
- ERR_FAIL_COND_MSG(err != OK, "Cannot save editor settings to file '" + path + "'.");
+ const String path = _get_project_metadata_path();
+
+ if (project_metadata.is_null()) {
+ project_metadata.instantiate();
+
+ Error err = project_metadata->load(path);
+ if (err != OK && err != ERR_FILE_NOT_FOUND) {
+ ERR_PRINT("Cannot load project metadata from file '" + path + "'.");
+ }
+ }
+ project_metadata->set_value(p_section, p_key, p_data);
+
+ Error err = project_metadata->save(path);
+ ERR_FAIL_COND_MSG(err != OK, "Cannot save project metadata to file '" + path + "'.");
}
Variant EditorSettings::get_project_metadata(const String &p_section, const String &p_key, Variant p_default) const {
- Ref<ConfigFile> cf = memnew(ConfigFile);
- String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("project_metadata.cfg");
- Error err = cf->load(path);
- if (err != OK) {
- return p_default;
+ if (project_metadata.is_null()) {
+ project_metadata.instantiate();
+
+ const String path = _get_project_metadata_path();
+ Error err = project_metadata->load(path);
+ ERR_FAIL_COND_V_MSG(err != OK && err != ERR_FILE_NOT_FOUND, p_default, "Cannot load project metadata from file '" + path + "'.");
}
- return cf->get_value(p_section, p_key, p_default);
+ return project_metadata->get_value(p_section, p_key, p_default);
}
void EditorSettings::set_favorites(const Vector<String> &p_favorites) {
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index 660a9501a2..c3ce790e0e 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -79,6 +79,7 @@ private:
HashSet<String> changed_settings;
+ mutable Ref<ConfigFile> project_metadata;
HashMap<String, PropertyInfo> hints;
HashMap<String, VariantContainer> props;
int last_order;
@@ -106,6 +107,7 @@ private:
void _load_godot2_text_editor_theme();
bool _save_text_editor_theme(String p_file);
bool _is_default_text_editor_theme(String p_theme_name);
+ const String _get_project_metadata_path() const;
protected:
static void _bind_methods();
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 96d2abf202..ef68554a3d 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -1423,8 +1423,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Tree
theme->set_icon("checked", "Tree", theme->get_icon(SNAME("GuiChecked"), EditorStringName(EditorIcons)));
+ theme->set_icon("checked_disabled", "Tree", theme->get_icon(SNAME("GuiCheckedDisabled"), EditorStringName(EditorIcons)));
theme->set_icon("indeterminate", "Tree", theme->get_icon(SNAME("GuiIndeterminate"), EditorStringName(EditorIcons)));
+ theme->set_icon("indeterminate_disabled", "Tree", theme->get_icon(SNAME("GuiIndeterminateDisabled"), EditorStringName(EditorIcons)));
theme->set_icon("unchecked", "Tree", theme->get_icon(SNAME("GuiUnchecked"), EditorStringName(EditorIcons)));
+ theme->set_icon("unchecked_disabled", "Tree", theme->get_icon(SNAME("GuiUncheckedDisabled"), EditorStringName(EditorIcons)));
theme->set_icon("arrow", "Tree", theme->get_icon(SNAME("GuiTreeArrowDown"), EditorStringName(EditorIcons)));
theme->set_icon("arrow_collapsed", "Tree", theme->get_icon(SNAME("GuiTreeArrowRight"), EditorStringName(EditorIcons)));
theme->set_icon("arrow_collapsed_mirrored", "Tree", theme->get_icon(SNAME("GuiTreeArrowLeft"), EditorStringName(EditorIcons)));
@@ -1437,6 +1440,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("custom_button_font_highlight", "Tree", font_hover_color);
theme->set_color("font_color", "Tree", font_color);
theme->set_color("font_selected_color", "Tree", mono_color);
+ theme->set_color("font_disabled_color", "Tree", font_disabled_color);
theme->set_color("font_outline_color", "Tree", font_outline_color);
theme->set_color("title_button_color", "Tree", font_color);
theme->set_color("drop_position_color", "Tree", accent_color);
diff --git a/editor/icons/GuiIndeterminateDisabled.svg b/editor/icons/GuiIndeterminateDisabled.svg
new file mode 100644
index 0000000000..edaf69f7a1
--- /dev/null
+++ b/editor/icons/GuiIndeterminateDisabled.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" rx="2.33" height="14" width="14" fill="#808080"/><path d="M3 7h10v2H3z" fill="#b3b3b3"/></svg>
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index a5813cf192..80dc3f194c 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -1933,7 +1933,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI),Static Lightmaps (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Static,Static Lightmaps,Dynamic", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/force_disable_compression"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 04eda502d2..eb934da4dd 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -2482,7 +2482,10 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT_OVERRIDE("script_text_editor/contextual_help", "macos", KeyModifierMask::ALT | KeyModifierMask::SHIFT | Key::SPACE);
ED_SHORTCUT("script_text_editor/toggle_bookmark", TTR("Toggle Bookmark"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::B);
+
ED_SHORTCUT("script_text_editor/goto_next_bookmark", TTR("Go to Next Bookmark"), KeyModifierMask::CMD_OR_CTRL | Key::B);
+ ED_SHORTCUT_OVERRIDE("script_text_editor/goto_next_bookmark", "macos", KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | KeyModifierMask::ALT | Key::B);
+
ED_SHORTCUT("script_text_editor/goto_previous_bookmark", TTR("Go to Previous Bookmark"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::B);
ED_SHORTCUT("script_text_editor/remove_all_bookmarks", TTR("Remove All Bookmarks"), Key::NONE);
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 7b3847e548..661af16ce8 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -3439,7 +3439,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_editor_theme_icon(SNAME("TerrainPath")));
- terrains_tile_list->set_item_tooltip(item_index, TTR("Path mode: paints a terrain, thens connects it to the previous tile painted within the same stroke."));
+ terrains_tile_list->set_item_tooltip(item_index, TTR("Path mode: paints a terrain, then connects it to the previous tile painted within the same stroke."));
list_metadata_dict = Dictionary();
list_metadata_dict["type"] = SELECTED_TYPE_PATH;
terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 291484600c..2073b1f374 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -2845,6 +2845,10 @@ ProjectManager::ProjectManager() {
AcceptDialog::set_swap_cancel_ok(swap_cancel_ok == 2);
}
+ int pm_root_dir = EDITOR_GET("interface/editor/ui_layout_direction");
+ Control::set_root_layout_direction(pm_root_dir);
+ Window::set_root_layout_direction(pm_root_dir);
+
EditorColorMap::create();
EditorTheme::initialize();
Ref<Theme> theme = create_custom_theme();
diff --git a/main/main.cpp b/main/main.cpp
index 350b8606b9..0570af3566 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2502,7 +2502,7 @@ Error Main::setup2() {
OS::get_singleton()->benchmark_begin_measure("Servers", "Tablet Driver");
GLOBAL_DEF_RST_NOVAL("input_devices/pen_tablet/driver", "");
- GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "input_devices/pen_tablet/driver.windows", PROPERTY_HINT_ENUM, "wintab,winink"), "");
+ GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "input_devices/pen_tablet/driver.windows", PROPERTY_HINT_ENUM, "winink,wintab,dummy"), "");
if (tablet_driver.is_empty()) { // specified in project.godot
tablet_driver = GLOBAL_GET("input_devices/pen_tablet/driver");
diff --git a/modules/csg/doc_classes/CSGPolygon3D.xml b/modules/csg/doc_classes/CSGPolygon3D.xml
index 338adc9b52..5d35c04e25 100644
--- a/modules/csg/doc_classes/CSGPolygon3D.xml
+++ b/modules/csg/doc_classes/CSGPolygon3D.xml
@@ -39,7 +39,7 @@
When [member mode] is [constant MODE_PATH], the location of the [Path3D] object used to extrude the [member polygon].
</member>
<member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon3D.PathRotation">
- When [member mode] is [constant MODE_PATH], the [enum PathRotation] method used to rotate the [member polygon] as it is extruded.
+ When [member mode] is [constant MODE_PATH], the path rotation method used to rotate the [member polygon] as it is extruded.
</member>
<member name="path_simplify_angle" type="float" setter="set_path_simplify_angle" getter="get_path_simplify_angle">
When [member mode] is [constant MODE_PATH], extrusions that are less than this angle, will be merged together to reduce polygon count.
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 55bd0f97d5..5478a46bbc 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3777,6 +3777,60 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
}
+ // Check non-GDScript scripts.
+ Ref<Script> script_type = base.script_type;
+
+ if (base_class == nullptr && script_type.is_valid()) {
+ List<PropertyInfo> property_list;
+ script_type->get_script_property_list(&property_list);
+
+ for (const PropertyInfo &property_info : property_list) {
+ if (property_info.name != p_identifier->name) {
+ continue;
+ }
+
+ const GDScriptParser::DataType property_type = GDScriptAnalyzer::type_from_property(property_info, false, false);
+
+ p_identifier->set_datatype(property_type);
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
+ return;
+ }
+
+ MethodInfo method_info = script_type->get_method_info(p_identifier->name);
+
+ if (method_info.name == p_identifier->name) {
+ p_identifier->set_datatype(make_callable_type(method_info));
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION;
+ return;
+ }
+
+ List<MethodInfo> signal_list;
+ script_type->get_script_signal_list(&signal_list);
+
+ for (const MethodInfo &signal_info : signal_list) {
+ if (signal_info.name != p_identifier->name) {
+ continue;
+ }
+
+ const GDScriptParser::DataType signal_type = make_signal_type(signal_info);
+
+ p_identifier->set_datatype(signal_type);
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
+ return;
+ }
+
+ HashMap<StringName, Variant> constant_map;
+ script_type->get_constants(&constant_map);
+
+ if (constant_map.has(p_identifier->name)) {
+ Variant constant = constant_map.get(p_identifier->name);
+
+ p_identifier->set_datatype(make_builtin_meta_type(constant.get_type()));
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
+ return;
+ }
+ }
+
// Check native members. No need for native class recursion because Node exposes all Object's properties.
const StringName &native = base.native_type;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index ea7abc9ded..f3a4f2eaa6 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -73,8 +73,11 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
HashMap<String, String> GDScriptParser::theme_color_names;
#endif
+HashMap<StringName, GDScriptParser::AnnotationInfo> GDScriptParser::valid_annotations;
+
void GDScriptParser::cleanup() {
builtin_types.clear();
+ valid_annotations.clear();
}
void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
@@ -89,41 +92,42 @@ bool GDScriptParser::annotation_exists(const String &p_annotation_name) const {
GDScriptParser::GDScriptParser() {
// Register valid annotations.
- // TODO: Should this be static?
- register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
- register_annotation(MethodInfo("@icon", PropertyInfo(Variant::STRING, "icon_path")), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
- register_annotation(MethodInfo("@static_unload"), AnnotationInfo::SCRIPT, &GDScriptParser::static_unload_annotation);
-
- register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
- // Export annotations.
- register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
- register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true);
- register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true);
- register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
- register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true);
- register_annotation(MethodInfo("@export_global_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_DIR, Variant::STRING>);
- register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>);
- register_annotation(MethodInfo("@export_placeholder", PropertyInfo(Variant::STRING, "placeholder")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>);
- register_annotation(MethodInfo("@export_range", PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"), PropertyInfo(Variant::FLOAT, "step"), PropertyInfo(Variant::STRING, "extra_hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, varray(1.0, ""), true);
- register_annotation(MethodInfo("@export_exp_easing", PropertyInfo(Variant::STRING, "hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, varray(""), true);
- register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>);
- register_annotation(MethodInfo("@export_node_path", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, varray(""), true);
- register_annotation(MethodInfo("@export_flags", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, varray(), true);
- register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
- register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
- // Export grouping annotations.
- register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
- register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray(""));
- register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray(""));
- // Warning annotations.
- register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true);
- // Networking.
- register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("authority", "call_remote", "unreliable", 0));
+ if (unlikely(valid_annotations.is_empty())) {
+ register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
+ register_annotation(MethodInfo("@icon", PropertyInfo(Variant::STRING, "icon_path")), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
+ register_annotation(MethodInfo("@static_unload"), AnnotationInfo::SCRIPT, &GDScriptParser::static_unload_annotation);
+
+ register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
+ // Export annotations.
+ register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
+ register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true);
+ register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true);
+ register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
+ register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true);
+ register_annotation(MethodInfo("@export_global_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_DIR, Variant::STRING>);
+ register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>);
+ register_annotation(MethodInfo("@export_placeholder", PropertyInfo(Variant::STRING, "placeholder")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>);
+ register_annotation(MethodInfo("@export_range", PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"), PropertyInfo(Variant::FLOAT, "step"), PropertyInfo(Variant::STRING, "extra_hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, varray(1.0, ""), true);
+ register_annotation(MethodInfo("@export_exp_easing", PropertyInfo(Variant::STRING, "hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, varray(""), true);
+ register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>);
+ register_annotation(MethodInfo("@export_node_path", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, varray(""), true);
+ register_annotation(MethodInfo("@export_flags", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, varray(), true);
+ register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
+ // Export grouping annotations.
+ register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
+ register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray(""));
+ register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray(""));
+ // Warning annotations.
+ register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true);
+ // Networking.
+ register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("authority", "call_remote", "unreliable", 0));
+ }
#ifdef DEBUG_ENABLED
is_ignoring_warnings = !(bool)GLOBAL_GET("debug/gdscript/warnings/enable");
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index f48ad48de0..88b5bdc43f 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -1370,7 +1370,7 @@ private:
AnnotationAction apply = nullptr;
MethodInfo info;
};
- HashMap<StringName, AnnotationInfo> valid_annotations;
+ static HashMap<StringName, AnnotationInfo> valid_annotations;
List<AnnotationNode *> annotation_stack;
typedef ExpressionNode *(GDScriptParser::*ParseFunction)(ExpressionNode *p_previous_operand, bool p_can_assign);
@@ -1470,7 +1470,7 @@ private:
SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr, bool p_for_lambda = false);
// Annotations
AnnotationNode *parse_annotation(uint32_t p_valid_targets);
- bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false);
+ static bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false);
bool validate_annotation_arguments(AnnotationNode *p_annotation);
void clear_unused_annotations();
bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index e05979efbc..348ac5194c 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -155,7 +155,6 @@ class GridMap : public Node3D {
Ref<PhysicsMaterial> physics_material;
bool bake_navigation = false;
RID map_override;
- uint32_t navigation_layers = 1;
Transform3D last_transform;
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 36fdda4625..25a5720bc4 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -69,6 +69,7 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
#define OPEN_BLOCK_L1 INDENT1 OPEN_BLOCK
#define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK
+#define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK
#define CLOSE_BLOCK_L1 INDENT1 CLOSE_BLOCK
#define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK
#define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK
@@ -2591,7 +2592,11 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall,
// Generate icall function
r_output << MEMBER_BEGIN "internal static unsafe " << (ret_void ? "void" : return_type->c_type_out) << " "
- << icall_method << "(" << c_func_sig.as_string() << ") " OPEN_BLOCK;
+ << icall_method << "(" << c_func_sig.as_string() << ")\n" OPEN_BLOCK_L1;
+
+ if (!p_icall.is_static) {
+ r_output << INDENT2 "ExceptionUtils.ThrowIfNullPtr(" CS_PARAM_INSTANCE ");\n";
+ }
if (!ret_void && (!p_icall.is_vararg || return_type->cname != name_cache.type_Variant)) {
String ptrcall_return_type;
@@ -2619,11 +2624,6 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall,
r_output << ptrcall_return_type << " " C_LOCAL_RET << initialization << ";\n";
}
- if (!p_icall.is_static) {
- r_output << INDENT2 "if (" CS_PARAM_INSTANCE " == IntPtr.Zero)\n"
- << INDENT3 "throw new ArgumentNullException(nameof(" CS_PARAM_INSTANCE "));\n";
- }
-
String argc_str = itos(p_icall.get_arguments_count());
auto generate_call_and_return_stmts = [&](const char *base_indent) {
@@ -2714,7 +2714,7 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall,
r_output << c_in_statements.as_string();
- r_output << INDENT3 "for (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
+ r_output << INDENT3 "for (int i = 0; i < vararg_length; i++)\n" OPEN_BLOCK_L3
<< INDENT4 "varargs[i] = " << vararg_arg << "[i].NativeVar;\n"
<< INDENT4 C_LOCAL_PTRCALL_ARGS "[" << real_argc_str << " + i] = new IntPtr(&varargs[i]);\n"
<< CLOSE_BLOCK_L3;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
index dc53e48bd0..537d863ef2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Runtime.CompilerServices;
using System.Text;
#nullable enable
@@ -239,5 +240,13 @@ namespace Godot.NativeInterop
return variant->Type.ToString();
}
+
+ internal static void ThrowIfNullPtr(IntPtr ptr, [CallerArgumentExpression(nameof(ptr))] string? paramName = null)
+ {
+ if (ptr == IntPtr.Zero)
+ {
+ throw new ArgumentNullException(paramName);
+ }
+ }
}
}
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 3b875b7fa7..6429513b53 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -372,7 +372,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
// Stores the further reachable end polygon, in case our goal is not reachable.
if (is_reachable) {
- real_t d = navigation_polys[least_cost_id].entry.distance_to(p_destination) * navigation_polys[least_cost_id].poly->owner->get_travel_cost();
+ real_t d = navigation_polys[least_cost_id].entry.distance_to(p_destination);
if (reachable_d > d) {
reachable_d = d;
reachable_end = navigation_polys[least_cost_id].poly;
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index 454f8f2ed4..8698c5755a 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -76,7 +76,7 @@
<method name="get_signaling_state" qualifiers="const">
<return type="int" enum="WebRTCPeerConnection.SignalingState" />
<description>
- Returns the [enum SignalingState] on the local end of the connection while connecting or reconnecting to another peer.
+ Returns the signaling state on the local end of the connection while connecting or reconnecting to another peer.
</description>
</method>
<method name="initialize">
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 77dfff2e5d..0f7003c303 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -4606,6 +4606,22 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
}
+ // Note: Windows Ink API for pen input, available on Windows 8+ only.
+ // Note: DPI conversion API, available on Windows 8.1+ only.
+ HMODULE user32_lib = LoadLibraryW(L"user32.dll");
+ if (user32_lib) {
+ win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
+ win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
+ win81p_LogicalToPhysicalPointForPerMonitorDPI = (LogicalToPhysicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "LogicalToPhysicalPointForPerMonitorDPI");
+ win81p_PhysicalToLogicalPointForPerMonitorDPI = (PhysicalToLogicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "PhysicalToLogicalPointForPerMonitorDPI");
+
+ winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo;
+ }
+
+ if (winink_available) {
+ tablet_drivers.push_back("winink");
+ }
+
// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");
if (wintab_lib) {
@@ -4622,21 +4638,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
tablet_drivers.push_back("wintab");
}
- // Note: Windows Ink API for pen input, available on Windows 8+ only.
- // Note: DPI conversion API, available on Windows 8.1+ only.
- HMODULE user32_lib = LoadLibraryW(L"user32.dll");
- if (user32_lib) {
- win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
- win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
- win81p_LogicalToPhysicalPointForPerMonitorDPI = (LogicalToPhysicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "LogicalToPhysicalPointForPerMonitorDPI");
- win81p_PhysicalToLogicalPointForPerMonitorDPI = (PhysicalToLogicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "PhysicalToLogicalPointForPerMonitorDPI");
-
- winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo;
- }
-
- if (winink_available) {
- tablet_drivers.push_back("winink");
- }
+ tablet_drivers.push_back("dummy");
if (OS::get_singleton()->is_hidpi_allowed()) {
HMODULE Shcore = LoadLibraryW(L"Shcore.dll");
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 1c193f991e..e04e6d7dce 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -881,7 +881,7 @@ void CPUParticles2D::_particles_process(double p_delta) {
force += diff.length() > 0.0 ? diff.normalized() * (tex_radial_accel)*Math::lerp(parameters_min[PARAM_RADIAL_ACCEL], parameters_max[PARAM_RADIAL_ACCEL], rand_from_seed(alt_seed)) : Vector2();
//apply tangential acceleration;
Vector2 yx = Vector2(diff.y, diff.x);
- force += yx.length() > 0.0 ? yx.normalized() * (tex_tangential_accel * Math::lerp(parameters_min[PARAM_TANGENTIAL_ACCEL], parameters_max[PARAM_TANGENTIAL_ACCEL], rand_from_seed(alt_seed))) : Vector2();
+ force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)).normalized() * (tex_tangential_accel * Math::lerp(parameters_min[PARAM_TANGENTIAL_ACCEL], parameters_max[PARAM_TANGENTIAL_ACCEL], rand_from_seed(alt_seed))) : Vector2();
//apply attractor forces
p.velocity += force * local_delta;
//orbit velocity
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index 187ce90284..ebcd69455f 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -377,7 +377,7 @@ void Light3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01,degrees"), "set_param", "get_param", PARAM_SIZE);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_specular", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_SPECULAR);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI/SDFGI only)"), "set_bake_mode", "get_bake_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Static,Dynamic"), "set_bake_mode", "get_bake_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
ADD_GROUP("Shadow", "shadow_");
diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index 3b1faca17e..33f865382d 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -511,7 +511,7 @@ void GeometryInstance3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling");
ADD_GROUP("Global Illumination", "gi_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)"), "set_gi_mode", "get_gi_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Static,Dynamic"), "set_gi_mode", "get_gi_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×")), "set_lightmap_scale", "get_lightmap_scale");
ADD_GROUP("Visibility Range", "visibility_range_");
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index ed54bd000c..84ca6bac5b 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -197,6 +197,12 @@ void Control::reparent(Node *p_parent, bool p_keep_global_transform) {
// Editor integration.
+int Control::root_layout_direction = 0;
+
+void Control::set_root_layout_direction(int p_root_dir) {
+ root_layout_direction = p_root_dir;
+}
+
void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
ERR_READ_THREAD_GUARD;
Node::get_argument_options(p_function, p_idx, r_options);
@@ -3024,10 +3030,35 @@ bool Control::is_layout_rtl() const {
if (data.is_rtl_dirty) {
const_cast<Control *>(this)->data.is_rtl_dirty = false;
if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) {
+#ifdef TOOLS_ENABLED
+ if (is_part_of_edited_scene() && GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
+ const_cast<Control *>(this)->data.is_rtl = true;
+ return data.is_rtl;
+ }
+ if (is_inside_tree()) {
+ Node *edited_scene_root = get_tree()->get_edited_scene_root();
+ if (edited_scene_root == this) {
+ int proj_root_layout_direction = GLOBAL_GET(SNAME("internationalization/rendering/root_node_layout_direction"));
+ if (proj_root_layout_direction == 1) {
+ const_cast<Control *>(this)->data.is_rtl = false;
+ } else if (proj_root_layout_direction == 2) {
+ const_cast<Control *>(this)->data.is_rtl = true;
+ } else if (proj_root_layout_direction == 3) {
+ String locale = OS::get_singleton()->get_locale();
+ const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
+ } else {
+ String locale = TranslationServer::get_singleton()->get_tool_locale();
+ const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
+ }
+ return data.is_rtl;
+ }
+ }
+#else
if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
const_cast<Control *>(this)->data.is_rtl = true;
return data.is_rtl;
}
+#endif
Node *parent_node = get_parent();
while (parent_node) {
Control *parent_control = Object::cast_to<Control>(parent_node);
@@ -3044,11 +3075,13 @@ bool Control::is_layout_rtl() const {
parent_node = parent_node->get_parent();
}
- int root_dir = GLOBAL_GET(SNAME("internationalization/rendering/root_node_layout_direction"));
- if (root_dir == 1) {
+ if (root_layout_direction == 1) {
const_cast<Control *>(this)->data.is_rtl = false;
- } else if (root_dir == 2) {
+ } else if (root_layout_direction == 2) {
const_cast<Control *>(this)->data.is_rtl = true;
+ } else if (root_layout_direction == 3) {
+ String locale = OS::get_singleton()->get_locale();
+ const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
} else {
String locale = TranslationServer::get_singleton()->get_tool_locale();
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index db1bd3a346..aec61dc2fe 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -317,6 +317,8 @@ private:
// Extra properties.
+ static int root_layout_direction;
+
String get_tooltip_text() const;
protected:
@@ -403,6 +405,8 @@ public:
// Editor integration.
+ static void set_root_layout_direction(int p_root_dir);
+
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
PackedStringArray get_configuration_warnings() const override;
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 957a8f276e..3e827e76dc 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -42,6 +42,7 @@ void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) {
if (close_on_escape && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) {
_cancel_pressed();
}
+ Window::_input_from_window(p_event);
}
void AcceptDialog::_parent_focused() {
@@ -428,8 +429,6 @@ AcceptDialog::AcceptDialog() {
ok_button->connect("pressed", callable_mp(this, &AcceptDialog::_ok_pressed));
set_title(TTRC("Alert!"));
-
- connect("window_input", callable_mp(this, &AcceptDialog::_input_from_window));
}
AcceptDialog::~AcceptDialog() {
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index d5cbaaeef8..e28d6b7467 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -65,11 +65,11 @@ class AcceptDialog : public Window {
static bool swap_cancel_ok;
- void _input_from_window(const Ref<InputEvent> &p_event);
void _parent_focused();
protected:
virtual Size2 _get_contents_minimum_size() const override;
+ virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index b16e8371a2..8369bedda9 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -39,6 +39,7 @@ void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
if (get_flag(FLAG_POPUP) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) {
_close_pressed();
}
+ Window::_input_from_window(p_event);
}
void Popup::_initialize_visible_parents() {
@@ -204,8 +205,6 @@ Popup::Popup() {
set_flag(FLAG_BORDERLESS, true);
set_flag(FLAG_RESIZE_DISABLED, true);
set_flag(FLAG_POPUP, true);
-
- connect("window_input", callable_mp(this, &Popup::_input_from_window));
}
Popup::~Popup() {
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index d524e448dd..25edca3657 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -47,14 +47,13 @@ class Popup : public Window {
Ref<StyleBox> panel_style;
} theme_cache;
- void _input_from_window(const Ref<InputEvent> &p_event);
-
void _initialize_visible_parents();
void _deinitialize_visible_parents();
protected:
void _close_pressed();
virtual Rect2i _popup_adjust_rect() const override;
+ virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
void _notification(int p_what);
void _validate_property(PropertyInfo &p_property) const;
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 605a3fe397..d9c633b238 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -414,9 +414,16 @@ void PopupMenu::_submenu_timeout() {
submenu_over = -1;
}
-void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
- ERR_FAIL_COND(p_event.is_null());
+void PopupMenu::_input_from_window(const Ref<InputEvent> &p_event) {
+ if (p_event.is_valid()) {
+ _input_from_window_internal(p_event);
+ } else {
+ WARN_PRINT_ONCE("PopupMenu has received an invalid InputEvent. Consider filtering invalid events out.");
+ }
+ Popup::_input_from_window(p_event);
+}
+void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
if (!items.is_empty()) {
Input *input = Input::get_singleton();
Ref<InputEventJoypadMotion> joypadmotion_event = p_event;
@@ -2849,8 +2856,6 @@ PopupMenu::PopupMenu() {
scroll_container->add_child(control, false, INTERNAL_MODE_FRONT);
control->connect("draw", callable_mp(this, &PopupMenu::_draw_items));
- connect("window_input", callable_mp(this, &PopupMenu::gui_input));
-
submenu_timer = memnew(Timer);
submenu_timer->set_wait_time(0.3);
submenu_timer->set_one_shot(true);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 4703686c51..c1ab9544ea 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -115,7 +115,6 @@ class PopupMenu : public Popup {
void _shape_item(int p_idx);
- virtual void gui_input(const Ref<InputEvent> &p_event);
void _activate_submenu(int p_over, bool p_by_keyboard = false);
void _submenu_timeout();
@@ -194,10 +193,12 @@ class PopupMenu : public Popup {
void _minimum_lifetime_timeout();
void _close_pressed();
void _menu_changed();
+ void _input_from_window_internal(const Ref<InputEvent> &p_event);
protected:
virtual void add_child_notify(Node *p_child) override;
virtual void remove_child_notify(Node *p_child) override;
+ virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
void _notification(int p_what);
bool _set(const StringName &p_name, const Variant &p_value);
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index 70c2cc9d65..248260a7d2 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -74,6 +74,7 @@ void TextureProgressBar::set_nine_patch_stretch(bool p_stretch) {
nine_patch_stretch = p_stretch;
queue_redraw();
update_minimum_size();
+ notify_property_list_changed();
}
bool TextureProgressBar::get_nine_patch_stretch() const {
@@ -615,6 +616,7 @@ void TextureProgressBar::set_fill_mode(int p_fill) {
mode = (FillMode)p_fill;
queue_redraw();
+ notify_property_list_changed();
}
int TextureProgressBar::get_fill_mode() {
@@ -669,6 +671,16 @@ Point2 TextureProgressBar::get_radial_center_offset() {
return rad_center_off;
}
+void TextureProgressBar::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name.begins_with("stretch_margin_") && !nine_patch_stretch) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+
+ if (p_property.name.begins_with("radial_") && (mode != FillMode::FILL_CLOCKWISE && mode != FillMode::FILL_COUNTER_CLOCKWISE && mode != FillMode::FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE)) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+}
+
void TextureProgressBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_under_texture", "tex"), &TextureProgressBar::set_under_texture);
ClassDB::bind_method(D_METHOD("get_under_texture"), &TextureProgressBar::get_under_texture);
@@ -710,8 +722,13 @@ void TextureProgressBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_nine_patch_stretch"), &TextureProgressBar::get_nine_patch_stretch);
ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom),Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "nine_patch_stretch"), "set_nine_patch_stretch", "get_nine_patch_stretch");
+ ADD_GROUP("Radial Fill", "radial_");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,degrees"), "set_radial_initial_angle", "get_radial_initial_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,degrees"), "set_fill_degrees", "get_fill_degrees");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "radial_center_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_radial_center_offset", "get_radial_center_offset");
+ ADD_GROUP("", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "nine_patch_stretch"), "set_nine_patch_stretch", "get_nine_patch_stretch");
ADD_GROUP("Stretch Margin", "stretch_margin_");
ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_stretch_margin", "get_stretch_margin", SIDE_LEFT);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_stretch_margin", "get_stretch_margin", SIDE_TOP);
@@ -729,11 +746,6 @@ void TextureProgressBar::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over"), "set_tint_over", "get_tint_over");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress"), "set_tint_progress", "get_tint_progress");
- ADD_GROUP("Radial Fill", "radial_");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider,degrees"), "set_radial_initial_angle", "get_radial_initial_angle");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider,degrees"), "set_fill_degrees", "get_fill_degrees");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "radial_center_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_radial_center_offset", "get_radial_center_offset");
-
BIND_ENUM_CONSTANT(FILL_LEFT_TO_RIGHT);
BIND_ENUM_CONSTANT(FILL_RIGHT_TO_LEFT);
BIND_ENUM_CONSTANT(FILL_TOP_TO_BOTTOM);
diff --git a/scene/gui/texture_progress_bar.h b/scene/gui/texture_progress_bar.h
index 5999aa986b..d890ce6e36 100644
--- a/scene/gui/texture_progress_bar.h
+++ b/scene/gui/texture_progress_bar.h
@@ -43,6 +43,7 @@ class TextureProgressBar : public Range {
protected:
static void _bind_methods();
void _notification(int p_what);
+ void _validate_property(PropertyInfo &p_property) const;
public:
enum FillMode {
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 8a243fd68f..18ee39aeec 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -2195,27 +2195,38 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
draw_item_rect(p_item->cells.write[i], item_rect, cell_color, icon_col, outline_size, font_outline_color);
} break;
case TreeItem::CELL_MODE_CHECK: {
- Ref<Texture2D> checked = theme_cache.checked;
- Ref<Texture2D> unchecked = theme_cache.unchecked;
- Ref<Texture2D> indeterminate = theme_cache.indeterminate;
Point2i check_ofs = item_rect.position;
- check_ofs.y += Math::floor((real_t)(item_rect.size.y - checked->get_height()) / 2);
+ check_ofs.y += Math::floor((real_t)(item_rect.size.y - theme_cache.checked->get_height()) / 2);
- if (p_item->cells[i].indeterminate) {
- indeterminate->draw(ci, check_ofs);
- } else if (p_item->cells[i].checked) {
- checked->draw(ci, check_ofs);
+ if (p_item->cells[i].editable) {
+ if (p_item->cells[i].indeterminate) {
+ theme_cache.indeterminate->draw(ci, check_ofs);
+ } else if (p_item->cells[i].checked) {
+ theme_cache.checked->draw(ci, check_ofs);
+ } else {
+ theme_cache.unchecked->draw(ci, check_ofs);
+ }
} else {
- unchecked->draw(ci, check_ofs);
+ if (p_item->cells[i].indeterminate) {
+ theme_cache.indeterminate_disabled->draw(ci, check_ofs);
+ } else if (p_item->cells[i].checked) {
+ theme_cache.checked_disabled->draw(ci, check_ofs);
+ } else {
+ theme_cache.unchecked_disabled->draw(ci, check_ofs);
+ }
}
- int check_w = checked->get_width() + theme_cache.h_separation;
+ int check_w = theme_cache.checked->get_width() + theme_cache.h_separation;
text_pos.x += check_w;
item_rect.size.x -= check_w;
item_rect.position.x += check_w;
+ if (!p_item->cells[i].editable) {
+ cell_color = theme_cache.font_disabled_color;
+ }
+
draw_item_rect(p_item->cells.write[i], item_rect, cell_color, icon_col, outline_size, font_outline_color);
} break;
@@ -5535,7 +5546,10 @@ void Tree::_bind_methods() {
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, checked);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, unchecked);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, checked_disabled);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, unchecked_disabled);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, indeterminate);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, indeterminate_disabled);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, arrow);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, arrow_collapsed);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, arrow_collapsed_mirrored);
@@ -5549,6 +5563,7 @@ void Tree::_bind_methods() {
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_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);
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, h_separation);
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, v_separation);
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 4a5814c45c..2dda408dd7 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -536,7 +536,10 @@ private:
Ref<Texture2D> checked;
Ref<Texture2D> unchecked;
+ Ref<Texture2D> checked_disabled;
+ Ref<Texture2D> unchecked_disabled;
Ref<Texture2D> indeterminate;
+ Ref<Texture2D> indeterminate_disabled;
Ref<Texture2D> arrow;
Ref<Texture2D> arrow_collapsed;
Ref<Texture2D> arrow_collapsed_mirrored;
@@ -545,6 +548,7 @@ private:
Color font_color;
Color font_selected_color;
+ Color font_disabled_color;
Color guide_color;
Color drop_position_color;
Color relationship_line_color;
diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp
index c61fa1d9b8..438a228c4f 100644
--- a/scene/gui/view_panner.cpp
+++ b/scene/gui/view_panner.cpp
@@ -43,12 +43,14 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
if (scroll_vec != Vector2() && mb->is_pressed()) {
if (control_scheme == SCROLL_PANS) {
if (mb->is_ctrl_pressed()) {
- // Compute the zoom factor.
- float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor();
- zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0;
- float zoom = (scroll_vec.x + scroll_vec.y) > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor;
- zoom_callback.call(zoom, mb->get_position(), p_event);
- return true;
+ if (scroll_vec.y != 0) {
+ // Compute the zoom factor.
+ float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor();
+ zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0;
+ float zoom = scroll_vec.y > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor;
+ zoom_callback.call(zoom, mb->get_position(), p_event);
+ return true;
+ }
} else {
Vector2 panning = scroll_vec * mb->get_factor();
if (pan_axis == PAN_AXIS_HORIZONTAL) {
@@ -73,11 +75,11 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
}
pan_callback.call(-panning * scroll_speed, p_event);
return true;
- } else if (!mb->is_shift_pressed()) {
+ } else if (!mb->is_shift_pressed() && scroll_vec.y != 0) {
// Compute the zoom factor.
float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor();
zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0;
- float zoom = (scroll_vec.x + scroll_vec.y) > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor;
+ float zoom = scroll_vec.y > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor;
zoom_callback.call(zoom, mb->get_position(), p_event);
return true;
}
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 36d7d079b2..eb431445ed 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -40,6 +40,14 @@
#include "scene/theme/theme_db.h"
#include "scene/theme/theme_owner.h"
+// Editor integration.
+
+int Window::root_layout_direction = 0;
+
+void Window::set_root_layout_direction(int p_root_dir) {
+ root_layout_direction = p_root_dir;
+}
+
// Dynamic properties.
bool Window::_set(const StringName &p_name, const Variant &p_value) {
@@ -1559,7 +1567,12 @@ void Window::_window_input(const Ref<InputEvent> &p_ev) {
}
}
- if (p_ev->get_device() != InputEvent::DEVICE_ID_INTERNAL) {
+ // If the event needs to be handled in a Window-derived class, then it should overwrite
+ // `_input_from_window` instead of subscribing to the `window_input` signal, because the signal
+ // filters out internal events.
+ _input_from_window(p_ev);
+
+ if (p_ev->get_device() != InputEvent::DEVICE_ID_INTERNAL && is_inside_tree()) {
emit_signal(SceneStringNames::get_singleton()->window_input, p_ev);
}
@@ -2533,9 +2546,32 @@ Window::LayoutDirection Window::get_layout_direction() const {
bool Window::is_layout_rtl() const {
ERR_READ_THREAD_GUARD_V(false);
if (layout_dir == LAYOUT_DIRECTION_INHERITED) {
+#ifdef TOOLS_ENABLED
+ if (is_part_of_edited_scene() && GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
+ return true;
+ }
+ if (is_inside_tree()) {
+ Node *edited_scene_root = get_tree()->get_edited_scene_root();
+ if (edited_scene_root == this) {
+ int proj_root_layout_direction = GLOBAL_GET(SNAME("internationalization/rendering/root_node_layout_direction"));
+ if (proj_root_layout_direction == 1) {
+ return false;
+ } else if (proj_root_layout_direction == 2) {
+ return true;
+ } else if (proj_root_layout_direction == 3) {
+ String locale = OS::get_singleton()->get_locale();
+ return TS->is_locale_right_to_left(locale);
+ } else {
+ String locale = TranslationServer::get_singleton()->get_tool_locale();
+ return TS->is_locale_right_to_left(locale);
+ }
+ }
+ }
+#else
if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
return true;
}
+#endif
Node *parent_node = get_parent();
while (parent_node) {
Control *parent_control = Object::cast_to<Control>(parent_node);
@@ -2550,11 +2586,13 @@ bool Window::is_layout_rtl() const {
parent_node = parent_node->get_parent();
}
- int root_dir = GLOBAL_GET(SNAME("internationalization/rendering/root_node_layout_direction"));
- if (root_dir == 1) {
+ if (root_layout_direction == 1) {
return false;
- } else if (root_dir == 2) {
+ } else if (root_layout_direction == 2) {
return true;
+ } else if (root_layout_direction == 3) {
+ String locale = OS::get_singleton()->get_locale();
+ return TS->is_locale_right_to_left(locale);
} else {
String locale = TranslationServer::get_singleton()->get_tool_locale();
return TS->is_locale_right_to_left(locale);
diff --git a/scene/main/window.h b/scene/main/window.h
index 8a54b6c7d3..0682abc3c7 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -234,11 +234,14 @@ private:
Ref<Shortcut> debugger_stop_shortcut;
+ static int root_layout_direction;
+
protected:
virtual Rect2i _popup_adjust_rect() const { return Rect2i(); }
virtual void _post_popup() {}
virtual void _update_theme_item_cache();
+ virtual void _input_from_window(const Ref<InputEvent> &p_event) {}
void _notification(int p_what);
static void _bind_methods();
@@ -260,6 +263,8 @@ public:
NOTIFICATION_THEME_CHANGED = 32
};
+ static void set_root_layout_direction(int p_root_dir);
+
void set_title(const String &p_title);
String get_title() const;
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 2e6759063b..baaa78f9f2 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -456,6 +456,10 @@ void register_scene_types() {
AcceptDialog::set_swap_cancel_ok(swap_cancel_ok);
#endif
+ int root_dir = GLOBAL_GET("internationalization/rendering/root_node_layout_direction");
+ Control::set_root_layout_direction(root_dir);
+ Window::set_root_layout_direction(root_dir);
+
/* REGISTER ANIMATION */
GDREGISTER_CLASS(Tween);
GDREGISTER_ABSTRACT_CLASS(Tweener);
diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp
index ddec5e826b..09fb5aba0d 100644
--- a/scene/theme/default_theme.cpp
+++ b/scene/theme/default_theme.cpp
@@ -773,8 +773,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("custom_button_hover", "Tree", button_hover);
theme->set_icon("checked", "Tree", icons["checked"]);
+ theme->set_icon("checked_disabled", "Tree", icons["checked_disabled"]);
theme->set_icon("unchecked", "Tree", icons["unchecked"]);
+ theme->set_icon("unchecked_disabled", "Tree", icons["unchecked_disabled"]);
theme->set_icon("indeterminate", "Tree", icons["indeterminate"]);
+ theme->set_icon("indeterminate_disabled", "Tree", icons["indeterminate_disabled"]);
theme->set_icon("updown", "Tree", icons["updown"]);
theme->set_icon("select_arrow", "Tree", icons["option_button_arrow"]);
theme->set_icon("arrow", "Tree", icons["arrow_down"]);
@@ -789,6 +792,7 @@ 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("font_color", "Tree", control_font_low_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(1, 1, 1));
theme->set_color("guide_color", "Tree", Color(0.7, 0.7, 0.7, 0.25));
theme->set_color("drop_position_color", "Tree", Color(1, 1, 1));
diff --git a/scene/theme/icons/indeterminate.svg b/scene/theme/icons/indeterminate.svg
index 2a742e1475..e6e8dec5ed 100644
--- a/scene/theme/icons/indeterminate.svg
+++ b/scene/theme/icons/indeterminate.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#fff" fill-opacity=".75" stroke-width="1.16667"/><path d="m3 7h10v2h-10z" fill="#1a1a1a" stroke-linecap="square" stroke-opacity=".75" stroke-width="2"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="14" height="14" rx="2.333" fill="#fff" fill-opacity=".75"/><path d="m3 7h10v2h-10z" fill="#1a1a1a"/></svg>
diff --git a/scene/theme/icons/indeterminate_disabled.svg b/scene/theme/icons/indeterminate_disabled.svg
new file mode 100644
index 0000000000..f238f6f31c
--- /dev/null
+++ b/scene/theme/icons/indeterminate_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="14" height="14" rx="2.333" fill="#fff" fill-opacity=".37"/><path d="m3 7h10v2h-10z" fill="#1a1a1a" fill-opacity=".5"/></svg>
diff --git a/tests/scene/test_camera_3d.h b/tests/scene/test_camera_3d.h
new file mode 100644
index 0000000000..169486d108
--- /dev/null
+++ b/tests/scene/test_camera_3d.h
@@ -0,0 +1,370 @@
+/**************************************************************************/
+/* test_camera_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_CAMERA_3D_H
+#define TEST_CAMERA_3D_H
+
+#include "scene/3d/camera_3d.h"
+#include "scene/main/viewport.h"
+#include "scene/main/window.h"
+
+#include "tests/test_macros.h"
+
+// Constants.
+#define SQRT3 (1.7320508f)
+
+TEST_CASE("[SceneTree][Camera3D] Getters and setters") {
+ Camera3D *test_camera = memnew(Camera3D);
+
+ SUBCASE("Cull mask") {
+ constexpr int cull_mask = (1 << 5) | (1 << 7) | (1 << 9);
+ constexpr int set_enable_layer = 3;
+ constexpr int set_disable_layer = 5;
+ test_camera->set_cull_mask(cull_mask);
+ CHECK(test_camera->get_cull_mask() == cull_mask);
+ test_camera->set_cull_mask_value(set_enable_layer, true);
+ CHECK(test_camera->get_cull_mask_value(set_enable_layer));
+ test_camera->set_cull_mask_value(set_disable_layer, false);
+ CHECK_FALSE(test_camera->get_cull_mask_value(set_disable_layer));
+ }
+
+ SUBCASE("Attributes") {
+ Ref<CameraAttributes> attributes = memnew(CameraAttributes);
+ test_camera->set_attributes(attributes);
+ CHECK(test_camera->get_attributes() == attributes);
+ Ref<CameraAttributesPhysical> physical_attributes = memnew(CameraAttributesPhysical);
+ test_camera->set_attributes(physical_attributes);
+ CHECK(test_camera->get_attributes() == physical_attributes);
+ }
+
+ SUBCASE("Camera frustum properties") {
+ constexpr float near = 0.2f;
+ constexpr float far = 995.0f;
+ constexpr float fov = 120.0f;
+ constexpr float size = 7.0f;
+ constexpr float h_offset = 1.1f;
+ constexpr float v_offset = -1.6f;
+ const Vector2 frustum_offset(5, 7);
+ test_camera->set_near(near);
+ CHECK(test_camera->get_near() == near);
+ test_camera->set_far(far);
+ CHECK(test_camera->get_far() == far);
+ test_camera->set_fov(fov);
+ CHECK(test_camera->get_fov() == fov);
+ test_camera->set_size(size);
+ CHECK(test_camera->get_size() == size);
+ test_camera->set_h_offset(h_offset);
+ CHECK(test_camera->get_h_offset() == h_offset);
+ test_camera->set_v_offset(v_offset);
+ CHECK(test_camera->get_v_offset() == v_offset);
+ test_camera->set_frustum_offset(frustum_offset);
+ CHECK(test_camera->get_frustum_offset() == frustum_offset);
+ test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_HEIGHT);
+ CHECK(test_camera->get_keep_aspect_mode() == Camera3D::KeepAspect::KEEP_HEIGHT);
+ test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_WIDTH);
+ CHECK(test_camera->get_keep_aspect_mode() == Camera3D::KeepAspect::KEEP_WIDTH);
+ }
+
+ SUBCASE("Projection mode") {
+ test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_ORTHOGONAL);
+ CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_ORTHOGONAL);
+ test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_PERSPECTIVE);
+ CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE);
+ }
+
+ SUBCASE("Helper setters") {
+ constexpr float fov = 90.0f, size = 6.0f;
+ constexpr float near1 = 0.1f, near2 = 0.5f;
+ constexpr float far1 = 1001.0f, far2 = 1005.0f;
+ test_camera->set_perspective(fov, near1, far1);
+ CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE);
+ CHECK(test_camera->get_near() == near1);
+ CHECK(test_camera->get_far() == far1);
+ CHECK(test_camera->get_fov() == fov);
+ test_camera->set_orthogonal(size, near2, far2);
+ CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_ORTHOGONAL);
+ CHECK(test_camera->get_near() == near2);
+ CHECK(test_camera->get_far() == far2);
+ CHECK(test_camera->get_size() == size);
+ }
+
+ SUBCASE("Doppler tracking") {
+ test_camera->set_doppler_tracking(Camera3D::DopplerTracking::DOPPLER_TRACKING_IDLE_STEP);
+ CHECK(test_camera->get_doppler_tracking() == Camera3D::DopplerTracking::DOPPLER_TRACKING_IDLE_STEP);
+ test_camera->set_doppler_tracking(Camera3D::DopplerTracking::DOPPLER_TRACKING_PHYSICS_STEP);
+ CHECK(test_camera->get_doppler_tracking() == Camera3D::DopplerTracking::DOPPLER_TRACKING_PHYSICS_STEP);
+ test_camera->set_doppler_tracking(Camera3D::DopplerTracking::DOPPLER_TRACKING_DISABLED);
+ CHECK(test_camera->get_doppler_tracking() == Camera3D::DopplerTracking::DOPPLER_TRACKING_DISABLED);
+ }
+
+ memdelete(test_camera);
+}
+
+TEST_CASE("[SceneTree][Camera3D] Position queries") {
+ // Cameras need a viewport to know how to compute their frustums, so we make a fake one here.
+ Camera3D *test_camera = memnew(Camera3D);
+ SubViewport *mock_viewport = memnew(SubViewport);
+ // 4:2.
+ mock_viewport->set_size(Vector2(400, 200));
+ SceneTree::get_singleton()->get_root()->add_child(mock_viewport);
+ mock_viewport->add_child(test_camera);
+ test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_WIDTH);
+ REQUIRE_MESSAGE(test_camera->is_current(), "Camera3D should be made current upon entering tree.");
+
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_ORTHOGONAL);
+ // The orthogonal case is simpler, so we test a more random position + rotation combination here.
+ // For the other cases we'll use zero translation and rotation instead.
+ test_camera->set_global_position(Vector3(1, 2, 3));
+ test_camera->look_at(Vector3(-4, 5, 1));
+ // Width = 5, Aspect Ratio = 400 / 200 = 2, so Height is 2.5.
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ const Basis basis = test_camera->get_global_basis();
+ // Subtract near so offset starts from the near plane.
+ const Vector3 offset1 = basis.xform(Vector3(-1.5f, 3.5f, 0.2f - test_camera->get_near()));
+ const Vector3 offset2 = basis.xform(Vector3(2.0f, -0.5f, -0.6f - test_camera->get_near()));
+ const Vector3 offset3 = basis.xform(Vector3(-3.0f, 1.0f, -0.6f - test_camera->get_near()));
+ const Vector3 offset4 = basis.xform(Vector3(-2.0f, 1.5f, -0.6f - test_camera->get_near()));
+ const Vector3 offset5 = basis.xform(Vector3(0, 0, 10000.0f - test_camera->get_near()));
+
+ SUBCASE("is_position_behind") {
+ CHECK(test_camera->is_position_behind(test_camera->get_global_position() + offset1));
+ CHECK_FALSE(test_camera->is_position_behind(test_camera->get_global_position() + offset2));
+
+ SUBCASE("h/v offset should have no effect on the result of is_position_behind") {
+ test_camera->set_h_offset(-11.0f);
+ test_camera->set_v_offset(22.1f);
+ CHECK(test_camera->is_position_behind(test_camera->get_global_position() + offset1));
+ test_camera->set_h_offset(4.7f);
+ test_camera->set_v_offset(-3.0f);
+ CHECK_FALSE(test_camera->is_position_behind(test_camera->get_global_position() + offset2));
+ }
+ // Reset h/v offsets.
+ test_camera->set_h_offset(0);
+ test_camera->set_v_offset(0);
+ }
+
+ SUBCASE("is_position_in_frustum") {
+ // If the point is behind the near plane, it is outside the camera frustum.
+ // So offset1 is not in frustum.
+ CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset1));
+ // If |right| > 5 / 2 or |up| > 2.5 / 2, the point is outside the camera frustum.
+ // So offset2 is in frustum and offset3 and offset4 are not.
+ CHECK(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset2));
+ CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset3));
+ CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset4));
+ // offset5 is beyond the far plane, so it is not in frustum.
+ CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset5));
+ }
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_PERSPECTIVE);
+ // Camera at origin, looking at +Z.
+ test_camera->set_global_position(Vector3(0, 0, 0));
+ test_camera->set_global_rotation(Vector3(0, 0, 0));
+ // Keep width, so horizontal fov = 120.
+ // Since the near plane distance is 1,
+ // with trig we know the near plane's width is 2 * sqrt(3), so its height is sqrt(3).
+ test_camera->set_perspective(120.0f, 1.0f, 1000.0f);
+
+ SUBCASE("is_position_behind") {
+ CHECK_FALSE(test_camera->is_position_behind(Vector3(0, 0, -1.5f)));
+ CHECK(test_camera->is_position_behind(Vector3(2, 0, -0.2f)));
+ }
+
+ SUBCASE("is_position_in_frustum") {
+ CHECK(test_camera->is_position_in_frustum(Vector3(-1.3f, 0, -1.1f)));
+ CHECK_FALSE(test_camera->is_position_in_frustum(Vector3(2, 0, -1.1f)));
+ CHECK(test_camera->is_position_in_frustum(Vector3(1, 0.5f, -1.1f)));
+ CHECK_FALSE(test_camera->is_position_in_frustum(Vector3(1, 1, -1.1f)));
+ CHECK(test_camera->is_position_in_frustum(Vector3(0, 0, -1.5f)));
+ CHECK_FALSE(test_camera->is_position_in_frustum(Vector3(0, 0, -0.5f)));
+ }
+ }
+
+ memdelete(test_camera);
+ memdelete(mock_viewport);
+}
+
+TEST_CASE("[SceneTree][Camera3D] Project/Unproject position") {
+ // Cameras need a viewport to know how to compute their frustums, so we make a fake one here.
+ Camera3D *test_camera = memnew(Camera3D);
+ SubViewport *mock_viewport = memnew(SubViewport);
+ // 4:2.
+ mock_viewport->set_size(Vector2(400, 200));
+ SceneTree::get_singleton()->get_root()->add_child(mock_viewport);
+ mock_viewport->add_child(test_camera);
+ test_camera->set_global_position(Vector3(0, 0, 0));
+ test_camera->set_global_rotation(Vector3(0, 0, 0));
+ test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_HEIGHT);
+
+ SUBCASE("project_position") {
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f)));
+ // Top left.
+ CHECK(test_camera->project_position(Vector2(0, 0), 1.5f).is_equal_approx(Vector3(-5.0f, 2.5f, -1.5f)));
+ // Bottom right.
+ CHECK(test_camera->project_position(Vector2(400, 200), 5.0f).is_equal_approx(Vector3(5.0f, -2.5f, -5.0f)));
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f)));
+ CHECK(test_camera->project_position(Vector2(200, 100), 100.0f).is_equal_approx(Vector3(0, 0, -100.0f)));
+ // 3/4th way to Top left.
+ CHECK(test_camera->project_position(Vector2(100, 50), 0.5f).is_equal_approx(Vector3(-SQRT3 * 0.5f, SQRT3 * 0.25f, -0.5f)));
+ CHECK(test_camera->project_position(Vector2(100, 50), 1.0f).is_equal_approx(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f)));
+ // 3/4th way to Bottom right.
+ CHECK(test_camera->project_position(Vector2(300, 150), 0.5f).is_equal_approx(Vector3(SQRT3 * 0.5f, -SQRT3 * 0.25f, -0.5f)));
+ CHECK(test_camera->project_position(Vector2(300, 150), 1.0f).is_equal_approx(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f)));
+ }
+ }
+
+ // Uses cases that are the inverse of the above sub-case.
+ SUBCASE("unproject_position") {
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ // Center
+ CHECK(test_camera->unproject_position(Vector3(0, 0, -0.5f)).is_equal_approx(Vector2(200, 100)));
+ // Top left
+ CHECK(test_camera->unproject_position(Vector3(-5.0f, 2.5f, -1.5f)).is_equal_approx(Vector2(0, 0)));
+ // Bottom right
+ CHECK(test_camera->unproject_position(Vector3(5.0f, -2.5f, -5.0f)).is_equal_approx(Vector2(400, 200)));
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->unproject_position(Vector3(0, 0, -0.5f)).is_equal_approx(Vector2(200, 100)));
+ CHECK(test_camera->unproject_position(Vector3(0, 0, -100.0f)).is_equal_approx(Vector2(200, 100)));
+ // 3/4th way to Top left.
+ WARN(test_camera->unproject_position(Vector3(-SQRT3 * 0.5f, SQRT3 * 0.25f, -0.5f)).is_equal_approx(Vector2(100, 50)));
+ WARN(test_camera->unproject_position(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f)).is_equal_approx(Vector2(100, 50)));
+ // 3/4th way to Bottom right.
+ CHECK(test_camera->unproject_position(Vector3(SQRT3 * 0.5f, -SQRT3 * 0.25f, -0.5f)).is_equal_approx(Vector2(300, 150)));
+ CHECK(test_camera->unproject_position(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f)).is_equal_approx(Vector2(300, 150)));
+ }
+ }
+
+ memdelete(test_camera);
+ memdelete(mock_viewport);
+}
+
+TEST_CASE("[SceneTree][Camera3D] Project ray") {
+ // Cameras need a viewport to know how to compute their frustums, so we make a fake one here.
+ Camera3D *test_camera = memnew(Camera3D);
+ SubViewport *mock_viewport = memnew(SubViewport);
+ // 4:2.
+ mock_viewport->set_size(Vector2(400, 200));
+ SceneTree::get_singleton()->get_root()->add_child(mock_viewport);
+ mock_viewport->add_child(test_camera);
+ test_camera->set_global_position(Vector3(0, 0, 0));
+ test_camera->set_global_rotation(Vector3(0, 0, 0));
+ test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_HEIGHT);
+
+ SUBCASE("project_ray_origin") {
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_ray_origin(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -0.5f)));
+ // Top left.
+ CHECK(test_camera->project_ray_origin(Vector2(0, 0)).is_equal_approx(Vector3(-5.0f, 2.5f, -0.5f)));
+ // Bottom right.
+ CHECK(test_camera->project_ray_origin(Vector2(400, 200)).is_equal_approx(Vector3(5.0f, -2.5f, -0.5f)));
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_ray_origin(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, 0)));
+ // Top left.
+ CHECK(test_camera->project_ray_origin(Vector2(0, 0)).is_equal_approx(Vector3(0, 0, 0)));
+ // Bottom right.
+ CHECK(test_camera->project_ray_origin(Vector2(400, 200)).is_equal_approx(Vector3(0, 0, 0)));
+ }
+ }
+
+ SUBCASE("project_ray_normal") {
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1)));
+ // Top left.
+ CHECK(test_camera->project_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(0, 0, -1)));
+ // Bottom right.
+ CHECK(test_camera->project_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(0, 0, -1)));
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1)));
+ // Top left.
+ CHECK(test_camera->project_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(-SQRT3, SQRT3 / 2, -0.5f).normalized()));
+ // Bottom right.
+ CHECK(test_camera->project_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(SQRT3, -SQRT3 / 2, -0.5f).normalized()));
+ }
+ }
+
+ SUBCASE("project_local_ray_normal") {
+ test_camera->set_rotation_degrees(Vector3(60, 60, 60));
+
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_local_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1)));
+ // Top left.
+ CHECK(test_camera->project_local_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(0, 0, -1)));
+ // Bottom right.
+ CHECK(test_camera->project_local_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(0, 0, -1)));
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_local_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1)));
+ // Top left.
+ CHECK(test_camera->project_local_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(-SQRT3, SQRT3 / 2, -0.5f).normalized()));
+ // Bottom right.
+ CHECK(test_camera->project_local_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(SQRT3, -SQRT3 / 2, -0.5f).normalized()));
+ }
+ }
+
+ memdelete(test_camera);
+ memdelete(mock_viewport);
+}
+
+#undef SQRT3
+
+#endif // TEST_CAMERA_3D_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 8c120f6d3a..5187ebd00f 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -94,6 +94,7 @@
#include "tests/scene/test_arraymesh.h"
#include "tests/scene/test_audio_stream_wav.h"
#include "tests/scene/test_bit_map.h"
+#include "tests/scene/test_camera_3d.h"
#include "tests/scene/test_code_edit.h"
#include "tests/scene/test_color_picker.h"
#include "tests/scene/test_control.h"