summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/math/vector3.cpp2
-rw-r--r--doc/classes/@GlobalScope.xml22
-rw-r--r--doc/classes/Color.xml16
-rw-r--r--doc/classes/NavigationServer3D.xml9
-rw-r--r--doc/classes/String.xml13
-rw-r--r--doc/classes/StringName.xml14
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp2
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp9
-rw-r--r--drivers/gles3/storage/material_storage.cpp1
-rw-r--r--editor/editor_help.cpp4
-rw-r--r--editor/export/editor_export_platform.cpp4
-rw-r--r--editor/export/editor_export_platform_pc.cpp3
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp7
-rw-r--r--editor/plugins/script_editor_plugin.cpp82
-rw-r--r--editor/plugins/script_editor_plugin.h18
-rw-r--r--editor/plugins/text_editor.cpp71
-rw-r--r--editor/plugins/text_editor.h2
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp11
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp11
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml74
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp14
-rw-r--r--modules/gltf/gltf_document.cpp1
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp6
-rw-r--r--modules/navigation/godot_navigation_server.cpp2
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp5
-rw-r--r--scene/2d/tile_map.cpp7
-rw-r--r--scene/2d/tile_map.h1
-rw-r--r--scene/gui/popup.cpp11
-rw-r--r--scene/main/viewport.cpp18
-rw-r--r--scene/main/window.cpp7
-rw-r--r--servers/navigation_server_3d.cpp1
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp1
32 files changed, 317 insertions, 132 deletions
diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp
index d732f1e8b2..ae009fc4ef 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -108,7 +108,9 @@ Vector3 Vector3::octahedron_decode(const Vector2 &p_oct) {
}
Vector2 Vector3::octahedron_tangent_encode(const float sign) const {
+ const float bias = 1.0f / 32767.0f;
Vector2 res = this->octahedron_encode();
+ res.y = MAX(res.y, bias);
res.y = res.y * 0.5f + 0.5f;
res.y = sign >= 0.0f ? res.y : 1 - res.y;
return res;
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index a8860721d6..d5280cb548 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -803,7 +803,8 @@
Returns the result of [param base] raised to the power of [param exp].
In GDScript, this is the equivalent of the [code]**[/code] operator.
[codeblock]
- pow(2, 5) # Returns 32
+ pow(2, 5) # Returns 32.0
+ pow(4, 1.5) # Returns 8.0
[/codeblock]
</description>
</method>
@@ -1287,14 +1288,14 @@
Converts a formatted [param string] that was returned by [method var_to_str] to the original [Variant].
[codeblocks]
[gdscript]
- var a = '{ "a": 1, "b": 2 }' # a is a String
- var b = str_to_var(a) # b is a Dictionary
- print(b["a"]) # Prints 1
+ var data = '{ "a": 1, "b": 2 }' # data is a String
+ var dict = str_to_var(data) # dict is a Dictionary
+ print(dict["a"]) # Prints 1
[/gdscript]
[csharp]
- string a = "{ \"a\": 1, \"b\": 2 }"; // a is a string
- var b = GD.StrToVar(a).AsGodotDictionary(); // b is a Dictionary
- GD.Print(b["a"]); // Prints 1
+ string data = "{ \"a\": 1, \"b\": 2 }"; // data is a string
+ var dict = GD.StrToVar(data).AsGodotDictionary(); // dict is a Dictionary
+ GD.Print(dict["a"]); // Prints 1
[/csharp]
[/codeblocks]
</description>
@@ -2272,7 +2273,7 @@
Command (on macOS) or Meta/Windows key mask.
</constant>
<constant name="KEY_MASK_CTRL" value="268435456" enum="KeyModifierMask" is_bitfield="true">
- Ctrl key mask.
+ Control key mask.
</constant>
<constant name="KEY_MASK_KPAD" value="536870912" enum="KeyModifierMask" is_bitfield="true">
Keypad key mask.
@@ -2293,10 +2294,10 @@
Middle mouse button.
</constant>
<constant name="MOUSE_BUTTON_WHEEL_UP" value="4" enum="MouseButton">
- Mouse wheel up.
+ Mouse wheel scrolling up.
</constant>
<constant name="MOUSE_BUTTON_WHEEL_DOWN" value="5" enum="MouseButton">
- Mouse wheel down.
+ Mouse wheel scrolling down.
</constant>
<constant name="MOUSE_BUTTON_WHEEL_LEFT" value="6" enum="MouseButton">
Mouse wheel left button (only present on some mice).
@@ -2760,6 +2761,7 @@
Hints that a string property is a password, and every character is replaced with the secret character.
</constant>
<constant name="PROPERTY_HINT_MAX" value="37" enum="PropertyHint">
+ Represents the size of the [enum PropertyHint] enum.
</constant>
<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags" is_bitfield="true">
The property is not stored, and does not display in the editor. This is the default for non-exported properties.
diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml
index cee0e3ef7d..faa658971d 100644
--- a/doc/classes/Color.xml
+++ b/doc/classes/Color.xml
@@ -203,18 +203,18 @@
<return type="Color" />
<param index="0" name="hex" type="int" />
<description>
- Returns the [Color] associated with the provided [param hex] integer in 32-bit ARGB format (8 bits per channel, alpha channel first).
+ Returns the [Color] associated with the provided [param hex] integer in 32-bit RGBA format (8 bits per channel, alpha channel first).
In GDScript and C#, the [int] is best visualized with hexadecimal notation ([code]"0x"[/code] prefix).
[codeblocks]
[gdscript]
- var red = Color.hex(0xffff0000)
- var dark_cyan = Color.hex(0xff008b8b)
- var my_color = Color.hex(0xa4bbefd2)
+ var red = Color.hex(0xff0000ff)
+ var dark_cyan = Color.hex(0x008b8bff)
+ var my_color = Color.hex(0xbbefd2a4)
[/gdscript]
[csharp]
- var red = new Color(0xffff0000);
- var dark_cyan = new Color(0xff008b8b);
- var my_color = new Color(0xa4bbefd2);
+ var red = new Color(0xff0000ff);
+ var dark_cyan = new Color(0x008b8bff);
+ var my_color = new Color(0xbbefd2a4);
[/csharp]
[/codeblocks]
</description>
@@ -223,7 +223,7 @@
<return type="Color" />
<param index="0" name="hex" type="int" />
<description>
- Returns the [Color] associated with the provided [param hex] integer in 64-bit ARGB format (16 bits per channel, alpha channel first).
+ Returns the [Color] associated with the provided [param hex] integer in 64-bit RGBA format (16 bits per channel, alpha channel first).
In GDScript and C#, the [int] is best visualized with hexadecimal notation ([code]"0x"[/code] prefix).
</description>
</method>
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index 16b850866a..1feb363999 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -422,15 +422,6 @@
Sets the map up direction.
</description>
</method>
- <method name="process">
- <return type="void" />
- <param index="0" name="delta_time" type="float" />
- <description>
- Process the collision avoidance agents.
- The result of this process is needed by the physics server, so this must be called in the main thread.
- [b]Note:[/b] This function is not thread safe.
- </description>
- </method>
<method name="query_path" qualifiers="const">
<return type="void" />
<param index="0" name="parameters" type="NavigationPathQueryParameters3D" />
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index 792cd38741..8535dacda7 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -193,8 +193,8 @@
GD.Print("Team".Find("I")); // Prints -1
GD.Print("Potato".Find("t")); // Prints 2
- GD.print("Potato".Find("t", 3)); // Prints 4
- GD.print("Potato".Find("t", 5)); // Prints -1
+ GD.Print("Potato".Find("t", 3)); // Prints 4
+ GD.Print("Potato".Find("t", 5)); // Prints -1
[/csharp]
[/codeblocks]
[b]Note:[/b] If you just want to know whether the string contains [param what], use [method contains]. In GDScript, you may also use the [code]in[/code] operator.
@@ -230,6 +230,7 @@
print("User {id} is {name}.".format([["id", 42], ["name", "Godot"]]))
[/codeblock]
See also the [url=$DOCS_URL/tutorials/scripting/gdscript/gdscript_format_string.html]GDScript format string[/url] tutorial.
+ [b]Note:[/b] In C#, it's recommended to [url=https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated]interpolate strings with "$"[/url], instead.
</description>
</method>
<method name="get_base_dir" qualifiers="const">
@@ -480,7 +481,7 @@
var fruits = new string[] {"Apple", "Orange", "Pear", "Kiwi"};
// In C#, this method is static.
- GD.Print(string.Join(", ", fruits); // Prints "Apple, Orange, Pear, Kiwi"
+ GD.Print(string.Join(", ", fruits)); // Prints "Apple, Orange, Pear, Kiwi"
GD.Print(string.Join("---", fruits)); // Prints "Apple---Orange---Pear---Kiwi"
[/csharp]
[/codeblocks]
@@ -1047,8 +1048,7 @@
<return type="String" />
<param index="0" name="right" type="Variant" />
<description>
- Formats the [String], replacing the placeholders with one or more parameters.
- To pass multiple parameters, [param right] needs to be an [Array].
+ Formats the [String], replacing the placeholders with one or more parameters. To pass multiple parameters, [param right] needs to be an [Array].
[codeblock]
print("I caught %d fishes!" % 2) # Prints "I caught 2 fishes!"
@@ -1057,8 +1057,8 @@
var speed = 40.3485
print(my_message % [location, speed]) # Prints "Travelling to Deep Valley, at 40.35 km/h."
[/codeblock]
- In C#, there is no direct equivalent to this operator. Use the [method format] method, instead.
For more information, see the [url=$DOCS_URL/tutorials/scripting/gdscript/gdscript_format_string.html]GDScript format strings[/url] tutorial.
+ [b]Note:[/b] In C#, this operator is not available. Instead, see [url=https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated]how to interpolate strings with "$"[/url].
</description>
</operator>
<operator name="operator +">
@@ -1072,6 +1072,7 @@
<return type="String" />
<param index="0" name="right" type="StringName" />
<description>
+ Appends [param right] at the end of this [String], returning a [String]. This is also known as a string concatenation.
</description>
</operator>
<operator name="operator &lt;">
diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml
index c103fb2287..96b958a5b9 100644
--- a/doc/classes/StringName.xml
+++ b/doc/classes/StringName.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
[StringName]s are immutable strings designed for general-purpose representation of unique names (also called "string interning"). [StringName] ensures that only one instance of a given name exists (so two [StringName]s with the same value are the same object). Comparing them is much faster than with regular [String]s, because only the pointers are compared, not the whole strings.
- You will usually just pass a [String] to methods expecting a [StringName] and it will be automatically converted, but you may occasionally want to construct a [StringName] ahead of time with [StringName] or, in GDScript, the literal syntax [code]&amp;"example"[/code].
+ You will usually just pass a [String] to methods expecting a [StringName] and it will be automatically converted, but you may occasionally want to construct a [StringName] ahead of time with the [StringName] constructor or, in GDScript, the literal syntax [code]&amp;"example"[/code].
See also [NodePath], which is a similar concept specifically designed to store pre-parsed node paths.
Some string methods have corresponding variations. Variations suffixed with [code]n[/code] ([method countn], [method findn], [method replacen], etc.) are [b]case-insensitive[/b] (they make no distinction between uppercase and lowercase letters). Method variations prefixed with [code]r[/code] ([method rfind], [method rsplit], etc.) are reversed, and start from the end of the string, instead of the beginning.
[b]Note:[/b] In a boolean context, a [StringName] will evaluate to [code]false[/code] if it is empty ([code]StringName("")[/code]). Otherwise, a [StringName] will always evaluate to [code]true[/code].
@@ -176,8 +176,8 @@
GD.Print("Team".Find("I")); // Prints -1
GD.Print("Potato".Find("t")); // Prints 2
- GD.print("Potato".Find("t", 3)); // Prints 4
- GD.print("Potato".Find("t", 5)); // Prints -1
+ GD.Print("Potato".Find("t", 3)); // Prints 4
+ GD.Print("Potato".Find("t", 5)); // Prints -1
[/csharp]
[/codeblocks]
[b]Note:[/b] If you just want to know whether the string contains [param what], use [method contains]. In GDScript, you may also use the [code]in[/code] operator.
@@ -213,6 +213,7 @@
print("User {id} is {name}.".format([["id", 42], ["name", "Godot"]]))
[/codeblock]
See also the [url=$DOCS_URL/tutorials/scripting/gdscript/gdscript_format_string.html]GDScript format string[/url] tutorial.
+ [b]Note:[/b] In C#, it's recommended to [url=https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated]interpolate strings with "$"[/url], instead.
</description>
</method>
<method name="get_base_dir" qualifiers="const">
@@ -455,7 +456,7 @@
var fruits = new string[] {"Apple", "Orange", "Pear", "Kiwi"};
// In C#, this method is static.
- GD.Print(string.Join(", ", fruits); // Prints "Apple, Orange, Pear, Kiwi"
+ GD.Print(string.Join(", ", fruits)); // Prints "Apple, Orange, Pear, Kiwi"
GD.Print(string.Join("---", fruits)); // Prints "Apple---Orange---Pear---Kiwi"
[/csharp]
[/codeblocks]
@@ -954,18 +955,23 @@
<return type="String" />
<param index="0" name="right" type="Variant" />
<description>
+ Formats the [StringName], replacing the placeholders with one or more parameters, returning a [String]. To pass multiple parameters, [param right] needs to be an [Array].
+ For more information, see the [url=$DOCS_URL/tutorials/scripting/gdscript/gdscript_format_string.html]GDScript format strings[/url] tutorial.
+ [b]Note:[/b] In C#, this operator is not available. Instead, see [url=https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated]how to interpolate strings with "$"[/url].
</description>
</operator>
<operator name="operator +">
<return type="String" />
<param index="0" name="right" type="String" />
<description>
+ Appends [param right] at the end of this [StringName], returning a [String]. This is also known as a string concatenation.
</description>
</operator>
<operator name="operator +">
<return type="String" />
<param index="0" name="right" type="StringName" />
<description>
+ Appends [param right] at the end of this [StringName], returning a [String]. This is also known as a string concatenation.
</description>
</operator>
<operator name="operator &lt;">
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 3c5441f3c4..2a524e8c3a 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -566,6 +566,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
uint32_t index = 0;
Item *current_clip = nullptr;
+ GLES3::CanvasShaderData *shader_data_cache = nullptr;
// Record Batches.
// First item always forms its own batch.
@@ -602,7 +603,6 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
}
- GLES3::CanvasShaderData *shader_data_cache = nullptr;
if (material != state.canvas_instance_batches[state.current_batch_index].material) {
_new_batch(batch_broken);
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index 2e3e6263ed..600aa908cc 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -306,6 +306,15 @@ void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, Display
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
+
+ if (p_screen_rect.position != Vector2()) {
+ // Viewport doesn't cover entire window so clear window to black before blitting.
+ Size2i win_size = DisplayServer::get_singleton()->window_get_size();
+ glViewport(0, 0, win_size.width, win_size.height);
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
Vector2i screen_rect_end = p_screen_rect.get_end();
glBlitFramebuffer(0, 0, rt->size.x, rt->size.y,
p_screen_rect.position.x, flip_y ? screen_rect_end.y : p_screen_rect.position.y, screen_rect_end.x, flip_y ? p_screen_rect.position.y : screen_rect_end.y,
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index aa8df606cf..2c530e3ae6 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -2881,6 +2881,7 @@ void MaterialStorage::material_set_render_priority(RID p_material, int priority)
if (material->data) {
material->data->set_render_priority(priority);
}
+ material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL);
}
bool MaterialStorage::material_is_animated(RID p_material) {
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 0c7ea33b54..acbc3ce0dc 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -1540,6 +1540,10 @@ void EditorHelp::_update_doc() {
if (cd.properties[i].overridden) {
continue;
}
+ // Ignore undocumented private.
+ if (cd.properties[i].name.begins_with("_") && cd.properties[i].description.strip_edges().is_empty()) {
+ continue;
+ }
property_line[cd.properties[i].name] = class_desc->get_paragraph_count() - 2;
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index a46484bb0e..96a8cc8b91 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -1513,7 +1513,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
f = FileAccess::open(p_path, FileAccess::WRITE);
if (f.is_null()) {
DirAccess::remove_file_or_error(tmppath);
- add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), vformat(TTR("Can't open file to read from path \"%s\"."), tmppath));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), vformat(TTR("Can't open file for writing at path \"%s\"."), p_path));
return ERR_CANT_CREATE;
}
} else {
@@ -1521,7 +1521,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
f = FileAccess::open(p_path, FileAccess::READ_WRITE);
if (f.is_null()) {
DirAccess::remove_file_or_error(tmppath);
- add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), vformat(TTR("Can't open executable file from path \"%s\"."), tmppath));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), vformat(TTR("Can't open file for reading-writing at path \"%s\"."), p_path));
return ERR_FILE_CANT_OPEN;
}
diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp
index bbfd73be5e..7f934bc45b 100644
--- a/editor/export/editor_export_platform_pc.cpp
+++ b/editor/export/editor_export_platform_pc.cpp
@@ -33,6 +33,9 @@
#include "core/config/project_settings.h"
void EditorExportPlatformPC::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
+ if (p_preset->get("texture_format/bptc")) {
+ r_features->push_back("bptc");
+ }
if (p_preset->get("texture_format/s3tc")) {
r_features->push_back("s3tc");
}
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index b001b4f766..fc1d8936d5 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -3392,7 +3392,6 @@ void Node3DEditorViewport::_menu_option(int p_option) {
VIEW_DISPLAY_SHADELESS,
VIEW_DISPLAY_LIGHTING,
VIEW_DISPLAY_NORMAL_BUFFER,
- VIEW_DISPLAY_WIREFRAME,
VIEW_DISPLAY_DEBUG_SHADOW_ATLAS,
VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS,
VIEW_DISPLAY_DEBUG_VOXEL_GI_ALBEDO,
@@ -3422,7 +3421,6 @@ void Node3DEditorViewport::_menu_option(int p_option) {
Viewport::DEBUG_DRAW_UNSHADED,
Viewport::DEBUG_DRAW_LIGHTING,
Viewport::DEBUG_DRAW_NORMAL_BUFFER,
- Viewport::DEBUG_DRAW_WIREFRAME,
Viewport::DEBUG_DRAW_SHADOW_ATLAS,
Viewport::DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS,
Viewport::DEBUG_DRAW_VOXEL_GI_ALBEDO,
@@ -3445,9 +3443,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
Viewport::DEBUG_DRAW_MOTION_VECTORS,
};
- int idx = 0;
-
- while (display_options[idx] != VIEW_MAX) {
+ for (int idx = 0; display_options[idx] != VIEW_MAX; idx++) {
int id = display_options[idx];
int item_idx = view_menu->get_popup()->get_item_index(id);
if (item_idx != -1) {
@@ -3461,7 +3457,6 @@ void Node3DEditorViewport::_menu_option(int p_option) {
if (id == p_option) {
viewport->set_debug_draw(debug_draw_modes[idx]);
}
- idx++;
}
} break;
}
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 74d82aa4a2..6d747ba6a8 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/input/input.h"
#include "core/io/file_access.h"
+#include "core/io/json.h"
#include "core/io/resource_loader.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@@ -209,6 +210,27 @@ Ref<EditorSyntaxHighlighter> EditorPlainTextSyntaxHighlighter::_create() const {
return syntax_highlighter;
}
+////
+
+void EditorJSONSyntaxHighlighter::_update_cache() {
+ highlighter->set_text_edit(text_edit);
+ highlighter->clear_keyword_colors();
+ highlighter->clear_member_keyword_colors();
+ highlighter->clear_color_regions();
+
+ highlighter->set_symbol_color(EDITOR_GET("text_editor/theme/highlighting/symbol_color"));
+ highlighter->set_number_color(EDITOR_GET("text_editor/theme/highlighting/number_color"));
+
+ const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color");
+ highlighter->add_color_region("\"", "\"", string_color);
+}
+
+Ref<EditorSyntaxHighlighter> EditorJSONSyntaxHighlighter::_create() const {
+ Ref<EditorJSONSyntaxHighlighter> syntax_highlighter;
+ syntax_highlighter.instantiate();
+ return syntax_highlighter;
+}
+
////////////////////////////////////////////////////////////////////////////////
/*** SCRIPT EDITOR ****/
@@ -702,9 +724,10 @@ void ScriptEditor::_open_recent_script(int p_idx) {
if (FileAccess::exists(path)) {
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
+ ResourceLoader::get_recognized_extensions_for_type("JSON", &extensions);
if (extensions.find(path.get_extension())) {
- Ref<Script> scr = ResourceLoader::load(path);
+ Ref<Resource> scr = ResourceLoader::load(path);
if (scr.is_valid()) {
edit(scr, true);
return;
@@ -1182,6 +1205,7 @@ void ScriptEditor::_menu_option(int p_option) {
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
+ ResourceLoader::get_recognized_extensions_for_type("JSON", &extensions);
bool built_in = !path.is_resource_file();
if (extensions.find(path.get_extension()) || built_in) {
@@ -1196,7 +1220,7 @@ void ScriptEditor::_menu_option(int p_option) {
}
}
- Ref<Script> scr = ResourceLoader::load(path);
+ Ref<Resource> scr = ResourceLoader::load(path);
if (!scr.is_valid()) {
EditorNode::get_singleton()->show_warning(TTR("Could not load file at:") + "\n\n" + path, TTR("Error!"));
file_dialog_option = -1;
@@ -2319,12 +2343,23 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col,
}
se->add_syntax_highlighter(highlighter);
- if (scr != nullptr && !highlighter_set) {
- PackedStringArray languages = highlighter->_get_supported_languages();
+ if (highlighter_set) {
+ continue;
+ }
+
+ PackedStringArray languages = highlighter->_get_supported_languages();
+ // If script try language, else use extension.
+ if (scr != nullptr) {
if (languages.has(scr->get_language()->get_name())) {
se->set_syntax_highlighter(highlighter);
highlighter_set = true;
}
+ continue;
+ }
+
+ if (languages.has(p_resource->get_path().get_extension())) {
+ se->set_syntax_highlighter(highlighter);
+ highlighter_set = true;
}
}
@@ -2536,6 +2571,14 @@ void ScriptEditor::reload_scripts(bool p_refresh_only) {
scr->reload(true);
}
+ Ref<JSON> json = edited_res;
+ if (json != nullptr) {
+ Ref<JSON> rel_json = ResourceLoader::load(json->get_path(), json->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
+ ERR_CONTINUE(!rel_json.is_valid());
+ json->parse(rel_json->get_parsed_text(), true);
+ json->set_last_modified_time(rel_json->get_last_modified_time());
+ }
+
Ref<TextFile> text_file = edited_res;
if (text_file.is_valid()) {
text_file->reload_from_file();
@@ -2564,8 +2607,9 @@ void ScriptEditor::open_text_file_create_dialog(const String &p_base_path, const
Ref<Resource> ScriptEditor::open_file(const String &p_file) {
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
+ ResourceLoader::get_recognized_extensions_for_type("JSON", &extensions);
if (extensions.find(p_file.get_extension())) {
- Ref<Script> scr = ResourceLoader::load(p_file);
+ Ref<Resource> scr = ResourceLoader::load(p_file);
if (!scr.is_valid()) {
EditorNode::get_singleton()->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
return Ref<Resource>();
@@ -2866,8 +2910,8 @@ bool ScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data
if (file.is_empty() || !FileAccess::exists(file)) {
continue;
}
- if (ResourceLoader::exists(file, "Script")) {
- Ref<Script> scr = ResourceLoader::load(file);
+ if (ResourceLoader::exists(file, "Script") || ResourceLoader::exists(file, "JSON")) {
+ Ref<Resource> scr = ResourceLoader::load(file);
if (scr.is_valid()) {
return true;
}
@@ -2947,7 +2991,7 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co
continue;
}
- if (!ResourceLoader::exists(file, "Script") && !textfile_extensions.has(file.get_extension())) {
+ if (!ResourceLoader::exists(file, "Script") && !ResourceLoader::exists(file, "JSON") && !textfile_extensions.has(file.get_extension())) {
continue;
}
@@ -3105,6 +3149,7 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
HashSet<String> loaded_scripts;
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
+ ResourceLoader::get_recognized_extensions_for_type("JSON", &extensions);
for (int i = 0; i < scripts.size(); i++) {
String path = scripts[i];
@@ -3123,7 +3168,7 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
loaded_scripts.insert(path);
if (extensions.find(path.get_extension())) {
- Ref<Script> scr = ResourceLoader::load(path);
+ Ref<Resource> scr = ResourceLoader::load(path);
if (!scr.is_valid()) {
continue;
}
@@ -3477,6 +3522,12 @@ void ScriptEditor::_open_script_request(const String &p_path) {
return;
}
+ Ref<JSON> json = ResourceLoader::load(p_path);
+ if (json.is_valid()) {
+ script_editor->edit(json, false);
+ return;
+ }
+
Error err;
Ref<TextFile> text_file = script_editor->_load_text_file(p_path, &err);
if (text_file.is_valid()) {
@@ -3539,7 +3590,8 @@ void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_numb
return;
} else {
Ref<Script> scr = res;
- if (scr.is_valid()) {
+ Ref<JSON> json = res;
+ if (scr.is_valid() || json.is_valid()) {
edit(scr);
ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(_get_current_editor());
@@ -3942,6 +3994,10 @@ ScriptEditor::ScriptEditor() {
add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("ScriptEditorPanel"), SNAME("EditorStyles")));
tab_container->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("ScriptEditor"), SNAME("EditorStyles")));
+
+ Ref<EditorJSONSyntaxHighlighter> json_syntax_highlighter;
+ json_syntax_highlighter.instantiate();
+ register_syntax_highlighter(json_syntax_highlighter);
}
ScriptEditor::~ScriptEditor() {
@@ -3963,6 +4019,8 @@ void ScriptEditorPlugin::edit(Object *p_object) {
}
}
script_editor->edit(p_script);
+ } else if (Object::cast_to<JSON>(p_object)) {
+ script_editor->edit(Object::cast_to<JSON>(p_object));
} else if (Object::cast_to<TextFile>(p_object)) {
script_editor->edit(Object::cast_to<TextFile>(p_object));
}
@@ -3977,6 +4035,10 @@ bool ScriptEditorPlugin::handles(Object *p_object) const {
return true;
}
+ if (Object::cast_to<JSON>(p_object)) {
+ return true;
+ }
+
return p_object->is_class("Script");
}
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 988d07621c..f8e684ae34 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -96,6 +96,24 @@ public:
virtual Ref<EditorSyntaxHighlighter> _create() const override;
};
+class EditorJSONSyntaxHighlighter : public EditorSyntaxHighlighter {
+ GDCLASS(EditorJSONSyntaxHighlighter, EditorSyntaxHighlighter)
+
+private:
+ Ref<CodeHighlighter> highlighter;
+
+public:
+ virtual void _update_cache() override;
+ virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override { return highlighter->get_line_syntax_highlighting(p_line); }
+
+ virtual PackedStringArray _get_supported_languages() const override { return PackedStringArray{ "json" }; }
+ virtual String _get_name() const override { return TTR("JSON"); }
+
+ virtual Ref<EditorSyntaxHighlighter> _create() const override;
+
+ EditorJSONSyntaxHighlighter() { highlighter.instantiate(); }
+};
+
///////////////////////////////////////////////////////////////////////////////
class ScriptEditorQuickOpen : public ConfirmationDialog {
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index a376699e54..ceb170d7d8 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -30,6 +30,7 @@
#include "text_editor.h"
+#include "core/io/json.h"
#include "core/os/keyboard.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
@@ -67,12 +68,12 @@ void TextEditor::_load_theme_settings() {
String TextEditor::get_name() {
String name;
- name = text_file->get_path().get_file();
+ name = edited_res->get_path().get_file();
if (name.is_empty()) {
// This appears for newly created built-in text_files before saving the scene.
name = TTR("[unsaved]");
- } else if (text_file->is_built_in()) {
- const String &text_file_name = text_file->get_name();
+ } else if (edited_res->is_built_in()) {
+ const String &text_file_name = edited_res->get_name();
if (!text_file_name.is_empty()) {
// If the built-in text_file has a custom resource name defined,
// display the built-in text_file name as follows: `ResourceName (scene_file.tscn)`
@@ -88,20 +89,29 @@ String TextEditor::get_name() {
}
Ref<Texture2D> TextEditor::get_theme_icon() {
- return EditorNode::get_singleton()->get_object_icon(text_file.ptr(), "");
+ return EditorNode::get_singleton()->get_object_icon(edited_res.ptr(), "TextFile");
}
Ref<Resource> TextEditor::get_edited_resource() const {
- return text_file;
+ return edited_res;
}
void TextEditor::set_edited_resource(const Ref<Resource> &p_res) {
- ERR_FAIL_COND(text_file.is_valid());
+ ERR_FAIL_COND(edited_res.is_valid());
ERR_FAIL_COND(p_res.is_null());
- text_file = p_res;
+ edited_res = p_res;
+
+ Ref<TextFile> text_file = edited_res;
+ if (text_file != nullptr) {
+ code_editor->get_text_editor()->set_text(text_file->get_text());
+ }
+
+ Ref<JSON> json_file = edited_res;
+ if (json_file != nullptr) {
+ code_editor->get_text_editor()->set_text(json_file->get_parsed_text());
+ }
- code_editor->get_text_editor()->set_text(text_file->get_text());
code_editor->get_text_editor()->clear_undo_history();
code_editor->get_text_editor()->tag_saved_version();
@@ -118,6 +128,8 @@ void TextEditor::enable_editor(Control *p_shortcut_context) {
_load_theme_settings();
+ _validate_script();
+
if (p_shortcut_context) {
for (int i = 0; i < edit_hb->get_child_count(); ++i) {
Control *c = cast_to<Control>(edit_hb->get_child(i));
@@ -143,7 +155,7 @@ PackedInt32Array TextEditor::get_breakpoints() {
}
void TextEditor::reload_text() {
- ERR_FAIL_COND(text_file.is_null());
+ ERR_FAIL_COND(edited_res.is_null());
CodeEdit *te = code_editor->get_text_editor();
int column = te->get_caret_column();
@@ -151,7 +163,16 @@ void TextEditor::reload_text() {
int h = te->get_h_scroll();
int v = te->get_v_scroll();
- te->set_text(text_file->get_text());
+ Ref<TextFile> text_file = edited_res;
+ if (text_file != nullptr) {
+ te->set_text(text_file->get_text());
+ }
+
+ Ref<JSON> json_file = edited_res;
+ if (json_file != nullptr) {
+ te->set_text(json_file->get_parsed_text());
+ }
+
te->set_caret_line(row);
te->set_caret_column(column);
te->set_h_scroll(h);
@@ -166,6 +187,20 @@ void TextEditor::reload_text() {
void TextEditor::_validate_script() {
emit_signal(SNAME("name_changed"));
emit_signal(SNAME("edited_script_changed"));
+
+ Ref<JSON> json_file = edited_res;
+ if (json_file != nullptr) {
+ CodeEdit *te = code_editor->get_text_editor();
+
+ te->set_line_background_color(code_editor->get_error_pos().x, Color(0, 0, 0, 0));
+ code_editor->set_error("");
+
+ if (json_file->parse(te->get_text(), true) != OK) {
+ code_editor->set_error(json_file->get_error_message());
+ code_editor->set_error_pos(json_file->get_error_line(), 0);
+ te->set_line_background_color(code_editor->get_error_pos().x, EDITOR_GET("text_editor/theme/highlighting/mark_color"));
+ }
+ }
}
void TextEditor::_update_bookmark_list() {
@@ -204,13 +239,22 @@ void TextEditor::_bookmark_item_pressed(int p_idx) {
}
void TextEditor::apply_code() {
- text_file->set_text(code_editor->get_text_editor()->get_text());
+ Ref<TextFile> text_file = edited_res;
+ if (text_file != nullptr) {
+ text_file->set_text(code_editor->get_text_editor()->get_text());
+ }
+
+ Ref<JSON> json_file = edited_res;
+ if (json_file != nullptr) {
+ json_file->parse(code_editor->get_text_editor()->get_text(), true);
+ }
+ code_editor->get_text_editor()->get_syntax_highlighter()->update_cache();
}
bool TextEditor::is_unsaved() {
const bool unsaved =
code_editor->get_text_editor()->get_version() != code_editor->get_text_editor()->get_saved_version() ||
- text_file->get_path().is_empty(); // In memory.
+ edited_res->get_path().is_empty(); // In memory.
return unsaved;
}
@@ -431,7 +475,7 @@ void TextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) {
}
static ScriptEditorBase *create_editor(const Ref<Resource> &p_resource) {
- if (Object::cast_to<TextFile>(*p_resource)) {
+ if (Object::cast_to<TextFile>(*p_resource) || Object::cast_to<JSON>(*p_resource)) {
return memnew(TextEditor);
}
return nullptr;
@@ -656,4 +700,5 @@ TextEditor::~TextEditor() {
}
void TextEditor::validate() {
+ this->code_editor->validate_script();
}
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index 6db81508b3..85e0fee627 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -41,7 +41,7 @@ class TextEditor : public ScriptEditorBase {
private:
CodeTextEditor *code_editor = nullptr;
- Ref<TextFile> text_file;
+ Ref<Resource> edited_res;
bool editor_enabled = false;
HBoxContainer *edit_hb = nullptr;
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index fd651dd507..43c6d1a48b 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -404,13 +404,16 @@ void TileAtlasView::_draw_background_right() {
}
void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
- ERR_FAIL_COND(!p_tile_set);
- ERR_FAIL_COND(!p_tile_set_atlas_source);
+ tile_set = p_tile_set;
+ tile_set_atlas_source = p_tile_set_atlas_source;
+
+ if (!tile_set) {
+ return;
+ }
+
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
- tile_set = p_tile_set;
- tile_set_atlas_source = p_tile_set_atlas_source;
source_id = p_source_id;
// Show or hide the view.
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 912fdb03a9..e56541b50d 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -120,10 +120,9 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_bind_methods() {
}
void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
- ERR_FAIL_COND(!p_tile_set.is_valid());
ERR_FAIL_COND(!p_tile_set_atlas_source);
ERR_FAIL_COND(p_source_id < 0);
- ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
+ ERR_FAIL_COND(p_tile_set.is_valid() && p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
if (p_tile_set == tile_set && p_tile_set_atlas_source == tile_set_atlas_source && p_source_id == source_id) {
return;
@@ -611,6 +610,10 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
tile_data_editors_tree->clear();
+ if (tile_set.is_null()) {
+ return;
+ }
+
TreeItem *root = tile_data_editors_tree->create_item();
TreeItem *group;
@@ -919,6 +922,10 @@ void TileSetAtlasSourceEditor::_update_atlas_view() {
alternative_tiles_control->get_child(i)->queue_free();
}
+ if (tile_set.is_null()) {
+ return;
+ }
+
Vector2i pos;
Vector2 texture_region_base_size = tile_set_atlas_source->get_texture_region_size();
int texture_region_base_size_min = MIN(texture_region_base_size.x, texture_region_base_size.y);
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 923b2fe30d..0b7e4e50e6 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -4,7 +4,7 @@
Built-in GDScript functions.
</brief_description>
<description>
- A list of GDScript-specific utility functions accessed in any script.
+ A list of GDScript-specific utility functions and annotations accessible from any script.
For the list of the global functions and constants see [@GlobalScope].
</description>
<tutorials>
@@ -20,7 +20,7 @@
<description>
Returns a [Color] constructed from red ([param r8]), green ([param g8]), blue ([param b8]), and optionally alpha ([param a8]) integer channels, each divided by [code]255.0[/code] for their final value.
[codeblock]
- var red = Color8(255, 0, 0) # Same as Color(1, 0, 0)
+ var red = Color8(255, 0, 0) # Same as Color(1, 0, 0).
var dark_blue = Color8(0, 0, 51) # Same as Color(0, 0, 0.2).
var my_color = Color8(306, 255, 0, 102) # Same as Color(1.2, 1, 0, 0.4).
[/codeblock]
@@ -37,10 +37,10 @@
[codeblock]
# Imagine we always want speed to be between 0 and 20.
var speed = -10
- assert(speed &lt; 20) # True, the program will continue
- assert(speed &gt;= 0) # False, the program will stop
- assert(speed &gt;= 0 and speed &lt; 20) # You can also combine the two conditional statements in one check
- assert(speed &lt; 20, "the speed limit is 20") # Show a message
+ assert(speed &lt; 20) # True, the program will continue.
+ assert(speed &gt;= 0) # False, the program will stop.
+ assert(speed &gt;= 0 and speed &lt; 20) # You can also combine the two conditional statements in one check.
+ assert(speed &lt; 20, "the speed limit is 20") # Show a message.
[/codeblock]
</description>
</method>
@@ -140,7 +140,7 @@
<param index="0" name="path" type="String" />
<description>
Returns a [Resource] from the filesystem located at the absolute [param path]. Unless it's already referenced elsewhere (such as in another script or in the scene), the resource is loaded from disk on function call, which might cause a slight delay, especially when loading large scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload].
- [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
+ [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing "Copy Path", or by dragging the file from the FileSystem dock into the current script.
[codeblock]
# Load a scene called "main" located in the root of the project directory and cache it in a variable.
var main = load("res://main.tscn") # main will contain a PackedScene resource.
@@ -155,7 +155,7 @@
<param index="0" name="path" type="String" />
<description>
Returns a [Resource] from the filesystem located at [param path]. During run-time, the resource is loaded when the script is being parsed. This function effectively acts as a reference to that resource. Note that this function requires [param path] to be a constant [String]. If you want to load a resource from a dynamic/variable path, use [method load].
- [b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
+ [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the Assets Panel and choosing "Copy Path", or by dragging the file from the FileSystem dock into the current script.
[codeblock]
# Create instance of a scene.
var diamond = preload("res://diamond.tscn").instantiate()
@@ -259,10 +259,12 @@
<annotation name="@export">
<return type="void" />
<description>
- Mark the following property as exported (editable in the Inspector dock and saved to disk). To control the type of the exported property use the type hint notation.
+ Mark the following property as exported (editable in the Inspector dock and saved to disk). To control the type of the exported property, use the type hint notation.
[codeblock]
+ @export var string = ""
@export var int_number = 5
@export var float_number: float = 5
+ @export var image : Image
[/codeblock]
</description>
</annotation>
@@ -273,20 +275,20 @@
Define a new category for the following exported properties. This helps to organize properties in the Inspector dock.
See also [constant PROPERTY_USAGE_CATEGORY].
[codeblock]
- @export_category("My Properties")
- @export var number = 3
- @export var string = ""
+ @export_category("Statistics")
+ @export var hp = 30
+ @export var speed = 1.25
[/codeblock]
- [b]Note:[/b] Categories in the property list are supposed to indicate different base types, so the use of this annotation is not encouraged. See [annotation @export_group] and [annotation @export_subgroup] instead.
+ [b]Note:[/b] Categories in the Inspector dock's list usually divide properties coming from different classes (Node, Node2D, Sprite, etc.). For better clarity, it's recommended to use [annotation @export_group] and [annotation @export_subgroup], instead.
</description>
</annotation>
<annotation name="@export_color_no_alpha">
<return type="void" />
<description>
- Export a [Color] property without transparency (its alpha fixed as [code]1.0[/code]).
+ Export a [Color] property without allowing its transparency ([member Color.a]) to be edited.
See also [constant PROPERTY_HINT_COLOR_NO_ALPHA].
[codeblock]
- @export_color_no_alpha var modulate_color: Color
+ @export_color_no_alpha var dye_color : Color
[/codeblock]
</description>
</annotation>
@@ -296,7 +298,7 @@
Export a [String] property as a path to a directory. The path will be limited to the project folder and its subfolders. See [annotation @export_global_dir] to allow picking from the entire filesystem.
See also [constant PROPERTY_HINT_DIR].
[codeblock]
- @export_dir var sprite_folder: String
+ @export_dir var sprite_folder_path: String
[/codeblock]
</description>
</annotation>
@@ -343,8 +345,8 @@
If [param filter] is provided, only matching files will be available for picking.
See also [constant PROPERTY_HINT_FILE].
[codeblock]
- @export_file var sound_effect_file: String
- @export_file("*.txt") var notes_file: String
+ @export_file var sound_effect_path: String
+ @export_file("*.txt") var notes_path: String
[/codeblock]
</description>
</annotation>
@@ -436,10 +438,10 @@
<annotation name="@export_global_dir">
<return type="void" />
<description>
- Export a [String] property as a path to a directory. The path can be picked from the entire filesystem. See [annotation @export_dir] to limit it to the project folder and its subfolders.
+ Export a [String] property as an absolute path to a directory. The path can be picked from the entire filesystem. See [annotation @export_dir] to limit it to the project folder and its subfolders.
See also [constant PROPERTY_HINT_GLOBAL_DIR].
[codeblock]
- @export_global_dir var sprite_folder: String
+ @export_global_dir var sprite_folder_path: String
[/codeblock]
</description>
</annotation>
@@ -447,12 +449,12 @@
<return type="void" />
<param index="0" name="filter" type="String" default="&quot;&quot;" />
<description>
- Export a [String] property as a path to a file. The path can be picked from the entire filesystem. See [annotation @export_file] to limit it to the project folder and its subfolders.
+ Export a [String] property as an absolute path to a file. The path can be picked from the entire filesystem. See [annotation @export_file] to limit it to the project folder and its subfolders.
If [param filter] is provided, only matching files will be available for picking.
See also [constant PROPERTY_HINT_GLOBAL_FILE].
[codeblock]
- @export_global_file var sound_effect_file: String
- @export_global_file("*.txt") var notes_file: String
+ @export_global_file var sound_effect_path: String
+ @export_global_file("*.txt") var notes_path: String
[/codeblock]
</description>
</annotation>
@@ -466,13 +468,13 @@
Groups cannot be nested, use [annotation @export_subgroup] to add subgroups within groups.
See also [constant PROPERTY_USAGE_GROUP].
[codeblock]
- @export_group("My Properties")
- @export var number = 3
- @export var string = ""
+ @export_group("Racer Properties")
+ @export var nickname = "Nick"
+ @export var age = 26
- @export_group("Prefixed Properties", "prefix_")
- @export var prefix_number = 3
- @export var prefix_string = ""
+ @export_group("Car Properties", "car_")
+ @export var car_label = "Speedy"
+ @export var car_number = 3
@export_group("", "")
@export var ungrouped_number = 3
@@ -544,13 +546,13 @@
Define a new subgroup for the following exported properties. This helps to organize properties in the Inspector dock. Subgroups work exactly like groups, except they need a parent group to exist. See [annotation @export_group].
See also [constant PROPERTY_USAGE_SUBGROUP].
[codeblock]
- @export_group("My Properties")
- @export var number = 3
- @export var string = ""
+ @export_group("Racer Properties")
+ @export var nickname = "Nick"
+ @export var age = 26
- @export_subgroup("My Prefixed Properties", "prefix_")
- @export var prefix_number = 3
- @export var prefix_string = ""
+ @export_subgroup("Car Properties", "car_")
+ @export var car_label = "Speedy"
+ @export var car_number = 3
[/codeblock]
[b]Note:[/b] Subgroups cannot be nested, they only provide one extra level of depth. Just like the next group ends the previous group, so do the subsequent subgroups.
</description>
@@ -571,7 +573,7 @@
<annotation name="@onready">
<return type="void" />
<description>
- Mark the following property as assigned on [Node]'s ready state change. Values for these properties are not assigned immediately upon the node's creation, and instead are computed and stored right before [method Node._ready].
+ Mark the following property as assigned when the [Node] is ready. Values for these properties are not assigned immediately when the node is initialized ([method Object._init]), and instead are computed and stored right before [method Node._ready].
[codeblock]
@onready var character_name: Label = $Label
[/codeblock]
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index de0dacece3..8d09249125 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -4278,11 +4278,15 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo
}
} else {
GDScriptParser::DataType datatype = p_variable->get_datatype();
- if (datatype.is_hard_type() && datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) {
- if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type()) {
- result = make_array_from_element_datatype(datatype.get_container_element_type());
- } else {
- VariantInternal::initialize(&result, datatype.builtin_type);
+ if (datatype.is_hard_type()) {
+ if (datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) {
+ if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type()) {
+ result = make_array_from_element_datatype(datatype.get_container_element_type());
+ } else {
+ VariantInternal::initialize(&result, datatype.builtin_type);
+ }
+ } else if (datatype.kind == GDScriptParser::DataType::ENUM) {
+ result = 0;
}
}
}
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 028028a103..e3ba290eb2 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -3285,7 +3285,6 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
tex.instantiate();
tex->set_name(img->get_name());
tex->set_keep_compressed_buffer(true);
- p_state->source_images.push_back(img);
tex->create_from_image(img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL);
p_state->images.push_back(tex);
p_state->source_images.push_back(img);
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index 3466cb10df..68b6bc4a24 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -742,6 +742,7 @@ Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_bu
ofs += 4;
uint32_t size = decode_uint32(&p_buffer[ofs]);
ofs += 4;
+ ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA);
MultiplayerSynchronizer *sync = nullptr;
if (net_id & 0x80000000) {
sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_path_cache()->get_cached_object(p_from, net_id & 0x7FFFFFFF));
@@ -756,14 +757,15 @@ Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_bu
}
Node *node = sync->get_root_node();
if (sync->get_multiplayer_authority() != p_from || !node) {
- ERR_CONTINUE(true);
+ // Not valid for me.
+ ofs += size;
+ ERR_CONTINUE_MSG(true, "Ignoring sync data from non-authority or for missing node.");
}
if (!sync->update_inbound_sync_time(time)) {
// State is too old.
ofs += size;
continue;
}
- ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_BUG);
const List<NodePath> props = sync->get_replication_config()->get_sync_properties();
Vector<Variant> vars;
vars.resize(props.size());
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index c3cb1c5f13..79e8c3a6d6 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -757,7 +757,7 @@ COMMAND_1(free, RID, p_object) {
agent_owner.free(p_object);
} else {
- ERR_FAIL_COND("Invalid ID.");
+ ERR_FAIL_COND("Attempted to free a NavigationServer RID that did not exist (or was already freed).");
}
}
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 525c62fbf2..5d70af56bd 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -4936,6 +4936,11 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
win_rect.position = wpos;
}
+ // Position and size hints are set from these values before they are updated to the actual
+ // window size, so we need to initialize them here.
+ wd.position = win_rect.position;
+ wd.size = win_rect.size;
+
{
wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo.screen), win_rect.position.x, win_rect.position.y, win_rect.size.width > 0 ? win_rect.size.width : 1, win_rect.size.height > 0 ? win_rect.size.height : 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes);
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 2e0b16388f..11e59d9858 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -754,8 +754,9 @@ TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() {
void TileMap::set_navigation_map(int p_layer, RID p_map) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
+ ERR_FAIL_COND_MSG(!is_inside_tree(), "A TileMap navigation map can only be changed while inside the SceneTree.");
layers[p_layer].navigation_map = p_map;
+ layers[p_layer].uses_world_navigation_map = p_map == get_world_2d()->get_navigation_map();
}
RID TileMap::get_navigation_map(int p_layer) const {
@@ -1111,10 +1112,12 @@ void TileMap::_navigation_update_layer(int p_layer) {
if (p_layer == 0 && is_inside_tree()) {
// Use the default World2D navigation map for the first layer when empty.
layers[p_layer].navigation_map = get_world_2d()->get_navigation_map();
+ layers[p_layer].uses_world_navigation_map = true;
} else {
RID new_layer_map = NavigationServer2D::get_singleton()->map_create();
NavigationServer2D::get_singleton()->map_set_active(new_layer_map, true);
layers[p_layer].navigation_map = new_layer_map;
+ layers[p_layer].uses_world_navigation_map = false;
}
}
}
@@ -1124,7 +1127,7 @@ void TileMap::_navigation_cleanup_layer(int p_layer) {
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
if (layers[p_layer].navigation_map.is_valid()) {
- if (is_inside_tree() && layers[p_layer].navigation_map == get_world_2d()->get_navigation_map()) {
+ if (layers[p_layer].uses_world_navigation_map) {
// Do not delete the World2D default navigation map.
return;
}
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 62d7612be2..e9c1cb0c11 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -212,6 +212,7 @@ private:
HashMap<Vector2i, TileMapQuadrant> quadrant_map;
SelfList<TileMapQuadrant>::List dirty_quadrant_list;
RID navigation_map;
+ bool uses_world_navigation_map = false;
};
LocalVector<TileMapLayer> layers;
int selected_layer = -1;
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index 2ea1b93810..432004dedc 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -36,7 +36,7 @@
void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
+ if (get_flag(FLAG_POPUP) && key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
_close_pressed();
}
}
@@ -102,12 +102,17 @@ void Popup::_notification(int p_what) {
}
} break;
- case NOTIFICATION_WM_CLOSE_REQUEST:
- case NOTIFICATION_APPLICATION_FOCUS_OUT: {
+ case NOTIFICATION_WM_CLOSE_REQUEST: {
if (!is_in_edited_scene_root()) {
_close_pressed();
}
} break;
+
+ case NOTIFICATION_APPLICATION_FOCUS_OUT: {
+ if (!is_in_edited_scene_root() && get_flag(FLAG_POPUP)) {
+ _close_pressed();
+ }
+ } break;
}
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 23b7c072be..126b1d54fc 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -166,6 +166,24 @@ ViewportTexture::~ViewportTexture() {
}
void Viewport::_sub_window_update_order() {
+ if (gui.sub_windows.size() < 2) {
+ return;
+ }
+
+ if (!gui.sub_windows[gui.sub_windows.size() - 1].window->get_flag(Window::FLAG_ALWAYS_ON_TOP)) {
+ int index = gui.sub_windows.size() - 1;
+
+ while (index > 0 && gui.sub_windows[index - 1].window->get_flag(Window::FLAG_ALWAYS_ON_TOP)) {
+ --index;
+ }
+
+ if (index != (gui.sub_windows.size() - 1)) {
+ SubWindow sw = gui.sub_windows[gui.sub_windows.size() - 1];
+ gui.sub_windows.remove_at(gui.sub_windows.size() - 1);
+ gui.sub_windows.insert(index, sw);
+ }
+ }
+
for (int i = 0; i < gui.sub_windows.size(); i++) {
RS::get_singleton()->canvas_item_set_draw_index(gui.sub_windows[i].canvas_item, i);
}
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 9092be93e6..b79a9ba444 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -984,17 +984,13 @@ void Window::_update_viewport_size() {
Size2 margin;
Size2 offset;
- //black bars and margin
+
if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.x < video_mode.x) {
margin.x = Math::round((video_mode.x - screen_size.x) / 2.0);
- //RenderingServer::get_singleton()->black_bars_set_margins(margin.x, 0, margin.x, 0);
offset.x = Math::round(margin.x * viewport_size.y / screen_size.y);
} else if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.y < video_mode.y) {
margin.y = Math::round((video_mode.y - screen_size.y) / 2.0);
- //RenderingServer::get_singleton()->black_bars_set_margins(0, margin.y, 0, margin.y);
offset.y = Math::round(margin.y * viewport_size.x / screen_size.x);
- } else {
- //RenderingServer::get_singleton()->black_bars_set_margins(0, 0, 0, 0);
}
switch (content_scale_mode) {
@@ -1121,6 +1117,7 @@ void Window::_notification(int p_what) {
position = DisplayServer::get_singleton()->window_get_position(window_id);
size = DisplayServer::get_singleton()->window_get_size(window_id);
}
+ _update_window_size(); // Inform DisplayServer of minimum and maximum size.
_update_viewport_size(); // Then feed back to the viewport.
_update_window_callbacks();
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_VISIBLE);
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index 02460bdb18..fbe97b9acb 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -115,7 +115,6 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free);
ClassDB::bind_method(D_METHOD("set_active", "active"), &NavigationServer3D::set_active);
- ClassDB::bind_method(D_METHOD("process", "delta_time"), &NavigationServer3D::process);
ADD_SIGNAL(MethodInfo("map_changed", PropertyInfo(Variant::RID, "map")));
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index d631a89dd2..6f67d628a9 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -2803,6 +2803,7 @@ void MaterialStorage::material_set_render_priority(RID p_material, int priority)
if (material->data) {
material->data->set_render_priority(priority);
}
+ material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL);
}
bool MaterialStorage::material_is_animated(RID p_material) {