summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/io/image.cpp105
-rw-r--r--core/io/image.h3
-rw-r--r--core/math/basis.cpp13
-rw-r--r--core/multiplayer/multiplayer_api.cpp8
-rw-r--r--core/multiplayer/multiplayer_api.h6
-rw-r--r--core/templates/safe_refcount.h4
-rw-r--r--doc/classes/Control.xml4
-rw-r--r--doc/classes/Image.xml13
-rw-r--r--doc/classes/Label.xml3
-rw-r--r--doc/classes/LabelSettings.xml39
-rw-r--r--doc/classes/MultiMesh.xml3
-rw-r--r--doc/classes/MultiplayerSpawner.xml2
-rw-r--r--doc/classes/MultiplayerSynchronizer.xml52
-rw-r--r--doc/classes/TileSet.xml2
-rw-r--r--drivers/vulkan/SCsub2
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp10
-rw-r--r--drivers/vulkan/vulkan_context.cpp4
-rw-r--r--editor/action_map_editor.cpp2
-rw-r--r--editor/animation_bezier_editor.cpp2
-rw-r--r--editor/animation_track_editor.cpp8
-rw-r--r--editor/debugger/editor_performance_profiler.cpp2
-rw-r--r--editor/editor_node.cpp12
-rw-r--r--editor/editor_path.cpp2
-rw-r--r--editor/editor_resource_picker.cpp2
-rw-r--r--editor/editor_spin_slider.cpp2
-rw-r--r--editor/groups_editor.cpp4
-rw-r--r--editor/icons/ControlAlignFullRect.svg (renamed from editor/icons/ControlAlignWide.svg)0
-rw-r--r--editor/icons/LabelSettings.svg1
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp4
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/audio_stream_editor_plugin.cpp4
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp89
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h7
-rw-r--r--editor/plugins/control_editor_plugin.cpp36
-rw-r--r--editor/plugins/control_editor_plugin.h6
-rw-r--r--editor/plugins/material_editor_plugin.cpp6
-rw-r--r--editor/plugins/mesh_editor_plugin.cpp2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp80
-rw-r--r--editor/plugins/node_3d_editor_plugin.h5
-rw-r--r--editor/plugins/replication_editor_plugin.cpp2
-rw-r--r--editor/plugins/script_text_editor.cpp8
-rw-r--r--editor/plugins/shader_editor_plugin.cpp8
-rw-r--r--editor/plugins/style_box_editor_plugin.cpp2
-rw-r--r--editor/plugins/text_editor.cpp2
-rw-r--r--editor/plugins/texture_editor_plugin.cpp2
-rw-r--r--editor/plugins/theme_editor_plugin.cpp37
-rw-r--r--editor/plugins/theme_editor_plugin.h1
-rw-r--r--editor/plugins/theme_editor_preview.cpp2
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp12
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp4
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp6
-rw-r--r--editor/progress_dialog.cpp4
-rw-r--r--editor/project_manager.cpp10
-rw-r--r--editor/property_editor.cpp6
-rw-r--r--editor/scene_create_dialog.cpp2
-rw-r--r--editor/scene_tree_dock.cpp2
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp2
-rw-r--r--modules/noise/editor/noise_editor_plugin.cpp2
-rw-r--r--modules/openxr/SCsub6
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp2
-rw-r--r--platform/linuxbsd/SCsub12
-rw-r--r--platform/linuxbsd/detect.py61
-rw-r--r--platform/linuxbsd/display_server_x11.cpp4
-rw-r--r--platform/osx/display_server_osx.mm4
-rw-r--r--platform/windows/display_server_windows.cpp6
-rw-r--r--scene/gui/color_picker.cpp4
-rw-r--r--scene/gui/control.cpp28
-rw-r--r--scene/gui/control.h2
-rw-r--r--scene/gui/file_dialog.cpp2
-rw-r--r--scene/gui/graph_edit.cpp2
-rw-r--r--scene/gui/label.cpp65
-rw-r--r--scene/gui/label.h7
-rw-r--r--scene/gui/popup_menu.cpp4
-rw-r--r--scene/gui/spin_box.cpp2
-rw-r--r--scene/gui/tab_container.cpp2
-rw-r--r--scene/gui/tree.cpp2
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/multiplayer/multiplayer_spawner.cpp17
-rw-r--r--scene/multiplayer/multiplayer_spawner.h3
-rw-r--r--scene/multiplayer/multiplayer_synchronizer.cpp142
-rw-r--r--scene/multiplayer/multiplayer_synchronizer.h25
-rw-r--r--scene/multiplayer/scene_cache_interface.cpp25
-rw-r--r--scene/multiplayer/scene_cache_interface.h3
-rw-r--r--scene/multiplayer/scene_replication_interface.cpp210
-rw-r--r--scene/multiplayer/scene_replication_interface.h7
-rw-r--r--scene/multiplayer/scene_replication_state.cpp81
-rw-r--r--scene/multiplayer/scene_replication_state.h23
-rw-r--r--scene/multiplayer/scene_rpc_interface.cpp6
-rw-r--r--scene/register_scene_types.cpp3
-rw-r--r--scene/resources/label_settings.cpp187
-rw-r--r--scene/resources/label_settings.h89
91 files changed, 1271 insertions, 432 deletions
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 8d15ede1e9..0f20aabd7e 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -1339,6 +1339,108 @@ void Image::crop(int p_width, int p_height) {
crop_from_point(0, 0, p_width, p_height);
}
+void Image::rotate_90(ClockDirection p_direction) {
+ ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot rotate in compressed or custom image formats.");
+ ERR_FAIL_COND_MSG(width <= 1, "The Image width specified (" + itos(width) + " pixels) must be greater than 1 pixels.");
+ ERR_FAIL_COND_MSG(height <= 1, "The Image height specified (" + itos(height) + " pixels) must be greater than 1 pixels.");
+
+ int saved_width = height;
+ int saved_height = width;
+
+ if (width != height) {
+ int n = MAX(width, height);
+ resize(n, n, INTERPOLATE_NEAREST);
+ }
+
+ bool used_mipmaps = has_mipmaps();
+ if (used_mipmaps) {
+ clear_mipmaps();
+ }
+
+ {
+ uint8_t *w = data.ptrw();
+ uint8_t src[16];
+ uint8_t dst[16];
+ uint32_t pixel_size = get_format_pixel_size(format);
+
+ // Flip.
+
+ if (p_direction == CLOCKWISE) {
+ for (int y = 0; y < height / 2; y++) {
+ for (int x = 0; x < width; x++) {
+ _get_pixelb(x, y, pixel_size, w, src);
+ _get_pixelb(x, height - y - 1, pixel_size, w, dst);
+
+ _put_pixelb(x, height - y - 1, pixel_size, w, src);
+ _put_pixelb(x, y, pixel_size, w, dst);
+ }
+ }
+ } else {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width / 2; x++) {
+ _get_pixelb(x, y, pixel_size, w, src);
+ _get_pixelb(width - x - 1, y, pixel_size, w, dst);
+
+ _put_pixelb(width - x - 1, y, pixel_size, w, src);
+ _put_pixelb(x, y, pixel_size, w, dst);
+ }
+ }
+ }
+
+ // Transpose.
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (x < y) {
+ _get_pixelb(x, y, pixel_size, w, src);
+ _get_pixelb(y, x, pixel_size, w, dst);
+
+ _put_pixelb(y, x, pixel_size, w, src);
+ _put_pixelb(x, y, pixel_size, w, dst);
+ }
+ }
+ }
+ }
+
+ if (saved_width != saved_height) {
+ resize(saved_width, saved_height, INTERPOLATE_NEAREST);
+ } else if (used_mipmaps) {
+ generate_mipmaps();
+ }
+}
+
+void Image::rotate_180() {
+ ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot rotate in compressed or custom image formats.");
+ ERR_FAIL_COND_MSG(width <= 1, "The Image width specified (" + itos(width) + " pixels) must be greater than 1 pixels.");
+ ERR_FAIL_COND_MSG(height <= 1, "The Image height specified (" + itos(height) + " pixels) must be greater than 1 pixels.");
+
+ bool used_mipmaps = has_mipmaps();
+ if (used_mipmaps) {
+ clear_mipmaps();
+ }
+
+ {
+ uint8_t *w = data.ptrw();
+ uint8_t src[16];
+ uint8_t dst[16];
+ uint32_t pixel_size = get_format_pixel_size(format);
+
+ for (int y = 0; y < height / 2; y++) {
+ for (int x = 0; x < width; x++) {
+ _get_pixelb(x, y, pixel_size, w, src);
+ _get_pixelb(width - x - 1, height - y - 1, pixel_size, w, dst);
+
+ _put_pixelb(width - x - 1, height - y - 1, pixel_size, w, src);
+ _put_pixelb(x, y, pixel_size, w, dst);
+ }
+ }
+ }
+
+ if (used_mipmaps) {
+ generate_mipmaps();
+ }
+}
+
void Image::flip_y() {
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot flip_y in compressed or custom image formats.");
@@ -3217,6 +3319,9 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("decompress"), &Image::decompress);
ClassDB::bind_method(D_METHOD("is_compressed"), &Image::is_compressed);
+ ClassDB::bind_method(D_METHOD("rotate_90", "direction"), &Image::rotate_90);
+ ClassDB::bind_method(D_METHOD("rotate_180"), &Image::rotate_180);
+
ClassDB::bind_method(D_METHOD("fix_alpha_edges"), &Image::fix_alpha_edges);
ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha);
ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear);
diff --git a/core/io/image.h b/core/io/image.h
index b926ed69ef..46820a4c08 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -254,6 +254,9 @@ public:
void crop_from_point(int p_x, int p_y, int p_width, int p_height);
void crop(int p_width, int p_height);
+ void rotate_90(ClockDirection p_direction);
+ void rotate_180();
+
void flip_x();
void flip_y();
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index ce5e9aa9b3..f8e7c47107 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -817,14 +817,13 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
#endif
*/
real_t angle, x, y, z; // variables for result
- real_t epsilon = 0.01; // margin to allow for rounding errors
- real_t epsilon2 = 0.1; // margin to distinguish between 0 and 180 degrees
+ real_t angle_epsilon = 0.1; // margin to distinguish between 0 and 180 degrees
- if ((Math::abs(rows[1][0] - rows[0][1]) < epsilon) && (Math::abs(rows[2][0] - rows[0][2]) < epsilon) && (Math::abs(rows[2][1] - rows[1][2]) < epsilon)) {
+ if ((Math::abs(rows[1][0] - rows[0][1]) < CMP_EPSILON) && (Math::abs(rows[2][0] - rows[0][2]) < CMP_EPSILON) && (Math::abs(rows[2][1] - rows[1][2]) < CMP_EPSILON)) {
// singularity found
// first check for identity matrix which must have +1 for all terms
// in leading diagonal and zero in other terms
- if ((Math::abs(rows[1][0] + rows[0][1]) < epsilon2) && (Math::abs(rows[2][0] + rows[0][2]) < epsilon2) && (Math::abs(rows[2][1] + rows[1][2]) < epsilon2) && (Math::abs(rows[0][0] + rows[1][1] + rows[2][2] - 3) < epsilon2)) {
+ if ((Math::abs(rows[1][0] + rows[0][1]) < angle_epsilon) && (Math::abs(rows[2][0] + rows[0][2]) < angle_epsilon) && (Math::abs(rows[2][1] + rows[1][2]) < angle_epsilon) && (Math::abs(rows[0][0] + rows[1][1] + rows[2][2] - 3) < angle_epsilon)) {
// this singularity is identity matrix so angle = 0
r_axis = Vector3(0, 1, 0);
r_angle = 0;
@@ -839,7 +838,7 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
real_t xz = (rows[2][0] + rows[0][2]) / 4;
real_t yz = (rows[2][1] + rows[1][2]) / 4;
if ((xx > yy) && (xx > zz)) { // rows[0][0] is the largest diagonal term
- if (xx < epsilon) {
+ if (xx < CMP_EPSILON) {
x = 0;
y = Math_SQRT12;
z = Math_SQRT12;
@@ -849,7 +848,7 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
z = xz / x;
}
} else if (yy > zz) { // rows[1][1] is the largest diagonal term
- if (yy < epsilon) {
+ if (yy < CMP_EPSILON) {
x = Math_SQRT12;
y = 0;
z = Math_SQRT12;
@@ -859,7 +858,7 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
z = yz / y;
}
} else { // rows[2][2] is the largest diagonal term so base result on this
- if (zz < epsilon) {
+ if (zz < CMP_EPSILON) {
x = Math_SQRT12;
y = Math_SQRT12;
z = 0;
diff --git a/core/multiplayer/multiplayer_api.cpp b/core/multiplayer/multiplayer_api.cpp
index 9605647b3f..6cce31e0d1 100644
--- a/core/multiplayer/multiplayer_api.cpp
+++ b/core/multiplayer/multiplayer_api.cpp
@@ -463,8 +463,12 @@ bool MultiplayerAPI::is_cache_confirmed(NodePath p_path, int p_peer) {
return cache->is_cache_confirmed(p_path, p_peer);
}
-bool MultiplayerAPI::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) {
- return cache->send_object_cache(p_obj, p_path, p_peer_id, r_id);
+bool MultiplayerAPI::send_object_cache(Object *p_obj, int p_peer_id, int &r_id) {
+ return cache->send_object_cache(p_obj, p_peer_id, r_id);
+}
+
+int MultiplayerAPI::make_object_cache(Object *p_obj) {
+ return cache->make_object_cache(p_obj);
}
Object *MultiplayerAPI::get_cached_object(int p_from, uint32_t p_cache_id) {
diff --git a/core/multiplayer/multiplayer_api.h b/core/multiplayer/multiplayer_api.h
index cc7743ccf8..35452acb1f 100644
--- a/core/multiplayer/multiplayer_api.h
+++ b/core/multiplayer/multiplayer_api.h
@@ -77,7 +77,8 @@ public:
virtual void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {}
// Returns true if all peers have cached path.
- virtual bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id) { return false; }
+ virtual bool send_object_cache(Object *p_obj, int p_target, int &r_id) { return false; }
+ virtual int make_object_cache(Object *p_obj) { return false; }
virtual Object *get_cached_object(int p_from, uint32_t p_cache_id) { return nullptr; }
virtual bool is_cache_confirmed(NodePath p_path, int p_peer) { return false; }
@@ -160,7 +161,8 @@ public:
Error replication_start(Object *p_object, Variant p_config);
Error replication_stop(Object *p_object, Variant p_config);
// Cache API
- bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id);
+ bool send_object_cache(Object *p_obj, int p_target, int &r_id);
+ int make_object_cache(Object *p_obj);
Object *get_cached_object(int p_from, uint32_t p_cache_id);
bool is_cache_confirmed(NodePath p_path, int p_peer);
diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h
index 76f76be96a..3148283dca 100644
--- a/core/templates/safe_refcount.h
+++ b/core/templates/safe_refcount.h
@@ -111,7 +111,7 @@ public:
if (tmp >= p_value) {
return tmp; // already greater, or equal
}
- if (value.compare_exchange_weak(tmp, p_value, std::memory_order_release)) {
+ if (value.compare_exchange_weak(tmp, p_value, std::memory_order_acq_rel)) {
return p_value;
}
}
@@ -123,7 +123,7 @@ public:
if (c == 0) {
return 0;
}
- if (value.compare_exchange_weak(c, c + 1, std::memory_order_release)) {
+ if (value.compare_exchange_weak(c, c + 1, std::memory_order_acq_rel)) {
return c + 1;
}
}
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 2846b564b4..9fc80e1aab 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -1281,8 +1281,8 @@
<constant name="PRESET_HCENTER_WIDE" value="14" enum="LayoutPreset">
Snap all 4 anchors to a horizontal line that cuts the parent control in half. Use with [method set_anchors_preset].
</constant>
- <constant name="PRESET_WIDE" value="15" enum="LayoutPreset">
- Snap all 4 anchors to the respective corners of the parent control. Set all 4 offsets to 0 after you applied this preset and the [Control] will fit its parent control. This is equivalent to the "Full Rect" layout option in the editor. Use with [method set_anchors_preset].
+ <constant name="PRESET_FULL_RECT" value="15" enum="LayoutPreset">
+ Snap all 4 anchors to the respective corners of the parent control. Set all 4 offsets to 0 after you applied this preset and the [Control] will fit its parent control. Use with [method set_anchors_preset].
</constant>
<constant name="PRESET_MODE_MINSIZE" value="0" enum="LayoutPresetMode">
The control will be resized to its minimum size.
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index b69dd00bb8..a927345e79 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -378,6 +378,19 @@
Converts a standard RGBE (Red Green Blue Exponent) image to an sRGB image.
</description>
</method>
+ <method name="rotate_180">
+ <return type="void" />
+ <description>
+ Rotates the image by [code]180[/code] degrees. The width and height of the image must be greater than [code]1[/code].
+ </description>
+ </method>
+ <method name="rotate_90">
+ <return type="void" />
+ <argument index="0" name="direction" type="int" enum="ClockDirection" />
+ <description>
+ Rotates the image in the specified [code]direction[/code] by [code]90[/code] degrees. The width and height of the image must be greater than [code]1[/code]. If the width and height are not equal, the image will be resized.
+ </description>
+ </method>
<method name="save_exr" qualifiers="const">
<return type="int" enum="Error" />
<argument index="0" name="path" type="String" />
diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml
index 1eac58b9f2..8448109f02 100644
--- a/doc/classes/Label.xml
+++ b/doc/classes/Label.xml
@@ -49,6 +49,9 @@
<member name="horizontal_alignment" type="int" setter="set_horizontal_alignment" getter="get_horizontal_alignment" enum="HorizontalAlignment" default="0">
Controls the text's horizontal alignment. Supports left, center, right, and fill, or justify. Set it to one of the [enum HorizontalAlignment] constants.
</member>
+ <member name="label_settings" type="LabelSettings" setter="set_label_settings" getter="get_label_settings">
+ Resource to override [Theme] font, outline and shadow properties.
+ </member>
<member name="language" type="String" setter="set_language" getter="get_language" default="&quot;&quot;">
Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.
</member>
diff --git a/doc/classes/LabelSettings.xml b/doc/classes/LabelSettings.xml
new file mode 100644
index 0000000000..5f7427f4aa
--- /dev/null
+++ b/doc/classes/LabelSettings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="LabelSettings" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Resource to override [Theme] font, outline and shadow properties of the [Label].
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="font" type="Font" setter="set_font" getter="get_font">
+ [Font] of the [Label]'s text.
+ </member>
+ <member name="font_color" type="Color" setter="set_font_color" getter="get_font_color" default="Color(0.875, 0.875, 0.875, 1)">
+ Default text [Color] of the [Label].
+ </member>
+ <member name="font_size" type="int" setter="set_font_size" getter="get_font_size" default="16">
+ Font size of the [Label]'s text.
+ </member>
+ <member name="line_spacing" type="float" setter="set_line_spacing" getter="get_line_spacing" default="0.0">
+ Vertical space between lines in multiline text.
+ </member>
+ <member name="outline_color" type="Color" setter="set_outline_color" getter="get_outline_color" default="Color(1, 1, 1, 1)">
+ The tint of text outline.
+ </member>
+ <member name="outline_size" type="int" setter="set_outline_size" getter="get_outline_size" default="0">
+ Text outline size.
+ </member>
+ <member name="shadow_color" type="Color" setter="set_shadow_color" getter="get_shadow_color" default="Color(1, 1, 1, 1)">
+ The tint of text shadow.
+ </member>
+ <member name="shadow_offset" type="Vector2" setter="set_shadow_offset" getter="get_shadow_offset" default="Vector2(1, 1)">
+ The offset of the text's shadow.
+ </member>
+ <member name="shadow_size" type="int" setter="set_shadow_size" getter="get_shadow_size" default="0">
+ The size of the text's shadow.
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml
index 631b2ea050..9d8f1e1e5d 100644
--- a/doc/classes/MultiMesh.xml
+++ b/doc/classes/MultiMesh.xml
@@ -91,7 +91,8 @@
<member name="custom_data_array" type="PackedColorArray" setter="_set_custom_data_array" getter="_get_custom_data_array">
</member>
<member name="instance_count" type="int" setter="set_instance_count" getter="get_instance_count" default="0">
- Number of instances that will get drawn. This clears and (re)sizes the buffers. By default, all instances are drawn but you can limit this with [member visible_instance_count].
+ Number of instances that will get drawn. This clears and (re)sizes the buffers. Setting data format or flags afterwards will have no effect.
+ By default, all instances are drawn but you can limit this with [member visible_instance_count].
</member>
<member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
Mesh to be drawn.
diff --git a/doc/classes/MultiplayerSpawner.xml b/doc/classes/MultiplayerSpawner.xml
index 4ca92728ff..9de67068eb 100644
--- a/doc/classes/MultiplayerSpawner.xml
+++ b/doc/classes/MultiplayerSpawner.xml
@@ -43,8 +43,6 @@
</method>
</methods>
<members>
- <member name="auto_spawn" type="bool" setter="set_auto_spawning" getter="is_auto_spawning" default="false">
- </member>
<member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0">
</member>
<member name="spawn_path" type="NodePath" setter="set_spawn_path" getter="get_spawn_path" default="NodePath(&quot;&quot;)">
diff --git a/doc/classes/MultiplayerSynchronizer.xml b/doc/classes/MultiplayerSynchronizer.xml
index ac067791e6..3766491a6c 100644
--- a/doc/classes/MultiplayerSynchronizer.xml
+++ b/doc/classes/MultiplayerSynchronizer.xml
@@ -6,12 +6,64 @@
</description>
<tutorials>
</tutorials>
+ <methods>
+ <method name="add_visibility_filter">
+ <return type="void" />
+ <argument index="0" name="filter" type="Callable" />
+ <description>
+ </description>
+ </method>
+ <method name="get_visibility_for" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="peer" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="remove_visibility_filter">
+ <return type="void" />
+ <argument index="0" name="filter" type="Callable" />
+ <description>
+ </description>
+ </method>
+ <method name="set_visibility_for">
+ <return type="void" />
+ <argument index="0" name="peer" type="int" />
+ <argument index="1" name="visible" type="bool" />
+ <description>
+ </description>
+ </method>
+ <method name="update_visibility">
+ <return type="void" />
+ <argument index="0" name="for_peer" type="int" default="0" />
+ <description>
+ </description>
+ </method>
+ </methods>
<members>
+ <member name="public_visibility" type="bool" setter="set_visibility_public" getter="is_visibility_public" default="true">
+ </member>
<member name="replication_config" type="SceneReplicationConfig" setter="set_replication_config" getter="get_replication_config">
</member>
<member name="replication_interval" type="float" setter="set_replication_interval" getter="get_replication_interval" default="0.0">
</member>
<member name="root_path" type="NodePath" setter="set_root_path" getter="get_root_path" default="NodePath(&quot;..&quot;)">
</member>
+ <member name="visibility_update_mode" type="int" setter="set_visibility_update_mode" getter="get_visibility_update_mode" enum="MultiplayerSynchronizer.VisibilityUpdateMode" default="0">
+ </member>
</members>
+ <signals>
+ <signal name="visibility_changed">
+ <argument index="0" name="for_peer" type="int" />
+ <description>
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ <constant name="VISIBILITY_PROCESS_IDLE" value="0" enum="VisibilityUpdateMode">
+ </constant>
+ <constant name="VISIBILITY_PROCESS_PHYSICS" value="1" enum="VisibilityUpdateMode">
+ </constant>
+ <constant name="VISIBILITY_PROCESS_NONE" value="2" enum="VisibilityUpdateMode">
+ </constant>
+ </constants>
</class>
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index 10ccad973f..ad52b2f2f1 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -591,7 +591,7 @@
Tile coordinates layout where both axis stay consistent with their respective local horizontal and vertical axis.
</constant>
<constant name="TILE_LAYOUT_STACKED_OFFSET" value="1" enum="TileLayout">
- Same as [code]TILE_LAYOUT_STAKED[/code], but the first half-offset is negative instead of positive.
+ Same as [constant TILE_LAYOUT_STACKED], but the first half-offset is negative instead of positive.
</constant>
<constant name="TILE_LAYOUT_STAIRS_RIGHT" value="2" enum="TileLayout">
Tile coordinates layout where the horizontal axis stay horizontal, and the vertical one goes down-right.
diff --git a/drivers/vulkan/SCsub b/drivers/vulkan/SCsub
index b6ceb1cdea..f7de2c4a7e 100644
--- a/drivers/vulkan/SCsub
+++ b/drivers/vulkan/SCsub
@@ -17,7 +17,7 @@ if env["platform"] == "android":
env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_ANDROID_KHR"])
elif env["platform"] == "iphone":
env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_IOS_MVK"])
-elif env["platform"] == "linuxbsd":
+elif env["platform"] == "linuxbsd" and env["x11"]:
env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_XLIB_KHR"])
elif env["platform"] == "osx":
env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_MACOS_MVK"])
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index e8b443cceb..9abd4780eb 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -3396,7 +3396,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
ERR_FAIL_INDEX_V(p_attachments[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE);
ERR_FAIL_INDEX_V(p_attachments[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE);
ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT | TEXTURE_USAGE_VRS_ATTACHMENT_BIT)),
- VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (color, depth, input or stencil) bit set.");
+ VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (color, depth-stencil, input or VRS) bit set.");
VkAttachmentDescription2KHR description = {};
description.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR;
@@ -3762,9 +3762,9 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
if (context->get_vrs_capabilities().attachment_vrs_supported && pass->vrs_attachment != FramebufferPass::ATTACHMENT_UNUSED) {
int32_t attachment = pass->vrs_attachment;
- ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), depth attachment.");
- ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as vrs, but it's not a vrs attachment.");
- ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer vrs attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass.");
+ ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), VRS attachment.");
+ ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as VRS, but it's not a VRS attachment.");
+ ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer VRS attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass.");
VkAttachmentReference2KHR &vrs_reference = vrs_reference_array[i];
vrs_reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
@@ -4016,6 +4016,8 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c
render_pass_create_info.pSubpasses = &subpass;
render_pass_create_info.dependencyCount = 0;
render_pass_create_info.pDependencies = nullptr;
+ render_pass_create_info.correlatedViewMaskCount = 0;
+ render_pass_create_info.pCorrelatedViewMasks = nullptr;
VkRenderPass render_pass;
VkResult res = context->vkCreateRenderPass2KHR(device, &render_pass_create_info, nullptr, &render_pass);
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index 814cec2ec0..524693eb03 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -646,7 +646,7 @@ Error VulkanContext::_check_capabilities() {
subgroup_capabilities.quadOperationsInAllStages = subgroupProperties.quadOperationsInAllStages;
if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) {
- print_verbose("- Vulkan Varying Shading Rates supported:");
+ print_verbose("- Vulkan Variable Rate Shading supported:");
if (vrs_capabilities.pipeline_vrs_supported) {
print_verbose(" Pipeline fragment shading rate");
}
@@ -664,7 +664,7 @@ Error VulkanContext::_check_capabilities() {
}
} else {
- print_verbose("- Vulkan Varying Shading Rates not supported");
+ print_verbose("- Vulkan Variable Rate Shading not supported");
}
if (multiview_capabilities.is_supported) {
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp
index 698390a61e..8c59d65c80 100644
--- a/editor/action_map_editor.cpp
+++ b/editor/action_map_editor.cpp
@@ -1199,7 +1199,7 @@ void ActionMapEditor::use_external_search_box(LineEdit *p_searchbox) {
ActionMapEditor::ActionMapEditor() {
// Main Vbox Container
VBoxContainer *main_vbox = memnew(VBoxContainer);
- main_vbox->set_anchors_and_offsets_preset(PRESET_WIDE);
+ main_vbox->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
add_child(main_vbox);
HBoxContainer *top_hbox = memnew(HBoxContainer);
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index 391cd009f1..44e04efb5d 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -1593,7 +1593,7 @@ AnimationBezierTrackEdit::AnimationBezierTrackEdit() {
play_position = memnew(Control);
play_position->set_mouse_filter(MOUSE_FILTER_PASS);
add_child(play_position);
- play_position->set_anchors_and_offsets_preset(PRESET_WIDE);
+ play_position->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
play_position->connect("draw", callable_mp(this, &AnimationBezierTrackEdit::_play_position_draw));
set_focus_mode(FOCUS_CLICK);
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index c5fd393746..703cfaee3d 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1890,7 +1890,7 @@ AnimationTimelineEdit::AnimationTimelineEdit() {
play_position = memnew(Control);
play_position->set_mouse_filter(MOUSE_FILTER_PASS);
add_child(play_position);
- play_position->set_anchors_and_offsets_preset(PRESET_WIDE);
+ play_position->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
play_position->connect("draw", callable_mp(this, &AnimationTimelineEdit::_play_position_draw));
add_track = memnew(MenuButton);
@@ -2919,7 +2919,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
add_child(path_popup);
path = memnew(LineEdit);
path_popup->add_child(path);
- path->set_anchors_and_offsets_preset(PRESET_WIDE);
+ path->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
path->connect("text_submitted", callable_mp(this, &AnimationTrackEdit::_path_submitted));
}
@@ -3212,7 +3212,7 @@ AnimationTrackEdit::AnimationTrackEdit() {
play_position = memnew(Control);
play_position->set_mouse_filter(MOUSE_FILTER_PASS);
add_child(play_position);
- play_position->set_anchors_and_offsets_preset(PRESET_WIDE);
+ play_position->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
play_position->connect("draw", callable_mp(this, &AnimationTrackEdit::_play_position_draw));
set_focus_mode(FOCUS_CLICK);
set_mouse_filter(MOUSE_FILTER_PASS); // Scroll has to work too for selection.
@@ -6245,7 +6245,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
info_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
info_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
- info_message->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
+ info_message->set_anchors_and_offsets_preset(PRESET_FULL_RECT, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
main_panel->add_child(info_message);
timeline = memnew(AnimationTimelineEdit);
diff --git a/editor/debugger/editor_performance_profiler.cpp b/editor/debugger/editor_performance_profiler.cpp
index 50eef7b8b8..55d025f675 100644
--- a/editor/debugger/editor_performance_profiler.cpp
+++ b/editor/debugger/editor_performance_profiler.cpp
@@ -393,7 +393,7 @@ EditorPerformanceProfiler::EditorPerformanceProfiler() {
info_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
info_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
- info_message->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
+ info_message->set_anchors_and_offsets_preset(PRESET_FULL_RECT, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
monitor_draw->add_child(info_message);
for (int i = 0; i < Performance::MONITOR_MAX; i++) {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index d9cdefbca7..166dcf19c8 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -4333,16 +4333,16 @@ void EditorNode::_dock_make_float() {
window->set_title(dock->get_name());
Panel *p = memnew(Panel);
p->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("PanelForeground"), SNAME("EditorStyles")));
- p->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ p->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
window->add_child(p);
MarginContainer *margin = memnew(MarginContainer);
- margin->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ margin->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
margin->add_theme_constant_override("margin_right", borders.width);
margin->add_theme_constant_override("margin_top", borders.height);
margin->add_theme_constant_override("margin_left", borders.width);
margin->add_theme_constant_override("margin_bottom", borders.height);
window->add_child(margin);
- dock->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ dock->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
margin->add_child(dock);
window->set_wrap_controls(true);
window->set_size(dock_size);
@@ -6142,11 +6142,11 @@ EditorNode::EditorNode() {
theme_base = memnew(Control);
add_child(theme_base);
- theme_base->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ theme_base->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
gui_base = memnew(Panel);
theme_base->add_child(gui_base);
- gui_base->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ gui_base->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
theme_base->set_theme(theme);
gui_base->set_theme(theme);
@@ -6164,7 +6164,7 @@ EditorNode::EditorNode() {
main_vbox = memnew(VBoxContainer);
gui_base->add_child(main_vbox);
- main_vbox->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 8);
+ main_vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 8);
main_vbox->add_theme_constant_override("separation", 8 * EDSCALE);
menu_hb = memnew(HBoxContainer);
diff --git a/editor/editor_path.cpp b/editor/editor_path.cpp
index 6453db3b0b..dc77b5fea9 100644
--- a/editor/editor_path.cpp
+++ b/editor/editor_path.cpp
@@ -198,7 +198,7 @@ EditorPath::EditorPath(EditorSelectionHistory *p_history) {
history = p_history;
MarginContainer *main_mc = memnew(MarginContainer);
- main_mc->set_anchors_and_offsets_preset(PRESET_WIDE);
+ main_mc->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
main_mc->add_theme_constant_override("margin_left", 4 * EDSCALE);
main_mc->add_theme_constant_override("margin_right", 6 * EDSCALE);
add_child(main_mc);
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 2e78b58e11..40e16bf717 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -879,7 +879,7 @@ EditorResourcePicker::EditorResourcePicker() {
preview_rect = memnew(TextureRect);
preview_rect->set_ignore_texture_size(true);
- preview_rect->set_anchors_and_offsets_preset(PRESET_WIDE);
+ preview_rect->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
preview_rect->set_offset(SIDE_TOP, 1);
preview_rect->set_offset(SIDE_BOTTOM, -1);
preview_rect->set_offset(SIDE_RIGHT, -1);
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
index c651d6bf6e..5c0ccd11f0 100644
--- a/editor/editor_spin_slider.cpp
+++ b/editor/editor_spin_slider.cpp
@@ -652,7 +652,7 @@ void EditorSpinSlider::_ensure_input_popup() {
value_input = memnew(LineEdit);
value_input_popup->add_child(value_input);
value_input_popup->set_wrap_controls(true);
- value_input->set_anchors_and_offsets_preset(PRESET_WIDE);
+ value_input->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
value_input_popup->connect("popup_hide", callable_mp(this, &EditorSpinSlider::_value_input_closed));
value_input->connect("text_submitted", callable_mp(this, &EditorSpinSlider::_value_input_submitted));
value_input->connect("focus_exited", callable_mp(this, &EditorSpinSlider::_value_focus_exited));
diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp
index bca8c95574..f1d08783ad 100644
--- a/editor/groups_editor.cpp
+++ b/editor/groups_editor.cpp
@@ -432,7 +432,7 @@ GroupDialog::GroupDialog() {
VBoxContainer *vbc = memnew(VBoxContainer);
add_child(vbc);
- vbc->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
+ vbc->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
HBoxContainer *hbc = memnew(HBoxContainer);
vbc->add_child(hbc);
@@ -562,7 +562,7 @@ GroupDialog::GroupDialog() {
group_empty->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
group_empty->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
nodes_to_remove->add_child(group_empty);
- group_empty->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
+ group_empty->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
set_title(TTR("Group Editor"));
diff --git a/editor/icons/ControlAlignWide.svg b/editor/icons/ControlAlignFullRect.svg
index 0099e04896..0099e04896 100644
--- a/editor/icons/ControlAlignWide.svg
+++ b/editor/icons/ControlAlignFullRect.svg
diff --git a/editor/icons/LabelSettings.svg b/editor/icons/LabelSettings.svg
new file mode 100644
index 0000000000..4dc3b9e86e
--- /dev/null
+++ b/editor/icons/LabelSettings.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M6 3a1 1 0 0 0-.707.293l-4 4a1 1 0 0 0 0 1.414l4 4A1 1 0 0 0 6 13h2.076a3.766 3.766 0 0 1-.058-.496c-.003-.058-.006-.115-.006-.174a2.606 2.606 0 0 1 .05-.508 3.212 3.212 0 0 1 .133-.496 5.104 5.104 0 0 1 .451-.982 8.303 8.303 0 0 1 .422-.656 14.41 14.41 0 0 1 .489-.667c.172-.223.351-.45.535-.68.163-.203.327-.408.492-.618a27.639 27.639 0 0 0 .732-.977 16.04 16.04 0 0 0 .465-.697c.075-.12.147-.242.219-.365a12.399 12.399 0 0 0 .684 1.062 27.555 27.555 0 0 0 .73.977c.165.21.331.415.494.619a43.298 43.298 0 0 1 .787 1.013c.082.111.16.222.237.332L15 9.79V4a1 1 0 0 0-1-1H6zM5 7a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1z" style="fill:#8eef97;fill-opacity:1"/><path d="M9.184 13A2.99 2.99 0 0 0 12 15a2.99 2.99 0 0 0 2.816-2z" fill="#ff4596"/><path d="M9.23 11c-.136.326-.23.656-.23 1 0 .352.072.686.184 1h5.632c.112-.314.184-.648.184-1 0-.344-.094-.674-.23-1H9.23z" fill="#8045ff"/><path d="M10.564 9c-.552.69-1.058 1.342-1.334 2h5.54c-.276-.658-.782-1.31-1.335-2Z" fill="#45d7ff"/><path d="M12 7c-.43.746-.945 1.387-1.435 2h2.87c-.49-.613-1.005-1.254-1.435-2Z" fill="#45ffa2"/></svg>
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index 05d7a5f973..1258b9a03c 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -1969,7 +1969,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() {
state_machine_play_pos = memnew(Control);
state_machine_draw->add_child(state_machine_play_pos);
state_machine_play_pos->set_mouse_filter(MOUSE_FILTER_PASS); //pass all to parent
- state_machine_play_pos->set_anchors_and_offsets_preset(PRESET_WIDE);
+ state_machine_play_pos->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
state_machine_play_pos->connect("draw", callable_mp(this, &AnimationNodeStateMachineEditor::_state_machine_pos_draw));
v_scroll = memnew(VScrollBar);
@@ -2022,7 +2022,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() {
add_child(name_edit_popup);
name_edit = memnew(LineEdit);
name_edit_popup->add_child(name_edit);
- name_edit->set_anchors_and_offsets_preset(PRESET_WIDE);
+ name_edit->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
name_edit->connect("text_submitted", callable_mp(this, &AnimationNodeStateMachineEditor::_name_edited));
name_edit->connect("focus_exited", callable_mp(this, &AnimationNodeStateMachineEditor::_name_edited_focus_out));
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index f89e387f92..bb393c652d 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -1616,7 +1616,7 @@ AssetLibraryEditorPlugin::AssetLibraryEditorPlugin() {
addon_library = memnew(EditorAssetLibrary);
addon_library->set_v_size_flags(Control::SIZE_EXPAND_FILL);
EditorNode::get_singleton()->get_main_control()->add_child(addon_library);
- addon_library->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ addon_library->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
addon_library->hide();
}
diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp
index a60e49ca9d..9b874ada45 100644
--- a/editor/plugins/audio_stream_editor_plugin.cpp
+++ b/editor/plugins/audio_stream_editor_plugin.cpp
@@ -213,7 +213,7 @@ AudioStreamEditor::AudioStreamEditor() {
add_child(_player);
VBoxContainer *vbox = memnew(VBoxContainer);
- vbox->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_MINSIZE, 0);
+ vbox->set_anchors_and_offsets_preset(PRESET_FULL_RECT, PRESET_MODE_MINSIZE, 0);
add_child(vbox);
_preview = memnew(ColorRect);
@@ -222,7 +222,7 @@ AudioStreamEditor::AudioStreamEditor() {
vbox->add_child(_preview);
_indicator = memnew(Control);
- _indicator->set_anchors_and_offsets_preset(PRESET_WIDE);
+ _indicator->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
_indicator->connect("draw", callable_mp(this, &AudioStreamEditor::_draw_indicator));
_indicator->connect("gui_input", callable_mp(this, &AudioStreamEditor::_on_input_indicator));
_preview->add_child(_indicator);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 9723fece5e..99ddd167d4 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -50,6 +50,7 @@
#include "scene/2d/skeleton_2d.h"
#include "scene/2d/sprite_2d.h"
#include "scene/2d/touch_screen_button.h"
+#include "scene/gui/flow_container.h"
#include "scene/gui/grid_container.h"
#include "scene/gui/nine_patch_rect.h"
#include "scene/gui/subviewport_container.h"
@@ -3861,7 +3862,7 @@ void CanvasItemEditor::_update_editor_settings() {
key_auto_insert_button->add_theme_color_override("icon_pressed_color", key_auto_color.lerp(Color(1, 0, 0), 0.55));
animation_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
- context_menu_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles")));
+ context_menu_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles")));
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/2d_editor_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
pan_speed = int(EditorSettings::get_singleton()->get("editors/panning/2d_editor_pan_speed"));
@@ -4926,11 +4927,11 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) {
ERR_FAIL_COND(!p_control);
- hbc_context_menu->add_child(p_control);
+ context_menu_hbox->add_child(p_control);
}
void CanvasItemEditor::remove_control_from_menu_panel(Control *p_control) {
- hbc_context_menu->remove_child(p_control);
+ context_menu_hbox->remove_child(p_control);
}
void CanvasItemEditor::add_control_to_left_panel(Control *p_control) {
@@ -4979,9 +4980,14 @@ CanvasItemEditor::CanvasItemEditor() {
EditorNode::get_singleton()->call_deferred(SNAME("connect"), "play_pressed", Callable(this, "_update_override_camera_button"), make_binds(true));
EditorNode::get_singleton()->call_deferred(SNAME("connect"), "stop_pressed", Callable(this, "_update_override_camera_button"), make_binds(false));
- hb = memnew(HBoxContainer);
- add_child(hb);
- hb->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ // A fluid container for all toolbars.
+ HFlowContainer *main_flow = memnew(HFlowContainer);
+ add_child(main_flow);
+
+ // Main toolbars.
+ HBoxContainer *main_menu_hbox = memnew(HBoxContainer);
+ main_menu_hbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+ main_flow->add_child(main_menu_hbox);
bottom_split = memnew(VSplitContainer);
add_child(bottom_split);
@@ -5006,7 +5012,7 @@ CanvasItemEditor::CanvasItemEditor() {
SubViewportContainer *scene_tree = memnew(SubViewportContainer);
viewport_scrollable->add_child(scene_tree);
scene_tree->set_stretch(true);
- scene_tree->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ scene_tree->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
scene_tree->add_child(EditorNode::get_singleton()->get_scene_root());
controls_vb = memnew(VBoxContainer);
@@ -5036,7 +5042,7 @@ CanvasItemEditor::CanvasItemEditor() {
viewport = memnew(CanvasItemEditorViewport(this));
viewport_scrollable->add_child(viewport);
viewport->set_mouse_filter(MOUSE_FILTER_PASS);
- viewport->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ viewport->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
viewport->set_clip_contents(true);
viewport->set_focus_mode(FOCUS_ALL);
viewport->connect("draw", callable_mp(this, &CanvasItemEditor::_draw_viewport));
@@ -5059,12 +5065,12 @@ CanvasItemEditor::CanvasItemEditor() {
// This prevents the first button's hover/pressed effect from "touching" the panel's border,
// which looks ugly.
Control *margin_left = memnew(Control);
- hb->add_child(margin_left);
+ main_menu_hbox->add_child(margin_left);
margin_left->set_custom_minimum_size(Size2(2, 0) * EDSCALE);
select_button = memnew(Button);
select_button->set_flat(true);
- hb->add_child(select_button);
+ main_menu_hbox->add_child(select_button);
select_button->set_toggle_mode(true);
select_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_SELECT));
select_button->set_pressed(true);
@@ -5072,11 +5078,11 @@ CanvasItemEditor::CanvasItemEditor() {
select_button->set_shortcut_context(this);
select_button->set_tooltip(keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+Drag: Move selected node.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Alt+Drag: Scale selected node.") + "\n" + TTR("V: Set selected node's pivot position.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("RMB: Add node at position clicked."));
- hb->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
move_button = memnew(Button);
move_button->set_flat(true);
- hb->add_child(move_button);
+ main_menu_hbox->add_child(move_button);
move_button->set_toggle_mode(true);
move_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_MOVE));
move_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/move_mode", TTR("Move Mode"), Key::W));
@@ -5085,7 +5091,7 @@ CanvasItemEditor::CanvasItemEditor() {
rotate_button = memnew(Button);
rotate_button->set_flat(true);
- hb->add_child(rotate_button);
+ main_menu_hbox->add_child(rotate_button);
rotate_button->set_toggle_mode(true);
rotate_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_ROTATE));
rotate_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/rotate_mode", TTR("Rotate Mode"), Key::E));
@@ -5094,32 +5100,32 @@ CanvasItemEditor::CanvasItemEditor() {
scale_button = memnew(Button);
scale_button->set_flat(true);
- hb->add_child(scale_button);
+ main_menu_hbox->add_child(scale_button);
scale_button->set_toggle_mode(true);
scale_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_SCALE));
scale_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/scale_mode", TTR("Scale Mode"), Key::S));
scale_button->set_shortcut_context(this);
scale_button->set_tooltip(TTR("Shift: Scale proportionally."));
- hb->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
list_select_button = memnew(Button);
list_select_button->set_flat(true);
- hb->add_child(list_select_button);
+ main_menu_hbox->add_child(list_select_button);
list_select_button->set_toggle_mode(true);
list_select_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_LIST_SELECT));
list_select_button->set_tooltip(TTR("Show list of selectable nodes at position clicked."));
pivot_button = memnew(Button);
pivot_button->set_flat(true);
- hb->add_child(pivot_button);
+ main_menu_hbox->add_child(pivot_button);
pivot_button->set_toggle_mode(true);
pivot_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_EDIT_PIVOT));
pivot_button->set_tooltip(TTR("Click to change object's rotation pivot."));
pan_button = memnew(Button);
pan_button->set_flat(true);
- hb->add_child(pan_button);
+ main_menu_hbox->add_child(pan_button);
pan_button->set_toggle_mode(true);
pan_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_PAN));
pan_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/pan_mode", TTR("Pan Mode"), Key::G));
@@ -5128,18 +5134,18 @@ CanvasItemEditor::CanvasItemEditor() {
ruler_button = memnew(Button);
ruler_button->set_flat(true);
- hb->add_child(ruler_button);
+ main_menu_hbox->add_child(ruler_button);
ruler_button->set_toggle_mode(true);
ruler_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_RULER));
ruler_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/ruler_mode", TTR("Ruler Mode"), Key::R));
ruler_button->set_shortcut_context(this);
ruler_button->set_tooltip(TTR("Ruler Mode"));
- hb->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
smart_snap_button = memnew(Button);
smart_snap_button->set_flat(true);
- hb->add_child(smart_snap_button);
+ main_menu_hbox->add_child(smart_snap_button);
smart_snap_button->set_toggle_mode(true);
smart_snap_button->connect("toggled", callable_mp(this, &CanvasItemEditor::_button_toggle_smart_snap));
smart_snap_button->set_tooltip(TTR("Toggle smart snapping."));
@@ -5148,7 +5154,7 @@ CanvasItemEditor::CanvasItemEditor() {
grid_snap_button = memnew(Button);
grid_snap_button->set_flat(true);
- hb->add_child(grid_snap_button);
+ main_menu_hbox->add_child(grid_snap_button);
grid_snap_button->set_toggle_mode(true);
grid_snap_button->connect("toggled", callable_mp(this, &CanvasItemEditor::_button_toggle_grid_snap));
grid_snap_button->set_tooltip(TTR("Toggle grid snapping."));
@@ -5157,7 +5163,7 @@ CanvasItemEditor::CanvasItemEditor() {
snap_config_menu = memnew(MenuButton);
snap_config_menu->set_shortcut_context(this);
- hb->add_child(snap_config_menu);
+ main_menu_hbox->add_child(snap_config_menu);
snap_config_menu->set_h_size_flags(SIZE_SHRINK_END);
snap_config_menu->set_tooltip(TTR("Snapping Options"));
snap_config_menu->set_switch_on_hover(true);
@@ -5186,11 +5192,11 @@ CanvasItemEditor::CanvasItemEditor() {
smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_other_nodes", TTR("Snap to Other Nodes")), SNAP_USE_OTHER_NODES);
smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_guides", TTR("Snap to Guides")), SNAP_USE_GUIDES);
- hb->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
lock_button = memnew(Button);
lock_button->set_flat(true);
- hb->add_child(lock_button);
+ main_menu_hbox->add_child(lock_button);
lock_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(LOCK_SELECTED));
lock_button->set_tooltip(TTR("Lock selected node, preventing selection and movement."));
@@ -5199,7 +5205,7 @@ CanvasItemEditor::CanvasItemEditor() {
unlock_button = memnew(Button);
unlock_button->set_flat(true);
- hb->add_child(unlock_button);
+ main_menu_hbox->add_child(unlock_button);
unlock_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(UNLOCK_SELECTED));
unlock_button->set_tooltip(TTR("Unlock selected node, allowing selection and movement."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
@@ -5207,7 +5213,7 @@ CanvasItemEditor::CanvasItemEditor() {
group_button = memnew(Button);
group_button->set_flat(true);
- hb->add_child(group_button);
+ main_menu_hbox->add_child(group_button);
group_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(GROUP_SELECTED));
group_button->set_tooltip(TTR("Makes sure the object's children are not selectable."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
@@ -5215,17 +5221,17 @@ CanvasItemEditor::CanvasItemEditor() {
ungroup_button = memnew(Button);
ungroup_button->set_flat(true);
- hb->add_child(ungroup_button);
+ main_menu_hbox->add_child(ungroup_button);
ungroup_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(UNGROUP_SELECTED));
ungroup_button->set_tooltip(TTR("Restores the object's children's ability to be selected."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
ungroup_button->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::G));
- hb->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
skeleton_menu = memnew(MenuButton);
skeleton_menu->set_shortcut_context(this);
- hb->add_child(skeleton_menu);
+ main_menu_hbox->add_child(skeleton_menu);
skeleton_menu->set_tooltip(TTR("Skeleton Options"));
skeleton_menu->set_switch_on_hover(true);
@@ -5236,24 +5242,24 @@ CanvasItemEditor::CanvasItemEditor() {
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Bone2D Node(s) from Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::B), SKELETON_MAKE_BONES);
p->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_popup_callback));
- hb->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
override_camera_button = memnew(Button);
override_camera_button->set_flat(true);
- hb->add_child(override_camera_button);
+ main_menu_hbox->add_child(override_camera_button);
override_camera_button->connect("toggled", callable_mp(this, &CanvasItemEditor::_button_override_camera));
override_camera_button->set_toggle_mode(true);
override_camera_button->set_disabled(true);
_update_override_camera_button(false);
- hb->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
view_menu = memnew(MenuButton);
// TRANSLATORS: Noun, name of the 2D/3D View menus.
view_menu->set_text(TTR("View"));
view_menu->set_switch_on_hover(true);
view_menu->set_shortcut_context(this);
- hb->add_child(view_menu);
+ main_menu_hbox->add_child(view_menu);
view_menu->get_popup()->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_popup_callback));
p = view_menu->get_popup();
@@ -5286,16 +5292,17 @@ CanvasItemEditor::CanvasItemEditor() {
p->add_separator();
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/preview_canvas_scale", TTR("Preview Canvas Scale"), KeyModifierMask::SHIFT | KeyModifierMask::CMD | Key::P), PREVIEW_CANVAS_SCALE);
- hb->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
- context_menu_container = memnew(PanelContainer);
- hbc_context_menu = memnew(HBoxContainer);
- context_menu_container->add_child(hbc_context_menu);
- hb->add_child(context_menu_container);
+ // Contextual toolbars.
+ context_menu_panel = memnew(PanelContainer);
+ context_menu_hbox = memnew(HBoxContainer);
+ context_menu_panel->add_child(context_menu_hbox);
+ main_flow->add_child(context_menu_panel);
// Animation controls.
animation_hb = memnew(HBoxContainer);
- hbc_context_menu->add_child(animation_hb);
+ context_menu_hbox->add_child(animation_hb);
animation_hb->add_child(memnew(VSeparator));
animation_hb->hide();
@@ -5425,7 +5432,7 @@ CanvasItemEditorPlugin::CanvasItemEditorPlugin() {
canvas_item_editor = memnew(CanvasItemEditor);
canvas_item_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
EditorNode::get_singleton()->get_main_control()->add_child(canvas_item_editor);
- canvas_item_editor->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ canvas_item_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
canvas_item_editor->hide();
}
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 83685baf7a..18c898521d 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -188,11 +188,10 @@ private:
HScrollBar *h_scroll = nullptr;
VScrollBar *v_scroll = nullptr;
- HBoxContainer *hb = nullptr;
// Used for secondary menu items which are displayed depending on the currently selected node
// (such as MeshInstance's "Mesh" menu).
- PanelContainer *context_menu_container = nullptr;
- HBoxContainer *hbc_context_menu = nullptr;
+ PanelContainer *context_menu_panel = nullptr;
+ HBoxContainer *context_menu_hbox = nullptr;
Transform2D transform;
GridVisibility grid_visibility = GRID_VISIBILITY_SHOW_WHEN_SNAPPING;
@@ -503,8 +502,6 @@ protected:
static void _bind_methods();
- HBoxContainer *get_panel_hb() { return hb; }
-
static CanvasItemEditor *singleton;
public:
diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp
index 3adaf8f601..ec038174fc 100644
--- a/editor/plugins/control_editor_plugin.cpp
+++ b/editor/plugins/control_editor_plugin.cpp
@@ -173,7 +173,7 @@ void EditorPropertyAnchorsPreset::setup(const Vector<String> &p_options) {
Vector<String> split_after;
split_after.append("Custom");
- split_after.append("PresetWide");
+ split_after.append("PresetFullRect");
split_after.append("PresetBottomLeft");
split_after.append("PresetCenter");
@@ -181,24 +181,18 @@ void EditorPropertyAnchorsPreset::setup(const Vector<String> &p_options) {
Vector<String> text_split = p_options[i].split(":");
int64_t current_val = text_split[1].to_int();
- String humanized_name = text_split[0];
- if (humanized_name.begins_with("Preset")) {
- if (humanized_name == "PresetWide") {
- humanized_name = "Full Rect";
- } else {
- humanized_name = humanized_name.trim_prefix("Preset");
- humanized_name = humanized_name.capitalize();
- }
-
- String icon_name = text_split[0].trim_prefix("Preset");
- icon_name = "ControlAlign" + icon_name;
+ String option_name = text_split[0];
+ if (option_name.begins_with("Preset")) {
+ String preset_name = option_name.trim_prefix("Preset");
+ String humanized_name = preset_name.capitalize();
+ String icon_name = "ControlAlign" + preset_name;
options->add_icon_item(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(icon_name, "EditorIcons"), humanized_name);
} else {
- options->add_item(humanized_name);
+ options->add_item(option_name);
}
options->set_item_metadata(j, current_val);
- if (split_after.has(text_split[0])) {
+ if (split_after.has(option_name)) {
options->add_separator();
j++;
}
@@ -479,7 +473,7 @@ void ControlEditorToolbar::_set_anchors_and_offsets_preset(Control::LayoutPreset
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_HCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
undo_redo->add_do_method(control, "set_offsets_preset", p_preset, Control::PRESET_MODE_MINSIZE);
break;
}
@@ -689,8 +683,8 @@ void ControlEditorToolbar::_popup_callback(int p_op) {
case ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE: {
_set_anchors_and_offsets_preset(PRESET_HCENTER_WIDE);
} break;
- case ANCHORS_AND_OFFSETS_PRESET_WIDE: {
- _set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ case ANCHORS_AND_OFFSETS_PRESET_FULL_RECT: {
+ _set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
} break;
case ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO: {
_set_anchors_and_offsets_to_keep_ratio();
@@ -741,8 +735,8 @@ void ControlEditorToolbar::_popup_callback(int p_op) {
case ANCHORS_PRESET_HCENTER_WIDE: {
_set_anchors_preset(PRESET_HCENTER_WIDE);
} break;
- case ANCHORS_PRESET_WIDE: {
- _set_anchors_preset(Control::PRESET_WIDE);
+ case ANCHORS_PRESET_FULL_RECT: {
+ _set_anchors_preset(Control::PRESET_FULL_RECT);
} break;
case CONTAINERS_H_PRESET_FILL: {
@@ -840,7 +834,7 @@ void ControlEditorToolbar::_notification(int p_what) {
p->add_icon_item(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons")), TTR("VCenter Wide"), ANCHORS_AND_OFFSETS_PRESET_VCENTER_WIDE);
p->add_icon_item(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons")), TTR("HCenter Wide"), ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE);
p->add_separator();
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignWide"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_AND_OFFSETS_PRESET_WIDE);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignFullRect"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_AND_OFFSETS_PRESET_FULL_RECT);
p->add_icon_item(get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons")), TTR("Keep Current Ratio"), ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO);
p->set_item_tooltip(19, TTR("Adjust anchors and offsets to match the current rect size."));
@@ -867,7 +861,7 @@ void ControlEditorToolbar::_notification(int p_what) {
anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons")), TTR("VCenter Wide"), ANCHORS_PRESET_VCENTER_WIDE);
anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons")), TTR("HCenter Wide"), ANCHORS_PRESET_HCENTER_WIDE);
anchors_popup->add_separator();
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignWide"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_PRESET_WIDE);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignFullRect"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_PRESET_FULL_RECT);
anchor_mode_button->set_icon(get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons")));
diff --git a/editor/plugins/control_editor_plugin.h b/editor/plugins/control_editor_plugin.h
index 96451f7dcf..d3f1d3acbb 100644
--- a/editor/plugins/control_editor_plugin.h
+++ b/editor/plugins/control_editor_plugin.h
@@ -147,7 +147,7 @@ class ControlEditorToolbar : public HBoxContainer {
ANCHORS_AND_OFFSETS_PRESET_BOTTOM_WIDE,
ANCHORS_AND_OFFSETS_PRESET_VCENTER_WIDE,
ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE,
- ANCHORS_AND_OFFSETS_PRESET_WIDE,
+ ANCHORS_AND_OFFSETS_PRESET_FULL_RECT,
ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO,
@@ -166,7 +166,7 @@ class ControlEditorToolbar : public HBoxContainer {
ANCHORS_PRESET_BOTTOM_WIDE,
ANCHORS_PRESET_VCENTER_WIDE,
ANCHORS_PRESET_HCENTER_WIDE,
- ANCHORS_PRESET_WIDE,
+ ANCHORS_PRESET_FULL_RECT,
// Offsets Presets are not currently in use.
OFFSETS_PRESET_TOP_LEFT,
@@ -184,7 +184,7 @@ class ControlEditorToolbar : public HBoxContainer {
OFFSETS_PRESET_BOTTOM_WIDE,
OFFSETS_PRESET_VCENTER_WIDE,
OFFSETS_PRESET_HCENTER_WIDE,
- OFFSETS_PRESET_WIDE,
+ OFFSETS_PRESET_FULL_RECT,
CONTAINERS_H_PRESET_FILL,
CONTAINERS_H_PRESET_FILL_EXPAND,
diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp
index eb004568d0..34db75f118 100644
--- a/editor/plugins/material_editor_plugin.cpp
+++ b/editor/plugins/material_editor_plugin.cpp
@@ -130,7 +130,7 @@ MaterialEditor::MaterialEditor() {
layout_2d = memnew(HBoxContainer);
layout_2d->set_alignment(BoxContainer::ALIGNMENT_CENTER);
add_child(layout_2d);
- layout_2d->set_anchors_and_offsets_preset(PRESET_WIDE);
+ layout_2d->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
rect_instance = memnew(ColorRect);
layout_2d->add_child(rect_instance);
@@ -143,7 +143,7 @@ MaterialEditor::MaterialEditor() {
vc = memnew(SubViewportContainer);
vc->set_stretch(true);
add_child(vc);
- vc->set_anchors_and_offsets_preset(PRESET_WIDE);
+ vc->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
viewport = memnew(SubViewport);
Ref<World3D> world_3d;
world_3d.instantiate();
@@ -190,7 +190,7 @@ MaterialEditor::MaterialEditor() {
layout_3d = memnew(HBoxContainer);
add_child(layout_3d);
- layout_3d->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 2);
+ layout_3d->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 2);
VBoxContainer *vb_shape = memnew(VBoxContainer);
layout_3d->add_child(vb_shape);
diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp
index 7029768479..b23395fea2 100644
--- a/editor/plugins/mesh_editor_plugin.cpp
+++ b/editor/plugins/mesh_editor_plugin.cpp
@@ -137,7 +137,7 @@ MeshEditor::MeshEditor() {
HBoxContainer *hb = memnew(HBoxContainer);
add_child(hb);
- hb->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 2);
+ hb->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 2);
hb->add_spacer();
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 7530f88fef..6543da8776 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -52,6 +52,7 @@
#include "scene/3d/visual_instance_3d.h"
#include "scene/3d/world_environment.h"
#include "scene/gui/center_container.h"
+#include "scene/gui/flow_container.h"
#include "scene/gui/subviewport_container.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/surface_tool.h"
@@ -4494,7 +4495,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
subviewport_container = c;
c->set_stretch(true);
add_child(c);
- c->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ c->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
viewport = memnew(SubViewport);
viewport->set_disable_input(true);
@@ -4502,7 +4503,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
surface = memnew(Control);
surface->set_drag_forwarding(this);
add_child(surface);
- surface->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ surface->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
surface->set_clip_contents(true);
camera = memnew(Camera3D);
camera->set_disable_gizmos(true);
@@ -6983,7 +6984,7 @@ void Node3DEditor::_update_theme() {
environ_sky_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor"))));
environ_ground_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor"))));
- context_menu_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles")));
+ context_menu_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles")));
}
void Node3DEditor::_notification(int p_what) {
@@ -7072,11 +7073,11 @@ Vector<int> Node3DEditor::get_subgizmo_selection() {
}
void Node3DEditor::add_control_to_menu_panel(Control *p_control) {
- hbc_context_menu->add_child(p_control);
+ context_menu_hbox->add_child(p_control);
}
void Node3DEditor::remove_control_from_menu_panel(Control *p_control) {
- hbc_context_menu->remove_child(p_control);
+ context_menu_hbox->remove_child(p_control);
}
void Node3DEditor::set_can_preview(Camera3D *p_preview) {
@@ -7233,7 +7234,7 @@ void Node3DEditor::_toggle_maximize_view(Object *p_viewport) {
if (!maximized) {
for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {
if (i == (uint32_t)index) {
- viewports[i]->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ viewports[i]->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
} else {
viewports[i]->hide();
}
@@ -7529,8 +7530,13 @@ Node3DEditor::Node3DEditor() {
camera_override_viewport_id = 0;
- hbc_menu = memnew(HBoxContainer);
- vbc->add_child(hbc_menu);
+ // A fluid container for all toolbars.
+ HFlowContainer *main_flow = memnew(HFlowContainer);
+ vbc->add_child(main_flow);
+
+ // Main toolbars.
+ HBoxContainer *main_menu_hbox = memnew(HBoxContainer);
+ main_flow->add_child(main_menu_hbox);
Vector<Variant> button_binds;
button_binds.resize(1);
@@ -7540,11 +7546,11 @@ Node3DEditor::Node3DEditor() {
// This prevents the first button's hover/pressed effect from "touching" the panel's border,
// which looks ugly.
Control *margin_left = memnew(Control);
- hbc_menu->add_child(margin_left);
+ main_menu_hbox->add_child(margin_left);
margin_left->set_custom_minimum_size(Size2(2, 0) * EDSCALE);
tool_button[TOOL_MODE_SELECT] = memnew(Button);
- hbc_menu->add_child(tool_button[TOOL_MODE_SELECT]);
+ main_menu_hbox->add_child(tool_button[TOOL_MODE_SELECT]);
tool_button[TOOL_MODE_SELECT]->set_toggle_mode(true);
tool_button[TOOL_MODE_SELECT]->set_flat(true);
tool_button[TOOL_MODE_SELECT]->set_pressed(true);
@@ -7553,10 +7559,10 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_MODE_SELECT]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTR("Select Mode"), Key::Q));
tool_button[TOOL_MODE_SELECT]->set_shortcut_context(this);
tool_button[TOOL_MODE_SELECT]->set_tooltip(keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
- hbc_menu->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
tool_button[TOOL_MODE_MOVE] = memnew(Button);
- hbc_menu->add_child(tool_button[TOOL_MODE_MOVE]);
+ main_menu_hbox->add_child(tool_button[TOOL_MODE_MOVE]);
tool_button[TOOL_MODE_MOVE]->set_toggle_mode(true);
tool_button[TOOL_MODE_MOVE]->set_flat(true);
button_binds.write[0] = MENU_TOOL_MOVE;
@@ -7565,7 +7571,7 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_MODE_MOVE]->set_shortcut_context(this);
tool_button[TOOL_MODE_ROTATE] = memnew(Button);
- hbc_menu->add_child(tool_button[TOOL_MODE_ROTATE]);
+ main_menu_hbox->add_child(tool_button[TOOL_MODE_ROTATE]);
tool_button[TOOL_MODE_ROTATE]->set_toggle_mode(true);
tool_button[TOOL_MODE_ROTATE]->set_flat(true);
button_binds.write[0] = MENU_TOOL_ROTATE;
@@ -7574,7 +7580,7 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_MODE_ROTATE]->set_shortcut_context(this);
tool_button[TOOL_MODE_SCALE] = memnew(Button);
- hbc_menu->add_child(tool_button[TOOL_MODE_SCALE]);
+ main_menu_hbox->add_child(tool_button[TOOL_MODE_SCALE]);
tool_button[TOOL_MODE_SCALE]->set_toggle_mode(true);
tool_button[TOOL_MODE_SCALE]->set_flat(true);
button_binds.write[0] = MENU_TOOL_SCALE;
@@ -7582,10 +7588,10 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_MODE_SCALE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_scale", TTR("Scale Mode"), Key::R));
tool_button[TOOL_MODE_SCALE]->set_shortcut_context(this);
- hbc_menu->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
tool_button[TOOL_MODE_LIST_SELECT] = memnew(Button);
- hbc_menu->add_child(tool_button[TOOL_MODE_LIST_SELECT]);
+ main_menu_hbox->add_child(tool_button[TOOL_MODE_LIST_SELECT]);
tool_button[TOOL_MODE_LIST_SELECT]->set_toggle_mode(true);
tool_button[TOOL_MODE_LIST_SELECT]->set_flat(true);
button_binds.write[0] = MENU_TOOL_LIST_SELECT;
@@ -7593,7 +7599,7 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_MODE_LIST_SELECT]->set_tooltip(TTR("Show list of selectable nodes at position clicked."));
tool_button[TOOL_LOCK_SELECTED] = memnew(Button);
- hbc_menu->add_child(tool_button[TOOL_LOCK_SELECTED]);
+ main_menu_hbox->add_child(tool_button[TOOL_LOCK_SELECTED]);
tool_button[TOOL_LOCK_SELECTED]->set_flat(true);
button_binds.write[0] = MENU_LOCK_SELECTED;
tool_button[TOOL_LOCK_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
@@ -7602,7 +7608,7 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_LOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD | Key::L));
tool_button[TOOL_UNLOCK_SELECTED] = memnew(Button);
- hbc_menu->add_child(tool_button[TOOL_UNLOCK_SELECTED]);
+ main_menu_hbox->add_child(tool_button[TOOL_UNLOCK_SELECTED]);
tool_button[TOOL_UNLOCK_SELECTED]->set_flat(true);
button_binds.write[0] = MENU_UNLOCK_SELECTED;
tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
@@ -7611,7 +7617,7 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_UNLOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::L));
tool_button[TOOL_GROUP_SELECTED] = memnew(Button);
- hbc_menu->add_child(tool_button[TOOL_GROUP_SELECTED]);
+ main_menu_hbox->add_child(tool_button[TOOL_GROUP_SELECTED]);
tool_button[TOOL_GROUP_SELECTED]->set_flat(true);
button_binds.write[0] = MENU_GROUP_SELECTED;
tool_button[TOOL_GROUP_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
@@ -7620,7 +7626,7 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_GROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD | Key::G));
tool_button[TOOL_UNGROUP_SELECTED] = memnew(Button);
- hbc_menu->add_child(tool_button[TOOL_UNGROUP_SELECTED]);
+ main_menu_hbox->add_child(tool_button[TOOL_UNGROUP_SELECTED]);
tool_button[TOOL_UNGROUP_SELECTED]->set_flat(true);
button_binds.write[0] = MENU_UNGROUP_SELECTED;
tool_button[TOOL_UNGROUP_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
@@ -7628,10 +7634,10 @@ Node3DEditor::Node3DEditor() {
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::G));
- hbc_menu->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
tool_option_button[TOOL_OPT_LOCAL_COORDS] = memnew(Button);
- hbc_menu->add_child(tool_option_button[TOOL_OPT_LOCAL_COORDS]);
+ main_menu_hbox->add_child(tool_option_button[TOOL_OPT_LOCAL_COORDS]);
tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_toggle_mode(true);
tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_flat(true);
button_binds.write[0] = MENU_TOOL_LOCAL_COORDS;
@@ -7640,7 +7646,7 @@ Node3DEditor::Node3DEditor() {
tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_shortcut_context(this);
tool_option_button[TOOL_OPT_USE_SNAP] = memnew(Button);
- hbc_menu->add_child(tool_option_button[TOOL_OPT_USE_SNAP]);
+ main_menu_hbox->add_child(tool_option_button[TOOL_OPT_USE_SNAP]);
tool_option_button[TOOL_OPT_USE_SNAP]->set_toggle_mode(true);
tool_option_button[TOOL_OPT_USE_SNAP]->set_flat(true);
button_binds.write[0] = MENU_TOOL_USE_SNAP;
@@ -7648,10 +7654,10 @@ Node3DEditor::Node3DEditor() {
tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut(ED_SHORTCUT("spatial_editor/snap", TTR("Use Snap"), Key::Y));
tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut_context(this);
- hbc_menu->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA] = memnew(Button);
- hbc_menu->add_child(tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]);
+ main_menu_hbox->add_child(tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]);
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_toggle_mode(true);
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_flat(true);
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_disabled(true);
@@ -7659,7 +7665,7 @@ Node3DEditor::Node3DEditor() {
tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->connect("toggled", callable_mp(this, &Node3DEditor::_menu_item_toggled), button_binds);
_update_camera_override_button(false);
- hbc_menu->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
sun_button = memnew(Button);
sun_button->set_tooltip(TTR("Toggle preview sunlight.\nIf a DirectionalLight3D node is added to the scene, preview sunlight is disabled."));
sun_button->set_toggle_mode(true);
@@ -7667,7 +7673,7 @@ Node3DEditor::Node3DEditor() {
sun_button->connect("pressed", callable_mp(this, &Node3DEditor::_update_preview_environment), varray(), CONNECT_DEFERRED);
sun_button->set_disabled(true);
- hbc_menu->add_child(sun_button);
+ main_menu_hbox->add_child(sun_button);
environ_button = memnew(Button);
environ_button->set_tooltip(TTR("Toggle preview environment.\nIf a WorldEnvironment node is added to the scene, preview environment is disabled."));
@@ -7676,16 +7682,16 @@ Node3DEditor::Node3DEditor() {
environ_button->connect("pressed", callable_mp(this, &Node3DEditor::_update_preview_environment), varray(), CONNECT_DEFERRED);
environ_button->set_disabled(true);
- hbc_menu->add_child(environ_button);
+ main_menu_hbox->add_child(environ_button);
sun_environ_settings = memnew(Button);
sun_environ_settings->set_tooltip(TTR("Edit Sun and Environment settings."));
sun_environ_settings->set_flat(true);
sun_environ_settings->connect("pressed", callable_mp(this, &Node3DEditor::_sun_environ_settings_pressed));
- hbc_menu->add_child(sun_environ_settings);
+ main_menu_hbox->add_child(sun_environ_settings);
- hbc_menu->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
// Drag and drop support;
preview_node = memnew(Node3D);
@@ -7719,7 +7725,7 @@ Node3DEditor::Node3DEditor() {
transform_menu->set_text(TTR("Transform"));
transform_menu->set_switch_on_hover(true);
transform_menu->set_shortcut_context(this);
- hbc_menu->add_child(transform_menu);
+ main_menu_hbox->add_child(transform_menu);
p = transform_menu->get_popup();
p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap Object to Floor"), Key::PAGEDOWN), MENU_SNAP_TO_FLOOR);
@@ -7735,14 +7741,14 @@ Node3DEditor::Node3DEditor() {
view_menu->set_text(TTR("View"));
view_menu->set_switch_on_hover(true);
view_menu->set_shortcut_context(this);
- hbc_menu->add_child(view_menu);
+ main_menu_hbox->add_child(view_menu);
- hbc_menu->add_child(memnew(VSeparator));
+ main_menu_hbox->add_child(memnew(VSeparator));
- context_menu_container = memnew(PanelContainer);
- hbc_context_menu = memnew(HBoxContainer);
- context_menu_container->add_child(hbc_context_menu);
- hbc_menu->add_child(context_menu_container);
+ context_menu_panel = memnew(PanelContainer);
+ context_menu_hbox = memnew(HBoxContainer);
+ context_menu_panel->add_child(context_menu_hbox);
+ main_flow->add_child(context_menu_panel);
// Get the view menu popup and have it stay open when a checkable item is selected
p = view_menu->get_popup();
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 8a602be08b..c98022bcf7 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -664,11 +664,10 @@ private:
void _menu_gizmo_toggled(int p_option);
void _update_camera_override_button(bool p_game_running);
void _update_camera_override_viewport(Object *p_viewport);
- HBoxContainer *hbc_menu = nullptr;
// Used for secondary menu items which are displayed depending on the currently selected node
// (such as MeshInstance's "Mesh" menu).
- PanelContainer *context_menu_container = nullptr;
- HBoxContainer *hbc_context_menu = nullptr;
+ PanelContainer *context_menu_panel = nullptr;
+ HBoxContainer *context_menu_hbox = nullptr;
void _generate_selection_boxes();
UndoRedo *undo_redo = nullptr;
diff --git a/editor/plugins/replication_editor_plugin.cpp b/editor/plugins/replication_editor_plugin.cpp
index 9e495c3aa3..3e06a6739f 100644
--- a/editor/plugins/replication_editor_plugin.cpp
+++ b/editor/plugins/replication_editor_plugin.cpp
@@ -242,7 +242,7 @@ ReplicationEditor::ReplicationEditor() {
drop_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
drop_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
tree->add_child(drop_label);
- drop_label->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ drop_label->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
tree->set_drag_forwarding(this);
}
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 66cd85a26a..fc545b44e8 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -1420,7 +1420,9 @@ Control *ScriptTextEditor::get_edit_menu() {
}
void ScriptTextEditor::clear_edit_menu() {
- memdelete(edit_hb);
+ if (editor_enabled) {
+ memdelete(edit_hb);
+ }
}
void ScriptTextEditor::set_find_replace_bar(FindReplaceBar *p_bar) {
@@ -1821,7 +1823,7 @@ void ScriptTextEditor::_enable_code_editor() {
VSplitContainer *editor_box = memnew(VSplitContainer);
add_child(editor_box);
- editor_box->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ editor_box->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
editor_box->set_v_size_flags(SIZE_EXPAND_FILL);
editor_box->add_child(code_editor);
@@ -1958,7 +1960,7 @@ void ScriptTextEditor::_enable_code_editor() {
ScriptTextEditor::ScriptTextEditor() {
code_editor = memnew(CodeTextEditor);
code_editor->add_theme_constant_override("separation", 2);
- code_editor->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ code_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
code_editor->set_code_complete_func(_code_complete_scripts, this);
code_editor->set_v_size_flags(SIZE_EXPAND_FILL);
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index d79d2e9012..70b8c3aaa7 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -706,7 +706,7 @@ ShaderEditor::ShaderEditor() {
shader_editor = memnew(ShaderTextEditor);
shader_editor->set_v_size_flags(SIZE_EXPAND_FILL);
shader_editor->add_theme_constant_override("separation", 0);
- shader_editor->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ shader_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
shader_editor->connect("show_warnings_panel", callable_mp(this, &ShaderEditor::_show_warnings_panel));
shader_editor->connect("script_changed", callable_mp(this, &ShaderEditor::apply_shaders));
@@ -797,7 +797,7 @@ ShaderEditor::ShaderEditor() {
VSplitContainer *editor_box = memnew(VSplitContainer);
main_container->add_child(editor_box);
- editor_box->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ editor_box->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
editor_box->set_v_size_flags(SIZE_EXPAND_FILL);
editor_box->add_child(shader_editor);
@@ -889,12 +889,12 @@ void ShaderEditorPlugin::edit(Object *p_object) {
Ref<VisualShader> vs = es.shader;
if (vs.is_valid()) {
es.visual_shader_editor = memnew(VisualShaderEditor);
- es.visual_shader_editor->edit(vs.ptr());
shader_tabs->add_child(es.visual_shader_editor);
+ es.visual_shader_editor->edit(vs.ptr());
} else {
es.shader_editor = memnew(ShaderEditor);
- es.shader_editor->edit(s);
shader_tabs->add_child(es.shader_editor);
+ es.shader_editor->edit(s);
}
shader_tabs->set_current_tab(shader_tabs->get_tab_count() - 1);
edited_shaders.push_back(es);
diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp
index 1281ce0cfd..d4baff34e2 100644
--- a/editor/plugins/style_box_editor_plugin.cpp
+++ b/editor/plugins/style_box_editor_plugin.cpp
@@ -132,7 +132,7 @@ StyleBoxPreview::StyleBoxPreview() {
preview->set_clip_contents(true);
preview->connect("draw", callable_mp(this, &StyleBoxPreview::_redraw));
checkerboard->add_child(preview);
- preview->set_anchors_and_offsets_preset(PRESET_WIDE);
+ preview->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
add_margin_child(TTR("Preview:"), checkerboard);
grid_preview = memnew(TextureButton);
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 7ca65c073d..84caede081 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -524,7 +524,7 @@ TextEditor::TextEditor() {
code_editor->add_theme_constant_override("separation", 0);
code_editor->connect("load_theme_settings", callable_mp(this, &TextEditor::_load_theme_settings));
code_editor->connect("validate_script", callable_mp(this, &TextEditor::_validate_script));
- code_editor->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ code_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
code_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
code_editor->show_toggle_scripts_button();
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index 98e80c5513..f6b02d5f80 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -124,7 +124,7 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
texture_display = memnew(TextureRect);
texture_display->set_texture(p_texture);
- texture_display->set_anchors_preset(TextureRect::PRESET_WIDE);
+ texture_display->set_anchors_preset(TextureRect::PRESET_FULL_RECT);
texture_display->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
texture_display->set_ignore_texture_size(true);
add_child(texture_display);
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 129af1bb1d..afd29ae8e5 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -70,9 +70,17 @@ void ThemeItemImportTree::_update_items_tree() {
for (const StringName &E : types) {
String type_name = (String)E;
+ Ref<Texture2D> type_icon;
+ if (E == "") {
+ type_icon = get_theme_icon(SNAME("NodeDisabled"), SNAME("EditorIcons"));
+ } else {
+ type_icon = EditorNode::get_singleton()->get_class_icon(E, "NodeDisabled");
+ }
+
TreeItem *type_node = import_items_tree->create_item(root);
type_node->set_meta("_can_be_imported", false);
type_node->set_collapsed(true);
+ type_node->set_icon(0, type_icon);
type_node->set_text(0, type_name);
type_node->set_cell_mode(IMPORT_ITEM, TreeItem::CELL_MODE_CHECK);
type_node->set_checked(IMPORT_ITEM, false);
@@ -214,7 +222,7 @@ void ThemeItemImportTree::_update_items_tree() {
if (color_amount > 0) {
Array arr;
arr.push_back(color_amount);
- select_colors_label->set_text(TTRN("One color", "{num} colors", color_amount).format(arr, "{num}"));
+ select_colors_label->set_text(TTRN("1 color", "{num} colors", color_amount).format(arr, "{num}"));
select_all_colors_button->set_visible(true);
select_full_colors_button->set_visible(true);
deselect_all_colors_button->set_visible(true);
@@ -228,7 +236,7 @@ void ThemeItemImportTree::_update_items_tree() {
if (constant_amount > 0) {
Array arr;
arr.push_back(constant_amount);
- select_constants_label->set_text(TTRN("One constant", "{num} constants", constant_amount).format(arr, "{num}"));
+ select_constants_label->set_text(TTRN("1 constant", "{num} constants", constant_amount).format(arr, "{num}"));
select_all_constants_button->set_visible(true);
select_full_constants_button->set_visible(true);
deselect_all_constants_button->set_visible(true);
@@ -242,7 +250,7 @@ void ThemeItemImportTree::_update_items_tree() {
if (font_amount > 0) {
Array arr;
arr.push_back(font_amount);
- select_fonts_label->set_text(TTRN("One font", "{num} fonts", font_amount).format(arr, "{num}"));
+ select_fonts_label->set_text(TTRN("1 font", "{num} fonts", font_amount).format(arr, "{num}"));
select_all_fonts_button->set_visible(true);
select_full_fonts_button->set_visible(true);
deselect_all_fonts_button->set_visible(true);
@@ -256,7 +264,7 @@ void ThemeItemImportTree::_update_items_tree() {
if (font_size_amount > 0) {
Array arr;
arr.push_back(font_size_amount);
- select_font_sizes_label->set_text(TTRN("One font size", "{num} font sizes", font_size_amount).format(arr, "{num}"));
+ select_font_sizes_label->set_text(TTRN("1 font size", "{num} font sizes", font_size_amount).format(arr, "{num}"));
select_all_font_sizes_button->set_visible(true);
select_full_font_sizes_button->set_visible(true);
deselect_all_font_sizes_button->set_visible(true);
@@ -270,7 +278,7 @@ void ThemeItemImportTree::_update_items_tree() {
if (icon_amount > 0) {
Array arr;
arr.push_back(icon_amount);
- select_icons_label->set_text(TTRN("One icon", "{num} icons", icon_amount).format(arr, "{num}"));
+ select_icons_label->set_text(TTRN("1 icon", "{num} icons", icon_amount).format(arr, "{num}"));
select_all_icons_button->set_visible(true);
select_full_icons_button->set_visible(true);
deselect_all_icons_button->set_visible(true);
@@ -286,7 +294,7 @@ void ThemeItemImportTree::_update_items_tree() {
if (stylebox_amount > 0) {
Array arr;
arr.push_back(stylebox_amount);
- select_styleboxes_label->set_text(TTRN("One stylebox", "{num} styleboxes", stylebox_amount).format(arr, "{num}"));
+ select_styleboxes_label->set_text(TTRN("1 stylebox", "{num} styleboxes", stylebox_amount).format(arr, "{num}"));
select_all_styleboxes_button->set_visible(true);
select_full_styleboxes_button->set_visible(true);
deselect_all_styleboxes_button->set_visible(true);
@@ -1170,6 +1178,8 @@ ThemeItemImportTree::ThemeItemImportTree() {
import_add_selected_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_import_selected));
}
+///////////////////////
+
void ThemeItemEditorDialog::ok_pressed() {
if (import_default_theme_items->has_selected_items() || import_editor_theme_items->has_selected_items() || import_other_theme_items->has_selected_items()) {
confirm_closing_dialog->set_text(TTR("Import Items tab has some items selected. Selection will be lost upon closing this window.\nClose anyway?"));
@@ -1867,6 +1877,8 @@ void ThemeItemEditorDialog::_notification(int p_what) {
edit_items_remove_custom->set_icon(get_theme_icon(SNAME("ThemeRemoveCustomItems"), SNAME("EditorIcons")));
edit_items_remove_all->set_icon(get_theme_icon(SNAME("ThemeRemoveAllItems"), SNAME("EditorIcons")));
+ edit_add_type_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+
import_another_theme_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
} break;
}
@@ -1924,8 +1936,7 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito
edit_add_type_value->set_h_size_flags(Control::SIZE_EXPAND_FILL);
edit_add_type_value->connect("text_submitted", callable_mp(this, &ThemeItemEditorDialog::_add_theme_type));
edit_add_type_hb->add_child(edit_add_type_value);
- Button *edit_add_type_button = memnew(Button);
- edit_add_type_button->set_text(TTR("Add"));
+ edit_add_type_button = memnew(Button);
edit_add_type_hb->add_child(edit_add_type_button);
edit_add_type_button->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_add_theme_type), varray(""));
@@ -2017,7 +2028,7 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito
edit_items_tree->connect("button_clicked", callable_mp(this, &ThemeItemEditorDialog::_item_tree_button_pressed));
edit_items_message = memnew(Label);
- edit_items_message->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ edit_items_message->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
edit_items_message->set_mouse_filter(Control::MOUSE_FILTER_STOP);
edit_items_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
edit_items_message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
@@ -2099,6 +2110,8 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito
confirm_closing_dialog->connect("confirmed", callable_mp(this, &ThemeItemEditorDialog::_close_dialog));
}
+///////////////////////
+
void ThemeTypeDialog::_dialog_about_to_show() {
add_type_filter->set_text("");
add_type_filter->grab_focus();
@@ -2237,6 +2250,8 @@ ThemeTypeDialog::ThemeTypeDialog() {
add_child(add_type_confirmation);
}
+///////////////////////
+
VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) {
VBoxContainer *items_tab = memnew(VBoxContainer);
items_tab->set_custom_minimum_size(Size2(0, 160) * EDSCALE);
@@ -3454,6 +3469,8 @@ ThemeTypeEditor::ThemeTypeEditor() {
add_child(update_debounce_timer);
}
+///////////////////////
+
void ThemeEditor::edit(const Ref<Theme> &p_theme) {
if (theme == p_theme) {
return;
@@ -3672,6 +3689,8 @@ ThemeEditor::ThemeEditor() {
theme_type_editor->set_custom_minimum_size(Size2(280, 0) * EDSCALE);
}
+///////////////////////
+
void ThemeEditorPlugin::edit(Object *p_node) {
if (Object::cast_to<Theme>(p_node)) {
theme_editor->edit(Object::cast_to<Theme>(p_node));
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index 543113a5eb..9f89a047cb 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -198,6 +198,7 @@ class ThemeItemEditorDialog : public AcceptDialog {
Tree *edit_type_list = nullptr;
LineEdit *edit_add_type_value = nullptr;
+ Button *edit_add_type_button = nullptr;
String edited_item_type;
Button *edit_items_add_color = nullptr;
diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp
index 826631d750..0c7303dda4 100644
--- a/editor/plugins/theme_editor_preview.cpp
+++ b/editor/plugins/theme_editor_preview.cpp
@@ -246,7 +246,7 @@ ThemeEditorPreview::ThemeEditorPreview() {
preview_root->set_h_size_flags(SIZE_EXPAND_FILL);
preview_bg = memnew(ColorRect);
- preview_bg->set_anchors_and_offsets_preset(PRESET_WIDE);
+ preview_bg->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
preview_root->add_child(preview_bg);
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 3073c8a7f2..f119ada810 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -544,7 +544,7 @@ TileAtlasView::TileAtlasView() {
Panel *panel = memnew(Panel);
panel->set_clip_contents(true);
panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- panel->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
panel->set_h_size_flags(SIZE_EXPAND_FILL);
panel->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(panel);
@@ -613,32 +613,32 @@ TileAtlasView::TileAtlasView() {
background_left = memnew(Control);
background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- background_left->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ background_left->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
background_left->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_left));
base_tiles_root_control->add_child(background_left);
base_tiles_drawing_root = memnew(Control);
base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- base_tiles_drawing_root->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_tiles_drawing_root->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
base_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
base_tiles_root_control->add_child(base_tiles_drawing_root);
base_tiles_draw = memnew(Control);
base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- base_tiles_draw->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_tiles_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
base_tiles_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles));
base_tiles_drawing_root->add_child(base_tiles_draw);
base_tiles_texture_grid = memnew(Control);
base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- base_tiles_texture_grid->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_tiles_texture_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
base_tiles_texture_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_texture_grid));
base_tiles_drawing_root->add_child(base_tiles_texture_grid);
base_tiles_shape_grid = memnew(Control);
base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- base_tiles_shape_grid->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_tiles_shape_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));
base_tiles_drawing_root->add_child(base_tiles_shape_grid);
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 1263ee5758..a00e1ed9e8 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -803,13 +803,13 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
add_child(root);
panel = memnew(Panel);
- panel->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
root->add_child(panel);
base_control = memnew(Control);
base_control->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
- base_control->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
base_control->connect("draw", callable_mp(this, &GenericTilePolygonEditor::_base_control_draw));
base_control->connect("gui_input", callable_mp(this, &GenericTilePolygonEditor::_base_control_gui_input));
base_control->set_clip_contents(true);
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index deffa48615..20e548acba 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2370,7 +2370,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_data_editors_tree = memnew(Tree);
tile_data_editors_tree->set_hide_root(true);
- tile_data_editors_tree->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ tile_data_editors_tree->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
tile_data_editors_tree->set_h_scroll_enabled(false);
tile_data_editors_tree->set_v_scroll_enabled(false);
tile_data_editors_tree->connect("item_selected", callable_mp(this, &TileSetAtlasSourceEditor::_tile_data_editors_tree_selected));
@@ -2509,7 +2509,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view->add_control_over_atlas_tiles(tile_atlas_control);
tile_atlas_control_unscaled = memnew(Control);
- tile_atlas_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ tile_atlas_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
tile_atlas_control_unscaled->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw));
tile_atlas_view->add_control_over_atlas_tiles(tile_atlas_control_unscaled, false);
tile_atlas_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
@@ -2526,7 +2526,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control);
alternative_tiles_control_unscaled = memnew(Control);
- alternative_tiles_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ alternative_tiles_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
alternative_tiles_control_unscaled->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw));
tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control_unscaled, false);
alternative_tiles_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp
index ed13afc235..1c9afa8be8 100644
--- a/editor/progress_dialog.cpp
+++ b/editor/progress_dialog.cpp
@@ -50,7 +50,7 @@ void BackgroundProgress::_add_task(const String &p_task, const String &p_label,
Control *ec = memnew(Control);
ec->set_h_size_flags(SIZE_EXPAND_FILL);
ec->set_v_size_flags(SIZE_EXPAND_FILL);
- t.progress->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ t.progress->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
ec->add_child(t.progress);
ec->set_custom_minimum_size(Size2(80, 5) * EDSCALE);
t.hb->add_child(ec);
@@ -235,7 +235,7 @@ void ProgressDialog::_bind_methods() {
ProgressDialog::ProgressDialog() {
main = memnew(VBoxContainer);
add_child(main);
- main->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ main->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
set_exclusive(true);
last_progress_tick = 0;
singleton = this;
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index f7ef574205..a98578ad7d 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -2562,19 +2562,19 @@ ProjectManager::ProjectManager() {
EditorFileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files"));
- set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
set_theme(create_custom_theme());
- set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
Panel *panel = memnew(Panel);
add_child(panel);
- panel->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("Background"), SNAME("EditorStyles")));
VBoxContainer *vb = memnew(VBoxContainer);
panel->add_child(vb);
- vb->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 8 * EDSCALE);
+ vb->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 8 * EDSCALE);
Control *center_box = memnew(Control);
center_box->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -2582,7 +2582,7 @@ ProjectManager::ProjectManager() {
tabs = memnew(TabContainer);
center_box->add_child(tabs);
- tabs->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ tabs->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
tabs->connect("tab_changed", callable_mp(this, &ProjectManager::_on_tab_changed));
local_projects_hb = memnew(HBoxContainer);
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index 277ae14e0e..0693294316 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -1826,7 +1826,7 @@ CustomPropertyEditor::CustomPropertyEditor() {
text_edit = memnew(TextEdit);
value_vbox->add_child(text_edit);
- text_edit->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 5);
+ text_edit->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 5);
text_edit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
text_edit->set_offset(SIDE_BOTTOM, -30);
@@ -1882,12 +1882,12 @@ CustomPropertyEditor::CustomPropertyEditor() {
spinbox = memnew(SpinBox);
value_vbox->add_child(spinbox);
- spinbox->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 5);
+ spinbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 5);
spinbox->connect("value_changed", callable_mp(this, &CustomPropertyEditor::_range_modified));
slider = memnew(HSlider);
value_vbox->add_child(slider);
- slider->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 5);
+ slider->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 5);
slider->connect("value_changed", callable_mp(this, &CustomPropertyEditor::_range_modified));
action_hboxes = memnew(HBoxContainer);
diff --git a/editor/scene_create_dialog.cpp b/editor/scene_create_dialog.cpp
index 6a35b22210..94a5c07709 100644
--- a/editor/scene_create_dialog.cpp
+++ b/editor/scene_create_dialog.cpp
@@ -171,7 +171,7 @@ Node *SceneCreateDialog::create_scene_root() {
break;
case ROOT_USER_INTERFACE: {
Control *gui = memnew(Control);
- gui->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ gui->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
root = gui;
} break;
case ROOT_OTHER:
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index fb8be5db81..be37d50a2e 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1122,7 +1122,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
case TOOL_CREATE_USER_INTERFACE: {
Control *node = memnew(Control);
- node->set_anchors_and_offsets_preset(PRESET_WIDE); //more useful for resizable UIs.
+ node->set_anchors_and_offsets_preset(PRESET_FULL_RECT); //more useful for resizable UIs.
new_node = node;
} break;
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index b694c109e1..f975d95079 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -1262,7 +1262,7 @@ GridMapEditor::GridMapEditor() {
info_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
info_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
- info_message->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
+ info_message->set_anchors_and_offsets_preset(PRESET_FULL_RECT, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
mesh_library_palette->add_child(info_message);
edit_axis = Vector3::AXIS_Y;
diff --git a/modules/noise/editor/noise_editor_plugin.cpp b/modules/noise/editor/noise_editor_plugin.cpp
index 32c3f0aad4..27a86f45b5 100644
--- a/modules/noise/editor/noise_editor_plugin.cpp
+++ b/modules/noise/editor/noise_editor_plugin.cpp
@@ -54,7 +54,7 @@ public:
set_custom_minimum_size(Size2(0, EDSCALE * PREVIEW_HEIGHT));
_texture_rect = memnew(TextureRect);
- _texture_rect->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ _texture_rect->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
_texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_COVERED);
add_child(_texture_rect);
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index 8783e061d2..593d1ff3c1 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -35,7 +35,11 @@ if env["platform"] == "android":
# may need to include java parts of the openxr loader
elif env["platform"] == "linuxbsd":
- env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_LINUX", "XR_USE_PLATFORM_XLIB"])
+ env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_LINUX"])
+
+ if env["x11"]:
+ env_thirdparty.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_XLIB"])
+
# FIXME: Review what needs to be set for Android and macOS.
env_thirdparty.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
elif env["platform"] == "windows":
diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index d9419292ba..61ff56b62a 100644
--- a/modules/visual_script/editor/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -4628,7 +4628,7 @@ VisualScriptEditor::VisualScriptEditor() {
graph = memnew(GraphEdit);
add_child(graph);
graph->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- graph->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ graph->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
graph->set_show_zoom_label(true);
graph->connect("node_selected", callable_mp(this, &VisualScriptEditor::_node_selected));
graph->connect("begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move));
diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub
index 09a432eae2..636a3c7db2 100644
--- a/platform/linuxbsd/SCsub
+++ b/platform/linuxbsd/SCsub
@@ -12,7 +12,7 @@ common_linuxbsd = [
"freedesktop_screensaver.cpp",
]
-if "x11" in env and env["x11"]:
+if env["x11"]:
common_linuxbsd += [
"gl_manager_x11.cpp",
"detect_prime_x11.cpp",
@@ -20,13 +20,13 @@ if "x11" in env and env["x11"]:
"key_mapping_x11.cpp",
]
-if "speechd" in env and env["speechd"]:
- common_linuxbsd.append(["speechd-so_wrap.c", "tts_linux.cpp"])
+ if env["vulkan"]:
+ common_linuxbsd.append("vulkan_context_x11.cpp")
-if "vulkan" in env and env["vulkan"]:
- common_linuxbsd.append("vulkan_context_x11.cpp")
+if env["speechd"]:
+ common_linuxbsd.append(["speechd-so_wrap.c", "tts_linux.cpp"])
-if "udev" in env and env["udev"]:
+if env["udev"]:
common_linuxbsd.append("libudev-so_wrap.c")
prog = env.add_program("#bin/godot", ["godot_linuxbsd.cpp"] + common_linuxbsd)
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 19cf341c85..63f0b7b3b9 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -15,47 +15,11 @@ def can_build():
if os.name != "posix" or sys.platform == "darwin":
return False
- # Check the minimal dependencies
- x11_error = os.system("pkg-config --version > /dev/null")
- if x11_error:
+ pkgconf_error = os.system("pkg-config --version > /dev/null")
+ if pkgconf_error:
print("Error: pkg-config not found. Aborting.")
return False
- x11_error = os.system("pkg-config x11 --modversion > /dev/null")
- if x11_error:
- print("Error: X11 libraries not found. Aborting.")
- return False
-
- x11_error = os.system("pkg-config xcursor --modversion > /dev/null")
- if x11_error:
- print("Error: Xcursor library not found. Aborting.")
- return False
-
- x11_error = os.system("pkg-config xinerama --modversion > /dev/null")
- if x11_error:
- print("Error: Xinerama library not found. Aborting.")
- return False
-
- x11_error = os.system("pkg-config xext --modversion > /dev/null")
- if x11_error:
- print("Error: Xext library not found. Aborting.")
- return False
-
- x11_error = os.system("pkg-config xrandr --modversion > /dev/null")
- if x11_error:
- print("Error: XrandR library not found. Aborting.")
- return False
-
- x11_error = os.system("pkg-config xrender --modversion > /dev/null")
- if x11_error:
- print("Error: XRender library not found. Aborting.")
- return False
-
- x11_error = os.system("pkg-config xi --modversion > /dev/null")
- if x11_error:
- print("Error: Xi library not found. Aborting.")
- return False
-
return True
@@ -216,17 +180,17 @@ def configure(env):
env["AR"] = "gcc-ar"
env.Append(CCFLAGS=["-pipe"])
- env.Append(LINKFLAGS=["-pipe"])
## Dependencies
- env.ParseConfig("pkg-config x11 --cflags --libs")
- env.ParseConfig("pkg-config xcursor --cflags --libs")
- env.ParseConfig("pkg-config xinerama --cflags --libs")
- env.ParseConfig("pkg-config xext --cflags --libs")
- env.ParseConfig("pkg-config xrandr --cflags --libs")
- env.ParseConfig("pkg-config xrender --cflags --libs")
- env.ParseConfig("pkg-config xi --cflags --libs")
+ if env["x11"]:
+ env.ParseConfig("pkg-config x11 --cflags --libs")
+ env.ParseConfig("pkg-config xcursor --cflags --libs")
+ env.ParseConfig("pkg-config xinerama --cflags --libs")
+ env.ParseConfig("pkg-config xext --cflags --libs")
+ env.ParseConfig("pkg-config xrandr --cflags --libs")
+ env.ParseConfig("pkg-config xrender --cflags --libs")
+ env.ParseConfig("pkg-config xi --cflags --libs")
if env["touch"]:
env.Append(CPPDEFINES=["TOUCH_ENABLED"])
@@ -382,8 +346,9 @@ def configure(env):
# No pkgconfig file so far, hardcode expected lib name.
env.Append(LIBS=["glslang", "SPIRV"])
- env.Append(CPPDEFINES=["GLES3_ENABLED"])
- env.ParseConfig("pkg-config gl --cflags --libs")
+ if env["opengl3"]:
+ env.Append(CPPDEFINES=["GLES3_ENABLED"])
+ env.ParseConfig("pkg-config gl --cflags --libs")
env.Append(LIBS=["pthread"])
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index b0f87484b9..ecd7723993 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -1489,8 +1489,8 @@ void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window
XMoveResizeWindow(x11_display, wd.x11_window, position.x, position.y, size.x, size.y);
} else {
if (p_screen != window_get_current_screen(p_window)) {
- Point2i position = screen_get_position(p_screen);
- XMoveWindow(x11_display, wd.x11_window, position.x, position.y);
+ Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
+ window_set_position(ofs + screen_get_position(p_screen), p_window);
}
}
}
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 91d64b50f0..11474dac46 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -2008,6 +2008,10 @@ void DisplayServerOSX::window_set_current_screen(int p_screen, WindowID p_window
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
+ if (window_get_current_screen(p_window) == p_screen) {
+ return;
+ }
+
bool was_fullscreen = false;
if (wd.fullscreen) {
// Temporary exit fullscreen mode to move window.
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 91e0fbe0dc..f9988b23bc 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -3458,6 +3458,12 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
windows.erase(id);
ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Windows OS window.");
}
+ if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
+ wd.fullscreen = true;
+ if (p_mode == WINDOW_MODE_FULLSCREEN) {
+ wd.multiwindow_fs = true;
+ }
+ }
if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
wd.pre_fs_valid = true;
}
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 492a81f933..bfe5ee335b 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -1071,7 +1071,7 @@ void ColorPicker::_screen_pick_pressed() {
screen = memnew(Control);
r->add_child(screen);
screen->set_as_top_level(true);
- screen->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ screen->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
screen->set_default_cursor_shape(CURSOR_POINTING_HAND);
screen->connect("gui_input", callable_mp(this, &ColorPicker::_screen_input));
// It immediately toggles off in the first press otherwise.
@@ -1456,7 +1456,7 @@ void ColorPickerButton::_update_picker() {
popup = memnew(PopupPanel);
popup->set_wrap_controls(true);
picker = memnew(ColorPicker);
- picker->set_anchors_and_offsets_preset(PRESET_WIDE);
+ picker->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
popup->add_child(picker);
add_child(popup, false, INTERNAL_MODE_FRONT);
picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed));
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 15ff1d3ed6..686045901c 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -1642,7 +1642,7 @@ void Control::_set_anchors_layout_preset(int p_preset) {
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_HCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
set_offsets_preset(preset, LayoutPresetMode::PRESET_MODE_MINSIZE);
break;
}
@@ -1718,7 +1718,7 @@ int Control::_get_anchors_layout_preset() const {
}
if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
- return (int)LayoutPreset::PRESET_WIDE;
+ return (int)LayoutPreset::PRESET_FULL_RECT;
}
// Does not match any preset, return "Custom".
@@ -1737,7 +1737,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) {
case PRESET_BOTTOM_WIDE:
case PRESET_LEFT_WIDE:
case PRESET_HCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
set_anchor(SIDE_LEFT, ANCHOR_BEGIN, p_keep_offsets);
break;
@@ -1765,7 +1765,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) {
case PRESET_RIGHT_WIDE:
case PRESET_TOP_WIDE:
case PRESET_VCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
set_anchor(SIDE_TOP, ANCHOR_BEGIN, p_keep_offsets);
break;
@@ -1807,7 +1807,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) {
case PRESET_RIGHT_WIDE:
case PRESET_BOTTOM_WIDE:
case PRESET_HCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
set_anchor(SIDE_RIGHT, ANCHOR_END, p_keep_offsets);
break;
}
@@ -1835,7 +1835,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) {
case PRESET_RIGHT_WIDE:
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
set_anchor(SIDE_BOTTOM, ANCHOR_END, p_keep_offsets);
break;
}
@@ -1870,7 +1870,7 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_WIDE:
case PRESET_LEFT_WIDE:
case PRESET_HCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
data.offset[0] = x * (0.0 - data.anchor[0]) + p_margin + parent_rect.position.x;
break;
@@ -1898,7 +1898,7 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_RIGHT_WIDE:
case PRESET_TOP_WIDE:
case PRESET_VCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
data.offset[1] = parent_rect.size.y * (0.0 - data.anchor[1]) + p_margin + parent_rect.position.y;
break;
@@ -1940,7 +1940,7 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_RIGHT_WIDE:
case PRESET_BOTTOM_WIDE:
case PRESET_HCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
data.offset[2] = x * (1.0 - data.anchor[2]) - p_margin + parent_rect.position.x;
break;
}
@@ -1968,7 +1968,7 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_RIGHT_WIDE:
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
data.offset[3] = parent_rect.size.y * (1.0 - data.anchor[3]) - p_margin + parent_rect.position.y;
break;
}
@@ -2003,7 +2003,7 @@ void Control::set_grow_direction_preset(LayoutPreset p_preset) {
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_HCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
set_h_grow_direction(GrowDirection::GROW_DIRECTION_BOTH);
break;
}
@@ -2031,7 +2031,7 @@ void Control::set_grow_direction_preset(LayoutPreset p_preset) {
case PRESET_RIGHT_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_HCENTER_WIDE:
- case PRESET_WIDE:
+ case PRESET_FULL_RECT:
set_v_grow_direction(GrowDirection::GROW_DIRECTION_BOTH);
break;
}
@@ -3300,7 +3300,7 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode");
ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION);
- const String anchors_presets_options = "Custom:-1,PresetWide:15,"
+ const String anchors_presets_options = "Custom:-1,PresetFullRect:15,"
"PresetTopLeft:0,PresetTopRight:1,PresetBottomRight:3,PresetBottomLeft:2,"
"PresetCenterLeft:4,PresetCenterTop:5,PresetCenterRight:6,PresetCenterBottom:7,PresetCenter:8,"
"PresetLeftWide:9,PresetTopWide:10,PresetRightWide:11,PresetBottomWide:12,PresetVCenterWide:13,PresetHCenterWide:14";
@@ -3408,7 +3408,7 @@ void Control::_bind_methods() {
BIND_ENUM_CONSTANT(PRESET_BOTTOM_WIDE);
BIND_ENUM_CONSTANT(PRESET_VCENTER_WIDE);
BIND_ENUM_CONSTANT(PRESET_HCENTER_WIDE);
- BIND_ENUM_CONSTANT(PRESET_WIDE);
+ BIND_ENUM_CONSTANT(PRESET_FULL_RECT);
BIND_ENUM_CONSTANT(PRESET_MODE_MINSIZE);
BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_WIDTH);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index f18dd99bff..50cf9faeed 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -116,7 +116,7 @@ public:
PRESET_BOTTOM_WIDE,
PRESET_VCENTER_WIDE,
PRESET_HCENTER_WIDE,
- PRESET_WIDE
+ PRESET_FULL_RECT
};
enum LayoutPresetMode {
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 8ec930b753..65bc359e2e 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -1060,7 +1060,7 @@ FileDialog::FileDialog() {
message = memnew(Label);
message->hide();
- message->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ message->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
tree->add_child(message);
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index c219eafbf7..e30759aa3e 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -2376,7 +2376,7 @@ GraphEdit::GraphEdit() {
top_layer = memnew(GraphEditFilter(this));
add_child(top_layer, false, INTERNAL_MODE_BACK);
top_layer->set_mouse_filter(MOUSE_FILTER_PASS);
- top_layer->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ top_layer->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
top_layer->connect("draw", callable_mp(this, &GraphEdit::_top_layer_draw));
top_layer->connect("gui_input", callable_mp(this, &GraphEdit::_top_layer_input));
top_layer->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 84f2ddf700..8094812203 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -31,6 +31,7 @@
#include "label.h"
#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
@@ -64,7 +65,7 @@ bool Label::is_uppercase() const {
}
int Label::get_line_height(int p_line) const {
- Ref<Font> font = get_theme_font(SNAME("font"));
+ Ref<Font> font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font"));
if (p_line >= 0 && p_line < lines_rid.size()) {
return TS->shaped_text_get_size(lines_rid[p_line]).y;
} else if (lines_rid.size() > 0) {
@@ -74,7 +75,8 @@ int Label::get_line_height(int p_line) const {
}
return h;
} else {
- return font->get_height(get_theme_font_size(SNAME("font_size")));
+ int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size"));
+ return font->get_height(font_size);
}
}
@@ -91,8 +93,8 @@ void Label::_shape() {
} else {
TS->shaped_text_set_direction(text_rid, (TextServer::Direction)text_direction);
}
- const Ref<Font> &font = get_theme_font(SNAME("font"));
- int font_size = get_theme_font_size(SNAME("font_size"));
+ const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font"));
+ int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size"));
ERR_FAIL_COND(font.is_null());
String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;
if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) {
@@ -223,9 +225,8 @@ void Label::_shape() {
}
void Label::_update_visible() {
- int line_spacing = get_theme_constant(SNAME("line_spacing"), SNAME("Label"));
+ int line_spacing = settings.is_valid() ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing"), SNAME("Label"));
Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label"));
- Ref<Font> font = get_theme_font(SNAME("font"));
int lines_visible = lines_rid.size();
if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
@@ -295,17 +296,19 @@ void Label::_notification(int p_what) {
RID ci = get_canvas_item();
+ bool has_settings = settings.is_valid();
+
Size2 string_size;
Size2 size = get_size();
Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
- Ref<Font> font = get_theme_font(SNAME("font"));
- Color font_color = get_theme_color(SNAME("font_color"));
- Color font_shadow_color = get_theme_color(SNAME("font_shadow_color"));
- Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
- int line_spacing = get_theme_constant(SNAME("line_spacing"));
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
- int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
+ Ref<Font> font = (has_settings && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font"));
+ Color font_color = has_settings ? settings->get_font_color() : get_theme_color(SNAME("font_color"));
+ Color font_shadow_color = has_settings ? settings->get_shadow_color() : get_theme_color(SNAME("font_shadow_color"));
+ Point2 shadow_ofs = has_settings ? settings->get_shadow_offset() : Point2(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
+ int line_spacing = has_settings ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing"));
+ Color font_outline_color = has_settings ? settings->get_outline_color() : get_theme_color(SNAME("font_outline_color"));
+ int outline_size = has_settings ? settings->get_outline_size() : get_theme_constant(SNAME("outline_size"));
+ int shadow_outline_size = has_settings ? settings->get_shadow_size() : get_theme_constant(SNAME("shadow_outline_size"));
bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL);
bool rtl_layout = is_layout_rtl();
@@ -552,8 +555,10 @@ Size2 Label::get_minimum_size() const {
Size2 min_size = minsize;
- Ref<Font> font = get_theme_font(SNAME("font"));
- min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size(SNAME("font_size"))));
+ const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font"));
+ int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size"));
+
+ min_size.height = MAX(min_size.height, font->get_height(font_size) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM));
Size2 min_style = get_theme_stylebox(SNAME("normal"))->get_minimum_size();
if (autowrap_mode != TextServer::AUTOWRAP_OFF) {
@@ -578,9 +583,8 @@ int Label::get_line_count() const {
}
int Label::get_visible_line_count() const {
- Ref<Font> font = get_theme_font(SNAME("font"));
Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
- int line_spacing = get_theme_constant(SNAME("line_spacing"));
+ int line_spacing = settings.is_valid() ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing"));
int lines_visible = 0;
float total_h = 0.0;
for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
@@ -641,6 +645,28 @@ void Label::set_text(const String &p_string) {
update_minimum_size();
}
+void Label::_invalidate() {
+ font_dirty = true;
+ update();
+}
+
+void Label::set_label_settings(const Ref<LabelSettings> &p_settings) {
+ if (settings != p_settings) {
+ if (settings.is_valid()) {
+ settings->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate));
+ }
+ settings = p_settings;
+ if (settings.is_valid()) {
+ settings->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+ _invalidate();
+ }
+}
+
+Ref<LabelSettings> Label::get_label_settings() const {
+ return settings;
+}
+
void Label::set_text_direction(Control::TextDirection p_text_direction) {
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (text_direction != p_text_direction) {
@@ -804,6 +830,8 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &Label::get_vertical_alignment);
ClassDB::bind_method(D_METHOD("set_text", "text"), &Label::set_text);
ClassDB::bind_method(D_METHOD("get_text"), &Label::get_text);
+ ClassDB::bind_method(D_METHOD("set_label_settings", "settings"), &Label::set_label_settings);
+ ClassDB::bind_method(D_METHOD("get_label_settings"), &Label::get_label_settings);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label::set_text_direction);
ClassDB::bind_method(D_METHOD("get_text_direction"), &Label::get_text_direction);
ClassDB::bind_method(D_METHOD("set_language", "language"), &Label::set_language);
@@ -836,6 +864,7 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label::get_structured_text_bidi_override_options);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "label_settings", PROPERTY_HINT_RESOURCE_TYPE, "LabelSettings"), "set_label_settings", "get_label_settings");
ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
diff --git a/scene/gui/label.h b/scene/gui/label.h
index a59d35950d..3734fce1bb 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -32,6 +32,7 @@
#define LABEL_H
#include "scene/gui/control.h"
+#include "scene/resources/label_settings.h"
class Label : public Control {
GDCLASS(Label, Control);
@@ -65,8 +66,11 @@ private:
int lines_skipped = 0;
int max_lines_visible = -1;
+ Ref<LabelSettings> settings;
+
void _update_visible();
void _shape();
+ void _invalidate();
protected:
void _notification(int p_what);
@@ -85,6 +89,9 @@ public:
void set_text(const String &p_string);
String get_text() const;
+ void set_label_settings(const Ref<LabelSettings> &p_settings);
+ Ref<LabelSettings> get_label_settings() const;
+
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 3a31246b17..928bab8842 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -1909,7 +1909,7 @@ void PopupMenu::popup(const Rect2 &p_bounds) {
PopupMenu::PopupMenu() {
// Margin Container
margin_container = memnew(MarginContainer);
- margin_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ margin_container->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
add_child(margin_container, false, INTERNAL_MODE_FRONT);
margin_container->connect("draw", callable_mp(this, &PopupMenu::_draw_background));
@@ -1921,7 +1921,7 @@ PopupMenu::PopupMenu() {
// The control which will display the items
control = memnew(Control);
control->set_clip_contents(false);
- control->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
control->set_h_size_flags(Control::SIZE_EXPAND_FILL);
control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
scroll_container->add_child(control, false, INTERNAL_MODE_FRONT);
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 7924bb13e9..a4733c455f 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -319,7 +319,7 @@ SpinBox::SpinBox() {
line_edit = memnew(LineEdit);
add_child(line_edit, false, INTERNAL_MODE_FRONT);
- line_edit->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ line_edit->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
line_edit->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 08db07c425..12f91a9873 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -233,7 +233,7 @@ void TabContainer::_repaint() {
if (i == current) {
c->show();
- c->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ c->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
if (tabs_visible) {
c->set_offset(SIDE_TOP, _get_top_margin());
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index f43a91b85f..2c4cba4954 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -5018,7 +5018,7 @@ Tree::Tree() {
popup_editor_vb = memnew(VBoxContainer);
popup_editor->add_child(popup_editor_vb);
popup_editor_vb->add_theme_constant_override("separation", 0);
- popup_editor_vb->set_anchors_and_offsets_preset(PRESET_WIDE);
+ popup_editor_vb->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
text_editor = memnew(LineEdit);
popup_editor_vb->add_child(text_editor);
text_editor->set_v_size_flags(SIZE_EXPAND_FILL);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 0031abd953..c2fa1ace8d 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1215,7 +1215,7 @@ void Viewport::_gui_show_tooltip() {
panel->connect("mouse_entered", callable_mp(this, &Viewport::_gui_cancel_tooltip));
}
- base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
panel->set_transient(true);
panel->set_flag(Window::FLAG_NO_FOCUS, true);
diff --git a/scene/multiplayer/multiplayer_spawner.cpp b/scene/multiplayer/multiplayer_spawner.cpp
index ddd01d0a43..8363d05e54 100644
--- a/scene/multiplayer/multiplayer_spawner.cpp
+++ b/scene/multiplayer/multiplayer_spawner.cpp
@@ -71,7 +71,7 @@ bool MultiplayerSpawner::_get(const StringName &p_name, Variant &r_ret) const {
}
void MultiplayerSpawner::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::INT, "_spawnable_scene_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Scenes,scenes/"));
+ p_list->push_back(PropertyInfo(Variant::INT, "_spawnable_scene_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Auto Spawn List,scenes/"));
List<String> exts;
ResourceLoader::get_recognized_extensions_for_type("PackedScene", &exts);
String ext_hint;
@@ -144,10 +144,6 @@ void MultiplayerSpawner::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit);
ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit");
- ClassDB::bind_method(D_METHOD("set_auto_spawning", "enabled"), &MultiplayerSpawner::set_auto_spawning);
- ClassDB::bind_method(D_METHOD("is_auto_spawning"), &MultiplayerSpawner::is_auto_spawning);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_spawn"), "set_auto_spawning", "is_auto_spawning");
-
GDVIRTUAL_BIND(_spawn_custom, "data");
ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
@@ -169,7 +165,7 @@ void MultiplayerSpawner::_update_spawn_node() {
Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path);
if (node) {
spawn_node = node->get_instance_id();
- if (auto_spawn) {
+ if (get_spawnable_scene_count() && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom)) {
node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
}
} else {
@@ -221,15 +217,6 @@ void MultiplayerSpawner::_node_added(Node *p_node) {
_track(p_node, Variant(), id);
}
-void MultiplayerSpawner::set_auto_spawning(bool p_enabled) {
- auto_spawn = p_enabled;
- _update_spawn_node();
-}
-
-bool MultiplayerSpawner::is_auto_spawning() const {
- return auto_spawn;
-}
-
NodePath MultiplayerSpawner::get_spawn_path() const {
return spawn_path;
}
diff --git a/scene/multiplayer/multiplayer_spawner.h b/scene/multiplayer/multiplayer_spawner.h
index e8abe702a0..2c0eb9a2f0 100644
--- a/scene/multiplayer/multiplayer_spawner.h
+++ b/scene/multiplayer/multiplayer_spawner.h
@@ -69,7 +69,6 @@ private:
ObjectID spawn_node;
HashMap<ObjectID, SpawnInfo> tracked_nodes;
- bool auto_spawn = false;
uint32_t spawn_limit = 0;
void _update_spawn_node();
@@ -102,8 +101,6 @@ public:
void set_spawn_path(const NodePath &p_path);
uint32_t get_spawn_limit() const { return spawn_limit; }
void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; }
- bool is_auto_spawning() const;
- void set_auto_spawning(bool p_enabled);
const Variant get_spawn_argument(const ObjectID &p_id) const;
int find_spawnable_scene_index_from_object(const ObjectID &p_id) const;
diff --git a/scene/multiplayer/multiplayer_synchronizer.cpp b/scene/multiplayer/multiplayer_synchronizer.cpp
index 68f6e54fa8..e1b7433968 100644
--- a/scene/multiplayer/multiplayer_synchronizer.cpp
+++ b/scene/multiplayer/multiplayer_synchronizer.cpp
@@ -43,6 +43,11 @@ Object *MultiplayerSynchronizer::_get_prop_target(Object *p_obj, const NodePath
}
void MultiplayerSynchronizer::_stop() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
if (node) {
get_multiplayer()->replication_stop(node, this);
@@ -50,9 +55,42 @@ void MultiplayerSynchronizer::_stop() {
}
void MultiplayerSynchronizer::_start() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
if (node) {
get_multiplayer()->replication_start(node, this);
+ _update_process();
+ }
+}
+
+void MultiplayerSynchronizer::_update_process() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (!node) {
+ return;
+ }
+ set_process_internal(false);
+ set_physics_process_internal(false);
+ if (!visibility_filters.size()) {
+ return;
+ }
+ switch (visibility_update_mode) {
+ case VISIBILITY_PROCESS_IDLE:
+ set_process_internal(true);
+ break;
+ case VISIBILITY_PROCESS_PHYSICS:
+ set_physics_process_internal(true);
+ break;
+ case VISIBILITY_PROCESS_NONE:
+ break;
}
}
@@ -85,6 +123,66 @@ Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Obj
return OK;
}
+bool MultiplayerSynchronizer::is_visibility_public() const {
+ return peer_visibility.has(0);
+}
+
+void MultiplayerSynchronizer::set_visibility_public(bool p_visible) {
+ set_visibility_for(0, p_visible);
+}
+
+bool MultiplayerSynchronizer::is_visible_to(int p_peer) {
+ if (visibility_filters.size()) {
+ Variant arg = p_peer;
+ const Variant *argv[1] = { &arg };
+ for (Callable filter : visibility_filters) {
+ Variant ret;
+ Callable::CallError err;
+ filter.call(argv, 1, ret, err);
+ ERR_FAIL_COND_V(err.error != Callable::CallError::CALL_OK || ret.get_type() != Variant::BOOL, false);
+ if (!ret.operator bool()) {
+ return false;
+ }
+ }
+ }
+ return peer_visibility.has(0) || peer_visibility.has(p_peer);
+}
+
+void MultiplayerSynchronizer::add_visibility_filter(Callable p_callback) {
+ visibility_filters.insert(p_callback);
+ _update_process();
+}
+
+void MultiplayerSynchronizer::remove_visibility_filter(Callable p_callback) {
+ visibility_filters.erase(p_callback);
+ _update_process();
+}
+
+void MultiplayerSynchronizer::set_visibility_for(int p_peer, bool p_visible) {
+ if (peer_visibility.has(p_peer) == p_visible) {
+ return;
+ }
+ if (p_visible) {
+ peer_visibility.insert(p_peer);
+ } else {
+ peer_visibility.erase(p_peer);
+ }
+ update_visibility(p_peer);
+}
+
+bool MultiplayerSynchronizer::get_visibility_for(int p_peer) const {
+ return peer_visibility.has(p_peer);
+}
+
+void MultiplayerSynchronizer::set_visibility_update_mode(VisibilityUpdateMode p_mode) {
+ visibility_update_mode = p_mode;
+ _update_process();
+}
+
+MultiplayerSynchronizer::VisibilityUpdateMode MultiplayerSynchronizer::get_visibility_update_mode() const {
+ return visibility_update_mode;
+}
+
void MultiplayerSynchronizer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_path", "path"), &MultiplayerSynchronizer::set_root_path);
ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerSynchronizer::get_root_path);
@@ -95,9 +193,29 @@ void MultiplayerSynchronizer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_replication_config", "config"), &MultiplayerSynchronizer::set_replication_config);
ClassDB::bind_method(D_METHOD("get_replication_config"), &MultiplayerSynchronizer::get_replication_config);
+ ClassDB::bind_method(D_METHOD("set_visibility_update_mode", "mode"), &MultiplayerSynchronizer::set_visibility_update_mode);
+ ClassDB::bind_method(D_METHOD("get_visibility_update_mode"), &MultiplayerSynchronizer::get_visibility_update_mode);
+ ClassDB::bind_method(D_METHOD("update_visibility", "for_peer"), &MultiplayerSynchronizer::update_visibility, DEFVAL(0));
+
+ ClassDB::bind_method(D_METHOD("set_visibility_public", "visible"), &MultiplayerSynchronizer::set_visibility_public);
+ ClassDB::bind_method(D_METHOD("is_visibility_public"), &MultiplayerSynchronizer::is_visibility_public);
+
+ ClassDB::bind_method(D_METHOD("add_visibility_filter", "filter"), &MultiplayerSynchronizer::add_visibility_filter);
+ ClassDB::bind_method(D_METHOD("remove_visibility_filter", "filter"), &MultiplayerSynchronizer::remove_visibility_filter);
+ ClassDB::bind_method(D_METHOD("set_visibility_for", "peer", "visible"), &MultiplayerSynchronizer::set_visibility_for);
+ ClassDB::bind_method(D_METHOD("get_visibility_for", "peer"), &MultiplayerSynchronizer::get_visibility_for);
+
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "replication_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_replication_interval", "get_replication_interval");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig"), "set_replication_config", "get_replication_config");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig", PROPERTY_USAGE_NO_EDITOR), "set_replication_config", "get_replication_config");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,None"), "set_visibility_update_mode", "get_visibility_update_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "public_visibility"), "set_visibility_public", "is_visibility_public");
+
+ BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_IDLE);
+ BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_PHYSICS);
+ BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_NONE);
+
+ ADD_SIGNAL(MethodInfo("visibility_changed", PropertyInfo(Variant::INT, "for_peer")));
}
void MultiplayerSynchronizer::_notification(int p_what) {
@@ -118,6 +236,11 @@ void MultiplayerSynchronizer::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
_stop();
} break;
+
+ case NOTIFICATION_INTERNAL_PROCESS:
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ update_visibility(0);
+ } break;
}
}
@@ -142,6 +265,18 @@ Ref<SceneReplicationConfig> MultiplayerSynchronizer::get_replication_config() {
return replication_config;
}
+void MultiplayerSynchronizer::update_visibility(int p_for_peer) {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (node && get_multiplayer()->has_multiplayer_peer() && is_multiplayer_authority()) {
+ emit_signal(SNAME("visibility_changed"), p_for_peer);
+ }
+}
+
void MultiplayerSynchronizer::set_root_path(const NodePath &p_path) {
_stop();
root_path = p_path;
@@ -162,3 +297,8 @@ void MultiplayerSynchronizer::set_multiplayer_authority(int p_peer_id, bool p_re
Node::set_multiplayer_authority(p_peer_id, p_recursive);
get_multiplayer()->replication_start(node, this);
}
+
+MultiplayerSynchronizer::MultiplayerSynchronizer() {
+ // Publicly visible by default.
+ peer_visibility.insert(0);
+}
diff --git a/scene/multiplayer/multiplayer_synchronizer.h b/scene/multiplayer/multiplayer_synchronizer.h
index f61ef459da..59f02b84c1 100644
--- a/scene/multiplayer/multiplayer_synchronizer.h
+++ b/scene/multiplayer/multiplayer_synchronizer.h
@@ -38,14 +38,25 @@
class MultiplayerSynchronizer : public Node {
GDCLASS(MultiplayerSynchronizer, Node);
+public:
+ enum VisibilityUpdateMode {
+ VISIBILITY_PROCESS_IDLE,
+ VISIBILITY_PROCESS_PHYSICS,
+ VISIBILITY_PROCESS_NONE,
+ };
+
private:
Ref<SceneReplicationConfig> replication_config;
NodePath root_path = NodePath(".."); // Start with parent, like with AnimationPlayer.
uint64_t interval_msec = 0;
+ VisibilityUpdateMode visibility_update_mode = VISIBILITY_PROCESS_IDLE;
+ HashSet<Callable> visibility_filters;
+ HashSet<int> peer_visibility;
static Object *_get_prop_target(Object *p_obj, const NodePath &p_prop);
void _start();
void _stop();
+ void _update_process();
protected:
static void _bind_methods();
@@ -66,7 +77,19 @@ public:
NodePath get_root_path() const;
virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true) override;
- MultiplayerSynchronizer() {}
+ bool is_visibility_public() const;
+ void set_visibility_public(bool p_public);
+ bool is_visible_to(int p_peer);
+ void set_visibility_for(int p_peer, bool p_visible);
+ bool get_visibility_for(int p_peer) const;
+ void update_visibility(int p_for_peer);
+ void set_visibility_update_mode(VisibilityUpdateMode p_mode);
+ void add_visibility_filter(Callable p_callback);
+ void remove_visibility_filter(Callable p_callback);
+ VisibilityUpdateMode get_visibility_update_mode() const;
+
+ MultiplayerSynchronizer();
};
+VARIANT_ENUM_CAST(MultiplayerSynchronizer::VisibilityUpdateMode);
#endif // MULTIPLAYER_SYNCHRONIZER_H
diff --git a/scene/multiplayer/scene_cache_interface.cpp b/scene/multiplayer/scene_cache_interface.cpp
index 7c271341db..79a7dc2d5a 100644
--- a/scene/multiplayer/scene_cache_interface.cpp
+++ b/scene/multiplayer/scene_cache_interface.cpp
@@ -187,18 +187,29 @@ bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) {
return F->value;
}
-bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) {
+int SceneCacheInterface::make_object_cache(Object *p_obj) {
Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V(!node, false);
+ ERR_FAIL_COND_V(!node, -1);
+ NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path());
// See if the path is cached.
- PathSentCache *psc = path_send_cache.getptr(p_path);
+ PathSentCache *psc = path_send_cache.getptr(for_path);
if (!psc) {
// Path is not cached, create.
- path_send_cache[p_path] = PathSentCache();
- psc = path_send_cache.getptr(p_path);
+ path_send_cache[for_path] = PathSentCache();
+ psc = path_send_cache.getptr(for_path);
psc->id = last_send_cache_id++;
}
- r_id = psc->id;
+ return psc->id;
+}
+
+bool SceneCacheInterface::send_object_cache(Object *p_obj, int p_peer_id, int &r_id) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node, false);
+
+ r_id = make_object_cache(p_obj);
+ ERR_FAIL_COND_V(r_id < 0, false);
+ NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path());
+ PathSentCache *psc = path_send_cache.getptr(for_path);
bool has_all_peers = true;
List<int> peers_to_add; // If one is missing, take note to add it.
@@ -233,7 +244,7 @@ bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int
}
if (peers_to_add.size()) {
- _send_confirm_path(node, p_path, psc, peers_to_add);
+ _send_confirm_path(node, for_path, psc, peers_to_add);
}
return has_all_peers;
diff --git a/scene/multiplayer/scene_cache_interface.h b/scene/multiplayer/scene_cache_interface.h
index 3116233b5b..6bfd683cf4 100644
--- a/scene/multiplayer/scene_cache_interface.h
+++ b/scene/multiplayer/scene_cache_interface.h
@@ -72,7 +72,8 @@ public:
virtual void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) override;
// Returns true if all peers have cached path.
- virtual bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id) override;
+ virtual bool send_object_cache(Object *p_obj, int p_target, int &p_id) override;
+ virtual int make_object_cache(Object *p_obj) override;
virtual Object *get_cached_object(int p_from, uint32_t p_cache_id) override;
virtual bool is_cache_confirmed(NodePath p_path, int p_peer) override;
diff --git a/scene/multiplayer/scene_replication_interface.cpp b/scene/multiplayer/scene_replication_interface.cpp
index e4715ceb88..c616c5bb85 100644
--- a/scene/multiplayer/scene_replication_interface.cpp
+++ b/scene/multiplayer/scene_replication_interface.cpp
@@ -60,14 +60,13 @@ void SceneReplicationInterface::on_peer_change(int p_id, bool p_connected) {
if (p_connected) {
rep_state->on_peer_change(p_id, p_connected);
for (const ObjectID &oid : rep_state->get_spawned_nodes()) {
- _send_spawn(rep_state->get_node(oid), rep_state->get_spawner(oid), p_id);
+ _update_spawn_visibility(p_id, oid);
}
- for (const ObjectID &oid : rep_state->get_path_only_nodes()) {
- Node *node = rep_state->get_node(oid);
+ for (const ObjectID &oid : rep_state->get_synced_nodes()) {
MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
- ERR_CONTINUE(!node || !sync);
+ ERR_CONTINUE(!sync); // ERR_BUG
if (sync->is_multiplayer_authority()) {
- rep_state->peer_add_node(p_id, oid);
+ _update_sync_visibility(p_id, oid);
}
}
} else {
@@ -97,7 +96,13 @@ Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
ERR_FAIL_COND_V(!spawner, ERR_INVALID_PARAMETER);
Error err = rep_state->config_add_spawn(node, spawner);
ERR_FAIL_COND_V(err != OK, err);
- return _send_spawn(node, spawner, 0);
+ const ObjectID oid = node->get_instance_id();
+ if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
+ rep_state->ensure_net_id(oid);
+ _update_spawn_visibility(0, oid);
+ }
+ ERR_FAIL_COND_V(err != OK, err);
+ return OK;
}
Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
@@ -105,9 +110,19 @@ Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object());
ERR_FAIL_COND_V(!p_obj || !spawner, ERR_INVALID_PARAMETER);
- Error err = rep_state->config_del_spawn(node, spawner);
- ERR_FAIL_COND_V(err != OK, err);
- return _send_despawn(node, 0);
+ // Forcibly despawn to all peers that knowns me.
+ int len = 0;
+ Error err = _make_despawn_packet(node, len);
+ ERR_FAIL_COND_V(err != OK, ERR_BUG);
+ const ObjectID oid = p_obj->get_instance_id();
+ for (int pid : rep_state->get_peers()) {
+ if (!rep_state->is_peer_spawn(pid, oid)) {
+ continue;
+ }
+ _send_raw(packet_cache.ptr(), len, pid, true);
+ }
+ // Also remove spawner tracking from the replication state.
+ return rep_state->config_del_spawn(node, spawner);
}
Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_config) {
@@ -115,7 +130,15 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c
ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
+
+ // Add to synchronizer list and setup visibility.
rep_state->config_add_sync(node, sync);
+ const ObjectID oid = node->get_instance_id();
+ sync->connect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed), varray(oid));
+ if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) {
+ _update_sync_visibility(0, oid);
+ }
+
// Try to apply initial state if spawning (hack to apply if before ready).
if (pending_spawn == p_obj->get_instance_id()) {
pending_spawn = ObjectID(); // Make sure this only happens once.
@@ -127,9 +150,6 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c
ERR_FAIL_COND_V(err, err);
err = MultiplayerSynchronizer::set_state(props, node, vars);
ERR_FAIL_COND_V(err, err);
- } else if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) {
- // Either it's a spawn or a static sync, in any case add it to the list of known nodes.
- rep_state->peer_add_node(0, p_obj->get_instance_id());
}
return OK;
}
@@ -138,10 +158,103 @@ Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_co
Node *node = Object::cast_to<Node>(p_obj);
ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
- ERR_FAIL_COND_V(!p_obj || !sync, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
+ sync->disconnect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed));
return rep_state->config_del_sync(node, sync);
}
+void SceneReplicationInterface::_visibility_changed(int p_peer, ObjectID p_oid) {
+ if (rep_state->is_spawned_node(p_oid)) {
+ _update_spawn_visibility(p_peer, p_oid);
+ }
+ if (rep_state->is_synced_node(p_oid)) {
+ _update_sync_visibility(p_peer, p_oid);
+ }
+}
+
+Error SceneReplicationInterface::_update_sync_visibility(int p_peer, const ObjectID &p_oid) {
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid);
+ ERR_FAIL_COND_V(!sync || !sync->is_multiplayer_authority(), ERR_BUG);
+ bool is_visible = sync->is_visible_to(p_peer);
+ if (p_peer == 0) {
+ for (int pid : rep_state->get_peers()) {
+ // Might be visible to this specific peer.
+ is_visible = is_visible || sync->is_visible_to(pid);
+ if (rep_state->is_peer_sync(pid, p_oid) == is_visible) {
+ continue;
+ }
+ if (is_visible) {
+ rep_state->peer_add_sync(pid, p_oid);
+ } else {
+ rep_state->peer_del_sync(pid, p_oid);
+ }
+ }
+ return OK;
+ } else {
+ if (is_visible == rep_state->is_peer_sync(p_peer, p_oid)) {
+ return OK;
+ }
+ if (is_visible) {
+ return rep_state->peer_add_sync(p_peer, p_oid);
+ } else {
+ return rep_state->peer_del_sync(p_peer, p_oid);
+ }
+ }
+}
+
+Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const ObjectID &p_oid) {
+ MultiplayerSpawner *spawner = rep_state->get_spawner(p_oid);
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid);
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_oid));
+ ERR_FAIL_COND_V(!node || !spawner || !spawner->is_multiplayer_authority(), ERR_BUG);
+ bool is_visible = !sync || sync->is_visible_to(p_peer);
+ // Spawn (and despawn) when needed.
+ HashSet<int> to_spawn;
+ HashSet<int> to_despawn;
+ if (p_peer) {
+ if (is_visible == rep_state->is_peer_spawn(p_peer, p_oid)) {
+ return OK;
+ }
+ if (is_visible) {
+ to_spawn.insert(p_peer);
+ } else {
+ to_despawn.insert(p_peer);
+ }
+ } else {
+ // Check visibility for each peers.
+ for (int pid : rep_state->get_peers()) {
+ bool peer_visible = is_visible || sync->is_visible_to(pid);
+ if (peer_visible == rep_state->is_peer_spawn(pid, p_oid)) {
+ continue;
+ }
+ if (peer_visible) {
+ to_spawn.insert(pid);
+ } else {
+ to_despawn.insert(pid);
+ }
+ }
+ }
+ if (to_spawn.size()) {
+ int len = 0;
+ _make_spawn_packet(node, len);
+ for (int pid : to_spawn) {
+ int path_id;
+ multiplayer->send_object_cache(spawner, pid, path_id);
+ _send_raw(packet_cache.ptr(), len, pid, true);
+ rep_state->peer_add_spawn(pid, p_oid);
+ }
+ }
+ if (to_despawn.size()) {
+ int len = 0;
+ _make_despawn_packet(node, len);
+ for (int pid : to_despawn) {
+ rep_state->peer_del_spawn(pid, p_oid);
+ _send_raw(packet_cache.ptr(), len, pid, true);
+ }
+ }
+ return OK;
+}
+
Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable) {
ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(!multiplayer, ERR_UNCONFIGURED);
@@ -158,18 +271,20 @@ Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size,
return peer->put_packet(p_buffer, p_size);
}
-Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer) {
- ERR_FAIL_COND_V(p_peer < 0, ERR_BUG);
+Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) {
ERR_FAIL_COND_V(!multiplayer, ERR_BUG);
- ERR_FAIL_COND_V(!p_spawner || !p_node, ERR_BUG);
const ObjectID oid = p_node->get_instance_id();
- uint32_t nid = rep_state->ensure_net_id(oid);
+ MultiplayerSpawner *spawner = rep_state->get_spawner(oid);
+ ERR_FAIL_COND_V(!spawner || !p_node, ERR_BUG);
+
+ uint32_t nid = rep_state->get_net_id(oid);
+ ERR_FAIL_COND_V(!nid, ERR_UNCONFIGURED);
// Prepare custom arg and scene_id
- uint8_t scene_id = p_spawner->find_spawnable_scene_index_from_object(oid);
+ uint8_t scene_id = spawner->find_spawnable_scene_index_from_object(oid);
bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID;
- Variant spawn_arg = p_spawner->get_spawn_argument(oid);
+ Variant spawn_arg = spawner->get_spawn_argument(oid);
int spawn_arg_size = 0;
if (is_custom) {
Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, nullptr, spawn_arg_size, false);
@@ -181,7 +296,8 @@ Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p
Vector<Variant> state_vars;
Vector<const Variant *> state_varp;
MultiplayerSynchronizer *synchronizer = rep_state->get_synchronizer(oid);
- if (synchronizer && synchronizer->get_replication_config().is_valid()) {
+ if (synchronizer) {
+ ERR_FAIL_COND_V(synchronizer->get_replication_config().is_null(), ERR_BUG);
const List<NodePath> props = synchronizer->get_replication_config()->get_spawn_properties();
Error err = MultiplayerSynchronizer::get_state(props, p_node, state_vars, state_varp);
ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to retrieve spawn state.");
@@ -189,13 +305,8 @@ Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p
ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to encode spawn state.");
}
- // Prepare simplified path.
- NodePath rel_path = multiplayer->get_root_path().rel_path_to(p_spawner->get_path());
-
- int path_id = 0;
- multiplayer->send_object_cache(p_spawner, rel_path, p_peer, path_id);
-
- // Encode name and parent ID.
+ // Encode scene ID, path ID, net ID, node name.
+ int path_id = multiplayer->make_object_cache(spawner);
CharString cname = p_node->get_name().operator String().utf8();
int nlen = encode_cstring(cname.get_data(), nullptr);
MAKE_ROOM(1 + 1 + 4 + 4 + 4 + nlen + (is_custom ? 4 + spawn_arg_size : 0) + state_size);
@@ -220,12 +331,11 @@ Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p
ERR_FAIL_COND_V(err, err);
ofs += state_size;
}
- Error err = _send_raw(ptr, ofs, p_peer, true);
- ERR_FAIL_COND_V(err, err);
- return rep_state->peer_add_node(p_peer, oid);
+ r_len = ofs;
+ return OK;
}
-Error SceneReplicationInterface::_send_despawn(Node *p_node, int p_peer) {
+Error SceneReplicationInterface::_make_despawn_packet(Node *p_node, int &r_len) {
const ObjectID oid = p_node->get_instance_id();
MAKE_ROOM(5);
uint8_t *ptr = packet_cache.ptrw();
@@ -233,9 +343,8 @@ Error SceneReplicationInterface::_send_despawn(Node *p_node, int p_peer) {
int ofs = 1;
uint32_t nid = rep_state->get_net_id(oid);
ofs += encode_uint32(nid, &ptr[ofs]);
- Error err = _send_raw(ptr, ofs, p_peer, true);
- ERR_FAIL_COND_V(err, err);
- return rep_state->peer_del_node(p_peer, oid);
+ r_len = ofs;
+ return OK;
}
Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
@@ -316,8 +425,8 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p
}
void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
- const HashSet<ObjectID> &known = rep_state->get_known_nodes(p_peer);
- if (known.is_empty()) {
+ const HashSet<ObjectID> &to_sync = rep_state->get_peer_sync_nodes(p_peer);
+ if (to_sync.is_empty()) {
return;
}
MAKE_ROOM(sync_mtu);
@@ -327,14 +436,29 @@ void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
ofs += encode_uint16(rep_state->peer_sync_next(p_peer), &ptr[1]);
// Can only send updates for already notified nodes.
// This is a lazy implementation, we could optimize much more here with by grouping by replication config.
- for (const ObjectID &oid : known) {
+ for (const ObjectID &oid : to_sync) {
if (!rep_state->update_sync_time(oid, p_msec)) {
continue; // nothing to sync.
}
MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
- ERR_CONTINUE(!sync);
+ ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid());
Node *node = rep_state->get_node(oid);
ERR_CONTINUE(!node);
+ uint32_t net_id = rep_state->get_net_id(oid);
+ if (net_id == 0 || (net_id & 0x80000000)) {
+ int path_id = 0;
+ bool verified = multiplayer->send_object_cache(sync, p_peer, path_id);
+ ERR_CONTINUE_MSG(path_id < 0, "This should never happen!");
+ if (net_id == 0) {
+ // First time path based ID.
+ net_id = path_id | 0x80000000;
+ rep_state->set_net_id(oid, net_id | 0x80000000);
+ }
+ if (!verified) {
+ // The path based sync is not yet confirmed, skipping.
+ continue;
+ }
+ }
int size;
Vector<Variant> vars;
Vector<const Variant *> varp;
@@ -351,16 +475,6 @@ void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
ofs = 3;
}
if (size) {
- uint32_t net_id = rep_state->get_net_id(oid);
- if (net_id == 0 || (net_id & 0x80000000)) {
- // First time path based ID.
- NodePath rel_path = multiplayer->get_root_path().rel_path_to(sync->get_path());
- int path_id = 0;
- multiplayer->send_object_cache(sync, rel_path, p_peer, path_id);
- ERR_CONTINUE_MSG(net_id && net_id != (uint32_t(path_id) | 0x80000000), "This should never happen!");
- net_id = path_id;
- rep_state->set_net_id(oid, net_id | 0x80000000);
- }
ofs += encode_uint32(rep_state->get_net_id(oid), &ptr[ofs]);
ofs += encode_uint32(size, &ptr[ofs]);
MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), &ptr[ofs], size);
diff --git a/scene/multiplayer/scene_replication_interface.h b/scene/multiplayer/scene_replication_interface.h
index 60ac95c93c..ad3a3be979 100644
--- a/scene/multiplayer/scene_replication_interface.h
+++ b/scene/multiplayer/scene_replication_interface.h
@@ -40,10 +40,13 @@ class SceneReplicationInterface : public MultiplayerReplicationInterface {
private:
void _send_sync(int p_peer, uint64_t p_msec);
- Error _send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer);
- Error _send_despawn(Node *p_node, int p_peer);
+ Error _make_spawn_packet(Node *p_node, int &r_len);
+ Error _make_despawn_packet(Node *p_node, int &r_len);
Error _send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable);
+ void _visibility_changed(int p_peer, ObjectID p_oid);
+ Error _update_sync_visibility(int p_peer, const ObjectID &p_oid);
+ Error _update_spawn_visibility(int p_peer, const ObjectID &p_oid);
void _free_remotes(int p_peer);
Ref<SceneReplicationState> rep_state;
diff --git a/scene/multiplayer/scene_replication_state.cpp b/scene/multiplayer/scene_replication_state.cpp
index 937b30cb36..f6a51ff9c7 100644
--- a/scene/multiplayer/scene_replication_state.cpp
+++ b/scene/multiplayer/scene_replication_state.cpp
@@ -56,7 +56,8 @@ void SceneReplicationState::_untrack(const ObjectID &p_id) {
// If we spawned or synced it, we need to remove it from any peer it was sent to.
if (net_id || peer == 0) {
for (KeyValue<int, PeerInfo> &E : peers_info) {
- E.value.known_nodes.erase(p_id);
+ E.value.sync_nodes.erase(p_id);
+ E.value.spawn_nodes.erase(p_id);
}
}
}
@@ -93,11 +94,6 @@ bool SceneReplicationState::update_sync_time(const ObjectID &p_id, uint64_t p_ms
return false;
}
-const HashSet<ObjectID> SceneReplicationState::get_known_nodes(int p_peer) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
- return peers_info[p_peer].known_nodes;
-}
-
uint32_t SceneReplicationState::get_net_id(const ObjectID &p_id) const {
const TrackedNode *tnode = tracked_nodes.getptr(p_id);
ERR_FAIL_COND_V(!tnode, 0);
@@ -147,8 +143,6 @@ Error SceneReplicationState::config_add_spawn(Node *p_node, MultiplayerSpawner *
ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
tobj.spawner = p_spawner->get_instance_id();
spawned_nodes.insert(oid);
- // The spawner may be notified after the synchronizer.
- path_only_nodes.erase(oid);
return OK;
}
@@ -159,6 +153,9 @@ Error SceneReplicationState::config_del_spawn(Node *p_node, MultiplayerSpawner *
ERR_FAIL_COND_V(tobj.spawner != p_spawner->get_instance_id(), ERR_INVALID_PARAMETER);
tobj.spawner = ObjectID();
spawned_nodes.erase(oid);
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.spawn_nodes.erase(oid);
+ }
return OK;
}
@@ -167,10 +164,7 @@ Error SceneReplicationState::config_add_sync(Node *p_node, MultiplayerSynchroniz
TrackedNode &tobj = _track(oid);
ERR_FAIL_COND_V(tobj.synchronizer != ObjectID(), ERR_ALREADY_IN_USE);
tobj.synchronizer = p_sync->get_instance_id();
- // If it doesn't have a spawner, we might need to assign ID for this node using it's path.
- if (tobj.spawner.is_null()) {
- path_only_nodes.insert(oid);
- }
+ synced_nodes.insert(oid);
return OK;
}
@@ -180,38 +174,57 @@ Error SceneReplicationState::config_del_sync(Node *p_node, MultiplayerSynchroniz
TrackedNode &tobj = _track(oid);
ERR_FAIL_COND_V(tobj.synchronizer != p_sync->get_instance_id(), ERR_INVALID_PARAMETER);
tobj.synchronizer = ObjectID();
- if (path_only_nodes.has(oid)) {
- p_node->disconnect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack));
- _untrack(oid);
- path_only_nodes.erase(oid);
+ synced_nodes.erase(oid);
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.sync_nodes.erase(oid);
}
return OK;
}
-Error SceneReplicationState::peer_add_node(int p_peer, const ObjectID &p_id) {
- if (p_peer) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
- peers_info[p_peer].known_nodes.insert(p_id);
- } else {
- for (KeyValue<int, PeerInfo> &E : peers_info) {
- E.value.known_nodes.insert(p_id);
- }
- }
+Error SceneReplicationState::peer_add_sync(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].sync_nodes.insert(p_id);
return OK;
}
-Error SceneReplicationState::peer_del_node(int p_peer, const ObjectID &p_id) {
- if (p_peer) {
- ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
- peers_info[p_peer].known_nodes.erase(p_id);
- } else {
- for (KeyValue<int, PeerInfo> &E : peers_info) {
- E.value.known_nodes.erase(p_id);
- }
- }
+Error SceneReplicationState::peer_del_sync(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].sync_nodes.erase(p_id);
return OK;
}
+const HashSet<ObjectID> SceneReplicationState::get_peer_sync_nodes(int p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
+ return peers_info[p_peer].sync_nodes;
+}
+
+bool SceneReplicationState::is_peer_sync(int p_peer, const ObjectID &p_id) const {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), false);
+ return peers_info[p_peer].sync_nodes.has(p_id);
+}
+
+Error SceneReplicationState::peer_add_spawn(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].spawn_nodes.insert(p_id);
+ return OK;
+}
+
+Error SceneReplicationState::peer_del_spawn(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].spawn_nodes.erase(p_id);
+ return OK;
+}
+
+const HashSet<ObjectID> SceneReplicationState::get_peer_spawn_nodes(int p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
+ return peers_info[p_peer].spawn_nodes;
+}
+
+bool SceneReplicationState::is_peer_spawn(int p_peer, const ObjectID &p_id) const {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), false);
+ return peers_info[p_peer].spawn_nodes.has(p_id);
+}
+
Node *SceneReplicationState::peer_get_remote(int p_peer, uint32_t p_net_id) {
PeerInfo *info = peers_info.getptr(p_peer);
return info && info->recv_nodes.has(p_net_id) ? Object::cast_to<Node>(ObjectDB::get_instance(info->recv_nodes[p_net_id])) : nullptr;
diff --git a/scene/multiplayer/scene_replication_state.h b/scene/multiplayer/scene_replication_state.h
index 60a6c5d70c..7973b5c904 100644
--- a/scene/multiplayer/scene_replication_state.h
+++ b/scene/multiplayer/scene_replication_state.h
@@ -62,7 +62,8 @@ private:
};
struct PeerInfo {
- HashSet<ObjectID> known_nodes;
+ HashSet<ObjectID> sync_nodes;
+ HashSet<ObjectID> spawn_nodes;
HashMap<uint32_t, ObjectID> recv_nodes;
uint16_t last_sent_sync = 0;
uint16_t last_recv_sync = 0;
@@ -73,7 +74,7 @@ private:
HashMap<ObjectID, TrackedNode> tracked_nodes;
HashMap<int, PeerInfo> peers_info;
HashSet<ObjectID> spawned_nodes;
- HashSet<ObjectID> path_only_nodes;
+ HashSet<ObjectID> synced_nodes;
TrackedNode &_track(const ObjectID &p_id);
void _untrack(const ObjectID &p_id);
@@ -82,7 +83,9 @@ private:
public:
const HashSet<int> get_peers() const { return known_peers; }
const HashSet<ObjectID> &get_spawned_nodes() const { return spawned_nodes; }
- const HashSet<ObjectID> &get_path_only_nodes() const { return path_only_nodes; }
+ bool is_spawned_node(const ObjectID &p_id) const { return spawned_nodes.has(p_id); }
+ const HashSet<ObjectID> &get_synced_nodes() const { return synced_nodes; }
+ bool is_synced_node(const ObjectID &p_id) const { return synced_nodes.has(p_id); }
MultiplayerSynchronizer *get_synchronizer(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_synchronizer() : nullptr; }
MultiplayerSpawner *get_spawner(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_spawner() : nullptr; }
@@ -90,7 +93,6 @@ public:
bool update_last_node_sync(const ObjectID &p_id, uint16_t p_time);
bool update_sync_time(const ObjectID &p_id, uint64_t p_msec);
- const HashSet<ObjectID> get_known_nodes(int p_peer);
uint32_t get_net_id(const ObjectID &p_id) const;
void set_net_id(const ObjectID &p_id, uint32_t p_net_id);
uint32_t ensure_net_id(const ObjectID &p_id);
@@ -104,8 +106,17 @@ public:
Error config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
Error config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
- Error peer_add_node(int p_peer, const ObjectID &p_id);
- Error peer_del_node(int p_peer, const ObjectID &p_id);
+ Error peer_add_sync(int p_peer, const ObjectID &p_id);
+ Error peer_del_sync(int p_peer, const ObjectID &p_id);
+
+ const HashSet<ObjectID> get_peer_sync_nodes(int p_peer);
+ bool is_peer_sync(int p_peer, const ObjectID &p_id) const;
+
+ Error peer_add_spawn(int p_peer, const ObjectID &p_id);
+ Error peer_del_spawn(int p_peer, const ObjectID &p_id);
+
+ const HashSet<ObjectID> get_peer_spawn_nodes(int p_peer);
+ bool is_peer_spawn(int p_peer, const ObjectID &p_id) const;
const HashMap<uint32_t, ObjectID> peer_get_remotes(int p_peer) const;
Node *peer_get_remote(int p_peer, uint32_t p_net_id);
diff --git a/scene/multiplayer/scene_rpc_interface.cpp b/scene/multiplayer/scene_rpc_interface.cpp
index 84700a82f3..144a10c665 100644
--- a/scene/multiplayer/scene_rpc_interface.cpp
+++ b/scene/multiplayer/scene_rpc_interface.cpp
@@ -302,12 +302,9 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + ".");
}
- NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path());
- ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
-
// See if all peers have cached path (if so, call can be fast).
int psc_id;
- const bool has_all_peers = multiplayer->send_object_cache(p_from, from_path, p_to, psc_id);
+ const bool has_all_peers = multiplayer->send_object_cache(p_from, p_to, psc_id);
// Create base packet, lots of hardcode because it must be tight.
@@ -414,6 +411,7 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
// Not all verified path, so send one by one.
// Append path at the end, since we will need it for some packets.
+ NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path());
CharString pname = String(from_path).utf8();
int path_len = encode_cstring(pname.get_data(), nullptr);
MAKE_ROOM(ofs + path_len);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index f462e36758..b1ef3d0f6f 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -159,6 +159,7 @@
#include "scene/resources/gradient.h"
#include "scene/resources/height_map_shape_3d.h"
#include "scene/resources/immediate_mesh.h"
+#include "scene/resources/label_settings.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
#include "scene/resources/mesh_data_tool.h"
@@ -861,6 +862,8 @@ void register_scene_types() {
GDREGISTER_CLASS(Curve);
+ GDREGISTER_CLASS(LabelSettings);
+
GDREGISTER_CLASS(SceneReplicationConfig);
GDREGISTER_CLASS(TextLine);
diff --git a/scene/resources/label_settings.cpp b/scene/resources/label_settings.cpp
new file mode 100644
index 0000000000..e8b986b431
--- /dev/null
+++ b/scene/resources/label_settings.cpp
@@ -0,0 +1,187 @@
+/*************************************************************************/
+/* label_settings.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#include "label_settings.h"
+
+#include "core/core_string_names.h"
+
+void LabelSettings::_font_changed() {
+ emit_changed();
+}
+
+void LabelSettings::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_line_spacing", "spacing"), &LabelSettings::set_line_spacing);
+ ClassDB::bind_method(D_METHOD("get_line_spacing"), &LabelSettings::get_line_spacing);
+
+ ClassDB::bind_method(D_METHOD("set_font", "font"), &LabelSettings::set_font);
+ ClassDB::bind_method(D_METHOD("get_font"), &LabelSettings::get_font);
+
+ ClassDB::bind_method(D_METHOD("set_font_size", "size"), &LabelSettings::set_font_size);
+ ClassDB::bind_method(D_METHOD("get_font_size"), &LabelSettings::get_font_size);
+
+ ClassDB::bind_method(D_METHOD("set_font_color", "color"), &LabelSettings::set_font_color);
+ ClassDB::bind_method(D_METHOD("get_font_color"), &LabelSettings::get_font_color);
+
+ ClassDB::bind_method(D_METHOD("set_outline_size", "size"), &LabelSettings::set_outline_size);
+ ClassDB::bind_method(D_METHOD("get_outline_size"), &LabelSettings::get_outline_size);
+
+ ClassDB::bind_method(D_METHOD("set_outline_color", "color"), &LabelSettings::set_outline_color);
+ ClassDB::bind_method(D_METHOD("get_outline_color"), &LabelSettings::get_outline_color);
+
+ ClassDB::bind_method(D_METHOD("set_shadow_size", "size"), &LabelSettings::set_shadow_size);
+ ClassDB::bind_method(D_METHOD("get_shadow_size"), &LabelSettings::get_shadow_size);
+
+ ClassDB::bind_method(D_METHOD("set_shadow_color", "color"), &LabelSettings::set_shadow_color);
+ ClassDB::bind_method(D_METHOD("get_shadow_color"), &LabelSettings::get_shadow_color);
+
+ ClassDB::bind_method(D_METHOD("set_shadow_offset", "offset"), &LabelSettings::set_shadow_offset);
+ ClassDB::bind_method(D_METHOD("get_shadow_offset"), &LabelSettings::get_shadow_offset);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing");
+
+ ADD_GROUP("Font", "font");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,1024,1,or_greater,suffix:px"), "set_font_size", "get_font_size");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "font_color"), "set_font_color", "get_font_color");
+
+ ADD_GROUP("Outline", "outline");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,127,1,or_greater,suffix:px"), "set_outline_size", "get_outline_size");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color");
+
+ ADD_GROUP("Shadow", "shadow");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,127,1,or_greater,suffix:px"), "set_shadow_size", "get_shadow_size");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_shadow_offset", "get_shadow_offset");
+}
+
+void LabelSettings::set_line_spacing(real_t p_spacing) {
+ if (line_spacing != p_spacing) {
+ line_spacing = p_spacing;
+ emit_changed();
+ }
+}
+
+real_t LabelSettings::get_line_spacing() const {
+ return line_spacing;
+}
+
+void LabelSettings::set_font(const Ref<Font> &p_font) {
+ if (font != p_font) {
+ if (font.is_valid()) {
+ font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed));
+ }
+ font = p_font;
+ if (font.is_valid()) {
+ font->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+ emit_changed();
+ }
+}
+
+Ref<Font> LabelSettings::get_font() const {
+ return font;
+}
+
+void LabelSettings::set_font_size(int p_size) {
+ if (font_size != p_size) {
+ font_size = p_size;
+ emit_changed();
+ }
+}
+
+int LabelSettings::get_font_size() const {
+ return font_size;
+}
+
+void LabelSettings::set_font_color(const Color &p_color) {
+ if (font_color != p_color) {
+ font_color = p_color;
+ emit_changed();
+ }
+}
+
+Color LabelSettings::get_font_color() const {
+ return font_color;
+}
+
+void LabelSettings::set_outline_size(int p_size) {
+ if (outline_size != p_size) {
+ outline_size = p_size;
+ emit_changed();
+ }
+}
+
+int LabelSettings::get_outline_size() const {
+ return outline_size;
+}
+
+void LabelSettings::set_outline_color(const Color &p_color) {
+ if (outline_color != p_color) {
+ outline_color = p_color;
+ emit_changed();
+ }
+}
+
+Color LabelSettings::get_outline_color() const {
+ return outline_color;
+}
+
+void LabelSettings::set_shadow_size(int p_size) {
+ if (shadow_size != p_size) {
+ shadow_size = p_size;
+ emit_changed();
+ }
+}
+
+int LabelSettings::get_shadow_size() const {
+ return shadow_size;
+}
+
+void LabelSettings::set_shadow_color(const Color &p_color) {
+ if (shadow_color != p_color) {
+ shadow_color = p_color;
+ emit_changed();
+ }
+}
+
+Color LabelSettings::get_shadow_color() const {
+ return shadow_color;
+}
+
+void LabelSettings::set_shadow_offset(const Vector2 &p_offset) {
+ if (shadow_offset != p_offset) {
+ shadow_offset = p_offset;
+ emit_changed();
+ }
+}
+
+Vector2 LabelSettings::get_shadow_offset() const {
+ return shadow_offset;
+}
diff --git a/scene/resources/label_settings.h b/scene/resources/label_settings.h
new file mode 100644
index 0000000000..d2644a7484
--- /dev/null
+++ b/scene/resources/label_settings.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* label_settings.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 LABEL_SETTINGS_H
+#define LABEL_SETTINGS_H
+
+#include "core/io/resource.h"
+#include "font.h"
+
+/*************************************************************************/
+
+class LabelSettings : public Resource {
+ GDCLASS(LabelSettings, Resource);
+
+ real_t line_spacing = 0;
+
+ Ref<Font> font;
+ int font_size = Font::DEFAULT_FONT_SIZE;
+ Color font_color = Color(0.875, 0.875, 0.875);
+
+ int outline_size = 0;
+ Color outline_color = Color(1, 1, 1);
+
+ int shadow_size = 0;
+ Color shadow_color = Color(1, 1, 1);
+ Vector2 shadow_offset = Vector2(1, 1);
+
+ void _font_changed();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_line_spacing(real_t p_spacing);
+ real_t get_line_spacing() const;
+
+ void set_font(const Ref<Font> &p_font);
+ Ref<Font> get_font() const;
+
+ void set_font_size(int p_size);
+ int get_font_size() const;
+
+ void set_font_color(const Color &p_color);
+ Color get_font_color() const;
+
+ void set_outline_size(int p_size);
+ int get_outline_size() const;
+
+ void set_outline_color(const Color &p_color);
+ Color get_outline_color() const;
+
+ void set_shadow_size(int p_size);
+ int get_shadow_size() const;
+
+ void set_shadow_color(const Color &p_color);
+ Color get_shadow_color() const;
+
+ void set_shadow_offset(const Vector2 &p_offset);
+ Vector2 get_shadow_offset() const;
+};
+
+#endif // LABEL_SETTINGS_H