summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.pre-commit-config.yaml23
-rw-r--r--SConstruct50
-rw-r--r--core/SCsub12
-rw-r--r--core/core_builders.py12
-rw-r--r--core/extension/SCsub2
-rw-r--r--core/extension/make_wrappers.py2
-rw-r--r--core/input/SCsub1
-rw-r--r--core/math/geometry_2d.h2
-rw-r--r--core/object/class_db.cpp2
-rw-r--r--doc/classes/@GlobalScope.xml3
-rw-r--r--doc/classes/Button.xml2
-rw-r--r--doc/classes/EditorInspector.xml1
-rw-r--r--doc/classes/Geometry2D.xml1
-rw-r--r--doc/classes/Tree.xml6
-rw-r--r--doc/classes/TreeItem.xml23
-rwxr-xr-xdoc/tools/doc_status.py8
-rwxr-xr-xdoc/tools/make_rst.py40
-rw-r--r--drivers/gles3/storage/texture_storage.cpp16
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.cpp6
-rw-r--r--editor/SCsub5
-rw-r--r--editor/action_map_editor.cpp6
-rw-r--r--editor/action_map_editor.h3
-rw-r--r--editor/doc_tools.cpp19
-rw-r--r--editor/editor_builders.py1
-rw-r--r--editor/editor_feature_profile.cpp20
-rw-r--r--editor/editor_feature_profile.h3
-rw-r--r--editor/editor_help.cpp2
-rw-r--r--editor/editor_inspector.cpp3
-rw-r--r--editor/editor_settings_dialog.cpp13
-rw-r--r--editor/editor_settings_dialog.h14
-rw-r--r--editor/export/project_export.cpp24
-rw-r--r--editor/gui/scene_tree_editor.cpp21
-rw-r--r--editor/icons/AudioStreamPlayer.svg2
-rw-r--r--editor/icons/AudioStreamPlayer2D.svg2
-rw-r--r--editor/icons/AudioStreamPlayer3D.svg2
-rw-r--r--editor/icons/SCsub2
-rw-r--r--editor/import/3d/resource_importer_scene.cpp3
-rw-r--r--editor/import_dock.cpp14
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp366
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h20
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp148
-rw-r--r--editor/plugins/node_3d_editor_plugin.h3
-rw-r--r--editor/plugins/script_editor_plugin.cpp3
-rw-r--r--editor/plugins/script_text_editor.cpp5
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp4
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp15
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp3
-rw-r--r--editor/project_manager/project_list.cpp59
-rw-r--r--editor/project_manager/project_list.h7
-rw-r--r--editor/project_settings_editor.cpp2
-rw-r--r--editor/rename_dialog.cpp1
-rw-r--r--editor/scene_tree_dock.cpp118
-rw-r--r--editor/scene_tree_dock.h3
-rw-r--r--editor/themes/SCsub2
-rw-r--r--gles3_builders.py13
-rw-r--r--glsl_builders.py9
-rw-r--r--main/main.cpp11
-rw-r--r--methods.py51
-rwxr-xr-x[-rw-r--r--]misc/scripts/dotnet_format.py0
-rwxr-xr-x[-rw-r--r--]misc/scripts/file_format.py0
-rwxr-xr-x[-rw-r--r--]misc/scripts/gitignore_check.sh0
-rwxr-xr-x[-rw-r--r--]misc/scripts/header_guards.py0
-rwxr-xr-xmisc/scripts/install_d3d12_sdk_windows.py4
-rw-r--r--modules/SCsub3
-rw-r--r--modules/fbx/editor/editor_scene_importer_ufbx.cpp2
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml3
-rw-r--r--modules/gdscript/gdscript.cpp48
-rw-r--r--modules/gdscript/gdscript_compiler.cpp6
-rw-r--r--modules/gdscript/gdscript_editor.cpp29
-rw-r--r--modules/gdscript/gdscript_parser.cpp43
-rw-r--r--modules/gdscript/gdscript_parser.h1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/export_enum_as_dictionary.out10
-rw-r--r--modules/gdscript/tests/scripts/parser/features/annotations.out24
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_arrays.out138
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_enum.out40
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_variable.gd34
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_variable.out60
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out6
-rw-r--r--modules/gdscript/tests/scripts/utils.notest.gd15
-rwxr-xr-xmodules/mono/build_scripts/build_assemblies.py10
-rw-r--r--modules/mono/build_scripts/mono_configure.py4
-rw-r--r--modules/mono/config.py2
-rw-r--r--modules/mono/doc_classes/CSharpScript.xml1
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml20
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs6
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp11
-rw-r--r--modules/mono/mono_gd/gd_mono.h8
-rw-r--r--modules/mono/register_types.cpp3
-rw-r--r--modules/navigation/3d/nav_mesh_generator_3d.cpp26
-rw-r--r--modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml6
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper.h1
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.cpp5
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.h2
-rw-r--r--modules/openxr/extensions/openxr_fb_foveation_extension.cpp37
-rw-r--r--modules/openxr/extensions/openxr_fb_foveation_extension.h12
-rw-r--r--modules/openxr/openxr_api.cpp4
-rw-r--r--modules/openxr/scene/openxr_composition_layer.cpp4
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct28
-rw-r--r--modules/text_server_adv/gdextension_build/methods.py62
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct26
-rw-r--r--modules/text_server_fb/gdextension_build/methods.py62
-rw-r--r--platform/SCsub3
-rw-r--r--platform/android/SCsub3
-rw-r--r--platform/android/api/jni_singleton.h1
-rw-r--r--platform/android/detect.py5
-rw-r--r--platform/android/java/app/config.gradle3
-rw-r--r--platform/android/java/build.gradle5
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt24
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java22
-rw-r--r--platform/android/java/nativeSrcsConfigs/CMakeLists.txt4
-rw-r--r--platform/android/java_godot_lib_jni.cpp4
-rw-r--r--platform/android/plugin/godot_plugin_jni.cpp18
-rw-r--r--platform/android/plugin/godot_plugin_jni.h4
-rw-r--r--platform/ios/SCsub6
-rw-r--r--platform/ios/detect.py4
-rw-r--r--platform/linuxbsd/detect.py6
-rw-r--r--platform/macos/SCsub10
-rw-r--r--platform/macos/detect.py8
-rw-r--r--platform/web/SCsub3
-rw-r--r--platform/web/detect.py11
-rw-r--r--platform/web/emscripten_helpers.py7
-rwxr-xr-xplatform/web/serve.py8
-rw-r--r--platform/windows/SCsub1
-rw-r--r--platform/windows/detect.py14
-rw-r--r--platform/windows/platform_windows_builders.py4
-rw-r--r--platform/windows/windows_terminal_logger.cpp28
-rw-r--r--platform_methods.py16
-rw-r--r--pyproject.toml17
-rw-r--r--scene/2d/tile_map.cpp85
-rw-r--r--scene/2d/tile_map.h4
-rw-r--r--scene/2d/tile_map_layer.cpp96
-rw-r--r--scene/2d/tile_map_layer.h4
-rw-r--r--scene/3d/skeleton_3d.cpp11
-rw-r--r--scene/gui/button.h3
-rw-r--r--scene/gui/check_box.cpp6
-rw-r--r--scene/gui/check_button.cpp6
-rw-r--r--scene/resources/shader.cpp2
-rw-r--r--scene/theme/SCsub1
-rw-r--r--scene/theme/icons/SCsub1
-rw-r--r--scu_builders.py21
-rw-r--r--servers/rendering/rendering_device.h2
-rw-r--r--servers/rendering/rendering_light_culler.h4
-rwxr-xr-x[-rw-r--r--]tests/create_test.py0
-rw-r--r--tests/python_build/test_gles3_builder.py2
-rw-r--r--tests/python_build/test_glsl_builder.py2
-rw-r--r--[-rwxr-xr-x]thirdparty/glslang/SPIRV/GlslangToSpv.cpp0
-rw-r--r--[-rwxr-xr-x]thirdparty/glslang/SPIRV/doc.cpp0
-rw-r--r--[-rwxr-xr-x]thirdparty/glslang/glslang/Include/BaseTypes.h0
-rw-r--r--[-rwxr-xr-x]thirdparty/glslang/glslang/MachineIndependent/Initialize.cpp0
-rw-r--r--[-rwxr-xr-x]thirdparty/glslang/glslang/MachineIndependent/Versions.h0
151 files changed, 1577 insertions, 934 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5a0e919c47..dd51d7b419 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -17,12 +17,12 @@ repos:
platform/android/java/lib/src/com/.*
)
- - repo: https://github.com/psf/black-pre-commit-mirror
- rev: 24.2.0
+ - repo: https://github.com/astral-sh/ruff-pre-commit
+ rev: v0.4.4
hooks:
- - id: black
- files: (\.py$|SConstruct|SCsub)
- types_or: [text]
+ - id: ruff
+ args: [--fix]
+ - id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.971
@@ -146,6 +146,7 @@ repos:
exclude: |
(?x)^(
core/math/bvh_.*\.inc$|
+ platform/(?!android|ios|linuxbsd|macos|web|windows)\w+/.*|
platform/android/java/lib/src/com/.*|
platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView\.java$|
platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper\.java$|
@@ -186,3 +187,15 @@ repos:
language: python
entry: python3 misc/scripts/dotnet_format.py
types_or: [c#]
+
+# End of upstream Godot pre-commit hooks.
+#
+# Keep this separation to let downstream forks add their own hooks to this file,
+# without running into merge conflicts when rebasing on latest upstream.
+#
+# Start of downstream pre-commit hooks.
+#
+# This is still the "repo: local" scope, so new local hooks can be defined directly at this indentation:
+# - id: new-local-hook
+# To add external repo hooks, bring the indentation back to:
+# - repo: my-remote-hook
diff --git a/SConstruct b/SConstruct
index c668ac7df3..7e51ef4fc4 100644
--- a/SConstruct
+++ b/SConstruct
@@ -10,21 +10,11 @@ import os
import pickle
import sys
import time
-from types import ModuleType
from collections import OrderedDict
-from importlib.util import spec_from_file_location, module_from_spec
-from SCons import __version__ as scons_raw_version
-
-# Enable ANSI escape code support on Windows 10 and later (for colored console output).
-# <https://github.com/python/cpython/issues/73245>
-if sys.platform == "win32":
- from ctypes import windll, c_int, byref
+from importlib.util import module_from_spec, spec_from_file_location
+from types import ModuleType
- stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
- mode = c_int(0)
- windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
- mode = c_int(mode.value | 4)
- windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
+from SCons import __version__ as scons_raw_version
# Explicitly resolve the helper modules, this is done to avoid clash with
# modules of the same name that might be randomly added (e.g. someone adding
@@ -63,17 +53,35 @@ _helper_module("core.core_builders", "core/core_builders.py")
_helper_module("main.main_builders", "main/main_builders.py")
# Local
-import methods
-import glsl_builders
import gles3_builders
+import glsl_builders
+import methods
import scu_builders
-from methods import print_warning, print_error
-from platform_methods import architectures, architecture_aliases
+from methods import print_error, print_warning
+from platform_methods import architecture_aliases, architectures
if ARGUMENTS.get("target", "editor") == "editor":
_helper_module("editor.editor_builders", "editor/editor_builders.py")
_helper_module("editor.template_builders", "editor/template_builders.py")
+# Enable ANSI escape code support on Windows 10 and later (for colored console output).
+# <https://github.com/python/cpython/issues/73245>
+if sys.stdout.isatty() and sys.platform == "win32":
+ try:
+ from ctypes import WinError, byref, windll # type: ignore
+ from ctypes.wintypes import DWORD # type: ignore
+
+ stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11))
+ mode = DWORD(0)
+ if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)):
+ raise WinError()
+ mode = DWORD(mode.value | 4)
+ if not windll.kernel32.SetConsoleMode(stdout_handle, mode):
+ raise WinError()
+ except Exception as e:
+ methods._colorize = False
+ print_error(f"Failed to enable ANSI escape code support, disabling color output.\n{e}")
+
# Scan possible build platforms
platform_list = [] # list of platforms
@@ -554,7 +562,7 @@ if env["build_profile"] != "":
dbo = ft["disabled_build_options"]
for c in dbo:
env[c] = dbo[c]
- except:
+ except json.JSONDecodeError:
print_error('Failed to open feature build profile: "{}"'.format(env["build_profile"]))
Exit(255)
@@ -562,7 +570,7 @@ if env["build_profile"] != "":
# These can sometimes override default options.
flag_list = platform_flags[env["platform"]]
for f in flag_list:
- if not (f[0] in ARGUMENTS) or ARGUMENTS[f[0]] == "auto": # Allow command line to override platform flags
+ if f[0] not in ARGUMENTS or ARGUMENTS[f[0]] == "auto": # Allow command line to override platform flags
env[f[0]] = f[1]
# 'dev_mode' and 'production' are aliases to set default options if they haven't been
@@ -583,7 +591,7 @@ if env["production"]:
# Run SCU file generation script if in a SCU build.
if env["scu_build"]:
max_includes_per_scu = 8
- if env.dev_build == True:
+ if env.dev_build:
max_includes_per_scu = 1024
read_scu_limit = int(env["scu_limit"])
@@ -976,7 +984,7 @@ GLSL_BUILDERS = {
env.Append(BUILDERS=GLSL_BUILDERS)
scons_cache_path = os.environ.get("SCONS_CACHE")
-if scons_cache_path != None:
+if scons_cache_path is not None:
CacheDir(scons_cache_path)
print("Scons cache enabled... (path: '" + scons_cache_path + "')")
diff --git a/core/SCsub b/core/SCsub
index a61c0b5fc2..52f3506416 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -2,9 +2,11 @@
Import("env")
+import os
+
import core_builders
+
import methods
-import os
env.core_sources = []
@@ -188,9 +190,7 @@ def version_info_builder(target, source, env):
#define VERSION_WEBSITE "{website}"
#define VERSION_DOCS_BRANCH "{docs_branch}"
#define VERSION_DOCS_URL "https://docs.godotengine.org/en/" VERSION_DOCS_BRANCH
-""".format(
- **env.version_info
- )
+""".format(**env.version_info)
)
@@ -206,9 +206,7 @@ def version_hash_builder(target, source, env):
const char *const VERSION_HASH = "{git_hash}";
const uint64_t VERSION_TIMESTAMP = {git_timestamp};
-""".format(
- **env.version_info
- )
+""".format(**env.version_info)
)
diff --git a/core/core_builders.py b/core/core_builders.py
index a401f03693..a3dc935b79 100644
--- a/core/core_builders.py
+++ b/core/core_builders.py
@@ -180,7 +180,7 @@ def make_license_header(target, source, env):
return line
def next_tag(self):
- if not ":" in self.current:
+ if ":" not in self.current:
return ("", [])
tag, line = self.current.split(":", 1)
lines = [line.strip()]
@@ -206,7 +206,7 @@ def make_license_header(target, source, env):
if not tag or not reader.current:
# end of a paragraph start a new part
- if "License" in part and not "Files" in part:
+ if "License" in part and "Files" not in part:
# no Files tag in this one, so assume standalone license
license_list.append(part["License"])
part = {}
@@ -298,13 +298,13 @@ def make_license_header(target, source, env):
f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n")
f.write("const char *const LICENSE_NAMES[] = {\n")
- for l in license_list:
- f.write('\t"' + escape_string(l[0]) + '",\n')
+ for license in license_list:
+ f.write('\t"' + escape_string(license[0]) + '",\n')
f.write("};\n\n")
f.write("const char *const LICENSE_BODIES[] = {\n\n")
- for l in license_list:
- for line in l[1:]:
+ for license in license_list:
+ for line in license[1:]:
if line == ".":
f.write('\t"\\n"\n')
else:
diff --git a/core/extension/SCsub b/core/extension/SCsub
index 901ceec1e8..6ab2d2b0a6 100644
--- a/core/extension/SCsub
+++ b/core/extension/SCsub
@@ -2,8 +2,8 @@
Import("env")
-import make_wrappers
import make_interface_dumper
+import make_wrappers
env.CommandNoCache(["ext_wrappers.gen.inc"], "make_wrappers.py", env.Run(make_wrappers.run))
env.CommandNoCache(
diff --git a/core/extension/make_wrappers.py b/core/extension/make_wrappers.py
index 655b90d2b1..54f4fd5579 100644
--- a/core/extension/make_wrappers.py
+++ b/core/extension/make_wrappers.py
@@ -10,7 +10,6 @@ _FORCE_INLINE_ virtual $RETVAL m_name($FUNCARGS) $CONST override { \\
def generate_mod_version(argcount, const=False, returns=False):
s = proto_mod
sproto = str(argcount)
- method_info = ""
if returns:
sproto += "R"
s = s.replace("$RETTYPE", "m_ret, ")
@@ -68,7 +67,6 @@ virtual $RETVAL m_name($FUNCARGS) $CONST override { \\
def generate_ex_version(argcount, const=False, returns=False):
s = proto_ex
sproto = str(argcount)
- method_info = ""
if returns:
sproto += "R"
s = s.replace("$RETTYPE", "m_ret, ")
diff --git a/core/input/SCsub b/core/input/SCsub
index da29637135..d8e6f33156 100644
--- a/core/input/SCsub
+++ b/core/input/SCsub
@@ -4,7 +4,6 @@ Import("env")
import input_builders
-
# Order matters here. Higher index controller database files write on top of lower index database files.
controller_databases = [
"gamecontrollerdb.txt",
diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h
index 1502b2807c..83ebdc5a84 100644
--- a/core/math/geometry_2d.h
+++ b/core/math/geometry_2d.h
@@ -350,6 +350,8 @@ public:
return triangles;
}
+ // Assumes cartesian coordinate system with +x to the right, +y up.
+ // If using screen coordinates (+x to the right, +y down) the result will need to be flipped.
static bool is_polygon_clockwise(const Vector<Vector2> &p_polygon) {
int c = p_polygon.size();
if (c < 3) {
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index e25703b93f..3d93ce8e73 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -522,7 +522,7 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require
#ifdef TOOLS_ENABLED
if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) {
if (!ti->inherits_ptr || !ti->inherits_ptr->creation_func) {
- ERR_PRINT_ONCE(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name));
+ ERR_PRINT(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name));
} else {
ObjectGDExtension *extension = get_placeholder_extension(ti->name);
return (Object *)extension->create_instance(extension->class_userdata);
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 4b32acaaa0..bcab80ea94 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -1571,9 +1571,6 @@
<member name="Geometry3D" type="Geometry3D" setter="" getter="">
The [Geometry3D] singleton.
</member>
- <member name="GodotSharp" type="GodotSharp" setter="" getter="">
- The [GodotSharp] singleton.
- </member>
<member name="IP" type="IP" setter="" getter="">
The [IP] singleton.
</member>
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index 30df4fd10d..d2af6179d9 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -122,7 +122,7 @@
The horizontal space between [Button]'s icon and text. Negative values will be treated as [code]0[/code] when used.
</theme_item>
<theme_item name="icon_max_width" data_type="constant" type="int" default="0">
- The maximum allowed width of the [Button]'s icon. This limit is applied on top of the default size of the icon, or its expanded size if [member expand_icon] is [code]true[/code]. The height is adjusted according to the icon's ratio.
+ The maximum allowed width of the [Button]'s icon. This limit is applied on top of the default size of the icon, or its expanded size if [member expand_icon] is [code]true[/code]. The height is adjusted according to the icon's ratio. If the button has additional icons (e.g. [CheckBox]), they will also be limited.
</theme_item>
<theme_item name="outline_size" data_type="constant" type="int" default="0">
The size of the text outline.
diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml
index 6b25be490e..cfdc172fd1 100644
--- a/doc/classes/EditorInspector.xml
+++ b/doc/classes/EditorInspector.xml
@@ -28,7 +28,6 @@
</method>
</methods>
<members>
- <member name="follow_focus" type="bool" setter="set_follow_focus" getter="is_following_focus" overrides="ScrollContainer" default="true" />
<member name="horizontal_scroll_mode" type="int" setter="set_horizontal_scroll_mode" getter="get_horizontal_scroll_mode" overrides="ScrollContainer" enum="ScrollContainer.ScrollMode" default="0" />
</members>
<signals>
diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml
index dfcf299fe6..f21696d02c 100644
--- a/doc/classes/Geometry2D.xml
+++ b/doc/classes/Geometry2D.xml
@@ -116,6 +116,7 @@
<param index="0" name="polygon" type="PackedVector2Array" />
<description>
Returns [code]true[/code] if [param polygon]'s vertices are ordered in clockwise order, otherwise returns [code]false[/code].
+ [b]Note:[/b] Assumes a Cartesian coordinate system where [code]+x[/code] is right and [code]+y[/code] is up. If using screen coordinates ([code]+y[/code] is down), the result will need to be flipped (i.e. a [code]true[/code] result will indicate counter-clockwise).
</description>
</method>
<method name="line_intersects_line">
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index 43cd1a8aaa..b0cb25fafd 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -655,13 +655,13 @@
[StyleBox] used for the cursor, when the [Tree] is not being focused.
</theme_item>
<theme_item name="custom_button" data_type="style" type="StyleBox">
- Default [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode cell.
+ Default [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode cell when button is enabled with [method TreeItem.set_custom_as_button].
</theme_item>
<theme_item name="custom_button_hover" data_type="style" type="StyleBox">
- [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode cell when it's hovered.
+ [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode button cell when it's hovered.
</theme_item>
<theme_item name="custom_button_pressed" data_type="style" type="StyleBox">
- [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode cell when it's pressed.
+ [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode button cell when it's pressed.
</theme_item>
<theme_item name="focus" data_type="style" type="StyleBox">
The focused style for the [Tree], drawn on top of everything.
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index c679838ec5..78a703c213 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -318,12 +318,14 @@
<return type="int" enum="TextServer.StructuredTextParser" />
<param index="0" name="column" type="int" />
<description>
+ Returns the BiDi algorithm override set for this cell.
</description>
</method>
<method name="get_structured_text_bidi_override_options" qualifiers="const">
<return type="Array" />
<param index="0" name="column" type="int" />
<description>
+ Returns the additional BiDi options set for this cell.
</description>
</method>
<method name="get_suffix" qualifiers="const">
@@ -401,6 +403,7 @@
<return type="bool" />
<param index="0" name="column" type="int" />
<description>
+ Returns [code]true[/code] if the cell was made into a button with [method set_custom_as_button].
</description>
</method>
<method name="is_edit_multiline" qualifiers="const">
@@ -532,7 +535,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="mode" type="int" enum="TreeItem.TreeCellMode" />
<description>
- Sets the given column's cell mode to [param mode]. See [enum TreeCellMode] constants.
+ Sets the given column's cell mode to [param mode]. This determines how the cell is displayed and edited. See [enum TreeCellMode] constants for details.
</description>
</method>
<method name="set_checked">
@@ -555,6 +558,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="enable" type="bool" />
<description>
+ Makes a cell with [constant CELL_MODE_CUSTOM] display as a non-flat button with a [StyleBox].
</description>
</method>
<method name="set_custom_bg_color">
@@ -589,7 +593,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="callback" type="Callable" />
<description>
- Sets the given column's custom draw callback. Use an empty [Callable] ([code skip-lint]Callable()[/code]) to clear the custom callback.
+ Sets the given column's custom draw callback. Use an empty [Callable] ([code skip-lint]Callable()[/code]) to clear the custom callback. The cell has to be in [constant CELL_MODE_CUSTOM] to use this feature.
The [param callback] should accept two arguments: the [TreeItem] that is drawn and its position and size as a [Rect2].
</description>
</method>
@@ -639,7 +643,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="texture" type="Texture2D" />
<description>
- Sets the given column's icon [Texture2D].
+ Sets the given cell's icon [Texture2D]. The cell has to be in [constant CELL_MODE_ICON] mode.
</description>
</method>
<method name="set_icon_max_width">
@@ -724,6 +728,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="parser" type="int" enum="TextServer.StructuredTextParser" />
<description>
+ Set BiDi algorithm override for the structured text. Has effect for cells that display text.
</description>
</method>
<method name="set_structured_text_bidi_override_options">
@@ -731,6 +736,7 @@
<param index="0" name="column" type="int" />
<param index="1" name="args" type="Array" />
<description>
+ Set additional options for BiDi override. Has effect for cells that display text.
</description>
</method>
<method name="set_suffix">
@@ -805,18 +811,21 @@
</members>
<constants>
<constant name="CELL_MODE_STRING" value="0" enum="TreeCellMode">
- Cell contains a string.
+ Cell shows a string label. When editable, the text can be edited using a [LineEdit], or a [TextEdit] popup if [method set_edit_multiline] is used.
</constant>
<constant name="CELL_MODE_CHECK" value="1" enum="TreeCellMode">
- Cell contains a checkbox.
+ Cell shows a checkbox, optionally with text. The checkbox can be pressed, released, or indeterminate (via [method set_indeterminate]). The checkbox can't be clicked unless the cell is editable.
</constant>
<constant name="CELL_MODE_RANGE" value="2" enum="TreeCellMode">
- Cell contains a range.
+ Cell shows a numeric range. When editable, it can be edited using a range slider. Use [method set_range] to set the value and [method set_range_config] to configure the range.
+ This cell can also be used in a text dropdown mode when you assign a text with [method set_text]. Separate options with a comma, e.g. [code]"Option1,Option2,Option3"[/code].
</constant>
<constant name="CELL_MODE_ICON" value="3" enum="TreeCellMode">
- Cell contains an icon.
+ Cell shows an icon. It can't be edited nor display text.
</constant>
<constant name="CELL_MODE_CUSTOM" value="4" enum="TreeCellMode">
+ Cell shows as a clickable button. It will display an arrow similar to [OptionButton], but doesn't feature a dropdown (for that you can use [constant CELL_MODE_RANGE]). Clicking the button emits the [signal Tree.item_edited] signal. The button is flat by default, you can use [method set_custom_as_button] to display it with a [StyleBox].
+ This mode also supports custom drawing using [method set_custom_draw_callback].
</constant>
</constants>
</class>
diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py
index a23c11b585..57c03bfdee 100755
--- a/doc/tools/doc_status.py
+++ b/doc/tools/doc_status.py
@@ -1,11 +1,11 @@
#!/usr/bin/env python3
import fnmatch
-import os
-import sys
-import re
import math
+import os
import platform
+import re
+import sys
import xml.etree.ElementTree as ET
from typing import Dict, List, Set
@@ -286,7 +286,7 @@ class ClassStatus:
status.progresses[tag.tag].increment(is_deprecated or is_experimental or has_descr)
elif tag.tag in ["constants", "members", "theme_items"]:
for sub_tag in list(tag):
- if not sub_tag.text is None:
+ if sub_tag.text is not None:
is_deprecated = "deprecated" in sub_tag.attrib
is_experimental = "experimental" in sub_tag.attrib
has_descr = len(sub_tag.text.strip()) > 0
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index 0b722757f9..bc379f8553 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -4,17 +4,16 @@
import argparse
import os
-import platform
import re
import sys
import xml.etree.ElementTree as ET
from collections import OrderedDict
-from typing import List, Dict, TextIO, Tuple, Optional, Any, Union
+from typing import Any, Dict, List, Optional, TextIO, Tuple, Union
# Import hardcoded version information from version.py
root_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")
sys.path.append(root_directory) # Include the root directory
-import version
+import version # noqa: E402
# $DOCS_URL/path/to/page.html(#fragment-tag)
GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$")
@@ -677,17 +676,6 @@ class ScriptLanguageParityCheck:
# Entry point for the RST generator.
def main() -> None:
- # Enable ANSI escape code support on Windows 10 and later (for colored console output).
- # <https://bugs.python.org/issue29059>
- if platform.system().lower() == "windows":
- from ctypes import windll, c_int, byref # type: ignore
-
- stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
- mode = c_int(0)
- windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
- mode = c_int(mode.value | 4)
- windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
-
parser = argparse.ArgumentParser()
parser.add_argument("path", nargs="+", help="A path to an XML file or a directory containing XML files to parse.")
parser.add_argument("--filter", default="", help="The filepath pattern for XML files to filter.")
@@ -711,7 +699,25 @@ def main() -> None:
)
args = parser.parse_args()
- should_color = args.color or (hasattr(sys.stdout, "isatty") and sys.stdout.isatty())
+ should_color = bool(args.color or sys.stdout.isatty() or os.environ.get("CI"))
+
+ # Enable ANSI escape code support on Windows 10 and later (for colored console output).
+ # <https://github.com/python/cpython/issues/73245>
+ if should_color and sys.stdout.isatty() and sys.platform == "win32":
+ try:
+ from ctypes import WinError, byref, windll # type: ignore
+ from ctypes.wintypes import DWORD # type: ignore
+
+ stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11))
+ mode = DWORD(0)
+ if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)):
+ raise WinError()
+ mode = DWORD(mode.value | 4)
+ if not windll.kernel32.SetConsoleMode(stdout_handle, mode):
+ raise WinError()
+ except Exception:
+ should_color = False
+
STYLES["red"] = "\x1b[91m" if should_color else ""
STYLES["green"] = "\x1b[92m" if should_color else ""
STYLES["yellow"] = "\x1b[93m" if should_color else ""
@@ -1406,7 +1412,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
operator_anchor = f".. _class_{class_name}_operator_{sanitize_operator_name(m.name, state)}"
for parameter in m.parameters:
operator_anchor += f"_{parameter.type_name.type_name}"
- operator_anchor += f":\n\n"
+ operator_anchor += ":\n\n"
f.write(operator_anchor)
f.write(".. rst-class:: classref-operator\n\n")
@@ -1546,7 +1552,7 @@ def make_method_signature(
out += f":ref:`{op_name}<class_{class_def.name}_{ref_type}_{sanitize_operator_name(definition.name, state)}"
for parameter in definition.parameters:
out += f"_{parameter.type_name.type_name}"
- out += f">`"
+ out += ">`"
elif ref_type == "method":
ref_type_qualifier = ""
if definition.name.startswith("_"):
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 57ef951bdf..2dcf623995 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1495,14 +1495,18 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref<Image> &p_image,
for (int i = 0; i < mipmaps; i++) {
int size, ofs;
img->get_mipmap_offset_and_size(i, ofs, size);
-
if (compressed) {
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-
- int bw = w;
- int bh = h;
-
- glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]);
+ if (texture->target == GL_TEXTURE_2D_ARRAY) {
+ if (p_initialize) {
+ glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, i, internal_format, w, h, texture->layers, 0,
+ size * texture->layers, &read[ofs]);
+ } else {
+ glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 1, internal_format, size, &read[ofs]);
+ }
+ } else {
+ glCompressedTexImage2D(blit_target, i, internal_format, w, h, 0, size, &read[ofs]);
+ }
} else {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if (texture->target == GL_TEXTURE_2D_ARRAY) {
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index 131e0e4a8a..896fc6ff91 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp
@@ -3816,11 +3816,11 @@ bool RenderingDeviceDriverVulkan::pipeline_cache_create(const Vector<uint8_t> &p
if (p_data.is_empty()) {
// No pre-existing cache, just create it.
} else if (p_data.size() <= (int)sizeof(PipelineCacheHeader)) {
- WARN_PRINT("Invalid/corrupt pipelines cache.");
+ print_verbose("Invalid/corrupt Vulkan pipelines cache. Existing shader pipeline cache will be ignored, which may result in stuttering during gameplay.");
} else {
const PipelineCacheHeader *loaded_header = reinterpret_cast<const PipelineCacheHeader *>(p_data.ptr());
if (loaded_header->magic != 868 + VK_PIPELINE_CACHE_HEADER_VERSION_ONE) {
- WARN_PRINT("Invalid pipelines cache magic number.");
+ print_verbose("Invalid Vulkan pipelines cache magic number. Existing shader pipeline cache will be ignored, which may result in stuttering during gameplay.");
} else {
const uint8_t *loaded_buffer_start = p_data.ptr() + sizeof(PipelineCacheHeader);
uint32_t loaded_buffer_size = p_data.size() - sizeof(PipelineCacheHeader);
@@ -3832,7 +3832,7 @@ bool RenderingDeviceDriverVulkan::pipeline_cache_create(const Vector<uint8_t> &p
loaded_header->driver_version != current_header->driver_version ||
memcmp(loaded_header->uuid, current_header->uuid, VK_UUID_SIZE) != 0 ||
loaded_header->driver_abi != current_header->driver_abi) {
- WARN_PRINT("Invalid pipelines cache header.");
+ print_verbose("Invalid Vulkan pipelines cache header. This may be due to an engine change, GPU change or graphics driver version change. Existing shader pipeline cache will be ignored, which may result in stuttering during gameplay.");
} else {
pipelines_cache.current_size = loaded_buffer_size;
pipelines_cache.buffer = p_data;
diff --git a/editor/SCsub b/editor/SCsub
index e613a71238..029048969a 100644
--- a/editor/SCsub
+++ b/editor/SCsub
@@ -4,11 +4,12 @@ Import("env")
env.editor_sources = []
-import os
import glob
+import os
+
import editor_builders
-import methods
+import methods
if env.editor_build:
# Generate doc data paths
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp
index 6ff0520ab6..3023c5907a 100644
--- a/editor/action_map_editor.cpp
+++ b/editor/action_map_editor.cpp
@@ -505,6 +505,9 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
event_item->set_button_color(2, 1, Color(1, 1, 1, 0.75));
}
}
+
+ // Update UI.
+ clear_all_search->set_disabled(action_list_search->get_text().is_empty() && action_list_search_by_event->get_event().is_null());
}
void ActionMapEditor::show_message(const String &p_message) {
@@ -550,8 +553,9 @@ ActionMapEditor::ActionMapEditor() {
action_list_search_by_event->connect(SceneStringName(focus_exited), callable_mp(this, &ActionMapEditor::_on_filter_unfocused));
top_hbox->add_child(action_list_search_by_event);
- Button *clear_all_search = memnew(Button);
+ clear_all_search = memnew(Button);
clear_all_search->set_text(TTR("Clear All"));
+ clear_all_search->set_tooltip_text(TTR("Clear all search filters."));
clear_all_search->connect(SceneStringName(pressed), callable_mp(action_list_search_by_event, &EventListenerLineEdit::clear_event));
clear_all_search->connect(SceneStringName(pressed), callable_mp(action_list_search, &LineEdit::clear));
top_hbox->add_child(clear_all_search);
diff --git a/editor/action_map_editor.h b/editor/action_map_editor.h
index 2b329f2fca..017296bfaa 100644
--- a/editor/action_map_editor.h
+++ b/editor/action_map_editor.h
@@ -52,7 +52,7 @@ public:
bool has_initial = false;
Dictionary action_initial;
- Ref<Texture2D> icon = Ref<Texture2D>();
+ Ref<Texture2D> icon;
bool editable = true;
};
@@ -85,6 +85,7 @@ private:
CheckButton *show_builtin_actions_checkbutton = nullptr;
LineEdit *action_list_search = nullptr;
EventListenerLineEdit *action_list_search_by_event = nullptr;
+ Button *clear_all_search = nullptr;
HBoxContainer *add_hbox = nullptr;
LineEdit *add_edit = nullptr;
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index b58e82b7e7..331dacf6ad 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -344,25 +344,6 @@ void DocTools::merge_from(const DocTools &p_data) {
merge_theme_properties(c.theme_properties, cf.theme_properties);
merge_operators(c.operators, cf.operators);
-
-#ifndef MODULE_MONO_ENABLED
- // The Mono module defines some properties that we want to keep when
- // re-generating docs with a non-Mono build, to prevent pointless diffs
- // (and loss of descriptions) depending on the config of the doc writer.
- // We use a horrible hack to force keeping the relevant properties,
- // hardcoded below. At least it's an ad hoc hack... ¯\_(ツ)_/¯
- // Don't show this to your kids.
- if (c.name == "@GlobalScope") {
- // Retrieve GodotSharp singleton.
- for (int j = 0; j < cf.properties.size(); j++) {
- if (cf.properties[j].name == "GodotSharp") {
- c.properties.push_back(cf.properties[j]);
- c.properties.sort();
- break;
- }
- }
- }
-#endif
}
}
diff --git a/editor/editor_builders.py b/editor/editor_builders.py
index cfe6e56b49..625d570666 100644
--- a/editor/editor_builders.py
+++ b/editor/editor_builders.py
@@ -7,6 +7,7 @@ import subprocess
import tempfile
import uuid
import zlib
+
from methods import print_warning
diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp
index 5a7e914c11..020706c714 100644
--- a/editor/editor_feature_profile.cpp
+++ b/editor/editor_feature_profile.cpp
@@ -497,8 +497,8 @@ void EditorFeatureProfileManager::_hide_requested() {
_cancel_pressed(); // From AcceptDialog.
}
-void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected) {
- TreeItem *class_item = class_list->create_item(p_parent);
+void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected, int p_class_insert_index) {
+ TreeItem *class_item = class_list->create_item(p_parent, p_class_insert_index);
class_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
class_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_class));
String text = p_class;
@@ -647,7 +647,7 @@ void EditorFeatureProfileManager::_class_list_item_edited() {
String class_selected = md;
edited->set_disable_class(class_selected, !checked);
_save_and_update();
- _update_selected_profile();
+ _update_profile_tree_from(item);
} else if (md.get_type() == Variant::INT) {
int feature_selected = md;
edited->set_disable_feature(EditorFeatureProfile::Feature(feature_selected), !checked);
@@ -703,19 +703,29 @@ void EditorFeatureProfileManager::_property_item_edited() {
String property_selected = md;
edited->set_disable_class_property(class_name, property_selected, !checked);
_save_and_update();
- _update_selected_profile();
+ _update_profile_tree_from(class_list->get_selected());
} else if (md.get_type() == Variant::INT) {
int feature_selected = md;
switch (feature_selected) {
case CLASS_OPTION_DISABLE_EDITOR: {
edited->set_disable_class_editor(class_name, !checked);
_save_and_update();
- _update_selected_profile();
+ _update_profile_tree_from(class_list->get_selected());
} break;
}
}
}
+void EditorFeatureProfileManager::_update_profile_tree_from(TreeItem *p_edited) {
+ String edited_class = p_edited->get_metadata(0);
+
+ TreeItem *edited_parent = p_edited->get_parent();
+ int class_insert_index = p_edited->get_index();
+ p_edited->get_parent()->remove_child(p_edited);
+
+ _fill_classes_from(edited_parent, edited_class, edited_class, class_insert_index);
+}
+
void EditorFeatureProfileManager::_update_selected_profile() {
String class_selected;
int feature_selected = -1;
diff --git a/editor/editor_feature_profile.h b/editor/editor_feature_profile.h
index 2fa6ae9813..7458a04e19 100644
--- a/editor/editor_feature_profile.h
+++ b/editor/editor_feature_profile.h
@@ -147,7 +147,8 @@ class EditorFeatureProfileManager : public AcceptDialog {
String current_profile;
void _update_profile_list(const String &p_select_profile = String());
void _update_selected_profile();
- void _fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected);
+ void _update_profile_tree_from(TreeItem *p_edited);
+ void _fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected, int p_class_insert_index = -1);
Ref<EditorFeatureProfile> current;
Ref<EditorFeatureProfile> edited;
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index a8d978c66d..9884241708 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -1967,7 +1967,7 @@ void EditorHelp::_update_doc() {
class_desc->add_text(argument.name);
class_desc->add_text(": ");
- _add_type(argument.type);
+ _add_type(argument.type, argument.enumeration, argument.is_bitfield);
if (!argument.default_value.is_empty()) {
class_desc->push_color(theme_cache.symbol_color);
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 8e608fb797..7c5e3877f2 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -2727,6 +2727,8 @@ void EditorInspector::update_tree() {
// TODO: Can be useful to store more context for the focusable, such as the caret position in LineEdit.
StringName current_selected = property_selected;
int current_focusable = -1;
+ // Temporarily disable focus following to avoid jumping while the inspector is updating.
+ set_follow_focus(false);
if (property_focusable != -1) {
// Check that focusable is actually focusable.
@@ -3482,6 +3484,7 @@ void EditorInspector::update_tree() {
// Updating inspector might invalidate some editing owners.
EditorNode::get_singleton()->hide_unused_editors();
}
+ set_follow_focus(true);
}
void EditorInspector::update_property(const String &p_prop) {
diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp
index 2c6c13401f..b1f3875175 100644
--- a/editor/editor_settings_dialog.cpp
+++ b/editor/editor_settings_dialog.cpp
@@ -30,14 +30,13 @@
#include "editor_settings_dialog.h"
-#include "core/config/project_settings.h"
#include "core/input/input_map.h"
#include "core/os/keyboard.h"
#include "editor/debugger/editor_debugger_node.h"
-#include "editor/editor_file_system.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_property_name_processor.h"
+#include "editor/editor_sectioned_inspector.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
@@ -45,7 +44,9 @@
#include "editor/input_event_configuration_dialog.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
-#include "scene/gui/margin_container.h"
+#include "scene/gui/panel_container.h"
+#include "scene/gui/tab_container.h"
+#include "scene/gui/texture_rect.h"
void EditorSettingsDialog::ok_pressed() {
if (!EditorSettings::get_singleton()) {
@@ -499,6 +500,9 @@ void EditorSettingsDialog::_update_shortcuts() {
memdelete(section);
}
}
+
+ // Update UI.
+ clear_all_search->set_disabled(shortcut_search_box->get_text().is_empty() && shortcut_search_by_event->get_event().is_null());
}
void EditorSettingsDialog::_shortcut_button_pressed(Object *p_item, int p_column, int p_idx, MouseButton p_button) {
@@ -782,8 +786,9 @@ EditorSettingsDialog::EditorSettingsDialog() {
shortcut_search_by_event->connect(SceneStringName(focus_exited), callable_mp((AcceptDialog *)this, &AcceptDialog::set_close_on_escape).bind(true));
top_hbox->add_child(shortcut_search_by_event);
- Button *clear_all_search = memnew(Button);
+ clear_all_search = memnew(Button);
clear_all_search->set_text(TTR("Clear All"));
+ clear_all_search->set_tooltip_text(TTR("Clear all search filters."));
clear_all_search->connect(SceneStringName(pressed), callable_mp(shortcut_search_box, &LineEdit::clear));
clear_all_search->connect(SceneStringName(pressed), callable_mp(shortcut_search_by_event, &EventListenerLineEdit::clear_event));
top_hbox->add_child(clear_all_search);
diff --git a/editor/editor_settings_dialog.h b/editor/editor_settings_dialog.h
index fdfc0a43ec..cab8fe9da1 100644
--- a/editor/editor_settings_dialog.h
+++ b/editor/editor_settings_dialog.h
@@ -32,13 +32,14 @@
#define EDITOR_SETTINGS_DIALOG_H
#include "editor/action_map_editor.h"
-#include "editor/editor_inspector.h"
-#include "editor/editor_sectioned_inspector.h"
#include "scene/gui/dialogs.h"
-#include "scene/gui/panel_container.h"
-#include "scene/gui/rich_text_label.h"
-#include "scene/gui/tab_container.h"
-#include "scene/gui/texture_rect.h"
+
+class PanelContainer;
+class SectionedInspector;
+class TabContainer;
+class TextureRect;
+class Tree;
+class TreeItem;
class EditorSettingsDialog : public AcceptDialog {
GDCLASS(EditorSettingsDialog, AcceptDialog);
@@ -53,6 +54,7 @@ class EditorSettingsDialog : public AcceptDialog {
LineEdit *shortcut_search_box = nullptr;
EventListenerLineEdit *shortcut_search_by_event = nullptr;
SectionedInspector *inspector = nullptr;
+ Button *clear_all_search = nullptr;
// Shortcuts
enum ShortcutButton {
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index c6a671b7f9..dd283ebfd6 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -131,7 +131,7 @@ void ProjectExportDialog::popup_export() {
if (saved_size != Rect2()) {
popup(saved_size);
} else {
- popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8);
+ popup_centered_clamped(Size2(900, 500) * EDSCALE, 0.7);
}
}
@@ -1311,9 +1311,15 @@ ProjectExportDialog::ProjectExportDialog() {
// Resources export parameters.
+ ScrollContainer *resources_scroll_container = memnew(ScrollContainer);
+ resources_scroll_container->set_name(TTR("Resources"));
+ resources_scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+ sections->add_child(resources_scroll_container);
+
VBoxContainer *resources_vb = memnew(VBoxContainer);
- sections->add_child(resources_vb);
- resources_vb->set_name(TTR("Resources"));
+ resources_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ resources_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ resources_scroll_container->add_child(resources_vb);
export_filter = memnew(OptionButton);
export_filter->add_item(TTR("Export all resources in the project"));
@@ -1332,6 +1338,7 @@ ProjectExportDialog::ProjectExportDialog() {
resources_vb->add_child(include_margin);
include_files = memnew(Tree);
+ include_files->set_custom_minimum_size(Size2(1, 75 * EDSCALE));
include_margin->add_child(include_files);
include_files->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
include_files->connect("item_edited", callable_mp(this, &ProjectExportDialog::_tree_changed));
@@ -1384,18 +1391,25 @@ ProjectExportDialog::ProjectExportDialog() {
VBoxContainer *feature_vb = memnew(VBoxContainer);
feature_vb->set_name(TTR("Features"));
+ feature_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
custom_features = memnew(LineEdit);
custom_features->connect("text_changed", callable_mp(this, &ProjectExportDialog::_custom_features_changed));
feature_vb->add_margin_child(TTR("Custom (comma-separated):"), custom_features);
custom_feature_display = memnew(RichTextLabel);
+ custom_feature_display->set_custom_minimum_size(Size2(1, 75 * EDSCALE));
custom_feature_display->set_v_size_flags(Control::SIZE_EXPAND_FILL);
feature_vb->add_margin_child(TTR("Feature List:"), custom_feature_display, true);
sections->add_child(feature_vb);
// Encryption export parameters.
+ ScrollContainer *sec_scroll_container = memnew(ScrollContainer);
+ sec_scroll_container->set_name(TTR("Encryption"));
+ sec_scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+
VBoxContainer *sec_vb = memnew(VBoxContainer);
- sec_vb->set_name(TTR("Encryption"));
+ sec_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ sec_scroll_container->add_child(sec_vb);
enc_pck = memnew(CheckButton);
enc_pck->connect("toggled", callable_mp(this, &ProjectExportDialog::_enc_pck_changed));
@@ -1426,7 +1440,7 @@ ProjectExportDialog::ProjectExportDialog() {
script_key_error->add_theme_color_override("font_color", EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("error_color"), EditorStringName(Editor)));
sec_vb->add_margin_child(TTR("Encryption Key (256-bits as hexadecimal):"), script_key);
sec_vb->add_child(script_key_error);
- sections->add_child(sec_vb);
+ sections->add_child(sec_scroll_container);
Label *sec_info = memnew(Label);
sec_info->set_text(TTR("Note: Encryption key needs to be stored in the binary,\nyou need to build the export templates from source."));
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index a06dd310ea..fa0dad41dc 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -1368,22 +1368,35 @@ bool SceneTreeEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_d
}
bool scene_drop = true;
+ bool audio_drop = true;
for (int i = 0; i < files.size(); i++) {
String ftype = EditorFileSystem::get_singleton()->get_file_type(files[i]);
if (ftype != "PackedScene") {
scene_drop = false;
- break;
+ }
+ if (audio_drop && !ClassDB::is_parent_class(ftype, "AudioStream")) {
+ audio_drop = false;
}
}
if (scene_drop) {
tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM);
- } else {
+ return true;
+ }
+
+ if (audio_drop) {
if (files.size() > 1) {
- return false;
+ tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN);
+ } else {
+ tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM);
}
- tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM);
+ return true;
+ }
+
+ if (files.size() > 1) {
+ return false;
}
+ tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM);
return true;
}
diff --git a/editor/icons/AudioStreamPlayer.svg b/editor/icons/AudioStreamPlayer.svg
index 317b25d3e9..ba84502d99 100644
--- a/editor/icons/AudioStreamPlayer.svg
+++ b/editor/icons/AudioStreamPlayer.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" gradientUnits="userSpaceOnUse" x2="0" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="M9 14a1 1 0 0 0 1.5.85l4-2.511a1 1 0 0 0 0-1.724l-4-2.511a1 1 0 0 0-1.5.85z" fill="#e0e0e0"/><path d="M13 2a1 1 0 0 0-1-1L4.754 3A1 1 0 0 0 4 4v5.55A2.5 2.5 0 1 0 6 12V4.756l5-1.428V6.5l2-1z" fill="url(#a)"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path d="M1.382.335.777.858H.204v.673h.564l.614.531Z" style="fill:#e0e0e0;stroke:#e0e0e0;stroke-width:.1764;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"/><path d="M1.718.572c.275.374.275.882 0 1.256M1.947.343c.402.5.402 1.213 0 1.714" style="fill:none;stroke:#e0e0e0;stroke-width:.176328;stroke-linecap:round;stroke-opacity:1"/></svg> \ No newline at end of file
diff --git a/editor/icons/AudioStreamPlayer2D.svg b/editor/icons/AudioStreamPlayer2D.svg
index 87beccd19a..54ff128a4d 100644
--- a/editor/icons/AudioStreamPlayer2D.svg
+++ b/editor/icons/AudioStreamPlayer2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" gradientUnits="userSpaceOnUse" x2="0" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="M9 14a1 1 0 0 0 1.5.85l4-2.511a1 1 0 0 0 0-1.724l-4-2.511a1 1 0 0 0-1.5.85z" fill="#8da5f3"/><path d="M13 2a1 1 0 0 0-1-1L4.754 3A1 1 0 0 0 4 4v5.55A2.5 2.5 0 1 0 6 12V4.756l5-1.428V6.5l2-1z" fill="url(#a)"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path d="M1.382.335.777.858H.204v.673h.564l.614.531Z" style="fill:#8da5f3;stroke:#8da5f3;stroke-width:.1764;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"/><path d="M1.718.572c.275.374.275.882 0 1.256M1.947.343c.402.5.402 1.213 0 1.714" style="fill:none;stroke:#8da5f3;stroke-width:.176328;stroke-linecap:round;stroke-opacity:1"/></svg> \ No newline at end of file
diff --git a/editor/icons/AudioStreamPlayer3D.svg b/editor/icons/AudioStreamPlayer3D.svg
index 440a5e52ef..fc0231a657 100644
--- a/editor/icons/AudioStreamPlayer3D.svg
+++ b/editor/icons/AudioStreamPlayer3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" gradientUnits="userSpaceOnUse" x2="0" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="M9 14a1 1 0 0 0 1.5.85l4-2.511a1 1 0 0 0 0-1.724l-4-2.511a1 1 0 0 0-1.5.85z" fill="#fc7f7f"/><path d="M13 2a1 1 0 0 0-1-1L4.754 3A1 1 0 0 0 4 4v5.55A2.5 2.5 0 1 0 6 12V4.756l5-1.428V6.5l2-1z" fill="url(#a)"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path d="M1.382.335.777.858H.204v.673h.564l.614.531Z" style="fill:#fc7f7f;stroke:#fc7f7f;stroke-width:.1764;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"/><path d="M1.718.572c.275.374.275.882 0 1.256M1.947.343c.402.5.402 1.213 0 1.714" style="fill:none;stroke:#fc7f7f;stroke-width:.176328;stroke-linecap:round;stroke-opacity:1"/></svg> \ No newline at end of file
diff --git a/editor/icons/SCsub b/editor/icons/SCsub
index d2d752cff4..0d9ac43c46 100644
--- a/editor/icons/SCsub
+++ b/editor/icons/SCsub
@@ -3,8 +3,8 @@
Import("env")
import os
-import editor_icons_builders
+import editor_icons_builders
env["BUILDERS"]["MakeEditorIconsBuilder"] = Builder(
action=env.Run(editor_icons_builders.make_editor_icons_action),
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index 070c44419a..23c22a8049 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -2329,6 +2329,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
}
script_ext_hint += "*." + E;
}
+ bool trimming_defaults_on = p_path.get_extension().to_lower() == "fbx";
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/apply_root_scale"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0));
@@ -2342,7 +2343,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), trimming_defaults_on));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/remove_immutable_tracks"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import_rest_as_RESET"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index fbe76e1d5c..2d87e6592f 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -188,10 +188,20 @@ void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_
params->checked.clear();
params->base_options_path = p_path;
+ HashMap<StringName, Variant> import_options;
+ List<String> section_keys;
+ p_config->get_section_keys("params", &section_keys);
+ for (const String &section_key : section_keys) {
+ import_options[section_key] = p_config->get_value("params", section_key);
+ }
+ if (params->importer.is_valid()) {
+ params->importer->handle_compatibility_options(import_options);
+ }
+
for (const ResourceImporter::ImportOption &E : options) {
params->properties.push_back(E.option);
- if (p_config.is_valid() && p_config->has_section_key("params", E.option.name)) {
- params->values[E.option.name] = p_config->get_value("params", E.option.name);
+ if (p_config.is_valid() && import_options.has(E.option.name)) {
+ params->values[E.option.name] = import_options[E.option.name];
} else {
params->values[E.option.name] = E.default_value;
}
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index a44430ca7f..2d47887027 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -46,10 +46,12 @@
#include "editor/scene_tree_dock.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
+#include "scene/2d/audio_stream_player_2d.h"
#include "scene/2d/polygon_2d.h"
#include "scene/2d/skeleton_2d.h"
#include "scene/2d/sprite_2d.h"
#include "scene/2d/touch_screen_button.h"
+#include "scene/gui/base_button.h"
#include "scene/gui/flow_container.h"
#include "scene/gui/grid_container.h"
#include "scene/gui/separator.h"
@@ -966,7 +968,7 @@ void CanvasItemEditor::_add_node_pressed(int p_result) {
}
}
-void CanvasItemEditor::_node_created(Node *p_node) {
+void CanvasItemEditor::_adjust_new_node_position(Node *p_node) {
if (node_create_position == Point2()) {
return;
}
@@ -5161,7 +5163,7 @@ CanvasItemEditor::CanvasItemEditor() {
editor_selection->connect("selection_changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
editor_selection->connect("selection_changed", callable_mp(this, &CanvasItemEditor::_selection_changed));
- SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created));
+ SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_adjust_new_node_position));
SceneTreeDock::get_singleton()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position));
// Add some margin to the sides for better esthetics.
@@ -5697,15 +5699,15 @@ CanvasItemEditorPlugin::~CanvasItemEditorPlugin() {
}
void CanvasItemEditorViewport::_on_mouse_exit() {
- if (!selector->is_visible()) {
+ if (!texture_node_type_selector->is_visible()) {
_remove_preview();
}
}
-void CanvasItemEditorViewport::_on_select_type(Object *selected) {
+void CanvasItemEditorViewport::_on_select_texture_node_type(Object *selected) {
CheckBox *check = Object::cast_to<CheckBox>(selected);
String type = check->get_text();
- selector->set_title(vformat(TTR("Add %s"), type));
+ texture_node_type_selector->set_title(vformat(TTR("Add %s"), type));
label->set_text(vformat(TTR("Adding %s..."), type));
}
@@ -5717,7 +5719,7 @@ void CanvasItemEditorViewport::_on_change_type_confirmed() {
CheckBox *check = Object::cast_to<CheckBox>(button_group->get_pressed_button());
default_texture_node_type = check->get_text();
_perform_drop_data();
- selector->hide();
+ texture_node_type_selector->hide();
}
void CanvasItemEditorViewport::_on_change_type_closed() {
@@ -5728,35 +5730,35 @@ void CanvasItemEditorViewport::_create_preview(const Vector<String> &files) cons
bool add_preview = false;
for (int i = 0; i < files.size(); i++) {
Ref<Resource> res = ResourceLoader::load(files[i]);
- ERR_FAIL_COND(res.is_null());
- Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res));
- Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
- if (texture != nullptr || scene != nullptr) {
- String desc = TTR("Drag and drop to add as sibling of selected node (except when root is selected).") +
- "\n" + TTR("Hold Shift when dropping to add as child of selected node.") +
- "\n" + TTR("Hold Alt when dropping to add as child of root node.");
-
- if (texture != nullptr) {
- Sprite2D *sprite = memnew(Sprite2D);
- sprite->set_texture(texture);
- sprite->set_modulate(Color(1, 1, 1, 0.7f));
- preview_node->add_child(sprite);
- label->show();
- label_desc->show();
- desc += "\n" + TTR("Hold Alt + Shift when dropping to add as a different node type.");
- label_desc->set_text(desc);
- } else {
- if (scene.is_valid()) {
- Node *instance = scene->instantiate();
- if (instance) {
- preview_node->add_child(instance);
- label_desc->show();
- label_desc->set_text(desc);
- }
- }
+ ERR_CONTINUE(res.is_null());
+
+ Ref<Texture2D> texture = res;
+ if (texture.is_valid()) {
+ Sprite2D *sprite = memnew(Sprite2D);
+ sprite->set_texture(texture);
+ sprite->set_modulate(Color(1, 1, 1, 0.7f));
+ preview_node->add_child(sprite);
+ add_preview = true;
+ }
+
+ Ref<PackedScene> scene = res;
+ if (scene.is_valid()) {
+ Node *instance = scene->instantiate();
+ if (instance) {
+ preview_node->add_child(instance);
}
add_preview = true;
}
+
+ Ref<AudioStream> audio = res;
+ if (audio.is_valid()) {
+ Sprite2D *sprite = memnew(Sprite2D);
+ sprite->set_texture(get_editor_theme_icon(SNAME("AudioStreamPlayer2D")));
+ sprite->set_modulate(Color(1, 1, 1, 0.7f));
+ sprite->set_position(Vector2(0, -sprite->get_texture()->get_size().height) * EDSCALE);
+ preview_node->add_child(sprite);
+ add_preview = true;
+ }
}
if (add_preview) {
@@ -5797,44 +5799,46 @@ bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String &p_targe
return false;
}
-void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &path, const Point2 &p_point) {
+void CanvasItemEditorViewport::_create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point) {
// Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
- String name = path.get_file().get_basename();
- child->set_name(Node::adjust_name_casing(name));
+ const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
+ if (!node_name.is_empty()) {
+ p_child->set_name(node_name);
+ }
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- Ref<Texture2D> texture = ResourceCache::get_ref(path);
+ Ref<Texture2D> texture = ResourceCache::get_ref(p_path);
- if (parent) {
- undo_redo->add_do_method(parent, "add_child", child, true);
- undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_reference(child);
- undo_redo->add_undo_method(parent, "remove_child", child);
+ if (p_parent) {
+ undo_redo->add_do_method(p_parent, "add_child", p_child, true);
+ undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->add_do_reference(p_child);
+ undo_redo->add_undo_method(p_parent, "remove_child", p_child);
} else { // If no parent is selected, set as root node of the scene.
- undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child);
- undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_reference(child);
+ undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", p_child);
+ undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->add_do_reference(p_child);
undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
}
- if (parent) {
- String new_name = parent->validate_child_name(child);
+ if (p_parent) {
+ String new_name = p_parent->validate_child_name(p_child);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
- undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), child->get_class(), new_name);
- undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
+ undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), p_child->get_class(), new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
}
- if (Object::cast_to<TouchScreenButton>(child) || Object::cast_to<TextureButton>(child)) {
- undo_redo->add_do_property(child, "texture_normal", texture);
+ if (Object::cast_to<TouchScreenButton>(p_child) || Object::cast_to<TextureButton>(p_child)) {
+ undo_redo->add_do_property(p_child, "texture_normal", texture);
} else {
- undo_redo->add_do_property(child, "texture", texture);
+ undo_redo->add_do_property(p_child, "texture", texture);
}
// make visible for certain node type
- if (Object::cast_to<Control>(child)) {
+ if (Object::cast_to<Control>(p_child)) {
Size2 texture_size = texture->get_size();
- undo_redo->add_do_property(child, "size", texture_size);
- } else if (Object::cast_to<Polygon2D>(child)) {
+ undo_redo->add_do_property(p_child, "size", texture_size);
+ } else if (Object::cast_to<Polygon2D>(p_child)) {
Size2 texture_size = texture->get_size();
Vector<Vector2> list = {
Vector2(0, 0),
@@ -5842,7 +5846,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
Vector2(texture_size.width, texture_size.height),
Vector2(0, texture_size.height)
};
- undo_redo->add_do_property(child, "polygon", list);
+ undo_redo->add_do_property(p_child, "polygon", list);
}
// Compute the global position
@@ -5850,21 +5854,68 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
Point2 target_position = xform.affine_inverse().xform(p_point);
// Adjust position for Control and TouchScreenButton
- if (Object::cast_to<Control>(child) || Object::cast_to<TouchScreenButton>(child)) {
+ if (Object::cast_to<Control>(p_child) || Object::cast_to<TouchScreenButton>(p_child)) {
target_position -= texture->get_size() / 2;
}
- // there's nothing to be used as source position so snapping will work as absolute if enabled
+ // There's nothing to be used as source position, so snapping will work as absolute if enabled.
+ target_position = canvas_item_editor->snap_point(target_position);
+
+ CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent);
+ Point2 local_target_pos = parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position;
+
+ undo_redo->add_do_method(p_child, "set_position", local_target_pos);
+}
+
+void CanvasItemEditorViewport::_create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point) {
+ AudioStreamPlayer2D *child = memnew(AudioStreamPlayer2D);
+ child->set_stream(ResourceCache::get_ref(p_path));
+
+ // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
+ const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
+ if (!node_name.is_empty()) {
+ child->set_name(node_name);
+ }
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+
+ if (p_parent) {
+ undo_redo->add_do_method(p_parent, "add_child", child, true);
+ undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->add_do_reference(child);
+ undo_redo->add_undo_method(p_parent, "remove_child", child);
+ } else { // If no parent is selected, set as root node of the scene.
+ undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child);
+ undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->add_do_reference(child);
+ undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
+ }
+
+ if (p_parent) {
+ String new_name = p_parent->validate_child_name(child);
+ EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
+ undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), child->get_class(), new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
+ }
+
+ // Compute the global position
+ Transform2D xform = canvas_item_editor->get_canvas_transform();
+ Point2 target_position = xform.affine_inverse().xform(p_point);
+
+ // There's nothing to be used as source position, so snapping will work as absolute if enabled.
target_position = canvas_item_editor->snap_point(target_position);
- CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent);
+ CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent);
Point2 local_target_pos = parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position;
undo_redo->add_do_method(child, "set_position", local_target_pos);
+
+ EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
+ undo_redo->add_do_method(editor_selection, "add_node", child);
}
-bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) {
- Ref<PackedScene> sdata = ResourceLoader::load(path);
+bool CanvasItemEditorViewport::_create_instance(Node *p_parent, const String &p_path, const Point2 &p_point) {
+ Ref<PackedScene> sdata = ResourceLoader::load(p_path);
if (!sdata.is_valid()) { // invalid scene
return false;
}
@@ -5883,27 +5934,27 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
}
}
- instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
+ instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_path));
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
- undo_redo->add_do_method(parent, "add_child", instantiated_scene, true);
+ undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true);
undo_redo->add_do_method(instantiated_scene, "set_owner", edited_scene);
undo_redo->add_do_reference(instantiated_scene);
- undo_redo->add_undo_method(parent, "remove_child", instantiated_scene);
+ undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene);
undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene);
- String new_name = parent->validate_child_name(instantiated_scene);
+ String new_name = p_parent->validate_child_name(instantiated_scene);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
- undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(parent), path, new_name);
- undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name));
+ undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(p_parent), p_path, new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)) + "/" + new_name));
CanvasItem *instance_ci = Object::cast_to<CanvasItem>(instantiated_scene);
if (instance_ci) {
Vector2 target_pos = canvas_item_editor->get_canvas_transform().affine_inverse().xform(p_point);
target_pos = canvas_item_editor->snap_point(target_pos);
- CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent);
+ CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent);
if (parent_ci) {
target_pos = parent_ci->get_global_transform_with_canvas().affine_inverse().xform(target_pos);
}
@@ -5922,12 +5973,8 @@ void CanvasItemEditorViewport::_perform_drop_data() {
_remove_preview();
if (!target_node) {
- // Without root dropping multiple files is not allowed
- if (selected_files.size() > 1) {
- accept->set_text(TTR("Cannot instantiate multiple nodes without root."));
- accept->popup_centered();
- return;
- }
+ // Should already be handled by `can_drop_data`.
+ ERR_FAIL_COND_MSG(selected_files.size() > 1, "Can't instantiate multiple nodes without root.");
const String &path = selected_files[0];
Ref<Resource> res = ResourceLoader::load(path);
@@ -5973,9 +6020,14 @@ void CanvasItemEditorViewport::_perform_drop_data() {
Ref<Texture2D> texture = res;
if (texture.is_valid()) {
Node *child = Object::cast_to<Node>(ClassDB::instantiate(default_texture_node_type));
- _create_nodes(target_node, child, path, drop_pos);
+ _create_texture_node(target_node, child, path, drop_pos);
undo_redo->add_do_method(editor_selection, "add_node", child);
}
+
+ Ref<AudioStream> audio = res;
+ if (audio.is_valid()) {
+ _create_audio_node(target_node, path, drop_pos);
+ }
}
undo_redo->commit_action();
@@ -5994,71 +6046,108 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
}
Vector<String> files = d["files"];
- bool can_instantiate = false;
- bool is_cyclical_dep = false;
- String error_file;
- // Check if at least one of the dragged files is a texture or scene.
- for (int i = 0; i < files.size(); i++) {
- bool is_scene = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "PackedScene");
- bool is_texture = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Texture2D");
+ const Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+ if (!edited_scene && files.size() > 1) {
+ canvas_item_editor->message = TTR("Can't instantiate multiple nodes without root.");
+ canvas_item_editor->update_viewport();
+ return false;
+ }
+
+ enum {
+ SCENE = 1 << 0,
+ TEXTURE = 1 << 1,
+ AUDIO = 1 << 2,
+ };
+ int instantiate_type = 0;
- if (is_scene || is_texture) {
- Ref<Resource> res = ResourceLoader::load(files[i]);
- if (res.is_null()) {
+ for (const String &path : files) {
+ const String &res_type = ResourceLoader::get_resource_type(path);
+ String error_message;
+
+ if (ClassDB::is_parent_class(res_type, "PackedScene")) {
+ Ref<PackedScene> scn = ResourceLoader::load(path);
+ ERR_CONTINUE(scn.is_null());
+
+ Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
+ if (!instantiated_scene) {
continue;
}
- Ref<PackedScene> scn = res;
- if (scn.is_valid()) {
- Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
- if (!instantiated_scene) {
- continue;
- }
-
- Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
- if (edited_scene && !edited_scene->get_scene_file_path().is_empty() && _cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) {
- memdelete(instantiated_scene);
- can_instantiate = false;
- is_cyclical_dep = true;
- error_file = files[i].get_file();
- break;
- }
- memdelete(instantiated_scene);
+ if (edited_scene && !edited_scene->get_scene_file_path().is_empty() && _cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) {
+ error_message = vformat(TTR("Circular dependency found at %s."), path.get_file());
}
- can_instantiate = true;
+ memdelete(instantiated_scene);
+ instantiate_type |= SCENE;
+ }
+ if (ClassDB::is_parent_class(res_type, "Texture2D")) {
+ instantiate_type |= TEXTURE;
+ }
+ if (ClassDB::is_parent_class(res_type, "AudioStream")) {
+ instantiate_type |= AUDIO;
+ }
+
+ if (!error_message.is_empty()) {
+ // TRANSLATORS: The placeholder is the error message.
+ canvas_item_editor->message = vformat(TTR("Can't instantiate: %s"), error_message);
+ canvas_item_editor->update_viewport();
+ return false;
}
}
- if (is_cyclical_dep) {
- canvas_item_editor->message = vformat(TTR("Can't instantiate: %s."), vformat(TTR("Circular dependency found at %s"), error_file));
- canvas_item_editor->update_viewport();
+ if (instantiate_type == 0) {
return false;
}
- if (can_instantiate) {
- if (!preview_node->get_parent()) { // create preview only once
- _create_preview(files);
- }
- ERR_FAIL_COND_V(preview_node->get_child_count() == 0, false);
- Transform2D trans = canvas_item_editor->get_canvas_transform();
- preview_node->set_position((p_point - trans.get_origin()) / trans.get_scale().x);
+ if (!preview_node->get_parent()) { // create preview only once
+ _create_preview(files);
+ }
+ ERR_FAIL_COND_V(preview_node->get_child_count() == 0, false);
+
+ const Transform2D trans = canvas_item_editor->get_canvas_transform();
+ preview_node->set_position((p_point - trans.get_origin()) / trans.get_scale().x);
+
+ if (!edited_scene && instantiate_type & SCENE) {
String scene_file_path = preview_node->get_child(0)->get_scene_file_path();
- if (scene_file_path.is_empty() || preview_node->get_tree()->get_edited_scene_root()) {
- double snap = EDITOR_GET("interface/inspector/default_float_step");
- int snap_step_decimals = Math::range_step_decimals(snap);
+ // TRANSLATORS: The placeholder is the file path of the scene being instantiated.
+ canvas_item_editor->message = vformat(TTR("Creating inherited scene from: %s"), scene_file_path);
+ } else {
+ double snap = EDITOR_GET("interface/inspector/default_float_step");
+ int snap_step_decimals = Math::range_step_decimals(snap);
#define FORMAT(value) (TS->format_number(String::num(value, snap_step_decimals)))
- Vector2 preview_node_pos = preview_node->get_global_position();
- canvas_item_editor->message = TTR("Instantiating:") + " (" + FORMAT(preview_node_pos.x) + ", " + FORMAT(preview_node_pos.y) + ") px";
- label->set_text(vformat(TTR("Adding %s..."), default_texture_node_type));
- } else {
- canvas_item_editor->message = TTR("Creating inherited scene from: ") + scene_file_path;
+ Vector2 preview_node_pos = preview_node->get_global_position();
+ canvas_item_editor->message = TTR("Instantiating: ") + "(" + FORMAT(preview_node_pos.x) + ", " + FORMAT(preview_node_pos.y) + ") px";
+ }
+ canvas_item_editor->update_viewport();
+
+ if (instantiate_type & TEXTURE && instantiate_type & AUDIO) {
+ // TRANSLATORS: The placeholders are the types of nodes being instantiated.
+ label->set_text(vformat(TTR("Adding %s and %s..."), default_texture_node_type, "AudioStreamPlayer2D"));
+ } else {
+ String node_type;
+ if (instantiate_type & TEXTURE) {
+ node_type = default_texture_node_type;
+ } else if (instantiate_type & AUDIO) {
+ node_type = "AudioStreamPlayer2D";
+ }
+ if (!node_type.is_empty()) {
+ // TRANSLATORS: The placeholder is the type of node being instantiated.
+ label->set_text(vformat(TTR("Adding %s..."), node_type));
}
+ }
+ label->set_visible(instantiate_type & ~SCENE);
- canvas_item_editor->update_viewport();
+ String desc = TTR("Drag and drop to add as sibling of selected node (except when root is selected).") +
+ "\n" + TTR("Hold Shift when dropping to add as child of selected node.") +
+ "\n" + TTR("Hold Alt when dropping to add as child of root node.");
+ if (instantiate_type & TEXTURE) {
+ desc += "\n" + TTR("Hold Alt + Shift when dropping to add as different node type.");
}
- return can_instantiate;
+ label_desc->set_text(desc);
+ label_desc->show();
+
+ return true;
}
-void CanvasItemEditorViewport::_show_resource_type_selector() {
+void CanvasItemEditorViewport::_show_texture_node_type_selector() {
_remove_preview();
List<BaseButton *> btn_list;
button_group->get_buttons(&btn_list);
@@ -6067,18 +6156,17 @@ void CanvasItemEditorViewport::_show_resource_type_selector() {
CheckBox *check = Object::cast_to<CheckBox>(btn);
check->set_pressed(check->get_text() == default_texture_node_type);
}
- selector->set_title(vformat(TTR("Add %s"), default_texture_node_type));
- selector->popup_centered();
+ texture_node_type_selector->set_title(vformat(TTR("Add %s"), default_texture_node_type));
+ texture_node_type_selector->popup_centered();
}
-bool CanvasItemEditorViewport::_only_packed_scenes_selected() const {
+bool CanvasItemEditorViewport::_is_any_texture_selected() const {
for (int i = 0; i < selected_files.size(); ++i) {
- if (ResourceLoader::load(selected_files[i])->get_class() != "PackedScene") {
- return false;
+ if (ClassDB::is_parent_class(ResourceLoader::get_resource_type(selected_files[i]), "Texture2D")) {
+ return true;
}
}
-
- return true;
+ return false;
}
void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p_data) {
@@ -6115,8 +6203,8 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p
drop_pos = p_point;
- if (is_alt && is_shift && !_only_packed_scenes_selected()) {
- _show_resource_type_selector();
+ if (is_alt && is_shift && _is_any_texture_selected()) {
+ _show_texture_node_type_selector();
} else {
_perform_drop_data();
}
@@ -6176,19 +6264,19 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_it
accept = memnew(AcceptDialog);
EditorNode::get_singleton()->get_gui_base()->add_child(accept);
- selector = memnew(AcceptDialog);
- EditorNode::get_singleton()->get_gui_base()->add_child(selector);
- selector->set_title(TTR("Change Default Type"));
- selector->connect("confirmed", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_confirmed));
- selector->connect("canceled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed));
+ texture_node_type_selector = memnew(AcceptDialog);
+ EditorNode::get_singleton()->get_gui_base()->add_child(texture_node_type_selector);
+ texture_node_type_selector->set_title(TTR("Change Default Type"));
+ texture_node_type_selector->connect("confirmed", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_confirmed));
+ texture_node_type_selector->connect("canceled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed));
VBoxContainer *vbc = memnew(VBoxContainer);
- selector->add_child(vbc);
+ texture_node_type_selector->add_child(vbc);
vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
vbc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vbc->set_custom_minimum_size(Size2(240, 260) * EDSCALE);
- btn_group = memnew(VBoxContainer);
+ VBoxContainer *btn_group = memnew(VBoxContainer);
vbc->add_child(btn_group);
btn_group->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -6197,7 +6285,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_it
CheckBox *check = memnew(CheckBox);
btn_group->add_child(check);
check->set_text(texture_node_types[i]);
- check->connect("button_down", callable_mp(this, &CanvasItemEditorViewport::_on_select_type).bind(check));
+ check->connect("button_down", callable_mp(this, &CanvasItemEditorViewport::_on_select_texture_node_type).bind(check));
check->set_button_group(button_group);
}
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 6d951a77ec..a9de5e9a0b 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -32,10 +32,11 @@
#define CANVAS_ITEM_EDITOR_PLUGIN_H
#include "editor/plugins/editor_plugin.h"
-#include "scene/gui/base_button.h"
#include "scene/gui/box_container.h"
class AcceptDialog;
+class Button;
+class ButtonGroup;
class CanvasItemEditorViewport;
class ConfirmationDialog;
class EditorData;
@@ -409,7 +410,7 @@ private:
void _selection_result_pressed(int);
void _selection_menu_hide();
void _add_node_pressed(int p_result);
- void _node_created(Node *p_node);
+ void _adjust_new_node_position(Node *p_node);
void _reset_create_position();
void _update_editor_settings();
bool _is_grid_visible() const;
@@ -634,15 +635,13 @@ class CanvasItemEditorViewport : public Control {
CanvasItemEditor *canvas_item_editor = nullptr;
Control *preview_node = nullptr;
AcceptDialog *accept = nullptr;
- AcceptDialog *selector = nullptr;
- Label *selector_label = nullptr;
+ AcceptDialog *texture_node_type_selector = nullptr;
Label *label = nullptr;
Label *label_desc = nullptr;
- VBoxContainer *btn_group = nullptr;
Ref<ButtonGroup> button_group;
void _on_mouse_exit();
- void _on_select_type(Object *selected);
+ void _on_select_texture_node_type(Object *selected);
void _on_change_type_confirmed();
void _on_change_type_closed();
@@ -650,11 +649,12 @@ class CanvasItemEditorViewport : public Control {
void _remove_preview();
bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) const;
- bool _only_packed_scenes_selected() const;
- void _create_nodes(Node *parent, Node *child, String &path, const Point2 &p_point);
- bool _create_instance(Node *parent, String &path, const Point2 &p_point);
+ bool _is_any_texture_selected() const;
+ void _create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point);
+ void _create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point);
+ bool _create_instance(Node *p_parent, const String &p_path, const Point2 &p_point);
void _perform_drop_data();
- void _show_resource_type_selector();
+ void _show_texture_node_type_selector();
void _update_theme();
protected:
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index ca7ea821e8..c8b229c62a 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -78,12 +78,14 @@
#include "editor/plugins/gizmos/voxel_gi_gizmo_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
#include "editor/scene_tree_dock.h"
+#include "scene/3d/audio_stream_player_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/decal.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics/collision_shape_3d.h"
#include "scene/3d/physics/physics_body_3d.h"
+#include "scene/3d/sprite_3d.h"
#include "scene/3d/visual_instance_3d.h"
#include "scene/3d/world_environment.h"
#include "scene/gui/center_container.h"
@@ -4192,27 +4194,37 @@ Node *Node3DEditorViewport::_sanitize_preview_node(Node *p_node) const {
void Node3DEditorViewport::_create_preview_node(const Vector<String> &files) const {
bool add_preview = false;
- for (int i = 0; i < files.size(); i++) {
- Ref<Resource> res = ResourceLoader::load(files[i]);
+ for (const String &path : files) {
+ Ref<Resource> res = ResourceLoader::load(path);
ERR_CONTINUE(res.is_null());
- Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
- Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res));
- if (mesh != nullptr || scene != nullptr) {
- if (mesh != nullptr) {
- MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
- mesh_instance->set_mesh(mesh);
- preview_node->add_child(mesh_instance);
- } else {
- if (scene.is_valid()) {
- Node *instance = scene->instantiate();
- if (instance) {
- instance = _sanitize_preview_node(instance);
- preview_node->add_child(instance);
- }
- }
+
+ Ref<PackedScene> scene = res;
+ if (scene.is_valid()) {
+ Node *instance = scene->instantiate();
+ if (instance) {
+ instance = _sanitize_preview_node(instance);
+ preview_node->add_child(instance);
}
add_preview = true;
}
+
+ Ref<Mesh> mesh = res;
+ if (mesh.is_valid()) {
+ MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
+ mesh_instance->set_mesh(mesh);
+ preview_node->add_child(mesh_instance);
+ add_preview = true;
+ }
+
+ Ref<AudioStream> audio = res;
+ if (audio.is_valid()) {
+ Sprite3D *sprite = memnew(Sprite3D);
+ sprite->set_texture(get_editor_theme_icon(SNAME("Gizmo3DSamplePlayer")));
+ sprite->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED);
+ sprite->set_pixel_size(0.005);
+ preview_node->add_child(sprite);
+ add_preview = true;
+ }
}
if (add_preview) {
EditorNode::get_singleton()->get_scene_root()->add_child(preview_node);
@@ -4346,12 +4358,12 @@ bool Node3DEditorViewport::_cyclical_dependency_exists(const String &p_target_sc
return false;
}
-bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) {
- Ref<Resource> res = ResourceLoader::load(path);
+bool Node3DEditorViewport::_create_instance(Node *p_parent, const String &p_path, const Point2 &p_point) {
+ Ref<Resource> res = ResourceLoader::load(p_path);
ERR_FAIL_COND_V(res.is_null(), false);
- Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
- Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res));
+ Ref<PackedScene> scene = res;
+ Ref<Mesh> mesh = res;
Node *instantiated_scene = nullptr;
@@ -4361,8 +4373,10 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
mesh_instance->set_mesh(mesh);
// Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
- String name = path.get_file().get_basename();
- mesh_instance->set_name(Node::adjust_name_casing(name));
+ const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
+ if (!node_name.is_empty()) {
+ mesh_instance->set_name(node_name);
+ }
instantiated_scene = mesh_instance;
} else {
@@ -4386,25 +4400,25 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
}
if (scene != nullptr) {
- instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
+ instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_path));
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->add_do_method(parent, "add_child", instantiated_scene, true);
+ undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true);
undo_redo->add_do_method(instantiated_scene, "set_owner", EditorNode::get_singleton()->get_edited_scene());
undo_redo->add_do_reference(instantiated_scene);
- undo_redo->add_undo_method(parent, "remove_child", instantiated_scene);
+ undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene);
undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene);
- String new_name = parent->validate_child_name(instantiated_scene);
+ String new_name = p_parent->validate_child_name(instantiated_scene);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
- undo_redo->add_do_method(ed, "live_debug_instantiate_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), path, new_name);
- undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
+ undo_redo->add_do_method(ed, "live_debug_instantiate_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), p_path, new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
Node3D *node3d = Object::cast_to<Node3D>(instantiated_scene);
if (node3d) {
Transform3D parent_tf;
- Node3D *parent_node3d = Object::cast_to<Node3D>(parent);
+ Node3D *parent_node3d = Object::cast_to<Node3D>(p_parent);
if (parent_node3d) {
parent_tf = parent_node3d->get_global_gizmo_transform();
}
@@ -4419,6 +4433,46 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
return true;
}
+bool Node3DEditorViewport::_create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point) {
+ Ref<AudioStream> audio = ResourceLoader::load(p_path);
+ ERR_FAIL_COND_V(audio.is_null(), false);
+
+ AudioStreamPlayer3D *audio_player = memnew(AudioStreamPlayer3D);
+ audio_player->set_stream(audio);
+
+ // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
+ const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
+ if (!node_name.is_empty()) {
+ audio_player->set_name(node_name);
+ }
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->add_do_method(p_parent, "add_child", audio_player, true);
+ undo_redo->add_do_method(audio_player, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->add_do_reference(audio_player);
+ undo_redo->add_undo_method(p_parent, "remove_child", audio_player);
+ undo_redo->add_do_method(editor_selection, "add_node", audio_player);
+
+ const String new_name = p_parent->validate_child_name(audio_player);
+ EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
+ undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), audio_player->get_class(), new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
+
+ Transform3D parent_tf;
+ Node3D *parent_node3d = Object::cast_to<Node3D>(p_parent);
+ if (parent_node3d) {
+ parent_tf = parent_node3d->get_global_gizmo_transform();
+ }
+
+ Transform3D new_tf = audio_player->get_transform();
+ new_tf.origin = parent_tf.affine_inverse().xform(preview_node_pos + audio_player->get_position());
+ new_tf.basis = parent_tf.affine_inverse().basis * new_tf.basis;
+
+ undo_redo->add_do_method(audio_player, "set_transform", new_tf);
+
+ return true;
+}
+
void Node3DEditorViewport::_perform_drop_data() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (spatial_editor->get_preview_material_target().is_valid()) {
@@ -4453,11 +4507,18 @@ void Node3DEditorViewport::_perform_drop_data() {
if (res.is_null()) {
continue;
}
- Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
- Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res));
- if (mesh != nullptr || scene != nullptr) {
- bool success = _create_instance(target_node, path, drop_pos);
- if (!success) {
+
+ Ref<PackedScene> scene = res;
+ Ref<Mesh> mesh = res;
+ if (mesh.is_valid() || scene.is_valid()) {
+ if (!_create_instance(target_node, path, drop_pos)) {
+ error_files.push_back(path.get_file());
+ }
+ }
+
+ Ref<AudioStream> audio = res;
+ if (audio.is_valid()) {
+ if (!_create_audio_node(target_node, path, drop_pos)) {
error_files.push_back(path.get_file());
}
}
@@ -4488,12 +4549,14 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant
bool is_other_valid = false;
// Check if at least one of the dragged files is a mesh, material, texture or scene.
for (int i = 0; i < files.size(); i++) {
- bool is_scene = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "PackedScene");
- bool is_mesh = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Mesh");
- bool is_material = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Material");
- bool is_texture = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Texture");
-
- if (is_mesh || is_scene || is_material || is_texture) {
+ const String &res_type = ResourceLoader::get_resource_type(files[i]);
+ bool is_scene = ClassDB::is_parent_class(res_type, "PackedScene");
+ bool is_mesh = ClassDB::is_parent_class(res_type, "Mesh");
+ bool is_material = ClassDB::is_parent_class(res_type, "Material");
+ bool is_texture = ClassDB::is_parent_class(res_type, "Texture");
+ bool is_audio = ClassDB::is_parent_class(res_type, "AudioStream");
+
+ if (is_mesh || is_scene || is_material || is_texture || is_audio) {
Ref<Resource> res = ResourceLoader::load(files[i]);
if (res.is_null()) {
continue;
@@ -4502,6 +4565,7 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant
Ref<Mesh> mesh = res;
Ref<Material> mat = res;
Ref<Texture2D> tex = res;
+ Ref<AudioStream> audio = res;
if (scn.is_valid()) {
Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
if (!instantiated_scene) {
@@ -4537,6 +4601,8 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant
spatial_editor->set_preview_material(new_mat);
is_other_valid = true;
continue;
+ } else if (!is_other_valid && audio.is_valid()) {
+ is_other_valid = true;
} else {
continue;
}
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index a3e1224cb8..4990b11a47 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -443,7 +443,8 @@ private:
void _reset_preview_material() const;
void _remove_preview_material();
bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) const;
- bool _create_instance(Node *parent, String &path, const Point2 &p_point);
+ bool _create_instance(Node *p_parent, const String &p_path, const Point2 &p_point);
+ bool _create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point);
void _perform_drop_data();
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index eb6282ca0c..63eecd357d 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -4122,6 +4122,9 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KeyModifierMask::ALT | Key::LEFT), WINDOW_PREV);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KeyModifierMask::ALT | Key::RIGHT), WINDOW_NEXT);
+ ED_SHORTCUT_OVERRIDE("script_editor/history_previous", "macos", KeyModifierMask::ALT | KeyModifierMask::META | Key::LEFT);
+ ED_SHORTCUT_OVERRIDE("script_editor/history_next", "macos", KeyModifierMask::ALT | KeyModifierMask::META | Key::RIGHT);
+
file_menu->get_popup()->add_separator();
theme_submenu = memnew(PopupMenu);
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 191d58228b..f3f6fc91a9 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -2133,10 +2133,11 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
void ScriptTextEditor::_color_changed(const Color &p_color) {
String new_args;
+ const int decimals = 3;
if (p_color.a == 1.0f) {
- new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ")");
+ new_args = String("(" + String::num(p_color.r, decimals) + ", " + String::num(p_color.g, decimals) + ", " + String::num(p_color.b, decimals) + ")");
} else {
- new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ", " + rtos(p_color.a) + ")");
+ new_args = String("(" + String::num(p_color.r, decimals) + ", " + String::num(p_color.g, decimals) + ", " + String::num(p_color.b, decimals) + ", " + String::num(p_color.a, decimals) + ")");
}
String line = code_editor->get_text_editor()->get_line(color_position.x);
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 2ee0dc0316..2b86268414 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -250,7 +250,7 @@ void TileAtlasView::_draw_base_tiles() {
Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin());
// Draw the tile.
- TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame);
+ TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame);
}
}
@@ -411,7 +411,7 @@ void TileAtlasView::_draw_alternatives() {
}
// Draw the tile.
- TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id);
+ TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id);
// Increment the x position.
current_pos.x += transposed ? texture_region_size.y : texture_region_size.x;
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 2d0e5214db..94cbb371af 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -144,7 +144,8 @@ void GenericTilePolygonEditor::_base_control_draw() {
}
// Draw tile-related things.
- Size2 tile_size = tile_set->get_tile_size();
+ const Size2 base_tile_size = tile_set->get_tile_size();
+ const Size2 tile_size = background_region.size;
Transform2D xform;
xform.set_origin(base_control->get_size() / 2 + panning);
@@ -170,12 +171,16 @@ void GenericTilePolygonEditor::_base_control_draw() {
// Draw grid.
if (current_snap_option == SNAP_GRID) {
- Vector2 spacing = tile_size / snap_subdivision->get_value();
+ Vector2 spacing = base_tile_size / snap_subdivision->get_value();
Vector2 offset = -tile_size / 2;
+ int w = snap_subdivision->get_value() * (tile_size / base_tile_size).x;
+ int h = snap_subdivision->get_value() * (tile_size / base_tile_size).y;
- for (int i = 1; i < snap_subdivision->get_value(); i++) {
- base_control->draw_line(Vector2(spacing.x * i, 0) + offset, Vector2(spacing.x * i, tile_size.y) + offset, Color(1, 1, 1, 0.33));
- base_control->draw_line(Vector2(0, spacing.y * i) + offset, Vector2(tile_size.x, spacing.y * i) + offset, Color(1, 1, 1, 0.33));
+ for (int y = 1; y < h; y++) {
+ for (int x = 1; x < w; x++) {
+ base_control->draw_line(Vector2(spacing.x * x, 0) + offset, Vector2(spacing.x * x, tile_size.y) + offset, Color(1, 1, 1, 0.33));
+ base_control->draw_line(Vector2(0, spacing.y * y) + offset, Vector2(tile_size.x, spacing.y * y) + offset, Color(1, 1, 1, 0.33));
+ }
}
}
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 01551a52c2..438d798120 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -60,7 +60,6 @@
#include "scene/gui/view_panner.h"
#include "scene/main/window.h"
#include "scene/resources/curve_texture.h"
-#include "scene/resources/image_texture.h"
#include "scene/resources/style_box_flat.h"
#include "scene/resources/visual_shader_nodes.h"
#include "scene/resources/visual_shader_particle_nodes.h"
@@ -5135,7 +5134,7 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, List<CopyItem> &r_items, c
}
int new_node_id = connection_remap[item.id];
- int new_frame_id = connection_remap[node->get_frame()];
+ int new_frame_id = node->get_frame();
undo_redo->add_do_method(visual_shader.ptr(), "attach_node_to_frame", type, new_node_id, new_frame_id);
undo_redo->add_do_method(graph_plugin.ptr(), "attach_node_to_frame", type, new_node_id, new_frame_id);
}
diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp
index 32f088ec60..f474464f6c 100644
--- a/editor/project_manager/project_list.cpp
+++ b/editor/project_manager/project_list.cpp
@@ -32,6 +32,8 @@
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
+#include "core/os/time.h"
+#include "core/version.h"
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
@@ -130,12 +132,29 @@ void ProjectListItemControl::set_project_icon(const Ref<Texture2D> &p_icon) {
project_icon->set_texture(p_icon);
}
+void ProjectListItemControl::set_last_edited_info(const String &p_info) {
+ last_edited_info->set_text(p_info);
+}
+
+void ProjectListItemControl::set_project_version(const String &p_info) {
+ project_version->set_text(p_info);
+}
+
void ProjectListItemControl::set_unsupported_features(PackedStringArray p_features) {
if (p_features.size() > 0) {
String tooltip_text = "";
for (int i = 0; i < p_features.size(); i++) {
if (ProjectList::project_feature_looks_like_version(p_features[i])) {
- tooltip_text += TTR("This project was last edited in a different Godot version: ") + p_features[i] + "\n";
+ PackedStringArray project_version_split = p_features[i].split(".");
+ int project_version_major = 0, project_version_minor = 0;
+ if (project_version_split.size() >= 2) {
+ project_version_major = project_version_split[0].to_int();
+ project_version_minor = project_version_split[1].to_int();
+ }
+ if (VERSION_MAJOR != project_version_major || VERSION_MINOR <= project_version_minor) {
+ // Don't show a warning if the project was last edited in a previous minor version.
+ tooltip_text += TTR("This project was last edited in a different Godot version: ") + p_features[i] + "\n";
+ }
p_features.remove_at(i);
i--;
}
@@ -144,6 +163,10 @@ void ProjectListItemControl::set_unsupported_features(PackedStringArray p_featur
String unsupported_features_str = String(", ").join(p_features);
tooltip_text += TTR("This project uses features unsupported by the current build:") + "\n" + unsupported_features_str;
}
+ if (tooltip_text.is_empty()) {
+ return;
+ }
+ project_version->set_tooltip_text(tooltip_text);
project_unsupported_features->set_tooltip_text(tooltip_text);
project_unsupported_features->show();
} else {
@@ -272,12 +295,24 @@ ProjectListItemControl::ProjectListItemControl() {
project_path->set_modulate(Color(1, 1, 1, 0.5));
path_hb->add_child(project_path);
+ last_edited_info = memnew(Label);
+ last_edited_info->set_name("LastEditedInfo");
+ last_edited_info->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ last_edited_info->set_tooltip_text("Last edited timestamp");
+ last_edited_info->set_modulate(Color(1, 1, 1, 0.5));
+ path_hb->add_child(last_edited_info);
+
project_unsupported_features = memnew(TextureRect);
project_unsupported_features->set_name("ProjectUnsupportedFeatures");
project_unsupported_features->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
path_hb->add_child(project_unsupported_features);
project_unsupported_features->hide();
+ project_version = memnew(Label);
+ project_version->set_name("ProjectVersion");
+ project_version->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ path_hb->add_child(project_version);
+
Control *spacer = memnew(Control);
spacer->set_custom_minimum_size(Size2(10, 10));
path_hb->add_child(spacer);
@@ -409,6 +444,24 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa
PackedStringArray project_features = cf->get_value("application", "config/features", PackedStringArray());
PackedStringArray unsupported_features = ProjectSettings::get_unsupported_features(project_features);
+ String project_version = "?";
+ for (int i = 0; i < project_features.size(); i++) {
+ if (ProjectList::project_feature_looks_like_version(project_features[i])) {
+ project_version = project_features[i];
+ break;
+ }
+ }
+
+ if (config_version < ProjectSettings::CONFIG_VERSION) {
+ // Previous versions may not have unsupported features.
+ if (config_version == 4) {
+ unsupported_features.push_back("3.x");
+ project_version = "3.x";
+ } else {
+ unsupported_features.push_back("Unknown version");
+ }
+ }
+
uint64_t last_edited = 0;
if (cf_err == OK) {
// The modification date marks the date the project was last edited.
@@ -433,7 +486,7 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa
ProjectManager::get_singleton()->add_new_tag(tag);
}
- return Item(project_name, description, tags, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version);
+ return Item(project_name, description, project_version, tags, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version);
}
void ProjectList::_update_icons_async() {
@@ -706,6 +759,8 @@ void ProjectList::_create_project_item_control(int p_index) {
hb->set_tooltip_text(item.description);
hb->set_tags(item.tags, this);
hb->set_unsupported_features(item.unsupported_features.duplicate());
+ hb->set_project_version(item.project_version);
+ hb->set_last_edited_info(Time::get_singleton()->get_datetime_string_from_unix_time(item.last_edited, true));
hb->set_is_favorite(item.favorite);
hb->set_is_missing(item.missing);
diff --git a/editor/project_manager/project_list.h b/editor/project_manager/project_list.h
index 981df0f3a0..6e0f5830ac 100644
--- a/editor/project_manager/project_list.h
+++ b/editor/project_manager/project_list.h
@@ -51,6 +51,8 @@ class ProjectListItemControl : public HBoxContainer {
TextureRect *project_icon = nullptr;
Label *project_title = nullptr;
Label *project_path = nullptr;
+ Label *last_edited_info = nullptr;
+ Label *project_version = nullptr;
TextureRect *project_unsupported_features = nullptr;
HBoxContainer *tag_container = nullptr;
@@ -71,6 +73,8 @@ public:
void set_project_path(const String &p_path);
void set_tags(const PackedStringArray &p_tags, ProjectList *p_parent_list);
void set_project_icon(const Ref<Texture2D> &p_icon);
+ void set_last_edited_info(const String &p_info);
+ void set_project_version(const String &p_version);
void set_unsupported_features(PackedStringArray p_features);
bool should_load_project_icon() const;
@@ -100,6 +104,7 @@ public:
struct Item {
String project_name;
String description;
+ String project_version;
PackedStringArray tags;
String tag_sort_string;
String path;
@@ -118,6 +123,7 @@ public:
Item(const String &p_name,
const String &p_description,
+ const String &p_project_version,
const PackedStringArray &p_tags,
const String &p_path,
const String &p_icon,
@@ -130,6 +136,7 @@ public:
int p_version) {
project_name = p_name;
description = p_description;
+ project_version = p_project_version;
tags = p_tags;
path = p_path;
icon = p_icon;
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index cff95cadc8..dc9d1df4e8 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -54,7 +54,7 @@ void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) {
if (saved_size != Rect2()) {
popup(saved_size);
} else {
- popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8);
+ popup_centered_clamped(Size2(1200, 700) * EDSCALE, 0.8);
}
_add_feature_overrides();
diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp
index 51f9d319a6..edefaa61a8 100644
--- a/editor/rename_dialog.cpp
+++ b/editor/rename_dialog.cpp
@@ -289,6 +289,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor) {
vbc->add_child(lbl_preview_title);
lbl_preview = memnew(Label);
+ lbl_preview->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
lbl_preview->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
vbc->add_child(lbl_preview);
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 53145c3450..66d26c0a6c 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -56,6 +56,7 @@
#include "editor/shader_create_dialog.h"
#include "editor/themes/editor_scale.h"
#include "scene/animation/animation_tree.h"
+#include "scene/audio/audio_stream_player.h"
#include "scene/gui/check_box.h"
#include "scene/property_utils.h"
#include "scene/resources/packed_scene.h"
@@ -254,8 +255,8 @@ void SceneTreeDock::instantiate_scenes(const Vector<String> &p_files, Node *p_pa
_perform_instantiate_scenes(p_files, parent, -1);
}
-void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, Node *parent, int p_pos) {
- ERR_FAIL_NULL(parent);
+void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, Node *p_parent, int p_pos) {
+ ERR_FAIL_NULL(p_parent);
Vector<Node *> instances;
@@ -302,25 +303,25 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Instantiate Scene(s)"));
+ undo_redo->create_action_for_history(TTRN("Instantiate Scene", "Instantiate Scenes", instances.size()), editor_data->get_current_edited_scene_history_id());
+ undo_redo->add_do_method(editor_selection, "clear");
for (int i = 0; i < instances.size(); i++) {
Node *instantiated_scene = instances[i];
- undo_redo->add_do_method(parent, "add_child", instantiated_scene, true);
+ undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true);
if (p_pos >= 0) {
- undo_redo->add_do_method(parent, "move_child", instantiated_scene, p_pos + i);
+ undo_redo->add_do_method(p_parent, "move_child", instantiated_scene, p_pos + i);
}
undo_redo->add_do_method(instantiated_scene, "set_owner", edited_scene);
- undo_redo->add_do_method(editor_selection, "clear");
undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene);
undo_redo->add_do_reference(instantiated_scene);
- undo_redo->add_undo_method(parent, "remove_child", instantiated_scene);
+ undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene);
- String new_name = parent->validate_child_name(instantiated_scene);
+ String new_name = p_parent->validate_child_name(instantiated_scene);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
- undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(parent), p_files[i], new_name);
- undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).path_join(new_name)));
+ undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(p_parent), p_files[i], new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).path_join(new_name)));
}
undo_redo->commit_action();
@@ -330,6 +331,75 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N
}
}
+void SceneTreeDock::_perform_create_audio_stream_players(const Vector<String> &p_files, Node *p_parent, int p_pos) {
+ ERR_FAIL_NULL(p_parent);
+
+ StringName node_type = "AudioStreamPlayer";
+ if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
+ if (Object::cast_to<Node2D>(p_parent)) {
+ node_type = "AudioStreamPlayer2D";
+ } else if (Object::cast_to<Node3D>(p_parent)) {
+ node_type = "AudioStreamPlayer3D";
+ }
+ }
+
+ Vector<Node *> nodes;
+ bool error = false;
+
+ for (const String &path : p_files) {
+ Ref<AudioStream> stream = ResourceLoader::load(path);
+ if (stream.is_null()) {
+ current_option = -1;
+ accept->set_text(vformat(TTR("Error loading audio stream from %s"), path));
+ accept->popup_centered();
+ error = true;
+ break;
+ }
+
+ Node *player = Object::cast_to<Node>(ClassDB::instantiate(node_type));
+ player->set("stream", stream);
+
+ // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
+ const String &node_name = Node::adjust_name_casing(path.get_file().get_basename());
+ if (!node_name.is_empty()) {
+ player->set_name(node_name);
+ }
+
+ nodes.push_back(player);
+ }
+
+ if (error) {
+ for (Node *node : nodes) {
+ memdelete(node);
+ }
+ return;
+ }
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action_for_history(TTRN("Create AudioStreamPlayer", "Create AudioStreamPlayers", nodes.size()), editor_data->get_current_edited_scene_history_id());
+ undo_redo->add_do_method(editor_selection, "clear");
+
+ for (int i = 0; i < nodes.size(); i++) {
+ Node *node = nodes[i];
+
+ undo_redo->add_do_method(p_parent, "add_child", node, true);
+ if (p_pos >= 0) {
+ undo_redo->add_do_method(p_parent, "move_child", node, p_pos + i);
+ }
+ undo_redo->add_do_method(node, "set_owner", edited_scene);
+ undo_redo->add_do_method(editor_selection, "add_node", node);
+ undo_redo->add_do_reference(node);
+ undo_redo->add_undo_method(p_parent, "remove_child", node);
+
+ String new_name = p_parent->validate_child_name(node);
+ EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
+ undo_redo->add_do_method(ed, "live_debug_create_node", edited_scene->get_path_to(p_parent), node->get_class(), new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).path_join(new_name)));
+ }
+
+ undo_redo->commit_action();
+}
+
void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) {
// `move_child` + `get_index` doesn't really work for internal nodes.
ERR_FAIL_COND_MSG(base->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to replace internal node, this is not supported.");
@@ -3196,15 +3266,13 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) {
void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to, int p_type) {
Node *node = get_node(p_to);
ERR_FAIL_NULL(node);
+ ERR_FAIL_COND(p_files.is_empty());
- if (scene_tree->get_scene_tree()->get_drop_mode_flags() & Tree::DROP_MODE_INBETWEEN) {
- // Dropped PackedScene, instance it.
- int to_pos = -1;
- _normalize_drop(node, to_pos, p_type);
- _perform_instantiate_scenes(p_files, node, to_pos);
- } else {
- const String &res_path = p_files[0];
- StringName res_type = EditorFileSystem::get_singleton()->get_file_type(res_path);
+ const String &res_path = p_files[0];
+ const StringName res_type = EditorFileSystem::get_singleton()->get_file_type(res_path);
+
+ // Dropping as property when possible.
+ if (p_type == 0 && p_files.size() == 1) {
List<String> valid_properties;
List<PropertyInfo> pinfo;
@@ -3238,10 +3306,22 @@ void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to,
menu_properties->reset_size();
menu_properties->set_position(get_screen_position() + get_local_mouse_position());
menu_properties->popup();
- } else if (!valid_properties.is_empty()) {
+ return;
+ }
+ if (!valid_properties.is_empty()) {
_perform_property_drop(node, valid_properties.front()->get(), ResourceLoader::load(res_path));
+ return;
}
}
+
+ // Either instantiate scenes or create AudioStreamPlayers.
+ int to_pos = -1;
+ _normalize_drop(node, to_pos, p_type);
+ if (res_type == "PackedScene") {
+ _perform_instantiate_scenes(p_files, node, to_pos);
+ } else {
+ _perform_create_audio_stream_players(p_files, node, to_pos);
+ }
}
void SceneTreeDock::_script_dropped(const String &p_file, NodePath p_to) {
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 21e1b00f93..abef990995 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -272,7 +272,8 @@ class SceneTreeDock : public VBoxContainer {
void _filter_option_selected(int option);
void _append_filter_options_to(PopupMenu *p_menu, bool p_include_separator = true);
- void _perform_instantiate_scenes(const Vector<String> &p_files, Node *parent, int p_pos);
+ void _perform_instantiate_scenes(const Vector<String> &p_files, Node *p_parent, int p_pos);
+ void _perform_create_audio_stream_players(const Vector<String> &p_files, Node *p_parent, int p_pos);
void _replace_with_branch_scene(const String &p_file, Node *base);
void _remote_tree_selected();
diff --git a/editor/themes/SCsub b/editor/themes/SCsub
index 65cfb6a8be..e8f96e4299 100644
--- a/editor/themes/SCsub
+++ b/editor/themes/SCsub
@@ -3,8 +3,8 @@
Import("env")
import glob
-import editor_theme_builders
+import editor_theme_builders
# Fonts
flist = glob.glob(env.Dir("#thirdparty").abspath + "/fonts/*.ttf")
diff --git a/gles3_builders.py b/gles3_builders.py
index 1491927feb..a4928c81c5 100644
--- a/gles3_builders.py
+++ b/gles3_builders.py
@@ -1,9 +1,10 @@
"""Functions used to generate source files during build time"""
import os.path
-from methods import print_error
from typing import Optional
+from methods import print_error
+
class GLES3HeaderStruct:
def __init__(self):
@@ -91,11 +92,11 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct,
includeline = line.replace("#include ", "").strip()[1:-1]
included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline)
- if not included_file in header_data.vertex_included_files and header_data.reading == "vertex":
+ if included_file not in header_data.vertex_included_files and header_data.reading == "vertex":
header_data.vertex_included_files += [included_file]
if include_file_in_gles3_header(included_file, header_data, depth + 1) is None:
print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
- elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment":
+ elif included_file not in header_data.fragment_included_files and header_data.reading == "fragment":
header_data.fragment_included_files += [included_file]
if include_file_in_gles3_header(included_file, header_data, depth + 1) is None:
print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
@@ -121,7 +122,7 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct,
# unfiorm array
x = x[: x.find("[")]
- if not x in header_data.texunit_names:
+ if x not in header_data.texunit_names:
header_data.texunits += [(x, texunit)]
header_data.texunit_names += [x]
@@ -142,7 +143,7 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct,
# unfiorm array
x = x[: x.find("[")]
- if not x in header_data.ubo_names:
+ if x not in header_data.ubo_names:
header_data.ubos += [(x, ubo)]
header_data.ubo_names += [x]
@@ -157,7 +158,7 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct,
# unfiorm array
x = x[: x.find("[")]
- if not x in header_data.uniforms:
+ if x not in header_data.uniforms:
header_data.uniforms += [x]
if (line.strip().find("out ") == 0 or line.strip().find("flat ") == 0) and line.find("tfb:") != -1:
diff --git a/glsl_builders.py b/glsl_builders.py
index 22f4de74b1..05aab3acbb 100644
--- a/glsl_builders.py
+++ b/glsl_builders.py
@@ -1,8 +1,9 @@
"""Functions used to generate source files during build time"""
import os.path
+from typing import Iterable, Optional
+
from methods import print_error
-from typing import Optional, Iterable
def generate_inline_code(input_lines: Iterable[str], insert_newline: bool = True):
@@ -77,15 +78,15 @@ def include_file_in_rd_header(filename: str, header_data: RDHeaderStruct, depth:
else:
included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline)
- if not included_file in header_data.vertex_included_files and header_data.reading == "vertex":
+ if included_file not in header_data.vertex_included_files and header_data.reading == "vertex":
header_data.vertex_included_files += [included_file]
if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
- elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment":
+ elif included_file not in header_data.fragment_included_files and header_data.reading == "fragment":
header_data.fragment_included_files += [included_file]
if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
- elif not included_file in header_data.compute_included_files and header_data.reading == "compute":
+ elif included_file not in header_data.compute_included_files and header_data.reading == "compute":
header_data.compute_included_files += [included_file]
if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
diff --git a/main/main.cpp b/main/main.cpp
index 66706be745..65f637d778 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2196,11 +2196,11 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (globals->has_setting("display/window/size/window_width_override") &&
globals->has_setting("display/window/size/window_height_override")) {
- int desired_width = globals->get("display/window/size/window_width_override");
+ int desired_width = GLOBAL_GET("display/window/size/window_width_override");
if (desired_width > 0) {
window_size.width = desired_width;
}
- int desired_height = globals->get("display/window/size/window_height_override");
+ int desired_height = GLOBAL_GET("display/window/size/window_height_override");
if (desired_height > 0) {
window_size.height = desired_height;
}
@@ -4295,9 +4295,6 @@ void Main::cleanup(bool p_force) {
if (globals) {
memdelete(globals);
}
- if (engine) {
- memdelete(engine);
- }
if (OS::get_singleton()->is_restart_on_exit_set()) {
//attempt to restart with arguments
@@ -4315,6 +4312,10 @@ void Main::cleanup(bool p_force) {
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
unregister_core_types();
+ if (engine) {
+ memdelete(engine);
+ }
+
OS::get_singleton()->benchmark_end_measure("Shutdown", "Total");
OS::get_singleton()->benchmark_dump();
diff --git a/methods.py b/methods.py
index da221cc0ea..7600e6d445 100644
--- a/methods.py
+++ b/methods.py
@@ -1,17 +1,14 @@
+import contextlib
+import glob
import os
-import sys
import re
-import glob
import subprocess
-import contextlib
+import sys
from collections import OrderedDict
-from collections.abc import Mapping
from enum import Enum
-from typing import Generator, Optional
-from io import TextIOWrapper, StringIO
+from io import StringIO, TextIOWrapper
from pathlib import Path
-from os.path import normpath, basename
-
+from typing import Generator, Optional
# Get the "Godot" folder name ahead of time
base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/"
@@ -199,7 +196,7 @@ def add_module_version_string(self, s):
def get_version_info(module_version_string="", silent=False):
build_name = "custom_build"
- if os.getenv("BUILD_NAME") != None:
+ if os.getenv("BUILD_NAME") is not None:
build_name = str(os.getenv("BUILD_NAME"))
if not silent:
print(f"Using custom build name: '{build_name}'.")
@@ -221,7 +218,7 @@ def get_version_info(module_version_string="", silent=False):
# For dev snapshots (alpha, beta, RC, etc.) we do not commit status change to Git,
# so this define provides a way to override it without having to modify the source.
- if os.getenv("GODOT_VERSION_STATUS") != None:
+ if os.getenv("GODOT_VERSION_STATUS") is not None:
version_info["status"] = str(os.getenv("GODOT_VERSION_STATUS"))
if not silent:
print(f"Using version status '{version_info['status']}', overriding the original '{version.status}'.")
@@ -435,7 +432,7 @@ def module_check_dependencies(self, module):
required_deps = self.module_dependencies[module][0] if module in self.module_dependencies else []
for dep in required_deps:
opt = "module_{}_enabled".format(dep)
- if not opt in self or not self[opt]:
+ if opt not in self or not self[opt]:
missing_deps.append(dep)
if missing_deps != []:
@@ -450,7 +447,6 @@ def module_check_dependencies(self, module):
def sort_module_list(env):
- out = OrderedDict()
deps = {k: v[0] + list(filter(lambda x: x in env.module_list, v[1])) for k, v in env.module_dependencies.items()}
frontier = list(env.module_list.keys())
@@ -650,7 +646,7 @@ def detect_visual_c_compiler_version(tools_env):
def find_visual_c_batch_file(env):
- from SCons.Tool.MSCommon.vc import get_default_version, get_host_target, find_batch_file, find_vc_pdir
+ from SCons.Tool.MSCommon.vc import find_batch_file, find_vc_pdir, get_default_version, get_host_target
msvc_version = get_default_version(env)
@@ -696,10 +692,7 @@ def glob_recursive(pattern, node="."):
def add_to_vs_project(env, sources):
for x in sources:
- if type(x) == type(""):
- fname = env.File(x).path
- else:
- fname = env.File(x)[0].path
+ fname = env.File(x).path if isinstance(x, str) else env.File(x)[0].path
pieces = fname.split(".")
if len(pieces) > 0:
basename = pieces[0]
@@ -884,7 +877,8 @@ def show_progress(env):
return
import sys
- from SCons.Script import Progress, Command, AlwaysBuild
+
+ from SCons.Script import AlwaysBuild, Command, Progress
screen = sys.stdout
# Progress reporting is not available in non-TTY environments since it
@@ -895,7 +889,8 @@ def show_progress(env):
node_count_interval = 1
node_count_fname = str(env.Dir("#")) + "/.scons_node_count"
- import time, math
+ import math
+ import time
class cache_progress:
# The default is 1 GB cache and 12 hours half life
@@ -903,7 +898,7 @@ def show_progress(env):
self.path = path
self.limit = limit
self.exponent_scale = math.log(2) / half_life
- if env["verbose"] and path != None:
+ if env["verbose"] and path is not None:
screen.write(
"Current cache limit is {} (used: {})\n".format(
self.convert_size(limit), self.convert_size(self.get_size(path))
@@ -1049,11 +1044,11 @@ def generate_vs_project(env, original_args, project_name="godot"):
if type(f) is Node.FS.Dir:
results += glob_recursive_2(pattern, dirs, f)
r = Glob(str(node) + "/" + pattern, source=True)
- if len(r) > 0 and not str(node) in dirs:
+ if len(r) > 0 and str(node) not in dirs:
d = ""
for part in str(node).split("\\"):
d += part
- if not d in dirs:
+ if d not in dirs:
dirs.append(d)
d += "\\"
results += r
@@ -1066,7 +1061,7 @@ def generate_vs_project(env, original_args, project_name="godot"):
if val is not None:
try:
return _text2bool(val)
- except:
+ except (ValueError, AttributeError):
return default
else:
return default
@@ -1239,13 +1234,13 @@ def generate_vs_project(env, original_args, project_name="godot"):
others_active = []
for x in envsources:
fname = ""
- if type(x) == type(""):
+ if isinstance(x, str):
fname = env.File(x).path
else:
# Some object files might get added directly as a File object and not a list.
try:
fname = env.File(x)[0].path
- except:
+ except Exception:
fname = x.path
pass
@@ -1324,7 +1319,7 @@ def generate_vs_project(env, original_args, project_name="godot"):
itemlist = {}
for item in activeItems:
key = os.path.dirname(item).replace("\\", "_")
- if not key in itemlist:
+ if key not in itemlist:
itemlist[key] = [item]
else:
itemlist[key] += [item]
@@ -1465,14 +1460,14 @@ def generate_vs_project(env, original_args, project_name="godot"):
if godot_platform != "windows":
configurations += [
f'<ProjectConfiguration Include="editor|{proj_plat}">',
- f" <Configuration>editor</Configuration>",
+ " <Configuration>editor</Configuration>",
f" <Platform>{proj_plat}</Platform>",
"</ProjectConfiguration>",
]
properties += [
f"<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='editor|{proj_plat}'\">",
- f" <GodotConfiguration>editor</GodotConfiguration>",
+ " <GodotConfiguration>editor</GodotConfiguration>",
f" <GodotPlatform>{proj_plat}</GodotPlatform>",
"</PropertyGroup>",
]
diff --git a/misc/scripts/dotnet_format.py b/misc/scripts/dotnet_format.py
index 51fd7a1223..51fd7a1223 100644..100755
--- a/misc/scripts/dotnet_format.py
+++ b/misc/scripts/dotnet_format.py
diff --git a/misc/scripts/file_format.py b/misc/scripts/file_format.py
index a4ea544a45..a4ea544a45 100644..100755
--- a/misc/scripts/file_format.py
+++ b/misc/scripts/file_format.py
diff --git a/misc/scripts/gitignore_check.sh b/misc/scripts/gitignore_check.sh
index f162e25391..f162e25391 100644..100755
--- a/misc/scripts/gitignore_check.sh
+++ b/misc/scripts/gitignore_check.sh
diff --git a/misc/scripts/header_guards.py b/misc/scripts/header_guards.py
index b554be5159..b554be5159 100644..100755
--- a/misc/scripts/header_guards.py
+++ b/misc/scripts/header_guards.py
diff --git a/misc/scripts/install_d3d12_sdk_windows.py b/misc/scripts/install_d3d12_sdk_windows.py
index aed80d81a4..d7574e6222 100755
--- a/misc/scripts/install_d3d12_sdk_windows.py
+++ b/misc/scripts/install_d3d12_sdk_windows.py
@@ -1,15 +1,15 @@
#!/usr/bin/env python
import os
-import urllib.request
import shutil
import subprocess
import sys
+import urllib.request
# Enable ANSI escape code support on Windows 10 and later (for colored console output).
# <https://github.com/python/cpython/issues/73245>
if sys.platform == "win32":
- from ctypes import windll, c_int, byref
+ from ctypes import byref, c_int, windll
stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
mode = c_int(0)
diff --git a/modules/SCsub b/modules/SCsub
index 739c5de0b5..db0e563dc4 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -1,8 +1,9 @@
#!/usr/bin/env python
-import methods
import os
+import methods
+
Import("env")
env_modules = env.Clone()
diff --git a/modules/fbx/editor/editor_scene_importer_ufbx.cpp b/modules/fbx/editor/editor_scene_importer_ufbx.cpp
index 3cc919fae2..e0f60fe998 100644
--- a/modules/fbx/editor/editor_scene_importer_ufbx.cpp
+++ b/modules/fbx/editor/editor_scene_importer_ufbx.cpp
@@ -111,7 +111,7 @@ void EditorSceneFormatImporterUFBX::get_import_options(const String &p_path,
void EditorSceneFormatImporterUFBX::handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {
if (!p_import_params.has("fbx/importer")) {
- p_import_params["fbx/importer"] = EditorSceneFormatImporterUFBX::FBX_IMPORTER_UFBX;
+ p_import_params["fbx/importer"] = EditorSceneFormatImporterUFBX::FBX_IMPORTER_FBX2GLTF;
}
}
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 07d917ea04..1909ca5ab5 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -349,10 +349,11 @@
<param index="1" name="hint_string" type="String" />
<param index="2" name="usage" type="int" enum="PropertyUsageFlags" is_bitfield="true" default="6" />
<description>
- Allows you to set a custom hint, hint string, and usage flags for the exported property. Note that there's no validation done in GDScript, it will just pass the hint along to the editor.
+ Allows you to set a custom hint, hint string, and usage flags for the exported property. Note that there's no validation done in GDScript, it will just pass the parameters to the editor.
[codeblock]
@export_custom(PROPERTY_HINT_NONE, "suffix:m") var suffix: Vector3
[/codeblock]
+ [b]Note:[/b] Regardless of the [param usage] value, the [constant PROPERTY_USAGE_SCRIPT_VARIABLE] flag is always added, as with any explicitly declared script variable.
</description>
</annotation>
<annotation name="@export_dir">
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index de56dd5ece..0c58b41fcb 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -396,6 +396,8 @@ bool GDScript::get_property_default_value(const StringName &p_property, Variant
}
ScriptInstance *GDScript::instance_create(Object *p_this) {
+ ERR_FAIL_COND_V_MSG(!valid, nullptr, "Script is invalid!");
+
GDScript *top = this;
while (top->_base) {
top = top->_base;
@@ -902,6 +904,11 @@ void GDScript::unload_static() const {
}
Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (unlikely(!valid)) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+ }
+
GDScript *top = this;
while (top) {
HashMap<StringName, GDScriptFunction *>::Iterator E = top->member_functions.find(p_method);
@@ -924,6 +931,10 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const {
return true;
}
+ if (unlikely(!valid)) {
+ return false;
+ }
+
const GDScript *top = this;
while (top) {
{
@@ -980,6 +991,10 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
+ if (unlikely(!valid)) {
+ return false;
+ }
+
GDScript *top = this;
while (top) {
HashMap<StringName, MemberInfo>::ConstIterator E = top->static_variables_indices.find(p_name);
@@ -1014,6 +1029,10 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) {
void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
p_properties->push_back(PropertyInfo(Variant::STRING, "script/source", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ if (unlikely(!valid)) {
+ return;
+ }
+
List<const GDScript *> classes;
const GDScript *top = this;
while (top) {
@@ -1630,6 +1649,10 @@ GDScript::~GDScript() {
//////////////////////////////
bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
+ if (unlikely(!script->valid)) {
+ return false;
+ }
+
{
HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name);
if (E) {
@@ -1703,6 +1726,10 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
}
bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
+ if (unlikely(!script->valid)) {
+ return false;
+ }
+
{
HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = script->member_indices.find(p_name);
if (E) {
@@ -1823,6 +1850,10 @@ void GDScriptInstance::validate_property(PropertyInfo &p_property) const {
}
void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
+ if (unlikely(!script->valid)) {
+ return;
+ }
+
// exported members, not done yet!
const GDScript *sptr = script.ptr();
@@ -1901,6 +1932,10 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
}
bool GDScriptInstance::property_can_revert(const StringName &p_name) const {
+ if (unlikely(!script->valid)) {
+ return false;
+ }
+
Variant name = p_name;
const Variant *args[1] = { &name };
@@ -1921,6 +1956,10 @@ bool GDScriptInstance::property_can_revert(const StringName &p_name) const {
}
bool GDScriptInstance::property_get_revert(const StringName &p_name, Variant &r_ret) const {
+ if (unlikely(!script->valid)) {
+ return false;
+ }
+
Variant name = p_name;
const Variant *args[1] = { &name };
@@ -1995,6 +2034,11 @@ void GDScriptInstance::_call_implicit_ready_recursively(GDScript *p_script) {
}
Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ if (unlikely(!script->valid)) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+ }
+
GDScript *sptr = script.ptr();
if (unlikely(p_method == SceneStringName(_ready))) {
// Call implicit ready first, including for the super classes recursively.
@@ -2012,6 +2056,10 @@ Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_ar
}
void GDScriptInstance::notification(int p_notification, bool p_reversed) {
+ if (unlikely(!script->valid)) {
+ return;
+ }
+
//notification is not virtual, it gets called at ALL levels just like in C.
Variant value = p_notification;
const Variant *args[1] = { &value };
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index e62972b949..eeffc13a10 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -3228,7 +3228,11 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
GDScriptCache::add_static_script(p_script);
}
- return GDScriptCache::finish_compiling(main_script->path);
+ err = GDScriptCache::finish_compiling(main_script->path);
+ if (err) {
+ main_script->valid = false;
+ }
+ return err;
}
String GDScriptCompiler::get_error() const {
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index cae85d7c70..4ae39d80cd 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -911,6 +911,29 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
option.insert_text = option.display.quote(p_quote_style);
r_result.insert(option.display, option);
}
+ } else if (p_annotation->name == SNAME("@export_custom")) {
+ switch (p_argument) {
+ case 0: {
+ static HashMap<StringName, int64_t> items;
+ if (unlikely(items.is_empty())) {
+ CoreConstants::get_enum_values(SNAME("PropertyHint"), &items);
+ }
+ for (const KeyValue<StringName, int64_t> &item : items) {
+ ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
+ r_result.insert(option.display, option);
+ }
+ } break;
+ case 2: {
+ static HashMap<StringName, int64_t> items;
+ if (unlikely(items.is_empty())) {
+ CoreConstants::get_enum_values(SNAME("PropertyUsageFlags"), &items);
+ }
+ for (const KeyValue<StringName, int64_t> &item : items) {
+ ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
+ r_result.insert(option.display, option);
+ }
+ } break;
+ }
} else if (p_annotation->name == SNAME("@warning_ignore")) {
for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {
ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
@@ -2065,6 +2088,12 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
found = false;
}
+ // If the found type was not fully analyzed we analyze it now.
+ if (found && r_type.type.kind == GDScriptParser::DataType::CLASS && !r_type.type.class_type->resolved_body) {
+ Error err;
+ Ref<GDScriptParserRef> r = GDScriptCache::get_parser(r_type.type.script_path, GDScriptParserRef::FULLY_SOLVED, err);
+ }
+
// Check type hint last. For collections we want chance to get the actual value first
// This way we can detect types from the content of dictionaries and arrays
if (!found && p_expression->get_datatype().is_hard_type()) {
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 76f02e44e9..9a4c92f601 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -101,7 +101,6 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
// Export annotations.
register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
- register_annotation(MethodInfo("@export_storage"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true);
register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true);
register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
@@ -121,6 +120,7 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
+ register_annotation(MethodInfo("@export_storage"), AnnotationInfo::VARIABLE, &GDScriptParser::export_storage_annotation);
register_annotation(MethodInfo("@export_custom", PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_ENUM, "PropertyHint"), PropertyInfo(Variant::STRING, "hint_string"), PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_BITFIELD, "PropertyUsageFlags")), AnnotationInfo::VARIABLE, &GDScriptParser::export_custom_annotation, varray(PROPERTY_USAGE_DEFAULT));
// Export grouping annotations.
register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
@@ -4299,7 +4299,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
case GDScriptParser::DataType::BUILTIN:
variable->export_info.type = export_type.builtin_type;
variable->export_info.hint = PROPERTY_HINT_NONE;
- variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type);
+ variable->export_info.hint_string = String();
break;
case GDScriptParser::DataType::NATIVE:
if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
@@ -4400,12 +4400,6 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
push_error(_get_annotation_error_string(p_annotation->name, expected_types, variable->get_datatype()), p_annotation);
return false;
}
- } else if (p_annotation->name == SNAME("@export_storage")) {
- use_default_variable_type_check = false; // Can be applied to a variable of any type.
-
- // Save the info because the compiler uses export info for overwriting member info.
- variable->export_info = export_type.to_property_info(variable->identifier->name);
- variable->export_info.usage |= PROPERTY_USAGE_STORAGE;
}
if (use_default_variable_type_check) {
@@ -4425,11 +4419,38 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
if (variable->export_info.hint) {
hint_prefix += "/" + itos(variable->export_info.hint);
}
+ variable->export_info.type = original_export_type_builtin;
variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
- variable->export_info.type = original_export_type_builtin;
+ variable->export_info.usage = PROPERTY_USAGE_DEFAULT;
+ variable->export_info.class_name = StringName();
+ }
+
+ return true;
+}
+
+// For `@export_storage` and `@export_custom`, there is no need to check the variable type, argument values,
+// or handle array exports in a special way, so they are implemented as separate methods.
+
+bool GDScriptParser::export_storage_annotation(const AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) {
+ ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
+
+ VariableNode *variable = static_cast<VariableNode *>(p_node);
+ if (variable->is_static) {
+ push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
+ return false;
+ }
+ if (variable->exported) {
+ push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
+ return false;
}
+ variable->exported = true;
+
+ // Save the info because the compiler uses export info for overwriting member info.
+ variable->export_info = variable->get_datatype().to_property_info(variable->identifier->name);
+ variable->export_info.usage |= PROPERTY_USAGE_STORAGE;
+
return true;
}
@@ -4438,6 +4459,10 @@ bool GDScriptParser::export_custom_annotation(const AnnotationNode *p_annotation
ERR_FAIL_COND_V_MSG(p_annotation->resolved_arguments.size() < 2, false, R"(Annotation "@export_custom" requires 2 arguments.)");
VariableNode *variable = static_cast<VariableNode *>(p_node);
+ if (variable->is_static) {
+ push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
+ return false;
+ }
if (variable->exported) {
push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
return false;
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 4c11fa7f8b..96358165c0 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -1497,6 +1497,7 @@ private:
bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
template <PropertyHint t_hint, Variant::Type t_type>
bool export_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
+ bool export_storage_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
bool export_custom_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
template <PropertyUsageFlags t_usage>
bool export_group_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
diff --git a/modules/gdscript/tests/scripts/analyzer/features/export_enum_as_dictionary.out b/modules/gdscript/tests/scripts/analyzer/features/export_enum_as_dictionary.out
index 505af5f1f3..0d96f8c021 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/export_enum_as_dictionary.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/export_enum_as_dictionary.out
@@ -1,11 +1,11 @@
GDTEST_OK
var test_1: Dictionary
- hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_2: TestExportEnumAsDictionary.MyEnum
- hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM
+ hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"TestExportEnumAsDictionary.MyEnum"
var test_3: Dictionary
- hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_4: TestExportEnumAsDictionary.MyEnum
- hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM
+ hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"TestExportEnumAsDictionary.MyEnum"
var test_5: TestExportEnumAsDictionary.MyEnum
- hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM
+ hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"TestExportEnumAsDictionary.MyEnum"
diff --git a/modules/gdscript/tests/scripts/parser/features/annotations.out b/modules/gdscript/tests/scripts/parser/features/annotations.out
index 2ba9dd7496..6516672820 100644
--- a/modules/gdscript/tests/scripts/parser/features/annotations.out
+++ b/modules/gdscript/tests/scripts/parser/features/annotations.out
@@ -1,25 +1,25 @@
GDTEST_OK
var test_1: int = null
- hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_2: int = null
- hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_3: int = null
- hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_4: int = null
- hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_5: int = 0
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_6: int = 0
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_7: int = 42
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_8: int = 0
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_9: int = 0
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_10: int = 0
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_11: int = 0
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_12: int = 0
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
diff --git a/modules/gdscript/tests/scripts/parser/features/export_arrays.out b/modules/gdscript/tests/scripts/parser/features/export_arrays.out
index acbf389645..f1522d096f 100644
--- a/modules/gdscript/tests/scripts/parser/features/export_arrays.out
+++ b/modules/gdscript/tests/scripts/parser/features/export_arrays.out
@@ -1,139 +1,139 @@
GDTEST_OK
var test_dir: Array
- hint=TYPE_STRING hint_string="String/DIR:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<DIR>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_dir_packed: PackedStringArray
- hint=TYPE_STRING hint_string="String/DIR:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<DIR>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_file: Array
- hint=TYPE_STRING hint_string="String/FILE:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<FILE>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_file_packed: PackedStringArray
- hint=TYPE_STRING hint_string="String/FILE:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<FILE>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_global_dir: Array
- hint=TYPE_STRING hint_string="String/GLOBAL_DIR:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<GLOBAL_DIR>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_global_dir_packed: PackedStringArray
- hint=TYPE_STRING hint_string="String/GLOBAL_DIR:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<GLOBAL_DIR>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_global_file: Array
- hint=TYPE_STRING hint_string="String/GLOBAL_FILE:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<GLOBAL_FILE>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_global_file_packed: PackedStringArray
- hint=TYPE_STRING hint_string="String/GLOBAL_FILE:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<GLOBAL_FILE>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag: Array
- hint=TYPE_STRING hint_string="int/FLAGS:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<FLAGS>:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_packed_byte: PackedByteArray
- hint=TYPE_STRING hint_string="int/FLAGS:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<FLAGS>:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_packed32: PackedInt32Array
- hint=TYPE_STRING hint_string="int/FLAGS:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<FLAGS>:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_packed64: PackedInt64Array
- hint=TYPE_STRING hint_string="int/FLAGS:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<FLAGS>:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_nav: Array
- hint=TYPE_STRING hint_string="int/LAYERS_2D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_nav_packed_byte: PackedByteArray
- hint=TYPE_STRING hint_string="int/LAYERS_2D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_nav_packed32: PackedInt32Array
- hint=TYPE_STRING hint_string="int/LAYERS_2D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_nav_packed64: PackedInt64Array
- hint=TYPE_STRING hint_string="int/LAYERS_2D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_phys: Array
- hint=TYPE_STRING hint_string="int/LAYERS_2D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_phys_packed_byte: PackedByteArray
- hint=TYPE_STRING hint_string="int/LAYERS_2D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_phys_packed32: PackedInt32Array
- hint=TYPE_STRING hint_string="int/LAYERS_2D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_phys_packed64: PackedInt64Array
- hint=TYPE_STRING hint_string="int/LAYERS_2D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_render: Array
- hint=TYPE_STRING hint_string="int/LAYERS_2D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_render_packed_byte: PackedByteArray
- hint=TYPE_STRING hint_string="int/LAYERS_2D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_render_packed32: PackedInt32Array
- hint=TYPE_STRING hint_string="int/LAYERS_2D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_2d_render_packed64: PackedInt64Array
- hint=TYPE_STRING hint_string="int/LAYERS_2D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_nav: Array
- hint=TYPE_STRING hint_string="int/LAYERS_3D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_nav_packed_byte: PackedByteArray
- hint=TYPE_STRING hint_string="int/LAYERS_3D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_nav_packed32: PackedInt32Array
- hint=TYPE_STRING hint_string="int/LAYERS_3D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_nav_packed64: PackedInt64Array
- hint=TYPE_STRING hint_string="int/LAYERS_3D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_phys: Array
- hint=TYPE_STRING hint_string="int/LAYERS_3D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_phys_packed_byte: PackedByteArray
- hint=TYPE_STRING hint_string="int/LAYERS_3D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_phys_packed32: PackedInt32Array
- hint=TYPE_STRING hint_string="int/LAYERS_3D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_phys_packed64: PackedInt64Array
- hint=TYPE_STRING hint_string="int/LAYERS_3D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_render: Array
- hint=TYPE_STRING hint_string="int/LAYERS_3D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_render_packed_byte: PackedByteArray
- hint=TYPE_STRING hint_string="int/LAYERS_3D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_render_packed32: PackedInt32Array
- hint=TYPE_STRING hint_string="int/LAYERS_3D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_bit_flag_3d_render_packed64: PackedInt64Array
- hint=TYPE_STRING hint_string="int/LAYERS_3D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_multiline: Array
- hint=TYPE_STRING hint_string="String/MULTILINE_TEXT:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<MULTILINE_TEXT>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_multiline_packed: PackedStringArray
- hint=TYPE_STRING hint_string="String/MULTILINE_TEXT:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<MULTILINE_TEXT>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_placeholder: Array
- hint=TYPE_STRING hint_string="String/PLACEHOLDER_TEXT:Placeholder" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<PLACEHOLDER_TEXT>:Placeholder" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_placeholder_packed: PackedStringArray
- hint=TYPE_STRING hint_string="String/PLACEHOLDER_TEXT:Placeholder" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<PLACEHOLDER_TEXT>:Placeholder" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_int: Array
- hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_int_packed_byte: PackedByteArray
- hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_int_packed32: PackedInt32Array
- hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_int_packed64: PackedInt64Array
- hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_int_float_step: Array
- hint=TYPE_STRING hint_string="int/RANGE:1,10,0.01" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10,0.01" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_float: Array
- hint=TYPE_STRING hint_string="float/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<float>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_float_packed32: PackedFloat32Array
- hint=TYPE_STRING hint_string="float/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<float>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_float_packed64: PackedFloat64Array
- hint=TYPE_STRING hint_string="float/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<float>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_exp_easing: Array
- hint=TYPE_STRING hint_string="float/EXP_EASING:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<float>/<EXP_EASING>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_exp_easing_packed32: PackedFloat32Array
- hint=TYPE_STRING hint_string="float/EXP_EASING:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<float>/<EXP_EASING>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_exp_easing_packed64: PackedFloat64Array
- hint=TYPE_STRING hint_string="float/EXP_EASING:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<float>/<EXP_EASING>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_node_path: Array
- hint=TYPE_STRING hint_string="NodePath/NODE_PATH_VALID_TYPES:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<NodePath>/<NODE_PATH_VALID_TYPES>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_color: Array
- hint=TYPE_STRING hint_string="Color/COLOR_NO_ALPHA:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<Color>/<COLOR_NO_ALPHA>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_color_packed: PackedColorArray
- hint=TYPE_STRING hint_string="Color/COLOR_NO_ALPHA:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<Color>/<COLOR_NO_ALPHA>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_byte_array: PackedByteArray
- hint=TYPE_STRING hint_string="int:int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_int32_array: PackedInt32Array
- hint=TYPE_STRING hint_string="int:int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_int64_array: PackedInt64Array
- hint=TYPE_STRING hint_string="int:int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_float32_array: PackedFloat32Array
- hint=TYPE_STRING hint_string="float:float" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<float>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_float64_array: PackedFloat64Array
- hint=TYPE_STRING hint_string="float:float" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<float>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_color_array: PackedColorArray
- hint=TYPE_STRING hint_string="Color:Color" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<Color>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_vector2_array: PackedVector2Array
- hint=TYPE_STRING hint_string="Vector2:Vector2" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<Vector2>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_vector3_array: PackedVector3Array
- hint=TYPE_STRING hint_string="Vector3:Vector3" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<Vector3>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_vector4_array: PackedVector4Array
- hint=TYPE_STRING hint_string="Vector4:Vector4" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<Vector4>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_weak_packed_byte_array: PackedByteArray
- hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_weak_packed_int32_array: PackedInt32Array
- hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_weak_packed_int64_array: PackedInt64Array
- hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_weak_packed_float32_array: PackedFloat32Array
- hint=TYPE_STRING hint_string="float/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<float>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_weak_packed_float64_array: PackedFloat64Array
- hint=TYPE_STRING hint_string="float/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<float>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_noalpha_weak_packed_color_array: PackedColorArray
- hint=TYPE_STRING hint_string="Color/COLOR_NO_ALPHA:" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<Color>/<COLOR_NO_ALPHA>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
diff --git a/modules/gdscript/tests/scripts/parser/features/export_enum.out b/modules/gdscript/tests/scripts/parser/features/export_enum.out
index c87f9b17f0..31d3fa8902 100644
--- a/modules/gdscript/tests/scripts/parser/features/export_enum.out
+++ b/modules/gdscript/tests/scripts/parser/features/export_enum.out
@@ -1,41 +1,41 @@
GDTEST_OK
var test_untyped: int = null
- hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_with_values: int = null
- hint=ENUM hint_string="Red:10,Green:20,Blue:30" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="Red:10,Green:20,Blue:30" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_variant: int = null
- hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_int: int = 0
- hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_string: String = ""
- hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_array_int: Array = Array[int]([])
- hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_array_string: Array = Array[String]([])
- hint=TYPE_STRING hint_string="String/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_byte_array: PackedByteArray = PackedByteArray()
- hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_int32_array: PackedInt32Array = PackedInt32Array()
- hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_int64_array: PackedInt64Array = PackedInt64Array()
- hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_weak_packed_string_array: PackedStringArray = PackedStringArray()
- hint=TYPE_STRING hint_string="String/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_hard_variant: int = null
- hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_hard_int: int = 0
- hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_hard_string: String = ""
- hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_hard_array_int: Array = Array[int]([])
- hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_hard_array_string: Array = Array[String]([])
- hint=TYPE_STRING hint_string="String/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_variant_array_int: Array = Array[int]([])
- hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_variant_packed_int32_array: PackedInt32Array = PackedInt32Array()
- hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_variant_array_string: Array = Array[String]([])
- hint=TYPE_STRING hint_string="String/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_variant_packed_string_array: PackedStringArray = PackedStringArray()
- hint=TYPE_STRING hint_string="String/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=TYPE_STRING hint_string="<String>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.gd b/modules/gdscript/tests/scripts/parser/features/export_variable.gd
index 2a218774de..8b343de5ef 100644
--- a/modules/gdscript/tests/scripts/parser/features/export_variable.gd
+++ b/modules/gdscript/tests/scripts/parser/features/export_variable.gd
@@ -2,19 +2,43 @@ extends Node
const Utils = preload("../../utils.notest.gd")
+# Built-in types.
@export var test_weak_int = 1
@export var test_hard_int: int = 2
-@export_storage var test_storage_untyped
-@export_storage var test_storage_weak_int = 3 # Property info still `Variant`, unlike `@export`.
-@export_storage var test_storage_hard_int: int = 4
@export_range(0, 100) var test_range = 100
@export_range(0, 100, 1) var test_range_step = 101
@export_range(0, 100, 1, "or_greater") var test_range_step_or_greater = 102
@export var test_color: Color
@export_color_no_alpha var test_color_no_alpha: Color
@export_node_path("Sprite2D", "Sprite3D", "Control", "Node") var test_node_path := ^"hello"
-@export var test_node: Node
-@export var test_node_array: Array[Node]
+
+# Enums.
+@export var test_side: Side
+@export var test_atm: AutoTranslateMode
+
+# Resources and nodes.
+@export var test_image: Image
+@export var test_timer: Timer
+
+# Arrays.
+@export var test_array: Array
+@export var test_array_bool: Array[bool]
+@export var test_array_array: Array[Array]
+@export var test_array_side: Array[Side]
+@export var test_array_atm: Array[AutoTranslateMode]
+@export var test_array_image: Array[Image]
+@export var test_array_timer: Array[Timer]
+
+# `@export_storage`.
+@export_storage var test_storage_untyped
+@export_storage var test_storage_weak_int = 3 # Property info still `Variant`, unlike `@export`.
+@export_storage var test_storage_hard_int: int = 4
+
+# `@export_custom`.
+# NOTE: `PROPERTY_USAGE_NIL_IS_VARIANT` flag will be removed.
+@export_custom(PROPERTY_HINT_ENUM, "A,B,C") var test_export_custom_untyped
+@export_custom(PROPERTY_HINT_ENUM, "A,B,C") var test_export_custom_weak_int = 5
+@export_custom(PROPERTY_HINT_ENUM, "A,B,C") var test_export_custom_hard_int: int = 6
func test():
for property in get_property_list():
diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.out b/modules/gdscript/tests/scripts/parser/features/export_variable.out
index b3f9d0ca9c..99d7b27130 100644
--- a/modules/gdscript/tests/scripts/parser/features/export_variable.out
+++ b/modules/gdscript/tests/scripts/parser/features/export_variable.out
@@ -1,27 +1,51 @@
GDTEST_OK
var test_weak_int: int = 1
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_hard_int: int = 2
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
-var test_storage_untyped: Variant = null
- hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE|NIL_IS_VARIANT
-var test_storage_weak_int: Variant = 3
- hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE|NIL_IS_VARIANT
-var test_storage_hard_int: int = 4
- hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range: int = 100
- hint=RANGE hint_string="0,100" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=RANGE hint_string="0,100" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_step: int = 101
- hint=RANGE hint_string="0,100,1" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=RANGE hint_string="0,100,1" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_range_step_or_greater: int = 102
- hint=RANGE hint_string="0,100,1,or_greater" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=RANGE hint_string="0,100,1,or_greater" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_color: Color = Color(0, 0, 0, 1)
- hint=NONE hint_string="Color" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_color_no_alpha: Color = Color(0, 0, 0, 1)
- hint=COLOR_NO_ALPHA hint_string="" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=COLOR_NO_ALPHA hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_node_path: NodePath = NodePath("hello")
- hint=NODE_PATH_VALID_TYPES hint_string="Sprite2D,Sprite3D,Control,Node" usage=DEFAULT|SCRIPT_VARIABLE
-var test_node: Node = null
- hint=NODE_TYPE hint_string="Node" usage=DEFAULT|SCRIPT_VARIABLE
-var test_node_array: Array = Array[Node]([])
- hint=TYPE_STRING hint_string="Object/NODE_TYPE:Node" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NODE_PATH_VALID_TYPES hint_string="Sprite2D,Sprite3D,Control,Node" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
+var test_side: Side = 0
+ hint=ENUM hint_string="Side Left:0,Side Top:1,Side Right:2,Side Bottom:3" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"Side"
+var test_atm: Node.AutoTranslateMode = 0
+ hint=ENUM hint_string="Auto Translate Mode Inherit:0,Auto Translate Mode Always:1,Auto Translate Mode Disabled:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"Node.AutoTranslateMode"
+var test_image: Image = null
+ hint=RESOURCE_TYPE hint_string="Image" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"Image"
+var test_timer: Timer = null
+ hint=NODE_TYPE hint_string="Timer" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"Timer"
+var test_array: Array = []
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
+var test_array_bool: Array = Array[bool]([])
+ hint=TYPE_STRING hint_string="<bool>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
+var test_array_array: Array = Array[Array]([])
+ hint=TYPE_STRING hint_string="<Array>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
+var test_array_side: Array = Array[int]([])
+ hint=TYPE_STRING hint_string="<int>/<ENUM>:Side Left:0,Side Top:1,Side Right:2,Side Bottom:3" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
+var test_array_atm: Array = Array[int]([])
+ hint=TYPE_STRING hint_string="<int>/<ENUM>:Auto Translate Mode Inherit:0,Auto Translate Mode Always:1,Auto Translate Mode Disabled:2" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
+var test_array_image: Array = Array[Image]([])
+ hint=TYPE_STRING hint_string="<Object>/<RESOURCE_TYPE>:Image" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
+var test_array_timer: Array = Array[Timer]([])
+ hint=TYPE_STRING hint_string="<Object>/<NODE_TYPE>:Timer" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
+var test_storage_untyped: Variant = null
+ hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE|NIL_IS_VARIANT class_name=&""
+var test_storage_weak_int: Variant = 3
+ hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE|NIL_IS_VARIANT class_name=&""
+var test_storage_hard_int: int = 4
+ hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE class_name=&""
+var test_export_custom_untyped: null = null
+ hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
+var test_export_custom_weak_int: int = 5
+ hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
+var test_export_custom_hard_int: int = 6
+ hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
diff --git a/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out
index 9387ec50d7..a1e7233078 100644
--- a/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out
+++ b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out
@@ -1,8 +1,8 @@
GDTEST_OK
Not shadowed: Resource
var test_1: int = 0
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
@export_category("test_1")
- hint=NONE hint_string="" usage=CATEGORY
+ hint=NONE hint_string="" usage=CATEGORY class_name=&""
var test_2: int = 0
- hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
+ hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
diff --git a/modules/gdscript/tests/scripts/utils.notest.gd b/modules/gdscript/tests/scripts/utils.notest.gd
index 1cf46c179e..7fdd6556ec 100644
--- a/modules/gdscript/tests/scripts/utils.notest.gd
+++ b/modules/gdscript/tests/scripts/utils.notest.gd
@@ -55,18 +55,18 @@ static func get_human_readable_hint_string(property: Dictionary) -> String:
if elem_type_hint.is_valid_int():
elem_type = elem_type_hint.to_int()
- type_hint_prefixes += type_string(elem_type) + ":"
+ type_hint_prefixes += "<%s>:" % type_string(elem_type)
else:
if elem_type_hint.count("/") != 1:
push_error("Invalid PROPERTY_HINT_TYPE_STRING format.")
elem_type = elem_type_hint.get_slice("/", 0).to_int()
elem_hint = elem_type_hint.get_slice("/", 1).to_int()
- type_hint_prefixes += "%s/%s:" % [
- type_string(elem_type),
- get_property_hint_name(elem_hint).trim_prefix("PROPERTY_HINT_"),
+ type_hint_prefixes += "<%s>/<%s>:" % [
+ type_string(elem_type),
+ get_property_hint_name(elem_hint).trim_prefix("PROPERTY_HINT_"),
]
- if elem_type < TYPE_ARRAY:
+ if elem_type < TYPE_ARRAY or hint_string.is_empty():
break
return type_hint_prefixes + hint_string
@@ -76,10 +76,11 @@ static func get_human_readable_hint_string(property: Dictionary) -> String:
static func print_property_extended_info(property: Dictionary, base: Object = null, is_static: bool = false) -> void:
print(get_property_signature(property, base, is_static))
- print(' hint=%s hint_string="%s" usage=%s' % [
+ print(' hint=%s hint_string="%s" usage=%s class_name=&"%s"' % [
get_property_hint_name(property.hint).trim_prefix("PROPERTY_HINT_"),
- get_human_readable_hint_string(property),
+ get_human_readable_hint_string(property).c_escape(),
get_property_usage_string(property.usage).replace("PROPERTY_USAGE_", ""),
+ property.class_name.c_escape(),
])
diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py
index f9709362eb..efbf298f99 100755
--- a/modules/mono/build_scripts/build_assemblies.py
+++ b/modules/mono/build_scripts/build_assemblies.py
@@ -5,7 +5,7 @@ import os.path
import shlex
import subprocess
from dataclasses import dataclass
-from typing import Optional, List
+from typing import List, Optional
def find_dotnet_cli():
@@ -304,9 +304,7 @@ def generate_sdk_package_versions():
<GodotVersionConstants>{1}</GodotVersionConstants>
</PropertyGroup>
</Project>
-""".format(
- version_str, ";".join(version_defines)
- )
+""".format(version_str, ";".join(version_defines))
# We write in ../SdkPackageVersions.props.
with open(os.path.join(dirname(script_path), "SdkPackageVersions.props"), "w", encoding="utf-8", newline="\n") as f:
@@ -323,9 +321,7 @@ def generate_sdk_package_versions():
public const string VersionDocsUrl = "https://docs.godotengine.org/en/{docs_branch}";
}}
}}
-""".format(
- **version_info
- )
+""".format(**version_info)
generators_dir = os.path.join(
dirname(script_path),
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index e5469c4980..98c50dcc22 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -1,7 +1,3 @@
-import os
-import os.path
-
-
def is_desktop(platform):
return platform in ["windows", "macos", "linuxbsd"]
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 3d087c9e27..5ebdb83b36 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -12,7 +12,7 @@ def configure(env):
# Check if the platform has marked mono as supported.
supported = env.get("supported", [])
- if not "mono" in supported:
+ if "mono" not in supported:
raise RuntimeError("This module does not currently support building for this platform")
env.add_module_version_string("mono")
diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml
index b559ca20b2..7e14259d5a 100644
--- a/modules/mono/doc_classes/CSharpScript.xml
+++ b/modules/mono/doc_classes/CSharpScript.xml
@@ -5,7 +5,6 @@
</brief_description>
<description>
This class represents a C# script. It is the C# equivalent of the [GDScript] class and is only available in Mono-enabled Godot builds.
- See also [GodotSharp].
</description>
<tutorials>
<link title="C# documentation index">$DOCS_URL/tutorials/scripting/c_sharp/index.html</link>
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
deleted file mode 100644
index 969ca14350..0000000000
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GodotSharp" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Bridge between Godot and the Mono runtime (Mono-enabled builds only).
- </brief_description>
- <description>
- This class is a bridge between Godot and the Mono runtime. It exposes several low-level operations and is only available in Mono-enabled Godot builds.
- See also [CSharpScript].
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="is_runtime_initialized">
- <return type="bool" />
- <description>
- Returns [code]true[/code] if the .NET runtime is initialized, [code]false[/code] otherwise.
- </description>
- </method>
- </methods>
-</class>
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 05dacd28fb..03d8b4eab6 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -143,7 +143,7 @@ bool godot_icall_Internal_IsAssembliesReloadingNeeded() {
void godot_icall_Internal_ReloadAssemblies(bool p_soft_reload) {
#ifdef GD_MONO_HOT_RELOAD
- mono_bind::GodotSharp::get_singleton()->call_deferred(SNAME("_reload_assemblies"), (bool)p_soft_reload);
+ callable_mp(mono_bind::GodotSharp::get_singleton(), &mono_bind::GodotSharp::reload_assemblies).call_deferred(p_soft_reload);
#endif
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
index ab7f8ede44..33f0850a8d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
@@ -318,9 +318,9 @@ namespace Godot
Vector3 ofs = _position + halfExtents;
return ofs + new Vector3(
- dir.X > 0f ? -halfExtents.X : halfExtents.X,
- dir.Y > 0f ? -halfExtents.Y : halfExtents.Y,
- dir.Z > 0f ? -halfExtents.Z : halfExtents.Z);
+ dir.X > 0f ? halfExtents.X : -halfExtents.X,
+ dir.Y > 0f ? halfExtents.Y : -halfExtents.Y,
+ dir.Z > 0f ? halfExtents.Z : -halfExtents.Z);
}
/// <summary>
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 0e34616951..48caae8523 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -564,11 +564,7 @@ namespace mono_bind {
GodotSharp *GodotSharp::singleton = nullptr;
-bool GodotSharp::_is_runtime_initialized() {
- return GDMono::get_singleton() != nullptr && GDMono::get_singleton()->is_runtime_initialized();
-}
-
-void GodotSharp::_reload_assemblies(bool p_soft_reload) {
+void GodotSharp::reload_assemblies(bool p_soft_reload) {
#ifdef GD_MONO_HOT_RELOAD
CRASH_COND(CSharpLanguage::get_singleton() == nullptr);
// This method may be called more than once with `call_deferred`, so we need to check
@@ -579,11 +575,6 @@ void GodotSharp::_reload_assemblies(bool p_soft_reload) {
#endif
}
-void GodotSharp::_bind_methods() {
- ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &GodotSharp::_is_runtime_initialized);
- ClassDB::bind_method(D_METHOD("_reload_assemblies"), &GodotSharp::_reload_assemblies);
-}
-
GodotSharp::GodotSharp() {
singleton = this;
}
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 0cb087db57..614bfc63fb 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -167,18 +167,14 @@ namespace mono_bind {
class GodotSharp : public Object {
GDCLASS(GodotSharp, Object);
- friend class GDMono;
-
- void _reload_assemblies(bool p_soft_reload);
- bool _is_runtime_initialized();
-
protected:
static GodotSharp *singleton;
- static void _bind_methods();
public:
static GodotSharp *get_singleton() { return singleton; }
+ void reload_assemblies(bool p_soft_reload);
+
GodotSharp();
~GodotSharp();
};
diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp
index beaa50ecb2..4d5426d96f 100644
--- a/modules/mono/register_types.cpp
+++ b/modules/mono/register_types.cpp
@@ -49,9 +49,6 @@ void initialize_mono_module(ModuleInitializationLevel p_level) {
_godotsharp = memnew(mono_bind::GodotSharp);
- GDREGISTER_CLASS(mono_bind::GodotSharp);
- Engine::get_singleton()->add_singleton(Engine::Singleton("GodotSharp", mono_bind::GodotSharp::get_singleton()));
-
script_language_cs = memnew(CSharpLanguage);
script_language_cs->set_language_index(ScriptServer::get_language_count());
ScriptServer::register_language(script_language_cs);
diff --git a/modules/navigation/3d/nav_mesh_generator_3d.cpp b/modules/navigation/3d/nav_mesh_generator_3d.cpp
index cc3bbdbf01..df0bdc9537 100644
--- a/modules/navigation/3d/nav_mesh_generator_3d.cpp
+++ b/modules/navigation/3d/nav_mesh_generator_3d.cpp
@@ -895,9 +895,22 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
Vector<Vector3> nav_vertices;
+ HashMap<Vector3, int> recast_vertex_to_native_index;
+ LocalVector<int> recast_index_to_native_index;
+ recast_index_to_native_index.resize(detail_mesh->nverts);
+
for (int i = 0; i < detail_mesh->nverts; i++) {
const float *v = &detail_mesh->verts[i * 3];
- nav_vertices.push_back(Vector3(v[0], v[1], v[2]));
+ const Vector3 vertex = Vector3(v[0], v[1], v[2]);
+ int *existing_index_ptr = recast_vertex_to_native_index.getptr(vertex);
+ if (!existing_index_ptr) {
+ int new_index = recast_vertex_to_native_index.size();
+ recast_index_to_native_index[i] = new_index;
+ recast_vertex_to_native_index[vertex] = new_index;
+ nav_vertices.push_back(vertex);
+ } else {
+ recast_index_to_native_index[i] = *existing_index_ptr;
+ }
}
p_navigation_mesh->set_vertices(nav_vertices);
p_navigation_mesh->clear_polygons();
@@ -912,9 +925,14 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
Vector<int> nav_indices;
nav_indices.resize(3);
// Polygon order in recast is opposite than godot's
- nav_indices.write[0] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 0]));
- nav_indices.write[1] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 2]));
- nav_indices.write[2] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 1]));
+ int index1 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 0]));
+ int index2 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 2]));
+ int index3 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 1]));
+
+ nav_indices.write[0] = recast_index_to_native_index[index1];
+ nav_indices.write[1] = recast_index_to_native_index[index2];
+ nav_indices.write[2] = recast_index_to_native_index[index3];
+
p_navigation_mesh->add_polygon(nav_indices);
}
}
diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
index 79aa547c52..338d632524 100644
--- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
@@ -84,6 +84,12 @@
Called right before the OpenXR instance is destroyed.
</description>
</method>
+ <method name="_on_main_swapchains_created" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called right after the main swapchains are (re)created.
+ </description>
+ </method>
<method name="_on_pre_render" qualifiers="virtual">
<return type="void" />
<description>
diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h
index ce03df0b30..8d05657afc 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper.h
@@ -84,6 +84,7 @@ public:
// This is when controller data is queried and made available to game logic.
virtual void on_process() {}
virtual void on_pre_render() {} // `on_pre_render` is called right before we start rendering our XR viewports.
+ virtual void on_main_swapchains_created() {} // `on_main_swapchains_created` is called right after our main swapchains are (re)created.
virtual void on_pre_draw_viewport(RID p_render_target) {} // `on_pre_draw_viewport` is called right before we start rendering this viewport
virtual void on_post_draw_viewport(RID p_render_target) {} // `on_port_draw_viewport` is called right after we start rendering this viewport (note that on Vulkan draw commands may only be queued)
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
index 3b31e1b1f6..e09ca484d5 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
@@ -50,6 +50,7 @@ void OpenXRExtensionWrapperExtension::_bind_methods() {
GDVIRTUAL_BIND(_on_session_created, "session");
GDVIRTUAL_BIND(_on_process);
GDVIRTUAL_BIND(_on_pre_render);
+ GDVIRTUAL_BIND(_on_main_swapchains_created);
GDVIRTUAL_BIND(_on_session_destroyed);
GDVIRTUAL_BIND(_on_state_idle);
GDVIRTUAL_BIND(_on_state_ready);
@@ -198,6 +199,10 @@ void OpenXRExtensionWrapperExtension::on_pre_render() {
GDVIRTUAL_CALL(_on_pre_render);
}
+void OpenXRExtensionWrapperExtension::on_main_swapchains_created() {
+ GDVIRTUAL_CALL(_on_main_swapchains_created);
+}
+
void OpenXRExtensionWrapperExtension::on_session_destroyed() {
GDVIRTUAL_CALL(_on_session_destroyed);
}
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
index 71d2a57ff8..e37853903b 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
@@ -86,6 +86,7 @@ public:
virtual void on_session_created(const XrSession p_session) override;
virtual void on_process() override;
virtual void on_pre_render() override;
+ virtual void on_main_swapchains_created() override;
virtual void on_session_destroyed() override;
GDVIRTUAL0(_on_register_metadata);
@@ -95,6 +96,7 @@ public:
GDVIRTUAL1(_on_session_created, uint64_t);
GDVIRTUAL0(_on_process);
GDVIRTUAL0(_on_pre_render);
+ GDVIRTUAL0(_on_main_swapchains_created);
GDVIRTUAL0(_on_session_destroyed);
virtual void on_state_idle() override;
diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp
index bbdd2e3c8a..8ce808dd3c 100644
--- a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp
+++ b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp
@@ -101,7 +101,7 @@ void *OpenXRFBFoveationExtension::set_swapchain_create_info_and_get_next_pointer
}
}
-void OpenXRFBFoveationExtension::on_state_ready() {
+void OpenXRFBFoveationExtension::on_main_swapchains_created() {
update_profile();
}
@@ -127,26 +127,41 @@ void OpenXRFBFoveationExtension::set_foveation_dynamic(XrFoveationDynamicFB p_fo
update_profile();
}
-void OpenXRFBFoveationExtension::update_profile() {
- if (!is_enabled()) {
+void OpenXRFBFoveationExtension::_update_profile() {
+ // Must be called from rendering thread!
+ ERR_NOT_ON_RENDER_THREAD;
+
+ OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
+ ERR_FAIL_NULL(fov_ext);
+
+ if (!fov_ext->is_enabled()) {
+ return;
+ }
+
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL(openxr_api);
+
+ XrSwapchain main_color_swapchain = openxr_api->get_color_swapchain();
+ if (main_color_swapchain == XR_NULL_HANDLE) {
+ // Our swapchain hasn't been created yet, we'll call this again once it has.
return;
}
XrFoveationLevelProfileCreateInfoFB level_profile_create_info;
level_profile_create_info.type = XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB;
level_profile_create_info.next = nullptr;
- level_profile_create_info.level = foveation_level;
+ level_profile_create_info.level = fov_ext->foveation_level;
level_profile_create_info.verticalOffset = 0.0f;
- level_profile_create_info.dynamic = foveation_dynamic;
+ level_profile_create_info.dynamic = fov_ext->foveation_dynamic;
XrFoveationProfileCreateInfoFB profile_create_info;
profile_create_info.type = XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB;
profile_create_info.next = &level_profile_create_info;
XrFoveationProfileFB foveation_profile;
- XrResult result = xrCreateFoveationProfileFB(OpenXRAPI::get_singleton()->get_session(), &profile_create_info, &foveation_profile);
+ XrResult result = fov_ext->xrCreateFoveationProfileFB(openxr_api->get_session(), &profile_create_info, &foveation_profile);
if (XR_FAILED(result)) {
- print_line("OpenXR: Unable to create the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ print_line("OpenXR: Unable to create the foveation profile [", openxr_api->get_error_string(result), "]");
return;
}
@@ -154,15 +169,15 @@ void OpenXRFBFoveationExtension::update_profile() {
foveation_update_state.type = XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB;
foveation_update_state.profile = foveation_profile;
- result = swapchain_update_state_ext->xrUpdateSwapchainFB(OpenXRAPI::get_singleton()->get_color_swapchain(), (XrSwapchainStateBaseHeaderFB *)&foveation_update_state);
+ result = fov_ext->swapchain_update_state_ext->xrUpdateSwapchainFB(main_color_swapchain, (XrSwapchainStateBaseHeaderFB *)&foveation_update_state);
if (XR_FAILED(result)) {
- print_line("OpenXR: Unable to update the swapchain [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ print_line("OpenXR: Unable to update the swapchain [", openxr_api->get_error_string(result), "]");
// We still want to destroy our profile so keep going...
}
- result = xrDestroyFoveationProfileFB(foveation_profile);
+ result = fov_ext->xrDestroyFoveationProfileFB(foveation_profile);
if (XR_FAILED(result)) {
- print_line("OpenXR: Unable to destroy the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ print_line("OpenXR: Unable to destroy the foveation profile [", openxr_api->get_error_string(result), "]");
}
}
diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.h b/modules/openxr/extensions/openxr_fb_foveation_extension.h
index 1c5e722731..84bd7011b5 100644
--- a/modules/openxr/extensions/openxr_fb_foveation_extension.h
+++ b/modules/openxr/extensions/openxr_fb_foveation_extension.h
@@ -60,7 +60,7 @@ public:
virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override;
- virtual void on_state_ready() override;
+ virtual void on_main_swapchains_created() override;
bool is_enabled() const;
@@ -82,7 +82,15 @@ private:
XrFoveationLevelFB foveation_level = XR_FOVEATION_LEVEL_NONE_FB;
XrFoveationDynamicFB foveation_dynamic = XR_FOVEATION_DYNAMIC_DISABLED_FB;
- void update_profile();
+ static void _update_profile();
+
+ void update_profile() {
+ // If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
+ RenderingServer *rendering_server = RenderingServer::get_singleton();
+ ERR_FAIL_NULL(rendering_server);
+
+ rendering_server->call_on_render_thread(callable_mp_static(&OpenXRFBFoveationExtension::_update_profile));
+ }
// Enable foveation on this swapchain
XrSwapchainCreateInfoFoveationFB swapchain_create_info_foveation_fb;
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 32512070d6..541e369925 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -1220,6 +1220,10 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
}
};
+ for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
+ wrapper->on_main_swapchains_created();
+ }
+
return true;
};
diff --git a/modules/openxr/scene/openxr_composition_layer.cpp b/modules/openxr/scene/openxr_composition_layer.cpp
index 50cc7b9e7b..b02f3082ab 100644
--- a/modules/openxr/scene/openxr_composition_layer.cpp
+++ b/modules/openxr/scene/openxr_composition_layer.cpp
@@ -165,7 +165,9 @@ void OpenXRCompositionLayer::set_layer_viewport(SubViewport *p_viewport) {
return;
}
- ERR_FAIL_COND_EDMSG(is_viewport_in_use(p_viewport), RTR("Cannot use the same SubViewport with multiple OpenXR composition layers. Clear it from its current layer first."));
+ if (p_viewport != nullptr) {
+ ERR_FAIL_COND_EDMSG(is_viewport_in_use(p_viewport), RTR("Cannot use the same SubViewport with multiple OpenXR composition layers. Clear it from its current layer first."));
+ }
layer_viewport = p_viewport;
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index 018984a52f..2f7a175d91 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -1,19 +1,27 @@
#!/usr/bin/env python
import atexit
import sys
-import methods
import time
+import methods
+
# Enable ANSI escape code support on Windows 10 and later (for colored console output).
# <https://github.com/python/cpython/issues/73245>
-if sys.platform == "win32":
- from ctypes import windll, c_int, byref
-
- stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
- mode = c_int(0)
- windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
- mode = c_int(mode.value | 4)
- windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
+if sys.stdout.isatty() and sys.platform == "win32":
+ try:
+ from ctypes import WinError, byref, windll # type: ignore
+ from ctypes.wintypes import DWORD # type: ignore
+
+ stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11))
+ mode = DWORD(0)
+ if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)):
+ raise WinError()
+ mode = DWORD(mode.value | 4)
+ if not windll.kernel32.SetConsoleMode(stdout_handle, mode):
+ raise WinError()
+ except Exception as e:
+ methods._colorize = False
+ methods.print_error(f"Failed to enable ANSI escape code support, disabling color output.\n{e}")
# For the reference:
# - CCFLAGS are compilation flags shared between C and C++
@@ -713,7 +721,7 @@ if env["static_icu_data"]:
env.Append(CXXFLAGS=["-DICU_STATIC_DATA"])
env.Append(CPPPATH=["../../../thirdparty/icu4c/"])
else:
- thirdparty_sources += ["../icu_data/icudata_stub.cpp"]
+ thirdparty_icu_sources += ["../icu_data/icudata_stub.cpp"]
env_icu.Append(CPPPATH=["../../../thirdparty/icu4c/common/", "../../../thirdparty/icu4c/i18n/"])
env_icu.Append(
diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py
index 3453c3e8f0..43bf56abfe 100644
--- a/modules/text_server_adv/gdextension_build/methods.py
+++ b/modules/text_server_adv/gdextension_build/methods.py
@@ -81,9 +81,9 @@ def disable_warnings(self):
self.Append(CCFLAGS=["/w"])
self.Append(CFLAGS=["/w"])
self.Append(CXXFLAGS=["/w"])
- self["CCFLAGS"] = [x for x in self["CCFLAGS"] if not x in warn_flags]
- self["CFLAGS"] = [x for x in self["CFLAGS"] if not x in warn_flags]
- self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if not x in warn_flags]
+ self["CCFLAGS"] = [x for x in self["CCFLAGS"] if x not in warn_flags]
+ self["CFLAGS"] = [x for x in self["CFLAGS"] if x not in warn_flags]
+ self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if x not in warn_flags]
else:
self.Append(CCFLAGS=["-w"])
self.Append(CFLAGS=["-w"])
@@ -117,31 +117,31 @@ def make_icu_data(target, source, env):
def write_macos_plist(target, binary_name, identifier, name):
os.makedirs(f"{target}/Resource/", exist_ok=True)
with open(f"{target}/Resource/Info.plist", "w", encoding="utf-8", newline="\n") as f:
- f.write(f'<?xml version="1.0" encoding="UTF-8"?>\n')
- f.write(
- f'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n'
- )
- f.write(f'<plist version="1.0">\n')
- f.write(f"<dict>\n")
- f.write(f"\t<key>CFBundleExecutable</key>\n")
- f.write(f"\t<string>{binary_name}</string>\n")
- f.write(f"\t<key>CFBundleIdentifier</key>\n")
- f.write(f"\t<string>{identifier}</string>\n")
- f.write(f"\t<key>CFBundleInfoDictionaryVersion</key>\n")
- f.write(f"\t<string>6.0</string>\n")
- f.write(f"\t<key>CFBundleName</key>\n")
- f.write(f"\t<string>{name}</string>\n")
- f.write(f"\t<key>CFBundlePackageType</key>\n")
- f.write(f"\t<string>FMWK</string>\n")
- f.write(f"\t<key>CFBundleShortVersionString</key>\n")
- f.write(f"\t<string>1.0.0</string>\n")
- f.write(f"\t<key>CFBundleSupportedPlatforms</key>\n")
- f.write(f"\t<array>\n")
- f.write(f"\t\t<string>MacOSX</string>\n")
- f.write(f"\t</array>\n")
- f.write(f"\t<key>CFBundleVersion</key>\n")
- f.write(f"\t<string>1.0.0</string>\n")
- f.write(f"\t<key>LSMinimumSystemVersion</key>\n")
- f.write(f"\t<string>10.14</string>\n")
- f.write(f"</dict>\n")
- f.write(f"</plist>\n")
+ f.write(f"""\
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleExecutable</key>
+ <string>{binary_name}</string>
+ <key>CFBundleIdentifier</key>
+ <string>{identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>{name}</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0.0</string>
+ <key>CFBundleSupportedPlatforms</key>
+ <array>
+ <string>MacOSX</string>
+ </array>
+ <key>CFBundleVersion</key>
+ <string>1.0.0</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.14</string>
+</dict>
+</plist>
+""")
diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index 07940719eb..f6ae7149be 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -1,19 +1,27 @@
#!/usr/bin/env python
import atexit
import sys
-import methods
import time
+import methods
+
# Enable ANSI escape code support on Windows 10 and later (for colored console output).
# <https://github.com/python/cpython/issues/73245>
-if sys.platform == "win32":
- from ctypes import windll, c_int, byref
-
- stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
- mode = c_int(0)
- windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
- mode = c_int(mode.value | 4)
- windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
+if sys.stdout.isatty() and sys.platform == "win32":
+ try:
+ from ctypes import WinError, byref, windll # type: ignore
+ from ctypes.wintypes import DWORD # type: ignore
+
+ stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11))
+ mode = DWORD(0)
+ if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)):
+ raise WinError()
+ mode = DWORD(mode.value | 4)
+ if not windll.kernel32.SetConsoleMode(stdout_handle, mode):
+ raise WinError()
+ except Exception as e:
+ methods._colorize = False
+ methods.print_error(f"Failed to enable ANSI escape code support, disabling color output.\n{e}")
# For the reference:
# - CCFLAGS are compilation flags shared between C and C++
diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py
index 3453c3e8f0..43bf56abfe 100644
--- a/modules/text_server_fb/gdextension_build/methods.py
+++ b/modules/text_server_fb/gdextension_build/methods.py
@@ -81,9 +81,9 @@ def disable_warnings(self):
self.Append(CCFLAGS=["/w"])
self.Append(CFLAGS=["/w"])
self.Append(CXXFLAGS=["/w"])
- self["CCFLAGS"] = [x for x in self["CCFLAGS"] if not x in warn_flags]
- self["CFLAGS"] = [x for x in self["CFLAGS"] if not x in warn_flags]
- self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if not x in warn_flags]
+ self["CCFLAGS"] = [x for x in self["CCFLAGS"] if x not in warn_flags]
+ self["CFLAGS"] = [x for x in self["CFLAGS"] if x not in warn_flags]
+ self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if x not in warn_flags]
else:
self.Append(CCFLAGS=["-w"])
self.Append(CFLAGS=["-w"])
@@ -117,31 +117,31 @@ def make_icu_data(target, source, env):
def write_macos_plist(target, binary_name, identifier, name):
os.makedirs(f"{target}/Resource/", exist_ok=True)
with open(f"{target}/Resource/Info.plist", "w", encoding="utf-8", newline="\n") as f:
- f.write(f'<?xml version="1.0" encoding="UTF-8"?>\n')
- f.write(
- f'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n'
- )
- f.write(f'<plist version="1.0">\n')
- f.write(f"<dict>\n")
- f.write(f"\t<key>CFBundleExecutable</key>\n")
- f.write(f"\t<string>{binary_name}</string>\n")
- f.write(f"\t<key>CFBundleIdentifier</key>\n")
- f.write(f"\t<string>{identifier}</string>\n")
- f.write(f"\t<key>CFBundleInfoDictionaryVersion</key>\n")
- f.write(f"\t<string>6.0</string>\n")
- f.write(f"\t<key>CFBundleName</key>\n")
- f.write(f"\t<string>{name}</string>\n")
- f.write(f"\t<key>CFBundlePackageType</key>\n")
- f.write(f"\t<string>FMWK</string>\n")
- f.write(f"\t<key>CFBundleShortVersionString</key>\n")
- f.write(f"\t<string>1.0.0</string>\n")
- f.write(f"\t<key>CFBundleSupportedPlatforms</key>\n")
- f.write(f"\t<array>\n")
- f.write(f"\t\t<string>MacOSX</string>\n")
- f.write(f"\t</array>\n")
- f.write(f"\t<key>CFBundleVersion</key>\n")
- f.write(f"\t<string>1.0.0</string>\n")
- f.write(f"\t<key>LSMinimumSystemVersion</key>\n")
- f.write(f"\t<string>10.14</string>\n")
- f.write(f"</dict>\n")
- f.write(f"</plist>\n")
+ f.write(f"""\
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleExecutable</key>
+ <string>{binary_name}</string>
+ <key>CFBundleIdentifier</key>
+ <string>{identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>{name}</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0.0</string>
+ <key>CFBundleSupportedPlatforms</key>
+ <array>
+ <string>MacOSX</string>
+ </array>
+ <key>CFBundleVersion</key>
+ <string>1.0.0</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.14</string>
+</dict>
+</plist>
+""")
diff --git a/platform/SCsub b/platform/SCsub
index b24c189848..b07023efed 100644
--- a/platform/SCsub
+++ b/platform/SCsub
@@ -1,9 +1,10 @@
#!/usr/bin/env python
-import methods
from glob import glob
from pathlib import Path
+import methods
+
Import("env")
env.platform_sources = []
diff --git a/platform/android/SCsub b/platform/android/SCsub
index 4d76ffb180..bc1b5e9200 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -1,7 +1,8 @@
#!/usr/bin/env python
-import sys
import subprocess
+import sys
+
from methods import print_warning
Import("env")
diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h
index 5b30c392e7..01da0c7935 100644
--- a/platform/android/api/jni_singleton.h
+++ b/platform/android/api/jni_singleton.h
@@ -244,6 +244,7 @@ public:
~JNISingleton() {
#ifdef ANDROID_ENABLED
+ method_map.clear();
if (instance) {
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL(env);
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 6a8c4ed86d..485f31dee2 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -1,10 +1,11 @@
import os
-import sys
import platform
import subprocess
-from methods import print_warning, print_error
+import sys
from typing import TYPE_CHECKING
+from methods import print_error, print_warning
+
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index d27e75b07a..c404af34d8 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -330,8 +330,7 @@ ext.getReleaseKeyAlias = { ->
}
ext.isAndroidStudio = { ->
- def sysProps = System.getProperties()
- return sysProps != null && sysProps['idea.platform.prefix'] != null
+ return project.hasProperty('android.injected.invoked.from.ide')
}
ext.shouldZipAlign = { ->
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index c609b33ef4..b91b023ce6 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -232,11 +232,6 @@ def generateBuildTasks(String flavor = "template") {
return tasks
}
-def isAndroidStudio() {
- def sysProps = System.getProperties()
- return sysProps != null && sysProps['idea.platform.prefix'] != null
-}
-
task copyEditorReleaseApkToBin(type: Copy) {
dependsOn ':editor:assembleRelease'
from('editor/build/outputs/apk/release')
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
index 89fbb9f580..cfbbcf7d0e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
@@ -61,6 +61,9 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
private var contextClickInProgress = false
private var pointerCaptureInProgress = false
+ private var lastDragX: Float = 0.0f
+ private var lastDragY: Float = 0.0f
+
override fun onDown(event: MotionEvent): Boolean {
GodotInputHandler.handleMotionEvent(event.source, MotionEvent.ACTION_DOWN, event.buttonState, event.x, event.y, nextDownIsDoubleTap)
nextDownIsDoubleTap = false
@@ -165,6 +168,8 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
pointerCaptureInProgress = false
dragInProgress = false
contextClickInProgress = false
+ lastDragX = 0.0f
+ lastDragY = 0.0f
return true
}
@@ -189,6 +194,17 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
sourceMouseRelative
)
return true
+ } else if (!scaleInProgress) {
+ // The 'onScroll' event is triggered with a long delay.
+ // Force the 'InputEventScreenDrag' event earlier here.
+ // We don't toggle 'dragInProgress' here so that the scaling logic can override the drag operation if needed.
+ // Once the 'onScroll' event kicks-in, 'dragInProgress' will be properly set.
+ if (lastDragX != event.getX(0) || lastDragY != event.getY(0)) {
+ lastDragX = event.getX(0)
+ lastDragY = event.getY(0)
+ GodotInputHandler.handleMotionEvent(event)
+ return true
+ }
}
return false
}
@@ -216,7 +232,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
distanceY: Float
): Boolean {
if (scaleInProgress) {
- if (dragInProgress) {
+ if (dragInProgress || lastDragX != 0.0f || lastDragY != 0.0f) {
if (originEvent != null) {
// Cancel the drag
GodotInputHandler.handleMotionEvent(
@@ -228,6 +244,8 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
)
}
dragInProgress = false
+ lastDragX = 0.0f
+ lastDragY = 0.0f
}
}
@@ -235,8 +253,10 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
val y = terminusEvent.y
if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress && !dragInProgress) {
GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f)
- } else if (!scaleInProgress){
+ } else if (!scaleInProgress) {
dragInProgress = true
+ lastDragX = terminusEvent.getX(0)
+ lastDragY = terminusEvent.getY(0)
GodotInputHandler.handleMotionEvent(terminusEvent)
}
return true
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
index c0912ca4dc..c975c29e96 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
@@ -112,19 +112,18 @@ public abstract class GodotPlugin {
/**
* Register the plugin with Godot native code.
* <p>
- * This method is invoked by the Godot Engine on the render thread.
+ * This method is invoked on the render thread to register the plugin on engine startup.
*/
public final void onRegisterPluginWithGodotNative() {
- registeredSignals.putAll(
- registerPluginWithGodotNative(this, getPluginName(), getPluginMethods(), getPluginSignals()));
- }
+ final String pluginName = getPluginName();
+ if (!nativeRegisterSingleton(pluginName, this)) {
+ return;
+ }
- private static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject,
- String pluginName, List<String> pluginMethods, Set<SignalInfo> pluginSignals) {
- nativeRegisterSingleton(pluginName, pluginObject);
+ List<String> pluginMethods = getPluginMethods();
Set<Method> filteredMethods = new HashSet<>();
- Class<?> clazz = pluginObject.getClass();
+ Class<?> clazz = getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
@@ -156,15 +155,14 @@ public abstract class GodotPlugin {
nativeRegisterMethod(pluginName, method.getName(), method.getReturnType().getName(), pt);
}
+ Set<SignalInfo> pluginSignals = getPluginSignals();
+
// Register the signals for this plugin.
- Map<String, SignalInfo> registeredSignals = new HashMap<>();
for (SignalInfo signalInfo : pluginSignals) {
String signalName = signalInfo.getName();
nativeRegisterSignal(pluginName, signalName, signalInfo.getParamTypesNames());
registeredSignals.put(signalName, signalInfo);
}
-
- return registeredSignals;
}
/**
@@ -408,7 +406,7 @@ public abstract class GodotPlugin {
* Used to setup a {@link GodotPlugin} instance.
* @param p_name Name of the instance.
*/
- private static native void nativeRegisterSingleton(String p_name, Object object);
+ private static native boolean nativeRegisterSingleton(String p_name, Object object);
/**
* Used to complete registration of the {@link GodotPlugin} instance's methods.
diff --git a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
index e1534c7685..96b6dfc9f3 100644
--- a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
+++ b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
@@ -7,6 +7,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(GODOT_ROOT_DIR ../../../..)
+set(ANDROID_ROOT_DIR "${GODOT_ROOT_DIR}/platform/android" CACHE STRING "")
# Get sources
file(GLOB_RECURSE SOURCES ${GODOT_ROOT_DIR}/*.c**)
@@ -15,6 +16,7 @@ file(GLOB_RECURSE HEADERS ${GODOT_ROOT_DIR}/*.h**)
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME}
SYSTEM PUBLIC
- ${GODOT_ROOT_DIR})
+ ${GODOT_ROOT_DIR}
+ ${ANDROID_ROOT_DIR})
add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DANDROID_ENABLED -DGLES3_ENABLED -DTOOLS_ENABLED)
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 6cab7e74fd..93743c4e35 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -42,6 +42,7 @@
#include "jni_utils.h"
#include "net_socket_android.h"
#include "os_android.h"
+#include "plugin/godot_plugin_jni.h"
#include "string_android.h"
#include "thread_jandroid.h"
#include "tts_android.h"
@@ -78,6 +79,9 @@ static void _terminate(JNIEnv *env, bool p_restart = false) {
step.set(-1); // Ensure no further steps are attempted and no further events are sent
// lets cleanup
+ // Unregister android plugins
+ unregister_plugins_singletons();
+
if (java_class_wrapper) {
memdelete(java_class_wrapper);
}
diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp
index fd60ba4ae7..e3cde145cb 100644
--- a/platform/android/plugin/godot_plugin_jni.cpp
+++ b/platform/android/plugin/godot_plugin_jni.cpp
@@ -40,16 +40,32 @@
static HashMap<String, JNISingleton *> jni_singletons;
+void unregister_plugins_singletons() {
+ for (const KeyValue<String, JNISingleton *> &E : jni_singletons) {
+ Engine::get_singleton()->remove_singleton(E.key);
+ ProjectSettings::get_singleton()->set(E.key, Variant());
+
+ if (E.value) {
+ memdelete(E.value);
+ }
+ }
+ jni_singletons.clear();
+}
+
extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj) {
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj) {
String singname = jstring_to_string(name, env);
+
+ ERR_FAIL_COND_V(jni_singletons.has(singname), false);
+
JNISingleton *s = (JNISingleton *)ClassDB::instantiate("JNISingleton");
s->set_instance(env->NewGlobalRef(obj));
jni_singletons[singname] = s;
Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s));
ProjectSettings::get_singleton()->set(singname, s);
+ return true;
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args) {
diff --git a/platform/android/plugin/godot_plugin_jni.h b/platform/android/plugin/godot_plugin_jni.h
index baa29a79ea..8c62fb0f68 100644
--- a/platform/android/plugin/godot_plugin_jni.h
+++ b/platform/android/plugin/godot_plugin_jni.h
@@ -34,8 +34,10 @@
#include <android/log.h>
#include <jni.h>
+void unregister_plugins_singletons();
+
extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj);
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args);
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types);
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params);
diff --git a/platform/ios/SCsub b/platform/ios/SCsub
index f38914434b..cff7dcc1fd 100644
--- a/platform/ios/SCsub
+++ b/platform/ios/SCsub
@@ -2,11 +2,11 @@
Import("env")
-import os, json
-from platform_methods import architectures, lipo, get_build_version, detect_mvk
-import subprocess
+import os
import shutil
+from platform_methods import detect_mvk, lipo
+
def generate_bundle(target, source, env):
bin_dir = Dir("#bin").abspath
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index e3bac4ec5c..eb233a5d54 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -1,9 +1,9 @@
import os
import sys
-from methods import print_error, detect_darwin_sdk_path
-
from typing import TYPE_CHECKING
+from methods import detect_darwin_sdk_path, print_error
+
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 47f3bcadc9..28825038ca 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -1,11 +1,11 @@
import os
import platform
import sys
-from methods import print_warning, print_error, get_compiler_version, using_gcc
-from platform_methods import detect_arch
-
from typing import TYPE_CHECKING
+from methods import get_compiler_version, print_error, print_warning, using_gcc
+from platform_methods import detect_arch
+
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
diff --git a/platform/macos/SCsub b/platform/macos/SCsub
index 59ef4ee85c..eb8524826f 100644
--- a/platform/macos/SCsub
+++ b/platform/macos/SCsub
@@ -2,11 +2,13 @@
Import("env")
-import os, json
-from platform_methods import architectures, lipo, get_build_version
-import platform_macos_builders
-import subprocess
+import os
import shutil
+import subprocess
+
+import platform_macos_builders
+
+from platform_methods import get_build_version, lipo
def generate_bundle(target, source, env):
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index a5ef29e34f..f02f693dd9 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -1,10 +1,10 @@
import os
import sys
-from methods import print_error, detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang
-from platform_methods import detect_arch, detect_mvk
-
from typing import TYPE_CHECKING
+from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang, print_error
+from platform_methods import detect_arch, detect_mvk
+
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
@@ -107,7 +107,7 @@ def configure(env: "SConsEnvironment"):
env.Append(CCFLAGS=["-fobjc-arc"])
- if not "osxcross" in env: # regular native build
+ if "osxcross" not in env: # regular native build
if env["macports_clang"] != "no":
mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local")
mpclangver = env["macports_clang"]
diff --git a/platform/web/SCsub b/platform/web/SCsub
index bc5893ab3a..fea2fa7df9 100644
--- a/platform/web/SCsub
+++ b/platform/web/SCsub
@@ -6,9 +6,10 @@ Import("env")
# The HTTP server "targets". Run with "scons p=web serve", or "scons p=web run"
if "serve" in COMMAND_LINE_TARGETS or "run" in COMMAND_LINE_TARGETS:
- from serve import serve
import os
+ from serve import serve
+
port = os.environ.get("GODOT_WEB_TEST_PORT", 8060)
try:
port = int(port)
diff --git a/platform/web/detect.py b/platform/web/detect.py
index ccd884b225..524ff44f4d 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -1,18 +1,19 @@
import os
import sys
+from typing import TYPE_CHECKING
from emscripten_helpers import (
- run_closure_compiler,
- create_engine_file,
+ add_js_externs,
add_js_libraries,
add_js_pre,
- add_js_externs,
+ create_engine_file,
create_template_zip,
get_template_zip_path,
+ run_closure_compiler,
)
-from methods import print_warning, print_error, get_compiler_version
from SCons.Util import WhereIs
-from typing import TYPE_CHECKING
+
+from methods import get_compiler_version, print_error, print_warning
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py
index 3ba133c9a1..745b2457fa 100644
--- a/platform/web/emscripten_helpers.py
+++ b/platform/web/emscripten_helpers.py
@@ -1,4 +1,5 @@
-import os, json
+import json
+import os
from SCons.Util import WhereIs
@@ -24,13 +25,13 @@ def get_build_version():
import version
name = "custom_build"
- if os.getenv("BUILD_NAME") != None:
+ if os.getenv("BUILD_NAME") is not None:
name = os.getenv("BUILD_NAME")
v = "%d.%d" % (version.major, version.minor)
if version.patch > 0:
v += ".%d" % version.patch
status = version.status
- if os.getenv("GODOT_VERSION_STATUS") != None:
+ if os.getenv("GODOT_VERSION_STATUS") is not None:
status = str(os.getenv("GODOT_VERSION_STATUS"))
v += ".%s.%s" % (status, name)
return v
diff --git a/platform/web/serve.py b/platform/web/serve.py
index 89dff63ca3..f0b0ec9622 100755
--- a/platform/web/serve.py
+++ b/platform/web/serve.py
@@ -1,13 +1,13 @@
#!/usr/bin/env python3
-from http.server import HTTPServer, SimpleHTTPRequestHandler, test # type: ignore
-from pathlib import Path
-import os
-import sys
import argparse
import contextlib
+import os
import socket
import subprocess
+import sys
+from http.server import HTTPServer, SimpleHTTPRequestHandler, test # type: ignore
+from pathlib import Path
# See cpython GH-17851 and GH-17864.
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 435c501956..1c2bfb9b75 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -4,6 +4,7 @@ Import("env")
import os
from pathlib import Path
+
import platform_windows_builders
sources = []
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 93eb34001e..b66cdadc41 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -1,12 +1,12 @@
-import methods
import os
import subprocess
import sys
-from methods import print_warning, print_error
-from platform_methods import detect_arch
-
from typing import TYPE_CHECKING
+import methods
+from methods import print_error, print_warning
+from platform_methods import detect_arch
+
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
@@ -178,7 +178,7 @@ def get_opts():
caller_frame = inspect.stack()[1]
caller_script_dir = os.path.dirname(os.path.abspath(caller_frame[1]))
d3d12_deps_folder = os.path.join(caller_script_dir, "bin", "build_deps")
- except: # Give up.
+ except Exception: # Give up.
d3d12_deps_folder = ""
return [
@@ -523,7 +523,7 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
env.Append(CXXFLAGS=["/bigobj"])
# PIX
- if not env["arch"] in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
+ if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
env["use_pix"] = False
if env["use_pix"]:
@@ -750,7 +750,7 @@ def configure_mingw(env: "SConsEnvironment"):
env.Append(LIBS=["dxgi", "dxguid"])
# PIX
- if not env["arch"] in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
+ if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
env["use_pix"] = False
if env["use_pix"]:
diff --git a/platform/windows/platform_windows_builders.py b/platform/windows/platform_windows_builders.py
index 729d55cea6..3fd9e1a581 100644
--- a/platform/windows/platform_windows_builders.py
+++ b/platform/windows/platform_windows_builders.py
@@ -1,8 +1,8 @@
"""Functions used to generate source files during build time"""
import os
-from detect import get_mingw_bin_prefix
-from detect import try_cmd
+
+from detect import get_mingw_bin_prefix, try_cmd
def make_debug_mingw(target, source, env):
diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp
index 6fc33afe99..6c54faa13a 100644
--- a/platform/windows/windows_terminal_logger.cpp
+++ b/platform/windows/windows_terminal_logger.cpp
@@ -42,20 +42,30 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er
return;
}
- const unsigned int BUFFER_SIZE = 16384;
- char buf[BUFFER_SIZE + 1]; // +1 for the terminating character
- int len = vsnprintf(buf, BUFFER_SIZE, p_format, p_list);
- if (len <= 0) {
- return;
+ const int static_buffer_size = 1024;
+ char static_buf[static_buffer_size];
+ char *buf = static_buf;
+ va_list list_copy;
+ va_copy(list_copy, p_list);
+ int len = vsnprintf(buf, static_buffer_size, p_format, p_list);
+ if (len >= static_buffer_size) {
+ buf = (char *)memalloc(len + 1);
+ len = vsnprintf(buf, len + 1, p_format, list_copy);
+ }
+ va_end(list_copy);
+
+ String str_buf = String::utf8(buf, len).replace("\r\n", "\n").replace("\n", "\r\n");
+ if (len >= static_buffer_size) {
+ memfree(buf);
}
- if ((unsigned int)len >= BUFFER_SIZE) {
- len = BUFFER_SIZE; // Output is too big, will be truncated
+ CharString cstr_buf = str_buf.utf8();
+ if (cstr_buf.length() == 0) {
+ return;
}
- buf[len] = 0;
DWORD written = 0;
HANDLE h = p_err ? GetStdHandle(STD_ERROR_HANDLE) : GetStdHandle(STD_OUTPUT_HANDLE);
- WriteFile(h, &buf[0], len, &written, nullptr);
+ WriteFile(h, cstr_buf.ptr(), cstr_buf.length(), &written, nullptr);
#ifdef DEBUG_ENABLED
FlushFileBuffers(h);
diff --git a/platform_methods.py b/platform_methods.py
index 5326e36077..2b157da22b 100644
--- a/platform_methods.py
+++ b/platform_methods.py
@@ -1,10 +1,7 @@
import os
-import sys
-import json
import platform
-import uuid
-import functools
import subprocess
+
import methods
# NOTE: The multiprocessing module is not compatible with SCons due to conflict on cPickle
@@ -47,14 +44,14 @@ def get_build_version(short):
import version
name = "custom_build"
- if os.getenv("BUILD_NAME") != None:
+ if os.getenv("BUILD_NAME") is not None:
name = os.getenv("BUILD_NAME")
v = "%d.%d" % (version.major, version.minor)
if version.patch > 0:
v += ".%d" % version.patch
status = version.status
if not short:
- if os.getenv("GODOT_VERSION_STATUS") != None:
+ if os.getenv("GODOT_VERSION_STATUS") is not None:
status = str(os.getenv("GODOT_VERSION_STATUS"))
v += ".%s.%s" % (status, name)
return v
@@ -86,7 +83,7 @@ def get_mvk_sdk_path(osname):
def int_or_zero(i):
try:
return int(i)
- except:
+ except (TypeError, ValueError):
return 0
def ver_parse(a):
@@ -140,9 +137,8 @@ def detect_mvk(env, osname):
)
for mvk_path in mvk_list:
- if mvk_path and os.path.isfile(os.path.join(mvk_path, osname + "/libMoltenVK.a")):
- mvk_found = True
- print("MoltenVK found at: " + mvk_path)
+ if mvk_path and os.path.isfile(os.path.join(mvk_path, f"{osname}/libMoltenVK.a")):
+ print(f"MoltenVK found at: {mvk_path}")
return mvk_path
return ""
diff --git a/pyproject.toml b/pyproject.toml
index f1ea10fbae..34ae075f2b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,6 +11,19 @@ namespace_packages = true
explicit_package_bases = true
exclude = ["thirdparty/"]
-[tool.black]
+[tool.ruff]
+extend-exclude = ["thirdparty"]
+extend-include = ["SConstruct", "SCsub"]
line-length = 120
-extend-exclude = ".*thirdparty/.*"
+target-version = "py37"
+
+[tool.ruff.lint]
+extend-select = [
+ "I", # isort
+]
+
+[tool.ruff.lint.per-file-ignores]
+"{SConstruct,SCsub}" = [
+ "E402", # Module level import not at top of file
+ "F821", # Undefined name
+]
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 523d81e874..f7d672620d 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -225,91 +225,6 @@ int TileMap::get_rendering_quadrant_size() const {
return rendering_quadrant_size;
}
-void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_normalized_animation_offset) {
- ERR_FAIL_COND(!p_tile_set.is_valid());
- ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id));
- ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords));
- ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile));
- TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id);
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source) {
- // Check for the frame.
- if (p_frame >= 0) {
- ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords));
- }
-
- // Get the texture.
- Ref<Texture2D> tex = atlas_source->get_runtime_texture();
- if (!tex.is_valid()) {
- return;
- }
-
- // Check if we are in the texture, return otherwise.
- Vector2i grid_size = atlas_source->get_atlas_grid_size();
- if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) {
- return;
- }
-
- // Get tile data.
- const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile);
-
- // Get the tile modulation.
- Color modulate = tile_data->get_modulate() * p_modulation;
-
- // Compute the offset.
- Vector2 tile_offset = tile_data->get_texture_origin();
-
- // Get destination rect.
- Rect2 dest_rect;
- dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size;
- dest_rect.size.x += FP_ADJUST;
- dest_rect.size.y += FP_ADJUST;
-
- bool transpose = tile_data->get_transpose() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
- if (transpose) {
- dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
- } else {
- dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset);
- }
-
- if (tile_data->get_flip_h() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) {
- dest_rect.size.x = -dest_rect.size.x;
- }
-
- if (tile_data->get_flip_v() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) {
- dest_rect.size.y = -dest_rect.size.y;
- }
-
- // Draw the tile.
- if (p_frame >= 0) {
- Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame);
- tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
- } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) {
- Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0);
- tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
- } else {
- real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords);
- real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed;
- real_t animation_offset = p_normalized_animation_offset * animation_duration;
- // Accumulate durations unaffected by the speed to avoid accumulating floating point division errors.
- // Aka do `sum(duration[i]) / speed` instead of `sum(duration[i] / speed)`.
- real_t time_unscaled = 0.0;
- for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) {
- real_t frame_duration_unscaled = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame);
- real_t slice_start = time_unscaled / speed;
- real_t slice_end = (time_unscaled + frame_duration_unscaled) / speed;
- RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, slice_start, slice_end, animation_offset);
-
- Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame);
- tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
-
- time_unscaled += frame_duration_unscaled;
- }
- RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0);
- }
- }
-}
-
void TileMap::set_tileset(const Ref<TileSet> &p_tileset) {
if (p_tileset == tile_set) {
return;
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 45604bfb8a..690102f730 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -63,8 +63,6 @@ private:
// A compatibility enum to specify how is the data if formatted.
mutable TileMapDataFormat format = TileMapDataFormat::TILE_MAP_DATA_FORMAT_3;
- static constexpr float FP_ADJUST = 0.00001;
-
// Properties.
Ref<TileSet> tile_set;
int rendering_quadrant_size = 16;
@@ -123,8 +121,6 @@ public:
void set_rendering_quadrant_size(int p_size);
int get_rendering_quadrant_size() const;
- static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0);
-
// Accessors.
void set_tileset(const Ref<TileSet> &p_tileset);
Ref<TileSet> get_tileset() const;
diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp
index 03fd7364c0..0ac236eaa7 100644
--- a/scene/2d/tile_map_layer.cpp
+++ b/scene/2d/tile_map_layer.cpp
@@ -340,7 +340,7 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) {
}
// Drawing the tile in the canvas item.
- TileMap::draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, get_self_modulate(), tile_data, random_animation_offset);
+ draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, get_self_modulate(), tile_data, random_animation_offset);
}
// Reset physics interpolation for any recreated canvas items.
@@ -603,6 +603,7 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) {
rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder(occlusion_layer_index, flip_h, flip_v, transpose)->get_rid());
rs->canvas_light_occluder_attach_to_canvas(occluder, get_canvas());
rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index));
+ rs->canvas_light_occluder_set_as_sdf_collision(occluder, tile_set->get_occlusion_layer_sdf_collision(occlusion_layer_index));
} else {
// Clear occluder.
if (occluder.is_valid()) {
@@ -1695,18 +1696,18 @@ void TileMapLayer::_notification(int p_what) {
_internal_update(true);
} break;
- case TileMap::NOTIFICATION_ENTER_CANVAS: {
+ case NOTIFICATION_ENTER_CANVAS: {
dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true;
_queue_internal_update();
} break;
- case TileMap::NOTIFICATION_EXIT_CANVAS: {
+ case NOTIFICATION_EXIT_CANVAS: {
dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true;
// Update immediately on exiting, and force cleanup.
_internal_update(true);
} break;
- case TileMap::NOTIFICATION_VISIBILITY_CHANGED: {
+ case NOTIFICATION_VISIBILITY_CHANGED: {
dirty.flags[DIRTY_FLAGS_LAYER_VISIBILITY] = true;
_queue_internal_update();
} break;
@@ -2161,6 +2162,91 @@ TileMapCell TileMapLayer::get_cell(const Vector2i &p_coords) const {
}
}
+void TileMapLayer::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_normalized_animation_offset) {
+ ERR_FAIL_COND(p_tile_set.is_null());
+ ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id));
+ ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords));
+ ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile));
+ TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Check for the frame.
+ if (p_frame >= 0) {
+ ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords));
+ }
+
+ // Get the texture.
+ Ref<Texture2D> tex = atlas_source->get_runtime_texture();
+ if (tex.is_null()) {
+ return;
+ }
+
+ // Check if we are in the texture, return otherwise.
+ Vector2i grid_size = atlas_source->get_atlas_grid_size();
+ if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) {
+ return;
+ }
+
+ // Get tile data.
+ const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile);
+
+ // Get the tile modulation.
+ Color modulate = tile_data->get_modulate() * p_modulation;
+
+ // Compute the offset.
+ Vector2 tile_offset = tile_data->get_texture_origin();
+
+ // Get destination rect.
+ Rect2 dest_rect;
+ dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size;
+ dest_rect.size.x += FP_ADJUST;
+ dest_rect.size.y += FP_ADJUST;
+
+ bool transpose = tile_data->get_transpose() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
+ if (transpose) {
+ dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
+ } else {
+ dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset);
+ }
+
+ if (tile_data->get_flip_h() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) {
+ dest_rect.size.x = -dest_rect.size.x;
+ }
+
+ if (tile_data->get_flip_v() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) {
+ dest_rect.size.y = -dest_rect.size.y;
+ }
+
+ // Draw the tile.
+ if (p_frame >= 0) {
+ Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame);
+ tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
+ } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) {
+ Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0);
+ tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
+ } else {
+ real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords);
+ real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed;
+ real_t animation_offset = p_normalized_animation_offset * animation_duration;
+ // Accumulate durations unaffected by the speed to avoid accumulating floating point division errors.
+ // Aka do `sum(duration[i]) / speed` instead of `sum(duration[i] / speed)`.
+ real_t time_unscaled = 0.0;
+ for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) {
+ real_t frame_duration_unscaled = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame);
+ real_t slice_start = time_unscaled / speed;
+ real_t slice_end = (time_unscaled + frame_duration_unscaled) / speed;
+ RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, slice_start, slice_end, animation_offset);
+
+ Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame);
+ tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
+
+ time_unscaled += frame_duration_unscaled;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0);
+ }
+ }
+}
+
void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile) {
// Set the current cell tile (using integer position).
Vector2i pk(p_coords);
@@ -2211,7 +2297,7 @@ void TileMapLayer::erase_cell(const Vector2i &p_coords) {
}
void TileMapLayer::fix_invalid_tiles() {
- ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot call fix_invalid_tiles() on a TileMap without a valid TileSet.");
+ ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot call fix_invalid_tiles() on a TileMapLayer without a valid TileSet.");
RBSet<Vector2i> coords;
for (const KeyValue<Vector2i, CellData> &E : tile_map_layer_data) {
diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h
index 5861433c8a..57c83d7c4c 100644
--- a/scene/2d/tile_map_layer.h
+++ b/scene/2d/tile_map_layer.h
@@ -269,6 +269,8 @@ public:
};
private:
+ static constexpr float FP_ADJUST = 0.00001;
+
// Properties.
HashMap<Vector2i, CellData> tile_map_layer_data;
@@ -407,6 +409,8 @@ public:
// Not exposed to users.
TileMapCell get_cell(const Vector2i &p_coords) const;
+ static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0);
+
////////////// Exposed functions //////////////
// --- Cells manipulation ---
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 498e101b3c..a4804e928a 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -847,12 +847,13 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
ERR_FAIL_INDEX(p_bone_idx, bone_size);
Bone *bonesptr = bones.ptrw();
- List<int> bones_to_process = List<int>();
+ thread_local LocalVector<int> bones_to_process;
+ bones_to_process.clear();
bones_to_process.push_back(p_bone_idx);
- while (bones_to_process.size() > 0) {
- int current_bone_idx = bones_to_process.front()->get();
- bones_to_process.erase(current_bone_idx);
+ uint32_t index = 0;
+ while (index < bones_to_process.size()) {
+ int current_bone_idx = bones_to_process[index];
Bone &b = bonesptr[current_bone_idx];
bool bone_enabled = b.enabled && !show_rest_only;
@@ -905,6 +906,8 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
for (int i = 0; i < child_bone_size; i++) {
bones_to_process.push_back(b.child_bones[i]);
}
+
+ index++;
}
}
diff --git a/scene/gui/button.h b/scene/gui/button.h
index d2f80893e0..eefb690913 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -100,8 +100,6 @@ private:
int icon_max_width = 0;
} theme_cache;
- Size2 _fit_icon_size(const Size2 &p_size) const;
-
void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = "");
void _texture_changed();
@@ -111,6 +109,7 @@ protected:
void _set_internal_margin(Side p_side, float p_value);
virtual void _queue_update_size_cache();
+ Size2 _fit_icon_size(const Size2 &p_size) const;
Ref<StyleBox> _get_current_stylebox() const;
Size2 _get_largest_stylebox_size() const;
void _notification(int p_what);
diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp
index 0dcd1d4ac4..99937aaf41 100644
--- a/scene/gui/check_box.cpp
+++ b/scene/gui/check_box.cpp
@@ -59,7 +59,7 @@ Size2 CheckBox::get_icon_size() const {
if (!theme_cache.radio_unchecked_disabled.is_null()) {
tex_size = tex_size.max(theme_cache.radio_unchecked_disabled->get_size());
}
- return tex_size;
+ return _fit_icon_size(tex_size);
}
Size2 CheckBox::get_minimum_size() const {
@@ -127,9 +127,9 @@ void CheckBox::_notification(int p_what) {
ofs.y = int((get_size().height - get_icon_size().height) / 2) + theme_cache.check_v_offset;
if (is_pressed()) {
- on_tex->draw(ci, ofs);
+ on_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(on_tex->get_size())));
} else {
- off_tex->draw(ci, ofs);
+ off_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(off_tex->get_size())));
}
} break;
}
diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp
index fe449fbfc3..29b9504776 100644
--- a/scene/gui/check_button.cpp
+++ b/scene/gui/check_button.cpp
@@ -63,7 +63,7 @@ Size2 CheckButton::get_icon_size() const {
tex_size = tex_size.max(off_tex->get_size());
}
- return tex_size;
+ return _fit_icon_size(tex_size);
}
Size2 CheckButton::get_minimum_size() const {
@@ -134,9 +134,9 @@ void CheckButton::_notification(int p_what) {
ofs.y = (get_size().height - tex_size.height) / 2 + theme_cache.check_v_offset;
if (is_pressed()) {
- on_tex->draw(ci, ofs);
+ on_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(on_tex->get_size())));
} else {
- off_tex->draw(ci, ofs);
+ off_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(off_tex->get_size())));
}
} break;
}
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 8267e958b4..dfe5bd4a47 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -173,7 +173,7 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr
}
}
#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint() && !class_doc.name.is_empty() && p_params) {
+ if (EditorHelp::get_doc_data() != nullptr && Engine::get_singleton()->is_editor_hint() && !class_doc.name.is_empty() && p_params) {
EditorHelp::get_doc_data()->add_doc(class_doc);
}
#endif
diff --git a/scene/theme/SCsub b/scene/theme/SCsub
index 5f62ae4b05..2372d1820a 100644
--- a/scene/theme/SCsub
+++ b/scene/theme/SCsub
@@ -4,7 +4,6 @@ Import("env")
import default_theme_builders
-
env.add_source_files(env.scene_sources, "*.cpp")
SConscript("icons/SCsub")
diff --git a/scene/theme/icons/SCsub b/scene/theme/icons/SCsub
index 46133ccceb..1f3b7f6d17 100644
--- a/scene/theme/icons/SCsub
+++ b/scene/theme/icons/SCsub
@@ -4,7 +4,6 @@ Import("env")
import default_theme_icons_builders
-
env["BUILDERS"]["MakeDefaultThemeIconsBuilder"] = Builder(
action=env.Run(default_theme_icons_builders.make_default_theme_icons_action),
suffix=".h",
diff --git a/scu_builders.py b/scu_builders.py
index a9ae428222..fc5461196f 100644
--- a/scu_builders.py
+++ b/scu_builders.py
@@ -1,11 +1,11 @@
-"""Functions used to generate scu build source files during build time
-"""
+"""Functions used to generate scu build source files during build time"""
-import glob, os
+import glob
import math
-from methods import print_error
+import os
from pathlib import Path
-from os.path import normpath, basename
+
+from methods import print_error
base_folder_path = str(Path(__file__).parent) + "/"
base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
@@ -25,7 +25,7 @@ def clear_out_stale_files(output_folder, extension, fresh_files):
for file in glob.glob(output_folder + "/*." + extension):
file = Path(file)
- if not file in fresh_files:
+ if file not in fresh_files:
# print("removed stale file: " + str(file))
os.remove(file)
@@ -56,7 +56,7 @@ def find_files_in_folder(folder, sub_folder, include_list, extension, sought_exc
li = '#include "' + folder + "/" + sub_folder_slashed + file + '"'
- if not simple_name in sought_exceptions:
+ if simple_name not in sought_exceptions:
include_list.append(li)
else:
found_exceptions.append(li)
@@ -78,9 +78,9 @@ def write_output_file(file_count, include_list, start_line, end_line, output_fol
file_text = ""
- for l in range(start_line, end_line):
- if l < len(include_list):
- line = include_list[l]
+ for i in range(start_line, end_line):
+ if i < len(include_list):
+ line = include_list[i]
li = line + "\n"
file_text += li
@@ -221,7 +221,6 @@ def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension=
lines_per_file = max(lines_per_file, 1)
start_line = 0
- file_number = 0
# These do not vary throughout the loop
output_folder = abs_main_folder + "/scu/"
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 020be6be18..42773fc347 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -797,6 +797,8 @@ private:
#endif
public:
+ RenderingContextDriver *get_context_driver() const { return context; }
+
const RDD::Capabilities &get_device_capabilities() const { return driver->get_capabilities(); }
bool has_feature(const Features p_feature) const;
diff --git a/servers/rendering/rendering_light_culler.h b/servers/rendering/rendering_light_culler.h
index 0bf975430b..b0437d2310 100644
--- a/servers/rendering/rendering_light_culler.h
+++ b/servers/rendering/rendering_light_culler.h
@@ -181,14 +181,14 @@ private:
}
// Prevent divide by zero.
- if (lc > 0.00001f) {
+ if (lc > 0.001f) {
// If the summed length of the smaller two
// sides is close to the length of the longest side,
// the points are colinear, and the triangle is near degenerate.
float ld = ((la + lb) - lc) / lc;
// ld will be close to zero for colinear tris.
- return ld < 0.00001f;
+ return ld < 0.001f;
}
// Don't create planes from tiny triangles,
diff --git a/tests/create_test.py b/tests/create_test.py
index deb53aca20..deb53aca20 100644..100755
--- a/tests/create_test.py
+++ b/tests/create_test.py
diff --git a/tests/python_build/test_gles3_builder.py b/tests/python_build/test_gles3_builder.py
index 6f16139eb9..b34d33bde7 100644
--- a/tests/python_build/test_gles3_builder.py
+++ b/tests/python_build/test_gles3_builder.py
@@ -2,7 +2,7 @@ import json
import pytest
-from gles3_builders import build_gles3_header, GLES3HeaderStruct
+from gles3_builders import GLES3HeaderStruct, build_gles3_header
@pytest.mark.parametrize(
diff --git a/tests/python_build/test_glsl_builder.py b/tests/python_build/test_glsl_builder.py
index 348ef8441c..9f548855ff 100644
--- a/tests/python_build/test_glsl_builder.py
+++ b/tests/python_build/test_glsl_builder.py
@@ -2,7 +2,7 @@ import json
import pytest
-from glsl_builders import build_raw_header, RAWHeaderStruct, build_rd_header, RDHeaderStruct
+from glsl_builders import RAWHeaderStruct, RDHeaderStruct, build_raw_header, build_rd_header
@pytest.mark.parametrize(
diff --git a/thirdparty/glslang/SPIRV/GlslangToSpv.cpp b/thirdparty/glslang/SPIRV/GlslangToSpv.cpp
index ec40f663a7..ec40f663a7 100755..100644
--- a/thirdparty/glslang/SPIRV/GlslangToSpv.cpp
+++ b/thirdparty/glslang/SPIRV/GlslangToSpv.cpp
diff --git a/thirdparty/glslang/SPIRV/doc.cpp b/thirdparty/glslang/SPIRV/doc.cpp
index 1a05c67360..1a05c67360 100755..100644
--- a/thirdparty/glslang/SPIRV/doc.cpp
+++ b/thirdparty/glslang/SPIRV/doc.cpp
diff --git a/thirdparty/glslang/glslang/Include/BaseTypes.h b/thirdparty/glslang/glslang/Include/BaseTypes.h
index 64bffa8926..64bffa8926 100755..100644
--- a/thirdparty/glslang/glslang/Include/BaseTypes.h
+++ b/thirdparty/glslang/glslang/Include/BaseTypes.h
diff --git a/thirdparty/glslang/glslang/MachineIndependent/Initialize.cpp b/thirdparty/glslang/glslang/MachineIndependent/Initialize.cpp
index af333f3f16..af333f3f16 100755..100644
--- a/thirdparty/glslang/glslang/MachineIndependent/Initialize.cpp
+++ b/thirdparty/glslang/glslang/MachineIndependent/Initialize.cpp
diff --git a/thirdparty/glslang/glslang/MachineIndependent/Versions.h b/thirdparty/glslang/glslang/MachineIndependent/Versions.h
index 475cb89341..475cb89341 100755..100644
--- a/thirdparty/glslang/glslang/MachineIndependent/Versions.h
+++ b/thirdparty/glslang/glslang/MachineIndependent/Versions.h