summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/CODEOWNERS4
-rw-r--r--.github/workflows/static_checks.yml6
-rw-r--r--README.md1
-rw-r--r--SConstruct2
-rw-r--r--core/core_bind.cpp5
-rw-r--r--core/core_bind.h1
-rw-r--r--core/core_builders.py6
-rw-r--r--core/input/godotcontrollerdb.txt1
-rw-r--r--core/input/input_builders.py2
-rw-r--r--core/math/a_star_grid_2d.cpp24
-rw-r--r--core/math/a_star_grid_2d.h2
-rw-r--r--core/math/bvh_abb.h8
-rw-r--r--core/math/vector2.h4
-rw-r--r--core/math/vector3.cpp10
-rw-r--r--core/math/vector3.h7
-rw-r--r--core/math/vector3i.cpp10
-rw-r--r--core/math/vector3i.h3
-rw-r--r--core/math/vector4.cpp10
-rw-r--r--core/math/vector4.h9
-rw-r--r--core/math/vector4i.cpp10
-rw-r--r--core/math/vector4i.h3
-rw-r--r--core/string/ustring.cpp16
-rw-r--r--doc/classes/AStarGrid2D.xml2
-rw-r--r--doc/classes/Basis.xml2
-rw-r--r--doc/classes/OS.xml8
-rwxr-xr-xdoc/tools/make_rst.py29
-rw-r--r--doc/translations/extract.py2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp1
-rw-r--r--editor/editor_builders.py5
-rw-r--r--editor/editor_inspector.cpp22
-rw-r--r--editor/editor_inspector.h4
-rw-r--r--editor/editor_resource_picker.cpp2
-rw-r--r--editor/plugins/script_editor_plugin.cpp3
-rw-r--r--editor/plugins/shader_editor_plugin.cpp1174
-rw-r--r--editor/plugins/shader_editor_plugin.h181
-rw-r--r--editor/plugins/text_shader_editor.cpp1191
-rw-r--r--editor/plugins/text_shader_editor.h199
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp204
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp54
-rwxr-xr-xeditor/translations/extract.py7
-rw-r--r--gles3_builders.py10
-rw-r--r--glsl_builders.py14
-rw-r--r--methods.py3
-rw-r--r--misc/scripts/mypy.ini11
-rwxr-xr-xmisc/scripts/mypy_check.sh6
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp61
-rw-r--r--modules/gdscript/gdscript_parser.cpp4
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp2
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.h4
-rwxr-xr-xmodules/mono/build_scripts/build_assemblies.py8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs348
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs2
-rw-r--r--modules/mono/glue/runtime_interop.cpp5
-rw-r--r--modules/mono/managed_callable.cpp6
-rw-r--r--modules/mono/managed_callable.h3
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp5
-rw-r--r--modules/navigation/nav_map.cpp1
-rw-r--r--platform/android/detect.py9
-rw-r--r--platform/ios/detect.py7
-rw-r--r--platform/linuxbsd/detect.py7
-rw-r--r--platform/linuxbsd/display_server_x11.cpp5
-rw-r--r--platform/macos/detect.py7
-rw-r--r--platform/uwp/detect.py11
-rw-r--r--platform/web/detect.py6
-rw-r--r--platform/windows/detect.py7
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/resources/tile_set.cpp1
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h1
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h2
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl15
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl15
-rw-r--r--tests/core/math/test_vector3.h8
-rw-r--r--tests/core/math/test_vector3i.h8
-rw-r--r--tests/core/math/test_vector4.h8
-rw-r--r--tests/core/math/test_vector4i.h8
-rw-r--r--tests/core/string/test_string.h16
78 files changed, 2123 insertions, 1752 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index bd4eb906c0..7ac048b24a 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -15,6 +15,7 @@ SCsub @godotengine/buildsystem
/core/ @godotengine/core
/core/crypto/ @godotengine/network
/core/debugger/ @godotengine/debugger
+/core/extension/ @godotengine/gdextension
/core/input/ @godotengine/input
# Doc
@@ -110,10 +111,9 @@ doc_classes/* @godotengine/documentation
/modules/xatlas_unwrap/ @godotengine/rendering
## Scripting
-/modules/gdnative/ @godotengine/gdnative
/modules/gdscript/ @godotengine/gdscript
/modules/jsonrpc/ @godotengine/gdscript
-/modules/mono/ @godotengine/mono
+/modules/mono/ @godotengine/dotnet
## Text
/modules/freetype/ @godotengine/buildsystem
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index 5b4de06e9e..d8951ddb78 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -27,7 +27,7 @@ jobs:
sudo apt-get install -qq dos2unix recode clang-format-13 libxml2-utils python3-pip moreutils
sudo update-alternatives --remove-all clang-format || true
sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-13 100
- sudo pip3 install black==22.3.0 pygments pytest
+ sudo pip3 install black==22.3.0 pygments pytest==7.1.2 mypy==0.971
- name: File formatting checks (file_format.sh)
run: |
@@ -41,6 +41,10 @@ jobs:
run: |
bash ./misc/scripts/black_format.sh
+ - name: Python scripts static analysis (mypy_check.sh)
+ run: |
+ bash ./misc/scripts/mypy_check.sh
+
- name: Python builders checks via pytest (pytest_builders.sh)
run: |
bash ./misc/scripts/pytest_builders.sh
diff --git a/README.md b/README.md
index 82dbdcfed1..6dfd4fc713 100644
--- a/README.md
+++ b/README.md
@@ -73,5 +73,4 @@ for more information.
[![Code Triagers Badge](https://www.codetriage.com/godotengine/godot/badges/users.svg)](https://www.codetriage.com/godotengine/godot)
[![Translate on Weblate](https://hosted.weblate.org/widgets/godot-engine/-/godot/svg-badge.svg)](https://hosted.weblate.org/engage/godot-engine/?utm_source=widget)
-[![Total alerts on LGTM](https://img.shields.io/lgtm/alerts/g/godotengine/godot.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/godotengine/godot/alerts)
[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/godotengine/godot)](https://www.tickgit.com/browse?repo=github.com/godotengine/godot)
diff --git a/SConstruct b/SConstruct
index c86422c4e4..646f41839d 100644
--- a/SConstruct
+++ b/SConstruct
@@ -518,7 +518,7 @@ if selected_platform in platform_list:
# are actually handled to change compile options, etc.
detect.configure(env)
- print(f'Building for platform "{selected_platform}", architecture "{env["arch"]}", target "{env["target"]}.')
+ print(f'Building for platform "{selected_platform}", architecture "{env["arch"]}", target "{env["target"]}".')
if env.dev_build:
print("NOTE: Developer build, with debug optimization level and debug symbols (unless overridden).")
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index a164221fc2..1582e8e983 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -253,6 +253,10 @@ Error OS::shell_open(String p_uri) {
return ::OS::get_singleton()->shell_open(p_uri);
}
+String OS::read_string_from_stdin(bool p_block) {
+ return ::OS::get_singleton()->get_stdin_string(true);
+}
+
int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr, bool p_open_console) {
List<String> args;
for (int i = 0; i < p_arguments.size(); i++) {
@@ -530,6 +534,7 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_system_fonts"), &OS::get_system_fonts);
ClassDB::bind_method(D_METHOD("get_system_font_path", "font_name", "bold", "italic"), &OS::get_system_font_path, DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
+ ClassDB::bind_method(D_METHOD("read_string_from_stdin", "block"), &OS::read_string_from_stdin, DEFVAL(true));
ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);
diff --git a/core/core_bind.h b/core/core_bind.h
index ba22971d78..fc4b88b481 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -172,6 +172,7 @@ public:
Vector<String> get_system_fonts() const;
String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const;
String get_executable_path() const;
+ String read_string_from_stdin(bool p_block = true);
int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false);
int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
int create_instance(const Vector<String> &p_arguments);
diff --git a/core/core_builders.py b/core/core_builders.py
index b07daa80ae..b0a3b85d58 100644
--- a/core/core_builders.py
+++ b/core/core_builders.py
@@ -2,6 +2,7 @@
All such functions are invoked in a subprocess on Windows to prevent build flakiness.
"""
+import zlib
from platform_methods import subprocess_main
@@ -33,7 +34,6 @@ def make_certs_header(target, source, env):
g = open(dst, "w", encoding="utf-8")
buf = f.read()
decomp_size = len(buf)
- import zlib
# Use maximum zlib compression level to further reduce file size
# (at the cost of initial build times).
@@ -208,7 +208,7 @@ def make_license_header(target, source, env):
from collections import OrderedDict
- projects = OrderedDict()
+ projects: dict = OrderedDict()
license_list = []
with open(src_copyright, "r", encoding="utf-8") as copyright_file:
@@ -230,7 +230,7 @@ def make_license_header(target, source, env):
part = {}
reader.next_line()
- data_list = []
+ data_list: list = []
for project in iter(projects.values()):
for part in project:
part["file_index"] = len(data_list)
diff --git a/core/input/godotcontrollerdb.txt b/core/input/godotcontrollerdb.txt
index b2a6160c6c..e11099f380 100644
--- a/core/input/godotcontrollerdb.txt
+++ b/core/input/godotcontrollerdb.txt
@@ -32,6 +32,7 @@ Linux046dc216,046d-c216-Logitech Logitech Dual Action,a:b1,b:b2,y:b3,x:b0,start:
Linux20d6a713,Bensussen Deutsch & Associates Inc.(BDA) NSW Wired controller,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:-a5,dpleft:-a4,dpdown:+a5,dpright:+a4,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Web
Linux054c05c4,Sony Computer Entertainment Wireless Controller,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web
Linux18d19400,18d1-9400-Google LLC Stadia Controller rev. A,a:b0,b:b1,y:b3,x:b2,start:b7,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Web
+Linux054c0268,054c-0268-Sony PLAYSTATION(R)3 Controller,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web
# UWP
__UWP_GAMEPAD__,Xbox Controller,a:b2,b:b3,x:b4,y:b5,start:b0,back:b1,leftstick:b12,rightstick:b13,leftshoulder:b10,rightshoulder:b11,dpup:b6,dpdown:b7,dpleft:b8,dpright:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:UWP,
diff --git a/core/input/input_builders.py b/core/input/input_builders.py
index 16f125ff38..a7729c9af2 100644
--- a/core/input/input_builders.py
+++ b/core/input/input_builders.py
@@ -16,7 +16,7 @@ def make_default_controller_mappings(target, source, env):
g.write('#include "core/input/default_controller_mappings.h"\n')
# ensure mappings have a consistent order
- platform_mappings = OrderedDict()
+ platform_mappings: dict = OrderedDict()
for src_path in source:
with open(src_path, "r") as f:
# read mapping file and skip header
diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp
index ad67cfa852..c30acf32bb 100644
--- a/core/math/a_star_grid_2d.cpp
+++ b/core/math/a_star_grid_2d.cpp
@@ -30,6 +30,8 @@
#include "a_star_grid_2d.h"
+#include "core/variant/typed_array.h"
+
static real_t heuristic_euclidian(const Vector2i &p_from, const Vector2i &p_to) {
real_t dx = (real_t)ABS(p_to.x - p_from.x);
real_t dy = (real_t)ABS(p_to.y - p_from.y);
@@ -492,17 +494,17 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
return path;
}
-Vector<Vector2> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) {
- ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method.");
- ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height));
- ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height));
+TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) {
+ ERR_FAIL_COND_V_MSG(dirty, TypedArray<Vector2i>(), "Grid is not initialized. Call the update method.");
+ ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height));
+ ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height));
Point *a = _get_point(p_from_id.x, p_from_id.y);
Point *b = _get_point(p_to_id.x, p_to_id.y);
if (a == b) {
- Vector<Vector2> ret;
- ret.push_back(Vector2((float)a->id.x, (float)a->id.y));
+ TypedArray<Vector2i> ret;
+ ret.push_back(a);
return ret;
}
@@ -511,7 +513,7 @@ Vector<Vector2> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return Vector<Vector2>();
+ return TypedArray<Vector2i>();
}
Point *p = end_point;
@@ -521,20 +523,18 @@ Vector<Vector2> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector
p = p->prev_point;
}
- Vector<Vector2> path;
+ TypedArray<Vector2i> path;
path.resize(pc);
{
- Vector2 *w = path.ptrw();
-
p = end_point;
int64_t idx = pc - 1;
while (p != begin_point) {
- w[idx--] = Vector2((float)p->id.x, (float)p->id.y);
+ path[idx--] = p->id;
p = p->prev_point;
}
- w[0] = p->id;
+ path[0] = p->id;
}
return path;
diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h
index bf6363aa01..1002f18738 100644
--- a/core/math/a_star_grid_2d.h
+++ b/core/math/a_star_grid_2d.h
@@ -169,7 +169,7 @@ public:
void clear();
Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to);
- Vector<Vector2> get_id_path(const Vector2i &p_from, const Vector2i &p_to);
+ TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to);
};
VARIANT_ENUM_CAST(AStarGrid2D::DiagonalMode);
diff --git a/core/math/bvh_abb.h b/core/math/bvh_abb.h
index 8a44f1c4da..699f7de604 100644
--- a/core/math/bvh_abb.h
+++ b/core/math/bvh_abb.h
@@ -251,7 +251,9 @@ struct BVH_ABB {
void expand(real_t p_change) {
POINT change;
- change.set_all(p_change);
+ for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) {
+ change[axis] = p_change;
+ }
grow(change);
}
@@ -262,7 +264,9 @@ struct BVH_ABB {
}
void set_to_max_opposite_extents() {
- neg_max.set_all(FLT_MAX);
+ for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) {
+ neg_max[axis] = FLT_MAX;
+ }
min = neg_max;
}
diff --git a/core/math/vector2.h b/core/math/vector2.h
index 9441f84087..75364f72f0 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -69,10 +69,6 @@ struct _NO_DISCARD_ Vector2 {
return coord[p_idx];
}
- _FORCE_INLINE_ void set_all(const real_t p_value) {
- x = y = p_value;
- }
-
_FORCE_INLINE_ Vector2::Axis min_axis_index() const {
return x < y ? Vector2::AXIS_X : Vector2::AXIS_Y;
}
diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp
index 4db45fe798..55ba509144 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -45,16 +45,6 @@ Vector3 Vector3::rotated(const Vector3 &p_axis, const real_t p_angle) const {
return r;
}
-void Vector3::set_axis(const int p_axis, const real_t p_value) {
- ERR_FAIL_INDEX(p_axis, 3);
- coord[p_axis] = p_value;
-}
-
-real_t Vector3::get_axis(const int p_axis) const {
- ERR_FAIL_INDEX_V(p_axis, 3, 0);
- return operator[](p_axis);
-}
-
Vector3 Vector3::clamp(const Vector3 &p_min, const Vector3 &p_max) const {
return Vector3(
CLAMP(x, p_min.x, p_max.x),
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 3944afa92e..62e810fb4d 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -68,13 +68,6 @@ struct _NO_DISCARD_ Vector3 {
return coord[p_axis];
}
- void set_axis(const int p_axis, const real_t p_value);
- real_t get_axis(const int p_axis) const;
-
- _FORCE_INLINE_ void set_all(const real_t p_value) {
- x = y = z = p_value;
- }
-
_FORCE_INLINE_ Vector3::Axis min_axis_index() const {
return x < y ? (x < z ? Vector3::AXIS_X : Vector3::AXIS_Z) : (y < z ? Vector3::AXIS_Y : Vector3::AXIS_Z);
}
diff --git a/core/math/vector3i.cpp b/core/math/vector3i.cpp
index b8e74ea6d2..b248f35035 100644
--- a/core/math/vector3i.cpp
+++ b/core/math/vector3i.cpp
@@ -33,16 +33,6 @@
#include "core/math/vector3.h"
#include "core/string/ustring.h"
-void Vector3i::set_axis(const int p_axis, const int32_t p_value) {
- ERR_FAIL_INDEX(p_axis, 3);
- coord[p_axis] = p_value;
-}
-
-int32_t Vector3i::get_axis(const int p_axis) const {
- ERR_FAIL_INDEX_V(p_axis, 3, 0);
- return operator[](p_axis);
-}
-
Vector3i::Axis Vector3i::min_axis_index() const {
return x < y ? (x < z ? Vector3i::AXIS_X : Vector3i::AXIS_Z) : (y < z ? Vector3i::AXIS_Y : Vector3i::AXIS_Z);
}
diff --git a/core/math/vector3i.h b/core/math/vector3i.h
index c6d03cd031..710fd96376 100644
--- a/core/math/vector3i.h
+++ b/core/math/vector3i.h
@@ -66,9 +66,6 @@ struct _NO_DISCARD_ Vector3i {
return coord[p_axis];
}
- void set_axis(const int p_axis, const int32_t p_value);
- int32_t get_axis(const int p_axis) const;
-
Vector3i::Axis min_axis_index() const;
Vector3i::Axis max_axis_index() const;
diff --git a/core/math/vector4.cpp b/core/math/vector4.cpp
index 3c25f454a3..55e51834df 100644
--- a/core/math/vector4.cpp
+++ b/core/math/vector4.cpp
@@ -33,16 +33,6 @@
#include "core/math/basis.h"
#include "core/string/print_string.h"
-void Vector4::set_axis(const int p_axis, const real_t p_value) {
- ERR_FAIL_INDEX(p_axis, 4);
- components[p_axis] = p_value;
-}
-
-real_t Vector4::get_axis(const int p_axis) const {
- ERR_FAIL_INDEX_V(p_axis, 4, 0);
- return operator[](p_axis);
-}
-
Vector4::Axis Vector4::min_axis_index() const {
uint32_t min_index = 0;
real_t min_value = x;
diff --git a/core/math/vector4.h b/core/math/vector4.h
index d89f3ddb05..426c473e13 100644
--- a/core/math/vector4.h
+++ b/core/math/vector4.h
@@ -65,11 +65,6 @@ struct _NO_DISCARD_ Vector4 {
return components[p_axis];
}
- _FORCE_INLINE_ void set_all(const real_t p_value);
-
- void set_axis(const int p_axis, const real_t p_value);
- real_t get_axis(const int p_axis) const;
-
Vector4::Axis min_axis_index() const;
Vector4::Axis max_axis_index() const;
@@ -150,10 +145,6 @@ struct _NO_DISCARD_ Vector4 {
}
};
-void Vector4::set_all(const real_t p_value) {
- x = y = z = p_value;
-}
-
real_t Vector4::dot(const Vector4 &p_vec4) const {
return x * p_vec4.x + y * p_vec4.y + z * p_vec4.z + w * p_vec4.w;
}
diff --git a/core/math/vector4i.cpp b/core/math/vector4i.cpp
index a89b802675..77f6fbd5b7 100644
--- a/core/math/vector4i.cpp
+++ b/core/math/vector4i.cpp
@@ -33,16 +33,6 @@
#include "core/math/vector4.h"
#include "core/string/ustring.h"
-void Vector4i::set_axis(const int p_axis, const int32_t p_value) {
- ERR_FAIL_INDEX(p_axis, 4);
- coord[p_axis] = p_value;
-}
-
-int32_t Vector4i::get_axis(const int p_axis) const {
- ERR_FAIL_INDEX_V(p_axis, 4, 0);
- return operator[](p_axis);
-}
-
Vector4i::Axis Vector4i::min_axis_index() const {
uint32_t min_index = 0;
int32_t min_value = x;
diff --git a/core/math/vector4i.h b/core/math/vector4i.h
index fdf33b9569..a32414bb18 100644
--- a/core/math/vector4i.h
+++ b/core/math/vector4i.h
@@ -68,9 +68,6 @@ struct _NO_DISCARD_ Vector4i {
return coord[p_axis];
}
- void set_axis(const int p_axis, const int32_t p_value);
- int32_t get_axis(const int p_axis) const;
-
Vector4i::Axis min_axis_index() const;
Vector4i::Axis max_axis_index() const;
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 75b797d397..6218c21cde 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -4651,15 +4651,18 @@ String String::sprintf(const Array &values, bool *error) const {
double value = values[value_index];
bool is_negative = (value < 0);
String str = String::num(ABS(value), min_decimals);
+ bool not_numeric = isinf(value) || isnan(value);
// Pad decimals out.
- str = str.pad_decimals(min_decimals);
+ if (!not_numeric) {
+ str = str.pad_decimals(min_decimals);
+ }
int initial_len = str.length();
// Padding. Leave room for sign later if required.
int pad_chars_count = (is_negative || show_sign) ? min_chars - 1 : min_chars;
- String pad_char = pad_with_zeros ? String("0") : String(" ");
+ String pad_char = (pad_with_zeros && !not_numeric) ? String("0") : String(" "); // Never pad NaN or inf with zeros
if (left_justified) {
str = str.rpad(pad_chars_count, pad_char);
} else {
@@ -4709,14 +4712,19 @@ String String::sprintf(const Array &values, bool *error) const {
String str = "(";
for (int i = 0; i < count; i++) {
double val = vec[i];
+ String number_str = String::num(ABS(val), min_decimals);
+ bool not_numeric = isinf(val) || isnan(val);
+
// Pad decimals out.
- String number_str = String::num(ABS(val), min_decimals).pad_decimals(min_decimals);
+ if (!not_numeric) {
+ number_str = number_str.pad_decimals(min_decimals);
+ }
int initial_len = number_str.length();
// Padding. Leave room for sign later if required.
int pad_chars_count = val < 0 ? min_chars - 1 : min_chars;
- String pad_char = pad_with_zeros ? String("0") : String(" ");
+ String pad_char = (pad_with_zeros && !not_numeric) ? String("0") : String(" "); // Never pad NaN or inf with zeros
if (left_justified) {
number_str = number_str.rpad(pad_chars_count, pad_char);
} else {
diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml
index 331862ebfa..8dde3748d7 100644
--- a/doc/classes/AStarGrid2D.xml
+++ b/doc/classes/AStarGrid2D.xml
@@ -53,7 +53,7 @@
</description>
</method>
<method name="get_id_path">
- <return type="PackedVector2Array" />
+ <return type="Vector2i[]" />
<param index="0" name="from_id" type="Vector2i" />
<param index="1" name="to_id" type="Vector2i" />
<description>
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index d62f704528..6d9b679fbc 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -83,7 +83,7 @@
<return type="Vector3" />
<param index="0" name="order" type="int" default="2" />
<description>
- Returns the basis's rotation in the form of Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last). The returned vector contains the rotation angles in the format (X angle, Y angle, Z angle).
+ Returns the basis's rotation in the form of Euler angles. The Euler order depends on the [param order] parameter, by default it uses the YXZ convention: when decomposing, first Z, then X, and Y last. The returned vector contains the rotation angles in the format (X angle, Y angle, Z angle).
Consider using the [method get_rotation_quaternion] method instead, which returns a [Quaternion] quaternion instead of Euler angles.
</description>
</method>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 313f3ab6db..a3fd27c00f 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -544,6 +544,14 @@
[b]Note:[/b] This method is implemented on Linux, macOS and Windows.
</description>
</method>
+ <method name="read_string_from_stdin">
+ <return type="String" />
+ <param index="0" name="block" type="bool" default="true" />
+ <description>
+ Reads a user input string from the standard input (usually the terminal).
+ [b]Note:[/b] This method is implemented on Linux, macOS and Windows. Non-blocking reads are not currently supported on any platform.
+ </description>
+ </method>
<method name="request_permission">
<return type="bool" />
<param index="0" name="name" type="String" />
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index a8569413ec..492a438d9b 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -526,7 +526,7 @@ def main() -> None:
)
if os.path.exists(lang_file):
try:
- import polib
+ import polib # type: ignore
except ImportError:
print("Base template strings localization requires `polib`.")
exit(1)
@@ -739,9 +739,10 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
f.write(f"- {make_link(url, title)}\n\n")
# Properties overview
+ ml: List[Tuple[Optional[str], ...]] = []
if len(class_def.properties) > 0:
f.write(make_heading("Properties", "-"))
- ml: List[Tuple[Optional[str], ...]] = []
+ ml = []
for property_def in class_def.properties.values():
type_rst = property_def.type_name.to_rst(state)
default = property_def.default_value
@@ -757,7 +758,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
# Constructors, Methods, Operators overview
if len(class_def.constructors) > 0:
f.write(make_heading("Constructors", "-"))
- ml: List[Tuple[Optional[str], ...]] = []
+ ml = []
for method_list in class_def.constructors.values():
for m in method_list:
ml.append(make_method_signature(class_def, m, "constructor", state))
@@ -765,7 +766,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
if len(class_def.methods) > 0:
f.write(make_heading("Methods", "-"))
- ml: List[Tuple[Optional[str], ...]] = []
+ ml = []
for method_list in class_def.methods.values():
for m in method_list:
ml.append(make_method_signature(class_def, m, "method", state))
@@ -773,7 +774,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
if len(class_def.operators) > 0:
f.write(make_heading("Operators", "-"))
- ml: List[Tuple[Optional[str], ...]] = []
+ ml = []
for method_list in class_def.operators.values():
for m in method_list:
ml.append(make_method_signature(class_def, m, "operator", state))
@@ -858,7 +859,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
f.write(make_heading("Annotations", "-"))
index = 0
- for method_list in class_def.annotations.values():
+ for method_list in class_def.annotations.values(): # type: ignore
for i, m in enumerate(method_list):
if index != 0:
f.write("----\n\n")
@@ -1039,17 +1040,15 @@ def make_method_signature(
) -> Tuple[str, str]:
ret_type = ""
- is_method_def = isinstance(definition, MethodDef)
- if is_method_def:
+ if isinstance(definition, MethodDef):
ret_type = definition.return_type.to_rst(state)
qualifiers = None
- if is_method_def or isinstance(definition, AnnotationDef):
+ if isinstance(definition, (MethodDef, AnnotationDef)):
qualifiers = definition.qualifiers
out = ""
-
- if is_method_def and ref_type != "":
+ if isinstance(definition, MethodDef) and ref_type != "":
if ref_type == "operator":
op_name = definition.name.replace("<", "\\<") # So operator "<" gets correctly displayed.
out += f":ref:`{op_name}<class_{class_def.name}_{ref_type}_{sanitize_operator_name(definition.name, state)}_{definition.return_type.type_name}>` "
@@ -1456,18 +1455,14 @@ def format_text_block(
escape_post = True
elif cmd.startswith("param"):
- valid_context = (
- isinstance(context, MethodDef)
- or isinstance(context, SignalDef)
- or isinstance(context, AnnotationDef)
- )
+ valid_context = isinstance(context, (MethodDef, SignalDef, AnnotationDef))
if not valid_context:
print_error(
f'{state.current_class}.xml: Argument reference "{link_target}" used outside of method, signal, or annotation context in {context_name}.',
state,
)
else:
- context_params: List[ParameterDef] = context.parameters
+ context_params: List[ParameterDef] = context.parameters # type: ignore
found = False
for param_def in context_params:
if param_def.name == link_target:
diff --git a/doc/translations/extract.py b/doc/translations/extract.py
index 5708e0072d..ce645436d9 100644
--- a/doc/translations/extract.py
+++ b/doc/translations/extract.py
@@ -60,7 +60,7 @@ BASE_STRINGS = [
## <xml-line-number-hack from="https://stackoverflow.com/a/36430270/10846399">
import sys
-sys.modules["_elementtree"] = None
+sys.modules["_elementtree"] = None # type: ignore
import xml.etree.ElementTree as ET
## override the parser to get the line number
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 39617167ef..d106bb31c7 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -737,7 +737,6 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
RS::EnvironmentBG background = environment_get_background(p_env);
if (sky) {
- ERR_FAIL_COND(!sky);
sky_material = sky->material;
if (sky_material.is_valid()) {
diff --git a/editor/editor_builders.py b/editor/editor_builders.py
index e73fbc6107..696e3b64ec 100644
--- a/editor/editor_builders.py
+++ b/editor/editor_builders.py
@@ -9,6 +9,7 @@ import shutil
import subprocess
import tempfile
import uuid
+import zlib
from platform_methods import subprocess_main
@@ -28,7 +29,6 @@ def make_doc_header(target, source, env):
buf = (docbegin + buf + docend).encode("utf-8")
decomp_size = len(buf)
- import zlib
# Use maximum zlib compression level to further reduce file size
# (at the cost of initial build times).
@@ -88,9 +88,6 @@ def make_translations_header(target, source, env, category):
g.write("#ifndef _{}_TRANSLATIONS_H\n".format(category.upper()))
g.write("#define _{}_TRANSLATIONS_H\n".format(category.upper()))
- import zlib
- import os.path
-
sorted_paths = sorted(source, key=lambda path: os.path.splitext(os.path.basename(path))[0])
msgfmt_available = shutil.which("msgfmt") is not None
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 270f4560b7..65c65a517f 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -716,11 +716,11 @@ void EditorProperty::shortcut_input(const Ref<InputEvent> &p_event) {
const Ref<InputEventKey> k = p_event;
if (k.is_valid() && k->is_pressed()) {
- if (ED_IS_SHORTCUT("property_editor/copy_property", p_event)) {
- menu_option(MENU_COPY_PROPERTY);
+ if (ED_IS_SHORTCUT("property_editor/copy_value", p_event)) {
+ menu_option(MENU_COPY_VALUE);
accept_event();
- } else if (ED_IS_SHORTCUT("property_editor/paste_property", p_event) && !is_read_only()) {
- menu_option(MENU_PASTE_PROPERTY);
+ } else if (ED_IS_SHORTCUT("property_editor/paste_value", p_event) && !is_read_only()) {
+ menu_option(MENU_PASTE_VALUE);
accept_event();
} else if (ED_IS_SHORTCUT("property_editor/copy_property_path", p_event)) {
menu_option(MENU_COPY_PROPERTY_PATH);
@@ -915,10 +915,10 @@ Control *EditorProperty::make_custom_tooltip(const String &p_text) const {
void EditorProperty::menu_option(int p_option) {
switch (p_option) {
- case MENU_COPY_PROPERTY: {
+ case MENU_COPY_VALUE: {
InspectorDock::get_inspector_singleton()->set_property_clipboard(object->get(property));
} break;
- case MENU_PASTE_PROPERTY: {
+ case MENU_PASTE_VALUE: {
emit_changed(property, InspectorDock::get_inspector_singleton()->get_property_clipboard());
} break;
case MENU_COPY_PROPERTY_PATH: {
@@ -1013,10 +1013,10 @@ void EditorProperty::_update_popup() {
add_child(menu);
menu->connect("id_pressed", callable_mp(this, &EditorProperty::menu_option));
}
- menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_GET_SHORTCUT("property_editor/copy_property"), MENU_COPY_PROPERTY);
- menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("property_editor/paste_property"), MENU_PASTE_PROPERTY);
+ menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_GET_SHORTCUT("property_editor/copy_value"), MENU_COPY_VALUE);
+ menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("property_editor/paste_value"), MENU_PASTE_VALUE);
menu->add_icon_shortcut(get_theme_icon(SNAME("CopyNodePath"), SNAME("EditorIcons")), ED_GET_SHORTCUT("property_editor/copy_property_path"), MENU_COPY_PROPERTY_PATH);
- menu->set_item_disabled(MENU_PASTE_PROPERTY, is_read_only());
+ menu->set_item_disabled(MENU_PASTE_VALUE, is_read_only());
if (!pin_hidden) {
menu->add_separator();
if (can_pin) {
@@ -4101,7 +4101,7 @@ EditorInspector::EditorInspector() {
refresh_countdown = 0.33;
}
- ED_SHORTCUT("property_editor/copy_property", TTR("Copy Property"), KeyModifierMask::CMD_OR_CTRL | Key::C);
- ED_SHORTCUT("property_editor/paste_property", TTR("Paste Property"), KeyModifierMask::CMD_OR_CTRL | Key::V);
+ ED_SHORTCUT("property_editor/copy_value", TTR("Copy Value"), KeyModifierMask::CMD_OR_CTRL | Key::C);
+ ED_SHORTCUT("property_editor/paste_value", TTR("Paste Value"), KeyModifierMask::CMD_OR_CTRL | Key::V);
ED_SHORTCUT("property_editor/copy_property_path", TTR("Copy Property Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C);
}
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 872007e637..bada02e254 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -58,8 +58,8 @@ class EditorProperty : public Container {
public:
enum MenuItems {
- MENU_COPY_PROPERTY,
- MENU_PASTE_PROPERTY,
+ MENU_COPY_VALUE,
+ MENU_PASTE_VALUE,
MENU_COPY_PROPERTY_PATH,
MENU_PIN_VALUE,
MENU_OPEN_DOCUMENTATION,
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index de8259c25c..f89aef4cc3 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -155,7 +155,7 @@ void EditorResourcePicker::_file_selected(const String &p_path) {
any_type_matches = is_global_class ? EditorNode::get_editor_data().script_class_is_parent(res_type, base) : loaded_resource->is_class(base);
- if (!any_type_matches) {
+ if (any_type_matches) {
break;
}
}
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 18561fe3a0..4dcd0573e6 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -49,6 +49,7 @@
#include "editor/find_in_files.h"
#include "editor/node_dock.h"
#include "editor/plugins/shader_editor_plugin.h"
+#include "editor/plugins/text_shader_editor.h"
#include "scene/main/window.h"
#include "scene/scene_string_names.h"
#include "script_text_editor.h"
@@ -1124,6 +1125,7 @@ TypedArray<Script> ScriptEditor::_get_open_scripts() const {
bool ScriptEditor::toggle_scripts_panel() {
list_split->set_visible(!list_split->is_visible());
+ EditorSettings::get_singleton()->set_project_metadata("scripts_panel", "show_scripts_panel", list_split->is_visible());
return list_split->is_visible();
}
@@ -3697,6 +3699,7 @@ ScriptEditor::ScriptEditor() {
overview_vbox->set_v_size_flags(SIZE_EXPAND_FILL);
list_split->add_child(overview_vbox);
+ list_split->set_visible(EditorSettings::get_singleton()->get_project_metadata("scripts_panel", "show_scripts_panel", true));
buttons_hbox = memnew(HBoxContainer);
overview_vbox->add_child(buttons_hbox);
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 2eafa4fc91..de1a807721 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -30,1172 +30,12 @@
#include "shader_editor_plugin.h"
-#include "core/io/resource_loader.h"
-#include "core/io/resource_saver.h"
-#include "core/os/keyboard.h"
-#include "core/os/os.h"
-#include "core/version_generated.gen.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
-#include "editor/editor_settings.h"
#include "editor/filesystem_dock.h"
+#include "editor/plugins/text_shader_editor.h"
#include "editor/plugins/visual_shader_editor_plugin.h"
-#include "editor/project_settings_editor.h"
#include "editor/shader_create_dialog.h"
-#include "scene/gui/split_container.h"
-#include "servers/display_server.h"
-#include "servers/rendering/shader_preprocessor.h"
-#include "servers/rendering/shader_types.h"
-
-/*** SHADER SYNTAX HIGHLIGHTER ****/
-
-Dictionary GDShaderSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
- Dictionary color_map;
-
- for (const Point2i &region : disabled_branch_regions) {
- if (p_line >= region.x && p_line <= region.y) {
- Dictionary highlighter_info;
- highlighter_info["color"] = disabled_branch_color;
-
- color_map[0] = highlighter_info;
- return color_map;
- }
- }
-
- return CodeHighlighter::_get_line_syntax_highlighting_impl(p_line);
-}
-
-void GDShaderSyntaxHighlighter::add_disabled_branch_region(const Point2i &p_region) {
- ERR_FAIL_COND(p_region.x < 0);
- ERR_FAIL_COND(p_region.y < 0);
-
- for (int i = 0; i < disabled_branch_regions.size(); i++) {
- ERR_FAIL_COND_MSG(disabled_branch_regions[i].x == p_region.x, "Branch region with a start line '" + itos(p_region.x) + "' already exists.");
- }
-
- Point2i disabled_branch_region;
- disabled_branch_region.x = p_region.x;
- disabled_branch_region.y = p_region.y;
- disabled_branch_regions.push_back(disabled_branch_region);
-
- clear_highlighting_cache();
-}
-
-void GDShaderSyntaxHighlighter::clear_disabled_branch_regions() {
- disabled_branch_regions.clear();
- clear_highlighting_cache();
-}
-
-void GDShaderSyntaxHighlighter::set_disabled_branch_color(const Color &p_color) {
- disabled_branch_color = p_color;
- clear_highlighting_cache();
-}
-
-/*** SHADER SCRIPT EDITOR ****/
-
-static bool saved_warnings_enabled = false;
-static bool saved_treat_warning_as_errors = false;
-static HashMap<ShaderWarning::Code, bool> saved_warnings;
-static uint32_t saved_warning_flags = 0U;
-
-void ShaderTextEditor::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_THEME_CHANGED: {
- if (is_visible_in_tree()) {
- _load_theme_settings();
- if (warnings.size() > 0 && last_compile_result == OK) {
- warnings_panel->clear();
- _update_warning_panel();
- }
- }
- } break;
- }
-}
-
-Ref<Shader> ShaderTextEditor::get_edited_shader() const {
- return shader;
-}
-
-Ref<ShaderInclude> ShaderTextEditor::get_edited_shader_include() const {
- return shader_inc;
-}
-
-void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) {
- set_edited_shader(p_shader, p_shader->get_code());
-}
-
-void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader, const String &p_code) {
- if (shader == p_shader) {
- return;
- }
- if (shader.is_valid()) {
- shader->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
- }
- shader = p_shader;
- shader_inc = Ref<ShaderInclude>();
-
- set_edited_code(p_code);
-
- if (shader.is_valid()) {
- shader->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
- }
-}
-
-void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc) {
- set_edited_shader_include(p_shader_inc, p_shader_inc->get_code());
-}
-
-void ShaderTextEditor::_shader_changed() {
- // This function is used for dependencies (include changing changes main shader and forces it to revalidate)
- if (block_shader_changed) {
- return;
- }
- dependencies_version++;
- _validate_script();
-}
-
-void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc, const String &p_code) {
- if (shader_inc == p_shader_inc) {
- return;
- }
- if (shader_inc.is_valid()) {
- shader_inc->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
- }
- shader_inc = p_shader_inc;
- shader = Ref<Shader>();
-
- set_edited_code(p_code);
-
- if (shader_inc.is_valid()) {
- shader_inc->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
- }
-}
-
-void ShaderTextEditor::set_edited_code(const String &p_code) {
- _load_theme_settings();
-
- get_text_editor()->set_text(p_code);
- get_text_editor()->clear_undo_history();
- get_text_editor()->call_deferred(SNAME("set_h_scroll"), 0);
- get_text_editor()->call_deferred(SNAME("set_v_scroll"), 0);
- get_text_editor()->tag_saved_version();
-
- _validate_script();
- _line_col_changed();
-}
-
-void ShaderTextEditor::reload_text() {
- ERR_FAIL_COND(shader.is_null());
-
- CodeEdit *te = get_text_editor();
- int column = te->get_caret_column();
- int row = te->get_caret_line();
- int h = te->get_h_scroll();
- int v = te->get_v_scroll();
-
- te->set_text(shader->get_code());
- te->set_caret_line(row);
- te->set_caret_column(column);
- te->set_h_scroll(h);
- te->set_v_scroll(v);
-
- te->tag_saved_version();
-
- update_line_and_column();
-}
-
-void ShaderTextEditor::set_warnings_panel(RichTextLabel *p_warnings_panel) {
- warnings_panel = p_warnings_panel;
-}
-
-void ShaderTextEditor::_load_theme_settings() {
- CodeEdit *text_editor = get_text_editor();
- Color updated_marked_line_color = EDITOR_GET("text_editor/theme/highlighting/mark_color");
- if (updated_marked_line_color != marked_line_color) {
- for (int i = 0; i < text_editor->get_line_count(); i++) {
- if (text_editor->get_line_background_color(i) == marked_line_color) {
- text_editor->set_line_background_color(i, updated_marked_line_color);
- }
- }
- marked_line_color = updated_marked_line_color;
- }
-
- syntax_highlighter->set_number_color(EDITOR_GET("text_editor/theme/highlighting/number_color"));
- syntax_highlighter->set_symbol_color(EDITOR_GET("text_editor/theme/highlighting/symbol_color"));
- syntax_highlighter->set_function_color(EDITOR_GET("text_editor/theme/highlighting/function_color"));
- syntax_highlighter->set_member_variable_color(EDITOR_GET("text_editor/theme/highlighting/member_variable_color"));
-
- syntax_highlighter->clear_keyword_colors();
-
- const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
- const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
-
- List<String> keywords;
- ShaderLanguage::get_keyword_list(&keywords);
-
- for (const String &E : keywords) {
- if (ShaderLanguage::is_control_flow_keyword(E)) {
- syntax_highlighter->add_keyword_color(E, control_flow_keyword_color);
- } else {
- syntax_highlighter->add_keyword_color(E, keyword_color);
- }
- }
-
- List<String> pp_keywords;
- ShaderPreprocessor::get_keyword_list(&pp_keywords, false);
-
- for (const String &E : pp_keywords) {
- syntax_highlighter->add_keyword_color(E, keyword_color);
- }
-
- // Colorize built-ins like `COLOR` differently to make them easier
- // to distinguish from keywords at a quick glance.
-
- List<String> built_ins;
-
- if (shader_inc.is_valid()) {
- for (int i = 0; i < RenderingServer::SHADER_MAX; i++) {
- for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(i))) {
- for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) {
- built_ins.push_back(F.key);
- }
- }
-
- const Vector<ShaderLanguage::ModeInfo> &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i));
-
- for (int j = 0; j < modes.size(); j++) {
- const ShaderLanguage::ModeInfo &info = modes[j];
-
- if (!info.options.is_empty()) {
- for (int k = 0; k < info.options.size(); k++) {
- built_ins.push_back(String(info.name) + "_" + String(info.options[k]));
- }
- } else {
- built_ins.push_back(String(info.name));
- }
- }
- }
- } else if (shader.is_valid()) {
- for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()))) {
- for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) {
- built_ins.push_back(F.key);
- }
- }
-
- const Vector<ShaderLanguage::ModeInfo> &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
-
- for (int i = 0; i < modes.size(); i++) {
- const ShaderLanguage::ModeInfo &info = modes[i];
-
- if (!info.options.is_empty()) {
- for (int j = 0; j < info.options.size(); j++) {
- built_ins.push_back(String(info.name) + "_" + String(info.options[j]));
- }
- } else {
- built_ins.push_back(String(info.name));
- }
- }
- }
-
- const Color user_type_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color");
-
- for (const String &E : built_ins) {
- syntax_highlighter->add_keyword_color(E, user_type_color);
- }
-
- // Colorize comments.
- const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
- syntax_highlighter->clear_color_regions();
- syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
- syntax_highlighter->add_color_region("//", "", comment_color, true);
- syntax_highlighter->set_disabled_branch_color(comment_color);
-
- text_editor->clear_comment_delimiters();
- text_editor->add_comment_delimiter("/*", "*/", false);
- text_editor->add_comment_delimiter("//", "", true);
-
- if (!text_editor->has_auto_brace_completion_open_key("/*")) {
- text_editor->add_auto_brace_completion_pair("/*", "*/");
- }
-
- // Colorize preprocessor include strings.
- const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color");
- syntax_highlighter->add_color_region("\"", "\"", string_color, false);
-
- if (warnings_panel) {
- // Warnings panel.
- warnings_panel->add_theme_font_override("normal_font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("main"), SNAME("EditorFonts")));
- warnings_panel->add_theme_font_size_override("normal_font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("main_size"), SNAME("EditorFonts")));
- }
-}
-
-void ShaderTextEditor::_check_shader_mode() {
- String type = ShaderLanguage::get_shader_type(get_text_editor()->get_text());
-
- Shader::Mode mode;
-
- if (type == "canvas_item") {
- mode = Shader::MODE_CANVAS_ITEM;
- } else if (type == "particles") {
- mode = Shader::MODE_PARTICLES;
- } else if (type == "sky") {
- mode = Shader::MODE_SKY;
- } else if (type == "fog") {
- mode = Shader::MODE_FOG;
- } else {
- mode = Shader::MODE_SPATIAL;
- }
-
- if (shader->get_mode() != mode) {
- set_block_shader_changed(true);
- shader->set_code(get_text_editor()->get_text());
- set_block_shader_changed(false);
- _load_theme_settings();
- }
-}
-
-static ShaderLanguage::DataType _get_global_shader_uniform_type(const StringName &p_variable) {
- RS::GlobalShaderParameterType gvt = RS::get_singleton()->global_shader_parameter_get_type(p_variable);
- return (ShaderLanguage::DataType)RS::global_shader_uniform_type_get_shader_datatype(gvt);
-}
-
-static String complete_from_path;
-
-static void _complete_include_paths_search(EditorFileSystemDirectory *p_efsd, List<ScriptLanguage::CodeCompletionOption> *r_options) {
- if (!p_efsd) {
- return;
- }
- for (int i = 0; i < p_efsd->get_file_count(); i++) {
- if (p_efsd->get_file_type(i) == SNAME("ShaderInclude")) {
- String path = p_efsd->get_file_path(i);
- if (path.begins_with(complete_from_path)) {
- path = path.replace_first(complete_from_path, "");
- }
- r_options->push_back(ScriptLanguage::CodeCompletionOption(path, ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH));
- }
- }
- for (int j = 0; j < p_efsd->get_subdir_count(); j++) {
- _complete_include_paths_search(p_efsd->get_subdir(j), r_options);
- }
-}
-
-static void _complete_include_paths(List<ScriptLanguage::CodeCompletionOption> *r_options) {
- _complete_include_paths_search(EditorFileSystem::get_singleton()->get_filesystem(), r_options);
-}
-
-void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) {
- List<ScriptLanguage::CodeCompletionOption> pp_options;
- List<ScriptLanguage::CodeCompletionOption> pp_defines;
- ShaderPreprocessor preprocessor;
- String code;
- complete_from_path = (shader.is_valid() ? shader->get_path() : shader_inc->get_path()).get_base_dir();
- if (!complete_from_path.ends_with("/")) {
- complete_from_path += "/";
- }
- preprocessor.preprocess(p_code, "", code, nullptr, nullptr, nullptr, nullptr, &pp_options, &pp_defines, _complete_include_paths);
- complete_from_path = String();
- if (pp_options.size()) {
- for (const ScriptLanguage::CodeCompletionOption &E : pp_options) {
- r_options->push_back(E);
- }
- return;
- }
- for (const ScriptLanguage::CodeCompletionOption &E : pp_defines) {
- r_options->push_back(E);
- }
-
- ShaderLanguage sl;
- String calltip;
- ShaderLanguage::ShaderCompileInfo info;
- info.global_shader_uniform_type_func = _get_global_shader_uniform_type;
-
- if (shader.is_null()) {
- info.is_include = true;
-
- sl.complete(code, info, r_options, calltip);
- get_text_editor()->set_code_hint(calltip);
- return;
- }
- _check_shader_mode();
- info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()));
- info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
- info.shader_types = ShaderTypes::get_singleton()->get_types();
-
- sl.complete(code, info, r_options, calltip);
- get_text_editor()->set_code_hint(calltip);
-}
-
-void ShaderTextEditor::_validate_script() {
- emit_signal(SNAME("script_changed")); // Ensure to notify that it changed, so it is applied
-
- String code;
-
- if (shader.is_valid()) {
- _check_shader_mode();
- code = shader->get_code();
- } else {
- code = shader_inc->get_code();
- }
-
- ShaderPreprocessor preprocessor;
- String code_pp;
- String error_pp;
- List<ShaderPreprocessor::FilePosition> err_positions;
- List<ShaderPreprocessor::Region> regions;
- String filename;
- if (shader.is_valid()) {
- filename = shader->get_path();
- } else if (shader_inc.is_valid()) {
- filename = shader_inc->get_path();
- }
- last_compile_result = preprocessor.preprocess(code, filename, code_pp, &error_pp, &err_positions, &regions);
-
- for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
- get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));
- }
-
- syntax_highlighter->clear_disabled_branch_regions();
- for (const ShaderPreprocessor::Region &region : regions) {
- if (!region.enabled) {
- if (filename != region.file) {
- continue;
- }
- syntax_highlighter->add_disabled_branch_region(Point2i(region.from_line, region.to_line));
- }
- }
-
- set_error("");
- set_error_count(0);
-
- if (last_compile_result != OK) {
- //preprocessor error
- ERR_FAIL_COND(err_positions.size() == 0);
-
- String error_text = error_pp;
- int error_line = err_positions.front()->get().line;
- if (err_positions.size() == 1) {
- // Error in main file
- error_text = "error(" + itos(error_line) + "): " + error_text;
- } else {
- error_text = "error(" + itos(error_line) + ") in include " + err_positions.back()->get().file.get_file() + ":" + itos(err_positions.back()->get().line) + ": " + error_text;
- set_error_count(err_positions.size() - 1);
- }
-
- set_error(error_text);
- set_error_pos(error_line - 1, 0);
- for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
- get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));
- }
- get_text_editor()->set_line_background_color(error_line - 1, marked_line_color);
-
- set_warning_count(0);
-
- } else {
- ShaderLanguage sl;
-
- sl.enable_warning_checking(saved_warnings_enabled);
- uint32_t flags = saved_warning_flags;
- if (shader.is_null()) {
- if (flags & ShaderWarning::UNUSED_CONSTANT) {
- flags &= ~(ShaderWarning::UNUSED_CONSTANT);
- }
- if (flags & ShaderWarning::UNUSED_FUNCTION) {
- flags &= ~(ShaderWarning::UNUSED_FUNCTION);
- }
- if (flags & ShaderWarning::UNUSED_STRUCT) {
- flags &= ~(ShaderWarning::UNUSED_STRUCT);
- }
- if (flags & ShaderWarning::UNUSED_UNIFORM) {
- flags &= ~(ShaderWarning::UNUSED_UNIFORM);
- }
- if (flags & ShaderWarning::UNUSED_VARYING) {
- flags &= ~(ShaderWarning::UNUSED_VARYING);
- }
- }
- sl.set_warning_flags(flags);
-
- ShaderLanguage::ShaderCompileInfo info;
- info.global_shader_uniform_type_func = _get_global_shader_uniform_type;
-
- if (shader.is_null()) {
- info.is_include = true;
- } else {
- Shader::Mode mode = shader->get_mode();
- info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(mode));
- info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(mode));
- info.shader_types = ShaderTypes::get_singleton()->get_types();
- }
-
- code = code_pp;
- //compiler error
- last_compile_result = sl.compile(code, info);
-
- if (last_compile_result != OK) {
- String error_text;
- int error_line;
- Vector<ShaderLanguage::FilePosition> include_positions = sl.get_include_positions();
- if (include_positions.size() > 1) {
- //error is in an include
- error_line = include_positions[0].line;
- error_text = "error(" + itos(error_line) + ") in include " + include_positions[include_positions.size() - 1].file + ":" + itos(include_positions[include_positions.size() - 1].line) + ": " + sl.get_error_text();
- set_error_count(include_positions.size() - 1);
- } else {
- error_line = sl.get_error_line();
- error_text = "error(" + itos(error_line) + "): " + sl.get_error_text();
- set_error_count(0);
- }
- set_error(error_text);
- set_error_pos(error_line - 1, 0);
- get_text_editor()->set_line_background_color(error_line - 1, marked_line_color);
- } else {
- set_error("");
- }
-
- if (warnings.size() > 0 || last_compile_result != OK) {
- warnings_panel->clear();
- }
- warnings.clear();
- for (List<ShaderWarning>::Element *E = sl.get_warnings_ptr(); E; E = E->next()) {
- warnings.push_back(E->get());
- }
- if (warnings.size() > 0 && last_compile_result == OK) {
- warnings.sort_custom<WarningsComparator>();
- _update_warning_panel();
- } else {
- set_warning_count(0);
- }
- }
-
- emit_signal(SNAME("script_validated"), last_compile_result == OK); // Notify that validation finished, to update the list of scripts
-}
-
-void ShaderTextEditor::_update_warning_panel() {
- int warning_count = 0;
-
- warnings_panel->push_table(2);
- for (int i = 0; i < warnings.size(); i++) {
- ShaderWarning &w = warnings[i];
-
- if (warning_count == 0) {
- if (saved_treat_warning_as_errors) {
- String error_text = "error(" + itos(w.get_line()) + "): " + w.get_message() + " " + TTR("Warnings should be fixed to prevent errors.");
- set_error_pos(w.get_line() - 1, 0);
- set_error(error_text);
- get_text_editor()->set_line_background_color(w.get_line() - 1, marked_line_color);
- }
- }
-
- warning_count++;
- int line = w.get_line();
-
- // First cell.
- warnings_panel->push_cell();
- warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), SNAME("Editor")));
- if (line != -1) {
- warnings_panel->push_meta(line - 1);
- warnings_panel->add_text(TTR("Line") + " " + itos(line));
- warnings_panel->add_text(" (" + w.get_name() + "):");
- warnings_panel->pop(); // Meta goto.
- } else {
- warnings_panel->add_text(w.get_name() + ":");
- }
- warnings_panel->pop(); // Color.
- warnings_panel->pop(); // Cell.
-
- // Second cell.
- warnings_panel->push_cell();
- warnings_panel->add_text(w.get_message());
- warnings_panel->pop(); // Cell.
- }
- warnings_panel->pop(); // Table.
-
- set_warning_count(warning_count);
-}
-
-void ShaderTextEditor::_bind_methods() {
- ADD_SIGNAL(MethodInfo("script_validated", PropertyInfo(Variant::BOOL, "valid")));
-}
-
-ShaderTextEditor::ShaderTextEditor() {
- syntax_highlighter.instantiate();
- get_text_editor()->set_syntax_highlighter(syntax_highlighter);
-}
-
-/*** SCRIPT EDITOR ******/
-
-void ShaderEditor::_menu_option(int p_option) {
- switch (p_option) {
- case EDIT_UNDO: {
- shader_editor->get_text_editor()->undo();
- } break;
- case EDIT_REDO: {
- shader_editor->get_text_editor()->redo();
- } break;
- case EDIT_CUT: {
- shader_editor->get_text_editor()->cut();
- } break;
- case EDIT_COPY: {
- shader_editor->get_text_editor()->copy();
- } break;
- case EDIT_PASTE: {
- shader_editor->get_text_editor()->paste();
- } break;
- case EDIT_SELECT_ALL: {
- shader_editor->get_text_editor()->select_all();
- } break;
- case EDIT_MOVE_LINE_UP: {
- shader_editor->move_lines_up();
- } break;
- case EDIT_MOVE_LINE_DOWN: {
- shader_editor->move_lines_down();
- } break;
- case EDIT_INDENT: {
- if (shader.is_null()) {
- return;
- }
- shader_editor->get_text_editor()->indent_lines();
- } break;
- case EDIT_UNINDENT: {
- if (shader.is_null()) {
- return;
- }
- shader_editor->get_text_editor()->unindent_lines();
- } break;
- case EDIT_DELETE_LINE: {
- shader_editor->delete_lines();
- } break;
- case EDIT_DUPLICATE_SELECTION: {
- shader_editor->duplicate_selection();
- } break;
- case EDIT_TOGGLE_COMMENT: {
- if (shader.is_null()) {
- return;
- }
-
- shader_editor->toggle_inline_comment("//");
-
- } break;
- case EDIT_COMPLETE: {
- shader_editor->get_text_editor()->request_code_completion();
- } break;
- case SEARCH_FIND: {
- shader_editor->get_find_replace_bar()->popup_search();
- } break;
- case SEARCH_FIND_NEXT: {
- shader_editor->get_find_replace_bar()->search_next();
- } break;
- case SEARCH_FIND_PREV: {
- shader_editor->get_find_replace_bar()->search_prev();
- } break;
- case SEARCH_REPLACE: {
- shader_editor->get_find_replace_bar()->popup_replace();
- } break;
- case SEARCH_GOTO_LINE: {
- goto_line_dialog->popup_find_line(shader_editor->get_text_editor());
- } break;
- case BOOKMARK_TOGGLE: {
- shader_editor->toggle_bookmark();
- } break;
- case BOOKMARK_GOTO_NEXT: {
- shader_editor->goto_next_bookmark();
- } break;
- case BOOKMARK_GOTO_PREV: {
- shader_editor->goto_prev_bookmark();
- } break;
- case BOOKMARK_REMOVE_ALL: {
- shader_editor->remove_all_bookmarks();
- } break;
- case HELP_DOCS: {
- OS::get_singleton()->shell_open(vformat("%s/tutorials/shaders/shader_reference/index.html", VERSION_DOCS_URL));
- } break;
- }
- if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) {
- shader_editor->get_text_editor()->call_deferred(SNAME("grab_focus"));
- }
-}
-
-void ShaderEditor::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
- case NOTIFICATION_THEME_CHANGED: {
- PopupMenu *popup = help_menu->get_popup();
- popup->set_item_icon(popup->get_item_index(HELP_DOCS), get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")));
- } break;
-
- case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
- _check_for_external_edit();
- } break;
- }
-}
-
-void ShaderEditor::_editor_settings_changed() {
- shader_editor->update_editor_settings();
-
- shader_editor->get_text_editor()->add_theme_constant_override("line_spacing", EditorSettings::get_singleton()->get("text_editor/appearance/whitespace/line_spacing"));
- shader_editor->get_text_editor()->set_draw_breakpoints_gutter(false);
- shader_editor->get_text_editor()->set_draw_executing_lines_gutter(false);
-}
-
-void ShaderEditor::_show_warnings_panel(bool p_show) {
- warnings_panel->set_visible(p_show);
-}
-
-void ShaderEditor::_warning_clicked(Variant p_line) {
- if (p_line.get_type() == Variant::INT) {
- shader_editor->get_text_editor()->set_caret_line(p_line.operator int64_t());
- }
-}
-
-void ShaderEditor::_bind_methods() {
- ClassDB::bind_method("_show_warnings_panel", &ShaderEditor::_show_warnings_panel);
- ClassDB::bind_method("_warning_clicked", &ShaderEditor::_warning_clicked);
-
- ADD_SIGNAL(MethodInfo("validation_changed"));
-}
-
-void ShaderEditor::ensure_select_current() {
-}
-
-void ShaderEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
- shader_editor->goto_line_selection(p_line, p_begin, p_end);
-}
-
-void ShaderEditor::_project_settings_changed() {
- _update_warnings(true);
-}
-
-void ShaderEditor::_update_warnings(bool p_validate) {
- bool changed = false;
-
- bool warnings_enabled = GLOBAL_GET("debug/shader_language/warnings/enable").booleanize();
- if (warnings_enabled != saved_warnings_enabled) {
- saved_warnings_enabled = warnings_enabled;
- changed = true;
- }
-
- bool treat_warning_as_errors = GLOBAL_GET("debug/shader_language/warnings/treat_warnings_as_errors").booleanize();
- if (treat_warning_as_errors != saved_treat_warning_as_errors) {
- saved_treat_warning_as_errors = treat_warning_as_errors;
- changed = true;
- }
-
- bool update_flags = false;
-
- for (int i = 0; i < ShaderWarning::WARNING_MAX; i++) {
- ShaderWarning::Code code = (ShaderWarning::Code)i;
- bool value = GLOBAL_GET("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code(code).to_lower());
-
- if (saved_warnings[code] != value) {
- saved_warnings[code] = value;
- update_flags = true;
- changed = true;
- }
- }
-
- if (update_flags) {
- saved_warning_flags = (uint32_t)ShaderWarning::get_flags_from_codemap(saved_warnings);
- }
-
- if (p_validate && changed && shader_editor && shader_editor->get_edited_shader().is_valid()) {
- shader_editor->validate_script();
- }
-}
-
-void ShaderEditor::_check_for_external_edit() {
- bool use_autoreload = bool(EDITOR_GET("text_editor/behavior/files/auto_reload_scripts_on_external_change"));
-
- if (shader_inc.is_valid()) {
- if (shader_inc->get_last_modified_time() != FileAccess::get_modified_time(shader_inc->get_path())) {
- if (use_autoreload) {
- _reload_shader_include_from_disk();
- } else {
- disk_changed->call_deferred(SNAME("popup_centered"));
- }
- }
- return;
- }
-
- if (shader.is_null() || shader->is_built_in()) {
- return;
- }
-
- if (shader->get_last_modified_time() != FileAccess::get_modified_time(shader->get_path())) {
- if (use_autoreload) {
- _reload_shader_from_disk();
- } else {
- disk_changed->call_deferred(SNAME("popup_centered"));
- }
- }
-}
-
-void ShaderEditor::_reload_shader_from_disk() {
- Ref<Shader> rel_shader = ResourceLoader::load(shader->get_path(), shader->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
- ERR_FAIL_COND(!rel_shader.is_valid());
-
- shader_editor->set_block_shader_changed(true);
- shader->set_code(rel_shader->get_code());
- shader_editor->set_block_shader_changed(false);
- shader->set_last_modified_time(rel_shader->get_last_modified_time());
- shader_editor->reload_text();
-}
-
-void ShaderEditor::_reload_shader_include_from_disk() {
- Ref<ShaderInclude> rel_shader_include = ResourceLoader::load(shader_inc->get_path(), shader_inc->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
- ERR_FAIL_COND(!rel_shader_include.is_valid());
-
- shader_editor->set_block_shader_changed(true);
- shader_inc->set_code(rel_shader_include->get_code());
- shader_editor->set_block_shader_changed(false);
- shader_inc->set_last_modified_time(rel_shader_include->get_last_modified_time());
- shader_editor->reload_text();
-}
-
-void ShaderEditor::_reload() {
- if (shader.is_valid()) {
- _reload_shader_from_disk();
- } else if (shader_inc.is_valid()) {
- _reload_shader_include_from_disk();
- }
-}
-
-void ShaderEditor::edit(const Ref<Shader> &p_shader) {
- if (p_shader.is_null() || !p_shader->is_text_shader()) {
- return;
- }
-
- if (shader == p_shader) {
- return;
- }
-
- shader = p_shader;
- shader_inc = Ref<ShaderInclude>();
-
- shader_editor->set_edited_shader(shader);
-}
-
-void ShaderEditor::edit(const Ref<ShaderInclude> &p_shader_inc) {
- if (p_shader_inc.is_null()) {
- return;
- }
-
- if (shader_inc == p_shader_inc) {
- return;
- }
-
- shader_inc = p_shader_inc;
- shader = Ref<Shader>();
-
- shader_editor->set_edited_shader_include(p_shader_inc);
-}
-
-void ShaderEditor::save_external_data(const String &p_str) {
- if (shader.is_null() && shader_inc.is_null()) {
- disk_changed->hide();
- return;
- }
-
- apply_shaders();
-
- Ref<Shader> edited_shader = shader_editor->get_edited_shader();
- if (edited_shader.is_valid()) {
- ResourceSaver::save(edited_shader);
- }
- if (shader.is_valid() && shader != edited_shader) {
- ResourceSaver::save(shader);
- }
-
- Ref<ShaderInclude> edited_shader_inc = shader_editor->get_edited_shader_include();
- if (edited_shader_inc.is_valid()) {
- ResourceSaver::save(edited_shader_inc);
- }
- if (shader_inc.is_valid() && shader_inc != edited_shader_inc) {
- ResourceSaver::save(shader_inc);
- }
- shader_editor->get_text_editor()->tag_saved_version();
-
- disk_changed->hide();
-}
-
-void ShaderEditor::validate_script() {
- shader_editor->_validate_script();
-}
-
-bool ShaderEditor::is_unsaved() const {
- return shader_editor->get_text_editor()->get_saved_version() != shader_editor->get_text_editor()->get_version();
-}
-
-void ShaderEditor::apply_shaders() {
- String editor_code = shader_editor->get_text_editor()->get_text();
- if (shader.is_valid()) {
- String shader_code = shader->get_code();
- if (shader_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) {
- shader_editor->set_block_shader_changed(true);
- shader->set_code(editor_code);
- shader_editor->set_block_shader_changed(false);
- shader->set_edited(true);
- }
- }
- if (shader_inc.is_valid()) {
- String shader_inc_code = shader_inc->get_code();
- if (shader_inc_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) {
- shader_editor->set_block_shader_changed(true);
- shader_inc->set_code(editor_code);
- shader_editor->set_block_shader_changed(false);
- shader_inc->set_edited(true);
- }
- }
-
- dependencies_version = shader_editor->get_dependencies_version();
-}
-
-void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
- Ref<InputEventMouseButton> mb = ev;
-
- if (mb.is_valid()) {
- if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
- CodeEdit *tx = shader_editor->get_text_editor();
-
- Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position());
- int row = pos.y;
- int col = pos.x;
- tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/move_caret_on_right_click"));
-
- if (tx->is_move_caret_on_right_click_enabled()) {
- if (tx->has_selection()) {
- int from_line = tx->get_selection_from_line();
- int to_line = tx->get_selection_to_line();
- int from_column = tx->get_selection_from_column();
- int to_column = tx->get_selection_to_column();
-
- if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) {
- // Right click is outside the selected text
- tx->deselect();
- }
- }
- if (!tx->has_selection()) {
- tx->set_caret_line(row, true, false);
- tx->set_caret_column(col);
- }
- }
- _make_context_menu(tx->has_selection(), get_local_mouse_position());
- }
- }
-
- Ref<InputEventKey> k = ev;
- if (k.is_valid() && k->is_pressed() && k->is_action("ui_menu", true)) {
- CodeEdit *tx = shader_editor->get_text_editor();
- tx->adjust_viewport_to_caret();
- _make_context_menu(tx->has_selection(), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos()));
- context_menu->grab_focus();
- }
-}
-
-void ShaderEditor::_update_bookmark_list() {
- bookmarks_menu->clear();
-
- bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);
- bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL);
- bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT);
- bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV);
-
- PackedInt32Array bookmark_list = shader_editor->get_text_editor()->get_bookmarked_lines();
- if (bookmark_list.size() == 0) {
- return;
- }
-
- bookmarks_menu->add_separator();
-
- for (int i = 0; i < bookmark_list.size(); i++) {
- String line = shader_editor->get_text_editor()->get_line(bookmark_list[i]).strip_edges();
- // Limit the size of the line if too big.
- if (line.length() > 50) {
- line = line.substr(0, 50);
- }
-
- bookmarks_menu->add_item(String::num((int)bookmark_list[i] + 1) + " - \"" + line + "\"");
- bookmarks_menu->set_item_metadata(-1, bookmark_list[i]);
- }
-}
-
-void ShaderEditor::_bookmark_item_pressed(int p_idx) {
- if (p_idx < 4) { // Any item before the separator.
- _menu_option(bookmarks_menu->get_item_id(p_idx));
- } else {
- shader_editor->goto_line(bookmarks_menu->get_item_metadata(p_idx));
- }
-}
-
-void ShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) {
- context_menu->clear();
- if (p_selection) {
- context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT);
- context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY);
- }
-
- context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE);
- context_menu->add_separator();
- context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL);
- context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);
- context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);
-
- context_menu->add_separator();
- context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT);
- context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT);
- context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
- context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);
-
- context_menu->set_position(get_screen_position() + p_position);
- context_menu->reset_size();
- context_menu->popup();
-}
-
-ShaderEditor::ShaderEditor() {
- GLOBAL_DEF("debug/shader_language/warnings/enable", true);
- GLOBAL_DEF("debug/shader_language/warnings/treat_warnings_as_errors", false);
- for (int i = 0; i < (int)ShaderWarning::WARNING_MAX; i++) {
- GLOBAL_DEF("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code((ShaderWarning::Code)i).to_lower(), true);
- }
- _update_warnings(false);
-
- shader_editor = memnew(ShaderTextEditor);
-
- shader_editor->connect("script_validated", callable_mp(this, &ShaderEditor::_script_validated));
-
- shader_editor->set_v_size_flags(SIZE_EXPAND_FILL);
- shader_editor->add_theme_constant_override("separation", 0);
- shader_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
-
- shader_editor->connect("show_warnings_panel", callable_mp(this, &ShaderEditor::_show_warnings_panel));
- shader_editor->connect("script_changed", callable_mp(this, &ShaderEditor::apply_shaders));
- EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ShaderEditor::_editor_settings_changed));
- ProjectSettingsEditor::get_singleton()->connect("confirmed", callable_mp(this, &ShaderEditor::_project_settings_changed));
-
- shader_editor->get_text_editor()->set_code_hint_draw_below(EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line"));
-
- shader_editor->get_text_editor()->set_symbol_lookup_on_click_enabled(true);
- shader_editor->get_text_editor()->set_context_menu_enabled(false);
- shader_editor->get_text_editor()->connect("gui_input", callable_mp(this, &ShaderEditor::_text_edit_gui_input));
-
- shader_editor->update_editor_settings();
-
- context_menu = memnew(PopupMenu);
- add_child(context_menu);
- context_menu->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option));
-
- VBoxContainer *main_container = memnew(VBoxContainer);
- HBoxContainer *hbc = memnew(HBoxContainer);
-
- edit_menu = memnew(MenuButton);
- edit_menu->set_shortcut_context(this);
- edit_menu->set_text(TTR("Edit"));
- edit_menu->set_switch_on_hover(true);
-
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);
- edit_menu->get_popup()->add_separator();
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE);
- edit_menu->get_popup()->add_separator();
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL);
- edit_menu->get_popup()->add_separator();
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_selection"), EDIT_DUPLICATE_SELECTION);
- edit_menu->get_popup()->add_separator();
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE);
- edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option));
-
- search_menu = memnew(MenuButton);
- search_menu->set_shortcut_context(this);
- search_menu->set_text(TTR("Search"));
- search_menu->set_switch_on_hover(true);
-
- search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND);
- search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT);
- search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV);
- search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE);
- search_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option));
-
- MenuButton *goto_menu = memnew(MenuButton);
- goto_menu->set_shortcut_context(this);
- goto_menu->set_text(TTR("Go To"));
- goto_menu->set_switch_on_hover(true);
- goto_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option));
-
- goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
- goto_menu->get_popup()->add_separator();
-
- bookmarks_menu = memnew(PopupMenu);
- bookmarks_menu->set_name("Bookmarks");
- goto_menu->get_popup()->add_child(bookmarks_menu);
- goto_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "Bookmarks");
- _update_bookmark_list();
- bookmarks_menu->connect("about_to_popup", callable_mp(this, &ShaderEditor::_update_bookmark_list));
- bookmarks_menu->connect("index_pressed", callable_mp(this, &ShaderEditor::_bookmark_item_pressed));
-
- help_menu = memnew(MenuButton);
- help_menu->set_text(TTR("Help"));
- help_menu->set_switch_on_hover(true);
- help_menu->get_popup()->add_item(TTR("Online Docs"), HELP_DOCS);
- help_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option));
-
- add_child(main_container);
- main_container->add_child(hbc);
- hbc->add_child(search_menu);
- hbc->add_child(edit_menu);
- hbc->add_child(goto_menu);
- hbc->add_child(help_menu);
- hbc->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("ScriptEditorPanel"), SNAME("EditorStyles")));
-
- VSplitContainer *editor_box = memnew(VSplitContainer);
- main_container->add_child(editor_box);
- editor_box->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
- editor_box->set_v_size_flags(SIZE_EXPAND_FILL);
- editor_box->add_child(shader_editor);
-
- FindReplaceBar *bar = memnew(FindReplaceBar);
- main_container->add_child(bar);
- bar->hide();
- shader_editor->set_find_replace_bar(bar);
-
- warnings_panel = memnew(RichTextLabel);
- warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE));
- warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL);
- warnings_panel->set_meta_underline(true);
- warnings_panel->set_selection_enabled(true);
- warnings_panel->set_focus_mode(FOCUS_CLICK);
- warnings_panel->hide();
- warnings_panel->connect("meta_clicked", callable_mp(this, &ShaderEditor::_warning_clicked));
- editor_box->add_child(warnings_panel);
- shader_editor->set_warnings_panel(warnings_panel);
-
- goto_line_dialog = memnew(GotoLineDialog);
- add_child(goto_line_dialog);
-
- disk_changed = memnew(ConfirmationDialog);
-
- VBoxContainer *vbc = memnew(VBoxContainer);
- disk_changed->add_child(vbc);
-
- Label *dl = memnew(Label);
- dl->set_text(TTR("This shader has been modified on disk.\nWhat action should be taken?"));
- vbc->add_child(dl);
-
- disk_changed->connect("confirmed", callable_mp(this, &ShaderEditor::_reload));
- disk_changed->set_ok_button_text(TTR("Reload"));
-
- disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
- disk_changed->connect("custom_action", callable_mp(this, &ShaderEditor::save_external_data));
-
- add_child(disk_changed);
-
- _editor_settings_changed();
-}
void ShaderEditorPlugin::_update_shader_list() {
shader_list->clear();
@@ -1241,7 +81,7 @@ void ShaderEditorPlugin::_update_shader_list() {
shader_list->select(shader_tabs->get_current_tab());
}
- for (int i = 1; i < FILE_MAX; i++) {
+ for (int i = FILE_SAVE; i < FILE_MAX; i++) {
file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), edited_shaders.size() == 0);
}
@@ -1250,7 +90,7 @@ void ShaderEditorPlugin::_update_shader_list() {
void ShaderEditorPlugin::_update_shader_list_status() {
for (int i = 0; i < shader_list->get_item_count(); i++) {
- ShaderEditor *se = Object::cast_to<ShaderEditor>(shader_tabs->get_tab_control(i));
+ TextShaderEditor *se = Object::cast_to<TextShaderEditor>(shader_tabs->get_tab_control(i));
if (se) {
if (se->was_compilation_successful()) {
shader_list->set_item_tag_icon(i, Ref<Texture2D>());
@@ -1285,7 +125,7 @@ void ShaderEditorPlugin::edit(Object *p_object) {
}
}
es.shader_inc = Ref<ShaderInclude>(si);
- es.shader_editor = memnew(ShaderEditor);
+ es.shader_editor = memnew(TextShaderEditor);
es.shader_editor->edit(si);
shader_tabs->add_child(es.shader_editor);
es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list));
@@ -1305,7 +145,7 @@ void ShaderEditorPlugin::edit(Object *p_object) {
shader_tabs->add_child(es.visual_shader_editor);
es.visual_shader_editor->edit(vs.ptr());
} else {
- es.shader_editor = memnew(ShaderEditor);
+ es.shader_editor = memnew(TextShaderEditor);
shader_tabs->add_child(es.shader_editor);
es.shader_editor->edit(s);
es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list));
@@ -1330,7 +170,7 @@ void ShaderEditorPlugin::make_visible(bool p_visible) {
void ShaderEditorPlugin::selected_notify() {
}
-ShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) {
+TextShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) {
for (uint32_t i = 0; i < edited_shaders.size(); i++) {
if (edited_shaders[i].shader == p_for_shader) {
return edited_shaders[i].shader_editor;
@@ -1592,7 +432,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
file_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditorPlugin::_menu_item_pressed));
file_hb->add_child(file_menu);
- for (int i = 2; i < FILE_MAX; i++) {
+ for (int i = FILE_SAVE; i < FILE_MAX; i++) {
file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), true);
}
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index 5188639bfc..7638778af7 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -31,181 +31,14 @@
#ifndef SHADER_EDITOR_PLUGIN_H
#define SHADER_EDITOR_PLUGIN_H
-#include "editor/code_editor.h"
#include "editor/editor_plugin.h"
-#include "scene/gui/menu_button.h"
-#include "scene/gui/panel_container.h"
-#include "scene/gui/rich_text_label.h"
-#include "scene/gui/tab_container.h"
-#include "scene/gui/text_edit.h"
-#include "scene/main/timer.h"
-#include "scene/resources/shader.h"
-#include "scene/resources/shader_include.h"
-#include "servers/rendering/shader_warnings.h"
-class ItemList;
-class VisualShaderEditor;
class HSplitContainer;
+class ItemList;
class ShaderCreateDialog;
-
-class GDShaderSyntaxHighlighter : public CodeHighlighter {
- GDCLASS(GDShaderSyntaxHighlighter, CodeHighlighter)
-
-private:
- Vector<Point2i> disabled_branch_regions;
- Color disabled_branch_color;
-
-public:
- virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override;
-
- void add_disabled_branch_region(const Point2i &p_region);
- void clear_disabled_branch_regions();
- void set_disabled_branch_color(const Color &p_color);
-};
-
-class ShaderTextEditor : public CodeTextEditor {
- GDCLASS(ShaderTextEditor, CodeTextEditor);
-
- Color marked_line_color = Color(1, 1, 1);
-
- struct WarningsComparator {
- _ALWAYS_INLINE_ bool operator()(const ShaderWarning &p_a, const ShaderWarning &p_b) const { return (p_a.get_line() < p_b.get_line()); }
- };
-
- Ref<GDShaderSyntaxHighlighter> syntax_highlighter;
- RichTextLabel *warnings_panel = nullptr;
- Ref<Shader> shader;
- Ref<ShaderInclude> shader_inc;
- List<ShaderWarning> warnings;
- Error last_compile_result = Error::OK;
-
- void _check_shader_mode();
- void _update_warning_panel();
-
- bool block_shader_changed = false;
- void _shader_changed();
-
- uint32_t dependencies_version = 0; // Incremented if deps changed
-
-protected:
- void _notification(int p_what);
- static void _bind_methods();
- virtual void _load_theme_settings() override;
-
- virtual void _code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) override;
-
-public:
- void set_block_shader_changed(bool p_block) { block_shader_changed = p_block; }
- uint32_t get_dependencies_version() const { return dependencies_version; }
-
- virtual void _validate_script() override;
-
- void reload_text();
- void set_warnings_panel(RichTextLabel *p_warnings_panel);
-
- Ref<Shader> get_edited_shader() const;
- Ref<ShaderInclude> get_edited_shader_include() const;
-
- void set_edited_shader(const Ref<Shader> &p_shader);
- void set_edited_shader(const Ref<Shader> &p_shader, const String &p_code);
- void set_edited_shader_include(const Ref<ShaderInclude> &p_include);
- void set_edited_shader_include(const Ref<ShaderInclude> &p_include, const String &p_code);
- void set_edited_code(const String &p_code);
-
- ShaderTextEditor();
-};
-
-class ShaderEditor : public MarginContainer {
- GDCLASS(ShaderEditor, MarginContainer);
-
- enum {
- EDIT_UNDO,
- EDIT_REDO,
- EDIT_CUT,
- EDIT_COPY,
- EDIT_PASTE,
- EDIT_SELECT_ALL,
- EDIT_MOVE_LINE_UP,
- EDIT_MOVE_LINE_DOWN,
- EDIT_INDENT,
- EDIT_UNINDENT,
- EDIT_DELETE_LINE,
- EDIT_DUPLICATE_SELECTION,
- EDIT_TOGGLE_COMMENT,
- EDIT_COMPLETE,
- SEARCH_FIND,
- SEARCH_FIND_NEXT,
- SEARCH_FIND_PREV,
- SEARCH_REPLACE,
- SEARCH_GOTO_LINE,
- BOOKMARK_TOGGLE,
- BOOKMARK_GOTO_NEXT,
- BOOKMARK_GOTO_PREV,
- BOOKMARK_REMOVE_ALL,
- HELP_DOCS,
- };
-
- MenuButton *edit_menu = nullptr;
- MenuButton *search_menu = nullptr;
- PopupMenu *bookmarks_menu = nullptr;
- MenuButton *help_menu = nullptr;
- PopupMenu *context_menu = nullptr;
- RichTextLabel *warnings_panel = nullptr;
- uint64_t idle = 0;
-
- GotoLineDialog *goto_line_dialog = nullptr;
- ConfirmationDialog *erase_tab_confirm = nullptr;
- ConfirmationDialog *disk_changed = nullptr;
-
- ShaderTextEditor *shader_editor = nullptr;
- bool compilation_success = true;
-
- void _menu_option(int p_option);
- mutable Ref<Shader> shader;
- mutable Ref<ShaderInclude> shader_inc;
-
- void _editor_settings_changed();
- void _project_settings_changed();
-
- void _check_for_external_edit();
- void _reload_shader_from_disk();
- void _reload_shader_include_from_disk();
- void _reload();
- void _show_warnings_panel(bool p_show);
- void _warning_clicked(Variant p_line);
- void _update_warnings(bool p_validate);
-
- void _script_validated(bool p_valid) {
- compilation_success = p_valid;
- emit_signal(SNAME("validation_changed"));
- }
-
- uint32_t dependencies_version = 0xFFFFFFFF;
-
-protected:
- void _notification(int p_what);
- static void _bind_methods();
- void _make_context_menu(bool p_selection, Vector2 p_position);
- void _text_edit_gui_input(const Ref<InputEvent> &p_ev);
-
- void _update_bookmark_list();
- void _bookmark_item_pressed(int p_idx);
-
-public:
- bool was_compilation_successful() const { return compilation_success; }
- void apply_shaders();
- void ensure_select_current();
- void edit(const Ref<Shader> &p_shader);
- void edit(const Ref<ShaderInclude> &p_shader_inc);
- void goto_line_selection(int p_line, int p_begin, int p_end);
- void save_external_data(const String &p_str = "");
- void validate_script();
- bool is_unsaved() const;
-
- virtual Size2 get_minimum_size() const override { return Size2(0, 200); }
-
- ShaderEditor();
-};
+class TabContainer;
+class TextShaderEditor;
+class VisualShaderEditor;
class ShaderEditorPlugin : public EditorPlugin {
GDCLASS(ShaderEditorPlugin, EditorPlugin);
@@ -213,12 +46,14 @@ class ShaderEditorPlugin : public EditorPlugin {
struct EditedShader {
Ref<Shader> shader;
Ref<ShaderInclude> shader_inc;
- ShaderEditor *shader_editor = nullptr;
+ TextShaderEditor *shader_editor = nullptr;
VisualShaderEditor *visual_shader_editor = nullptr;
};
LocalVector<EditedShader> edited_shaders;
+ // Always valid operations come first in the enum, file-specific ones
+ // should go after FILE_SAVE which is used to build the menu accordingly.
enum {
FILE_NEW,
FILE_NEW_INCLUDE,
@@ -265,7 +100,7 @@ public:
virtual void make_visible(bool p_visible) override;
virtual void selected_notify() override;
- ShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader);
+ TextShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader);
VisualShaderEditor *get_visual_shader_editor(const Ref<Shader> &p_for_shader);
virtual void save_external_data() override;
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
new file mode 100644
index 0000000000..dfd5e76ba6
--- /dev/null
+++ b/editor/plugins/text_shader_editor.cpp
@@ -0,0 +1,1191 @@
+/*************************************************************************/
+/* text_shader_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "text_shader_editor.h"
+
+#include "core/version_generated.gen.h"
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
+#include "editor/filesystem_dock.h"
+#include "editor/project_settings_editor.h"
+#include "scene/gui/split_container.h"
+#include "servers/rendering/shader_preprocessor.h"
+#include "servers/rendering/shader_types.h"
+
+/*** SHADER SYNTAX HIGHLIGHTER ****/
+
+Dictionary GDShaderSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
+ Dictionary color_map;
+
+ for (const Point2i &region : disabled_branch_regions) {
+ if (p_line >= region.x && p_line <= region.y) {
+ Dictionary highlighter_info;
+ highlighter_info["color"] = disabled_branch_color;
+
+ color_map[0] = highlighter_info;
+ return color_map;
+ }
+ }
+
+ return CodeHighlighter::_get_line_syntax_highlighting_impl(p_line);
+}
+
+void GDShaderSyntaxHighlighter::add_disabled_branch_region(const Point2i &p_region) {
+ ERR_FAIL_COND(p_region.x < 0);
+ ERR_FAIL_COND(p_region.y < 0);
+
+ for (int i = 0; i < disabled_branch_regions.size(); i++) {
+ ERR_FAIL_COND_MSG(disabled_branch_regions[i].x == p_region.x, "Branch region with a start line '" + itos(p_region.x) + "' already exists.");
+ }
+
+ Point2i disabled_branch_region;
+ disabled_branch_region.x = p_region.x;
+ disabled_branch_region.y = p_region.y;
+ disabled_branch_regions.push_back(disabled_branch_region);
+
+ clear_highlighting_cache();
+}
+
+void GDShaderSyntaxHighlighter::clear_disabled_branch_regions() {
+ disabled_branch_regions.clear();
+ clear_highlighting_cache();
+}
+
+void GDShaderSyntaxHighlighter::set_disabled_branch_color(const Color &p_color) {
+ disabled_branch_color = p_color;
+ clear_highlighting_cache();
+}
+
+/*** SHADER SCRIPT EDITOR ****/
+
+static bool saved_warnings_enabled = false;
+static bool saved_treat_warning_as_errors = false;
+static HashMap<ShaderWarning::Code, bool> saved_warnings;
+static uint32_t saved_warning_flags = 0U;
+
+void ShaderTextEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ if (is_visible_in_tree()) {
+ _load_theme_settings();
+ if (warnings.size() > 0 && last_compile_result == OK) {
+ warnings_panel->clear();
+ _update_warning_panel();
+ }
+ }
+ } break;
+ }
+}
+
+Ref<Shader> ShaderTextEditor::get_edited_shader() const {
+ return shader;
+}
+
+Ref<ShaderInclude> ShaderTextEditor::get_edited_shader_include() const {
+ return shader_inc;
+}
+
+void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) {
+ set_edited_shader(p_shader, p_shader->get_code());
+}
+
+void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader, const String &p_code) {
+ if (shader == p_shader) {
+ return;
+ }
+ if (shader.is_valid()) {
+ shader->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
+ }
+ shader = p_shader;
+ shader_inc = Ref<ShaderInclude>();
+
+ set_edited_code(p_code);
+
+ if (shader.is_valid()) {
+ shader->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
+ }
+}
+
+void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc) {
+ set_edited_shader_include(p_shader_inc, p_shader_inc->get_code());
+}
+
+void ShaderTextEditor::_shader_changed() {
+ // This function is used for dependencies (include changing changes main shader and forces it to revalidate)
+ if (block_shader_changed) {
+ return;
+ }
+ dependencies_version++;
+ _validate_script();
+}
+
+void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc, const String &p_code) {
+ if (shader_inc == p_shader_inc) {
+ return;
+ }
+ if (shader_inc.is_valid()) {
+ shader_inc->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
+ }
+ shader_inc = p_shader_inc;
+ shader = Ref<Shader>();
+
+ set_edited_code(p_code);
+
+ if (shader_inc.is_valid()) {
+ shader_inc->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
+ }
+}
+
+void ShaderTextEditor::set_edited_code(const String &p_code) {
+ _load_theme_settings();
+
+ get_text_editor()->set_text(p_code);
+ get_text_editor()->clear_undo_history();
+ get_text_editor()->call_deferred(SNAME("set_h_scroll"), 0);
+ get_text_editor()->call_deferred(SNAME("set_v_scroll"), 0);
+ get_text_editor()->tag_saved_version();
+
+ _validate_script();
+ _line_col_changed();
+}
+
+void ShaderTextEditor::reload_text() {
+ ERR_FAIL_COND(shader.is_null());
+
+ CodeEdit *te = get_text_editor();
+ int column = te->get_caret_column();
+ int row = te->get_caret_line();
+ int h = te->get_h_scroll();
+ int v = te->get_v_scroll();
+
+ te->set_text(shader->get_code());
+ te->set_caret_line(row);
+ te->set_caret_column(column);
+ te->set_h_scroll(h);
+ te->set_v_scroll(v);
+
+ te->tag_saved_version();
+
+ update_line_and_column();
+}
+
+void ShaderTextEditor::set_warnings_panel(RichTextLabel *p_warnings_panel) {
+ warnings_panel = p_warnings_panel;
+}
+
+void ShaderTextEditor::_load_theme_settings() {
+ CodeEdit *text_editor = get_text_editor();
+ Color updated_marked_line_color = EDITOR_GET("text_editor/theme/highlighting/mark_color");
+ if (updated_marked_line_color != marked_line_color) {
+ for (int i = 0; i < text_editor->get_line_count(); i++) {
+ if (text_editor->get_line_background_color(i) == marked_line_color) {
+ text_editor->set_line_background_color(i, updated_marked_line_color);
+ }
+ }
+ marked_line_color = updated_marked_line_color;
+ }
+
+ syntax_highlighter->set_number_color(EDITOR_GET("text_editor/theme/highlighting/number_color"));
+ syntax_highlighter->set_symbol_color(EDITOR_GET("text_editor/theme/highlighting/symbol_color"));
+ syntax_highlighter->set_function_color(EDITOR_GET("text_editor/theme/highlighting/function_color"));
+ syntax_highlighter->set_member_variable_color(EDITOR_GET("text_editor/theme/highlighting/member_variable_color"));
+
+ syntax_highlighter->clear_keyword_colors();
+
+ const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
+ const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
+
+ List<String> keywords;
+ ShaderLanguage::get_keyword_list(&keywords);
+
+ for (const String &E : keywords) {
+ if (ShaderLanguage::is_control_flow_keyword(E)) {
+ syntax_highlighter->add_keyword_color(E, control_flow_keyword_color);
+ } else {
+ syntax_highlighter->add_keyword_color(E, keyword_color);
+ }
+ }
+
+ List<String> pp_keywords;
+ ShaderPreprocessor::get_keyword_list(&pp_keywords, false);
+
+ for (const String &E : pp_keywords) {
+ syntax_highlighter->add_keyword_color(E, keyword_color);
+ }
+
+ // Colorize built-ins like `COLOR` differently to make them easier
+ // to distinguish from keywords at a quick glance.
+
+ List<String> built_ins;
+
+ if (shader_inc.is_valid()) {
+ for (int i = 0; i < RenderingServer::SHADER_MAX; i++) {
+ for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(i))) {
+ for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) {
+ built_ins.push_back(F.key);
+ }
+ }
+
+ const Vector<ShaderLanguage::ModeInfo> &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(i));
+
+ for (int j = 0; j < modes.size(); j++) {
+ const ShaderLanguage::ModeInfo &info = modes[j];
+
+ if (!info.options.is_empty()) {
+ for (int k = 0; k < info.options.size(); k++) {
+ built_ins.push_back(String(info.name) + "_" + String(info.options[k]));
+ }
+ } else {
+ built_ins.push_back(String(info.name));
+ }
+ }
+ }
+ } else if (shader.is_valid()) {
+ for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()))) {
+ for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) {
+ built_ins.push_back(F.key);
+ }
+ }
+
+ const Vector<ShaderLanguage::ModeInfo> &modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
+
+ for (int i = 0; i < modes.size(); i++) {
+ const ShaderLanguage::ModeInfo &info = modes[i];
+
+ if (!info.options.is_empty()) {
+ for (int j = 0; j < info.options.size(); j++) {
+ built_ins.push_back(String(info.name) + "_" + String(info.options[j]));
+ }
+ } else {
+ built_ins.push_back(String(info.name));
+ }
+ }
+ }
+
+ const Color user_type_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color");
+
+ for (const String &E : built_ins) {
+ syntax_highlighter->add_keyword_color(E, user_type_color);
+ }
+
+ // Colorize comments.
+ const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
+ syntax_highlighter->clear_color_regions();
+ syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
+ syntax_highlighter->add_color_region("//", "", comment_color, true);
+ syntax_highlighter->set_disabled_branch_color(comment_color);
+
+ text_editor->clear_comment_delimiters();
+ text_editor->add_comment_delimiter("/*", "*/", false);
+ text_editor->add_comment_delimiter("//", "", true);
+
+ if (!text_editor->has_auto_brace_completion_open_key("/*")) {
+ text_editor->add_auto_brace_completion_pair("/*", "*/");
+ }
+
+ // Colorize preprocessor include strings.
+ const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color");
+ syntax_highlighter->add_color_region("\"", "\"", string_color, false);
+
+ if (warnings_panel) {
+ // Warnings panel.
+ warnings_panel->add_theme_font_override("normal_font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("main"), SNAME("EditorFonts")));
+ warnings_panel->add_theme_font_size_override("normal_font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("main_size"), SNAME("EditorFonts")));
+ }
+}
+
+void ShaderTextEditor::_check_shader_mode() {
+ String type = ShaderLanguage::get_shader_type(get_text_editor()->get_text());
+
+ Shader::Mode mode;
+
+ if (type == "canvas_item") {
+ mode = Shader::MODE_CANVAS_ITEM;
+ } else if (type == "particles") {
+ mode = Shader::MODE_PARTICLES;
+ } else if (type == "sky") {
+ mode = Shader::MODE_SKY;
+ } else if (type == "fog") {
+ mode = Shader::MODE_FOG;
+ } else {
+ mode = Shader::MODE_SPATIAL;
+ }
+
+ if (shader->get_mode() != mode) {
+ set_block_shader_changed(true);
+ shader->set_code(get_text_editor()->get_text());
+ set_block_shader_changed(false);
+ _load_theme_settings();
+ }
+}
+
+static ShaderLanguage::DataType _get_global_shader_uniform_type(const StringName &p_variable) {
+ RS::GlobalShaderParameterType gvt = RS::get_singleton()->global_shader_parameter_get_type(p_variable);
+ return (ShaderLanguage::DataType)RS::global_shader_uniform_type_get_shader_datatype(gvt);
+}
+
+static String complete_from_path;
+
+static void _complete_include_paths_search(EditorFileSystemDirectory *p_efsd, List<ScriptLanguage::CodeCompletionOption> *r_options) {
+ if (!p_efsd) {
+ return;
+ }
+ for (int i = 0; i < p_efsd->get_file_count(); i++) {
+ if (p_efsd->get_file_type(i) == SNAME("ShaderInclude")) {
+ String path = p_efsd->get_file_path(i);
+ if (path.begins_with(complete_from_path)) {
+ path = path.replace_first(complete_from_path, "");
+ }
+ r_options->push_back(ScriptLanguage::CodeCompletionOption(path, ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH));
+ }
+ }
+ for (int j = 0; j < p_efsd->get_subdir_count(); j++) {
+ _complete_include_paths_search(p_efsd->get_subdir(j), r_options);
+ }
+}
+
+static void _complete_include_paths(List<ScriptLanguage::CodeCompletionOption> *r_options) {
+ _complete_include_paths_search(EditorFileSystem::get_singleton()->get_filesystem(), r_options);
+}
+
+void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) {
+ List<ScriptLanguage::CodeCompletionOption> pp_options;
+ List<ScriptLanguage::CodeCompletionOption> pp_defines;
+ ShaderPreprocessor preprocessor;
+ String code;
+ complete_from_path = (shader.is_valid() ? shader->get_path() : shader_inc->get_path()).get_base_dir();
+ if (!complete_from_path.ends_with("/")) {
+ complete_from_path += "/";
+ }
+ preprocessor.preprocess(p_code, "", code, nullptr, nullptr, nullptr, nullptr, &pp_options, &pp_defines, _complete_include_paths);
+ complete_from_path = String();
+ if (pp_options.size()) {
+ for (const ScriptLanguage::CodeCompletionOption &E : pp_options) {
+ r_options->push_back(E);
+ }
+ return;
+ }
+ for (const ScriptLanguage::CodeCompletionOption &E : pp_defines) {
+ r_options->push_back(E);
+ }
+
+ ShaderLanguage sl;
+ String calltip;
+ ShaderLanguage::ShaderCompileInfo info;
+ info.global_shader_uniform_type_func = _get_global_shader_uniform_type;
+
+ if (shader.is_null()) {
+ info.is_include = true;
+
+ sl.complete(code, info, r_options, calltip);
+ get_text_editor()->set_code_hint(calltip);
+ return;
+ }
+ _check_shader_mode();
+ info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()));
+ info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
+ info.shader_types = ShaderTypes::get_singleton()->get_types();
+
+ sl.complete(code, info, r_options, calltip);
+ get_text_editor()->set_code_hint(calltip);
+}
+
+void ShaderTextEditor::_validate_script() {
+ emit_signal(SNAME("script_changed")); // Ensure to notify that it changed, so it is applied
+
+ String code;
+
+ if (shader.is_valid()) {
+ _check_shader_mode();
+ code = shader->get_code();
+ } else {
+ code = shader_inc->get_code();
+ }
+
+ ShaderPreprocessor preprocessor;
+ String code_pp;
+ String error_pp;
+ List<ShaderPreprocessor::FilePosition> err_positions;
+ List<ShaderPreprocessor::Region> regions;
+ String filename;
+ if (shader.is_valid()) {
+ filename = shader->get_path();
+ } else if (shader_inc.is_valid()) {
+ filename = shader_inc->get_path();
+ }
+ last_compile_result = preprocessor.preprocess(code, filename, code_pp, &error_pp, &err_positions, &regions);
+
+ for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
+ get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));
+ }
+
+ syntax_highlighter->clear_disabled_branch_regions();
+ for (const ShaderPreprocessor::Region &region : regions) {
+ if (!region.enabled) {
+ if (filename != region.file) {
+ continue;
+ }
+ syntax_highlighter->add_disabled_branch_region(Point2i(region.from_line, region.to_line));
+ }
+ }
+
+ set_error("");
+ set_error_count(0);
+
+ if (last_compile_result != OK) {
+ //preprocessor error
+ ERR_FAIL_COND(err_positions.size() == 0);
+
+ String error_text = error_pp;
+ int error_line = err_positions.front()->get().line;
+ if (err_positions.size() == 1) {
+ // Error in main file
+ error_text = "error(" + itos(error_line) + "): " + error_text;
+ } else {
+ error_text = "error(" + itos(error_line) + ") in include " + err_positions.back()->get().file.get_file() + ":" + itos(err_positions.back()->get().line) + ": " + error_text;
+ set_error_count(err_positions.size() - 1);
+ }
+
+ set_error(error_text);
+ set_error_pos(error_line - 1, 0);
+ for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
+ get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));
+ }
+ get_text_editor()->set_line_background_color(error_line - 1, marked_line_color);
+
+ set_warning_count(0);
+
+ } else {
+ ShaderLanguage sl;
+
+ sl.enable_warning_checking(saved_warnings_enabled);
+ uint32_t flags = saved_warning_flags;
+ if (shader.is_null()) {
+ if (flags & ShaderWarning::UNUSED_CONSTANT) {
+ flags &= ~(ShaderWarning::UNUSED_CONSTANT);
+ }
+ if (flags & ShaderWarning::UNUSED_FUNCTION) {
+ flags &= ~(ShaderWarning::UNUSED_FUNCTION);
+ }
+ if (flags & ShaderWarning::UNUSED_STRUCT) {
+ flags &= ~(ShaderWarning::UNUSED_STRUCT);
+ }
+ if (flags & ShaderWarning::UNUSED_UNIFORM) {
+ flags &= ~(ShaderWarning::UNUSED_UNIFORM);
+ }
+ if (flags & ShaderWarning::UNUSED_VARYING) {
+ flags &= ~(ShaderWarning::UNUSED_VARYING);
+ }
+ }
+ sl.set_warning_flags(flags);
+
+ ShaderLanguage::ShaderCompileInfo info;
+ info.global_shader_uniform_type_func = _get_global_shader_uniform_type;
+
+ if (shader.is_null()) {
+ info.is_include = true;
+ } else {
+ Shader::Mode mode = shader->get_mode();
+ info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(mode));
+ info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(mode));
+ info.shader_types = ShaderTypes::get_singleton()->get_types();
+ }
+
+ code = code_pp;
+ //compiler error
+ last_compile_result = sl.compile(code, info);
+
+ if (last_compile_result != OK) {
+ String error_text;
+ int error_line;
+ Vector<ShaderLanguage::FilePosition> include_positions = sl.get_include_positions();
+ if (include_positions.size() > 1) {
+ //error is in an include
+ error_line = include_positions[0].line;
+ error_text = "error(" + itos(error_line) + ") in include " + include_positions[include_positions.size() - 1].file + ":" + itos(include_positions[include_positions.size() - 1].line) + ": " + sl.get_error_text();
+ set_error_count(include_positions.size() - 1);
+ } else {
+ error_line = sl.get_error_line();
+ error_text = "error(" + itos(error_line) + "): " + sl.get_error_text();
+ set_error_count(0);
+ }
+ set_error(error_text);
+ set_error_pos(error_line - 1, 0);
+ get_text_editor()->set_line_background_color(error_line - 1, marked_line_color);
+ } else {
+ set_error("");
+ }
+
+ if (warnings.size() > 0 || last_compile_result != OK) {
+ warnings_panel->clear();
+ }
+ warnings.clear();
+ for (List<ShaderWarning>::Element *E = sl.get_warnings_ptr(); E; E = E->next()) {
+ warnings.push_back(E->get());
+ }
+ if (warnings.size() > 0 && last_compile_result == OK) {
+ warnings.sort_custom<WarningsComparator>();
+ _update_warning_panel();
+ } else {
+ set_warning_count(0);
+ }
+ }
+
+ emit_signal(SNAME("script_validated"), last_compile_result == OK); // Notify that validation finished, to update the list of scripts
+}
+
+void ShaderTextEditor::_update_warning_panel() {
+ int warning_count = 0;
+
+ warnings_panel->push_table(2);
+ for (int i = 0; i < warnings.size(); i++) {
+ ShaderWarning &w = warnings[i];
+
+ if (warning_count == 0) {
+ if (saved_treat_warning_as_errors) {
+ String error_text = "error(" + itos(w.get_line()) + "): " + w.get_message() + " " + TTR("Warnings should be fixed to prevent errors.");
+ set_error_pos(w.get_line() - 1, 0);
+ set_error(error_text);
+ get_text_editor()->set_line_background_color(w.get_line() - 1, marked_line_color);
+ }
+ }
+
+ warning_count++;
+ int line = w.get_line();
+
+ // First cell.
+ warnings_panel->push_cell();
+ warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), SNAME("Editor")));
+ if (line != -1) {
+ warnings_panel->push_meta(line - 1);
+ warnings_panel->add_text(TTR("Line") + " " + itos(line));
+ warnings_panel->add_text(" (" + w.get_name() + "):");
+ warnings_panel->pop(); // Meta goto.
+ } else {
+ warnings_panel->add_text(w.get_name() + ":");
+ }
+ warnings_panel->pop(); // Color.
+ warnings_panel->pop(); // Cell.
+
+ // Second cell.
+ warnings_panel->push_cell();
+ warnings_panel->add_text(w.get_message());
+ warnings_panel->pop(); // Cell.
+ }
+ warnings_panel->pop(); // Table.
+
+ set_warning_count(warning_count);
+}
+
+void ShaderTextEditor::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("script_validated", PropertyInfo(Variant::BOOL, "valid")));
+}
+
+ShaderTextEditor::ShaderTextEditor() {
+ syntax_highlighter.instantiate();
+ get_text_editor()->set_syntax_highlighter(syntax_highlighter);
+}
+
+/*** SCRIPT EDITOR ******/
+
+void TextShaderEditor::_menu_option(int p_option) {
+ switch (p_option) {
+ case EDIT_UNDO: {
+ shader_editor->get_text_editor()->undo();
+ } break;
+ case EDIT_REDO: {
+ shader_editor->get_text_editor()->redo();
+ } break;
+ case EDIT_CUT: {
+ shader_editor->get_text_editor()->cut();
+ } break;
+ case EDIT_COPY: {
+ shader_editor->get_text_editor()->copy();
+ } break;
+ case EDIT_PASTE: {
+ shader_editor->get_text_editor()->paste();
+ } break;
+ case EDIT_SELECT_ALL: {
+ shader_editor->get_text_editor()->select_all();
+ } break;
+ case EDIT_MOVE_LINE_UP: {
+ shader_editor->move_lines_up();
+ } break;
+ case EDIT_MOVE_LINE_DOWN: {
+ shader_editor->move_lines_down();
+ } break;
+ case EDIT_INDENT: {
+ if (shader.is_null()) {
+ return;
+ }
+ shader_editor->get_text_editor()->indent_lines();
+ } break;
+ case EDIT_UNINDENT: {
+ if (shader.is_null()) {
+ return;
+ }
+ shader_editor->get_text_editor()->unindent_lines();
+ } break;
+ case EDIT_DELETE_LINE: {
+ shader_editor->delete_lines();
+ } break;
+ case EDIT_DUPLICATE_SELECTION: {
+ shader_editor->duplicate_selection();
+ } break;
+ case EDIT_TOGGLE_COMMENT: {
+ if (shader.is_null()) {
+ return;
+ }
+
+ shader_editor->toggle_inline_comment("//");
+
+ } break;
+ case EDIT_COMPLETE: {
+ shader_editor->get_text_editor()->request_code_completion();
+ } break;
+ case SEARCH_FIND: {
+ shader_editor->get_find_replace_bar()->popup_search();
+ } break;
+ case SEARCH_FIND_NEXT: {
+ shader_editor->get_find_replace_bar()->search_next();
+ } break;
+ case SEARCH_FIND_PREV: {
+ shader_editor->get_find_replace_bar()->search_prev();
+ } break;
+ case SEARCH_REPLACE: {
+ shader_editor->get_find_replace_bar()->popup_replace();
+ } break;
+ case SEARCH_GOTO_LINE: {
+ goto_line_dialog->popup_find_line(shader_editor->get_text_editor());
+ } break;
+ case BOOKMARK_TOGGLE: {
+ shader_editor->toggle_bookmark();
+ } break;
+ case BOOKMARK_GOTO_NEXT: {
+ shader_editor->goto_next_bookmark();
+ } break;
+ case BOOKMARK_GOTO_PREV: {
+ shader_editor->goto_prev_bookmark();
+ } break;
+ case BOOKMARK_REMOVE_ALL: {
+ shader_editor->remove_all_bookmarks();
+ } break;
+ case HELP_DOCS: {
+ OS::get_singleton()->shell_open(vformat("%s/tutorials/shaders/shader_reference/index.html", VERSION_DOCS_URL));
+ } break;
+ }
+ if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) {
+ shader_editor->get_text_editor()->call_deferred(SNAME("grab_focus"));
+ }
+}
+
+void TextShaderEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ PopupMenu *popup = help_menu->get_popup();
+ popup->set_item_icon(popup->get_item_index(HELP_DOCS), get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")));
+ } break;
+
+ case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
+ _check_for_external_edit();
+ } break;
+ }
+}
+
+void TextShaderEditor::_editor_settings_changed() {
+ shader_editor->update_editor_settings();
+
+ shader_editor->get_text_editor()->add_theme_constant_override("line_spacing", EditorSettings::get_singleton()->get("text_editor/appearance/whitespace/line_spacing"));
+ shader_editor->get_text_editor()->set_draw_breakpoints_gutter(false);
+ shader_editor->get_text_editor()->set_draw_executing_lines_gutter(false);
+}
+
+void TextShaderEditor::_show_warnings_panel(bool p_show) {
+ warnings_panel->set_visible(p_show);
+}
+
+void TextShaderEditor::_warning_clicked(Variant p_line) {
+ if (p_line.get_type() == Variant::INT) {
+ shader_editor->get_text_editor()->set_caret_line(p_line.operator int64_t());
+ }
+}
+
+void TextShaderEditor::_bind_methods() {
+ ClassDB::bind_method("_show_warnings_panel", &TextShaderEditor::_show_warnings_panel);
+ ClassDB::bind_method("_warning_clicked", &TextShaderEditor::_warning_clicked);
+
+ ADD_SIGNAL(MethodInfo("validation_changed"));
+}
+
+void TextShaderEditor::ensure_select_current() {
+}
+
+void TextShaderEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
+ shader_editor->goto_line_selection(p_line, p_begin, p_end);
+}
+
+void TextShaderEditor::_project_settings_changed() {
+ _update_warnings(true);
+}
+
+void TextShaderEditor::_update_warnings(bool p_validate) {
+ bool changed = false;
+
+ bool warnings_enabled = GLOBAL_GET("debug/shader_language/warnings/enable").booleanize();
+ if (warnings_enabled != saved_warnings_enabled) {
+ saved_warnings_enabled = warnings_enabled;
+ changed = true;
+ }
+
+ bool treat_warning_as_errors = GLOBAL_GET("debug/shader_language/warnings/treat_warnings_as_errors").booleanize();
+ if (treat_warning_as_errors != saved_treat_warning_as_errors) {
+ saved_treat_warning_as_errors = treat_warning_as_errors;
+ changed = true;
+ }
+
+ bool update_flags = false;
+
+ for (int i = 0; i < ShaderWarning::WARNING_MAX; i++) {
+ ShaderWarning::Code code = (ShaderWarning::Code)i;
+ bool value = GLOBAL_GET("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code(code).to_lower());
+
+ if (saved_warnings[code] != value) {
+ saved_warnings[code] = value;
+ update_flags = true;
+ changed = true;
+ }
+ }
+
+ if (update_flags) {
+ saved_warning_flags = (uint32_t)ShaderWarning::get_flags_from_codemap(saved_warnings);
+ }
+
+ if (p_validate && changed && shader_editor && shader_editor->get_edited_shader().is_valid()) {
+ shader_editor->validate_script();
+ }
+}
+
+void TextShaderEditor::_check_for_external_edit() {
+ bool use_autoreload = bool(EDITOR_GET("text_editor/behavior/files/auto_reload_scripts_on_external_change"));
+
+ if (shader_inc.is_valid()) {
+ if (shader_inc->get_last_modified_time() != FileAccess::get_modified_time(shader_inc->get_path())) {
+ if (use_autoreload) {
+ _reload_shader_include_from_disk();
+ } else {
+ disk_changed->call_deferred(SNAME("popup_centered"));
+ }
+ }
+ return;
+ }
+
+ if (shader.is_null() || shader->is_built_in()) {
+ return;
+ }
+
+ if (shader->get_last_modified_time() != FileAccess::get_modified_time(shader->get_path())) {
+ if (use_autoreload) {
+ _reload_shader_from_disk();
+ } else {
+ disk_changed->call_deferred(SNAME("popup_centered"));
+ }
+ }
+}
+
+void TextShaderEditor::_reload_shader_from_disk() {
+ Ref<Shader> rel_shader = ResourceLoader::load(shader->get_path(), shader->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
+ ERR_FAIL_COND(!rel_shader.is_valid());
+
+ shader_editor->set_block_shader_changed(true);
+ shader->set_code(rel_shader->get_code());
+ shader_editor->set_block_shader_changed(false);
+ shader->set_last_modified_time(rel_shader->get_last_modified_time());
+ shader_editor->reload_text();
+}
+
+void TextShaderEditor::_reload_shader_include_from_disk() {
+ Ref<ShaderInclude> rel_shader_include = ResourceLoader::load(shader_inc->get_path(), shader_inc->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
+ ERR_FAIL_COND(!rel_shader_include.is_valid());
+
+ shader_editor->set_block_shader_changed(true);
+ shader_inc->set_code(rel_shader_include->get_code());
+ shader_editor->set_block_shader_changed(false);
+ shader_inc->set_last_modified_time(rel_shader_include->get_last_modified_time());
+ shader_editor->reload_text();
+}
+
+void TextShaderEditor::_reload() {
+ if (shader.is_valid()) {
+ _reload_shader_from_disk();
+ } else if (shader_inc.is_valid()) {
+ _reload_shader_include_from_disk();
+ }
+}
+
+void TextShaderEditor::edit(const Ref<Shader> &p_shader) {
+ if (p_shader.is_null() || !p_shader->is_text_shader()) {
+ return;
+ }
+
+ if (shader == p_shader) {
+ return;
+ }
+
+ shader = p_shader;
+ shader_inc = Ref<ShaderInclude>();
+
+ shader_editor->set_edited_shader(shader);
+}
+
+void TextShaderEditor::edit(const Ref<ShaderInclude> &p_shader_inc) {
+ if (p_shader_inc.is_null()) {
+ return;
+ }
+
+ if (shader_inc == p_shader_inc) {
+ return;
+ }
+
+ shader_inc = p_shader_inc;
+ shader = Ref<Shader>();
+
+ shader_editor->set_edited_shader_include(p_shader_inc);
+}
+
+void TextShaderEditor::save_external_data(const String &p_str) {
+ if (shader.is_null() && shader_inc.is_null()) {
+ disk_changed->hide();
+ return;
+ }
+
+ apply_shaders();
+
+ Ref<Shader> edited_shader = shader_editor->get_edited_shader();
+ if (edited_shader.is_valid()) {
+ ResourceSaver::save(edited_shader);
+ }
+ if (shader.is_valid() && shader != edited_shader) {
+ ResourceSaver::save(shader);
+ }
+
+ Ref<ShaderInclude> edited_shader_inc = shader_editor->get_edited_shader_include();
+ if (edited_shader_inc.is_valid()) {
+ ResourceSaver::save(edited_shader_inc);
+ }
+ if (shader_inc.is_valid() && shader_inc != edited_shader_inc) {
+ ResourceSaver::save(shader_inc);
+ }
+ shader_editor->get_text_editor()->tag_saved_version();
+
+ disk_changed->hide();
+}
+
+void TextShaderEditor::validate_script() {
+ shader_editor->_validate_script();
+}
+
+bool TextShaderEditor::is_unsaved() const {
+ return shader_editor->get_text_editor()->get_saved_version() != shader_editor->get_text_editor()->get_version();
+}
+
+void TextShaderEditor::apply_shaders() {
+ String editor_code = shader_editor->get_text_editor()->get_text();
+ if (shader.is_valid()) {
+ String shader_code = shader->get_code();
+ if (shader_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) {
+ shader_editor->set_block_shader_changed(true);
+ shader->set_code(editor_code);
+ shader_editor->set_block_shader_changed(false);
+ shader->set_edited(true);
+ }
+ }
+ if (shader_inc.is_valid()) {
+ String shader_inc_code = shader_inc->get_code();
+ if (shader_inc_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) {
+ shader_editor->set_block_shader_changed(true);
+ shader_inc->set_code(editor_code);
+ shader_editor->set_block_shader_changed(false);
+ shader_inc->set_edited(true);
+ }
+ }
+
+ dependencies_version = shader_editor->get_dependencies_version();
+}
+
+void TextShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
+ Ref<InputEventMouseButton> mb = ev;
+
+ if (mb.is_valid()) {
+ if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
+ CodeEdit *tx = shader_editor->get_text_editor();
+
+ Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position());
+ int row = pos.y;
+ int col = pos.x;
+ tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/behavior/navigation/move_caret_on_right_click"));
+
+ if (tx->is_move_caret_on_right_click_enabled()) {
+ if (tx->has_selection()) {
+ int from_line = tx->get_selection_from_line();
+ int to_line = tx->get_selection_to_line();
+ int from_column = tx->get_selection_from_column();
+ int to_column = tx->get_selection_to_column();
+
+ if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) {
+ // Right click is outside the selected text
+ tx->deselect();
+ }
+ }
+ if (!tx->has_selection()) {
+ tx->set_caret_line(row, true, false);
+ tx->set_caret_column(col);
+ }
+ }
+ _make_context_menu(tx->has_selection(), get_local_mouse_position());
+ }
+ }
+
+ Ref<InputEventKey> k = ev;
+ if (k.is_valid() && k->is_pressed() && k->is_action("ui_menu", true)) {
+ CodeEdit *tx = shader_editor->get_text_editor();
+ tx->adjust_viewport_to_caret();
+ _make_context_menu(tx->has_selection(), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos()));
+ context_menu->grab_focus();
+ }
+}
+
+void TextShaderEditor::_update_bookmark_list() {
+ bookmarks_menu->clear();
+
+ bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);
+ bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL);
+ bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT);
+ bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV);
+
+ PackedInt32Array bookmark_list = shader_editor->get_text_editor()->get_bookmarked_lines();
+ if (bookmark_list.size() == 0) {
+ return;
+ }
+
+ bookmarks_menu->add_separator();
+
+ for (int i = 0; i < bookmark_list.size(); i++) {
+ String line = shader_editor->get_text_editor()->get_line(bookmark_list[i]).strip_edges();
+ // Limit the size of the line if too big.
+ if (line.length() > 50) {
+ line = line.substr(0, 50);
+ }
+
+ bookmarks_menu->add_item(String::num((int)bookmark_list[i] + 1) + " - \"" + line + "\"");
+ bookmarks_menu->set_item_metadata(-1, bookmark_list[i]);
+ }
+}
+
+void TextShaderEditor::_bookmark_item_pressed(int p_idx) {
+ if (p_idx < 4) { // Any item before the separator.
+ _menu_option(bookmarks_menu->get_item_id(p_idx));
+ } else {
+ shader_editor->goto_line(bookmarks_menu->get_item_metadata(p_idx));
+ }
+}
+
+void TextShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) {
+ context_menu->clear();
+ if (p_selection) {
+ context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY);
+ }
+
+ context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE);
+ context_menu->add_separator();
+ context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);
+
+ context_menu->add_separator();
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);
+
+ context_menu->set_position(get_screen_position() + p_position);
+ context_menu->reset_size();
+ context_menu->popup();
+}
+
+TextShaderEditor::TextShaderEditor() {
+ GLOBAL_DEF("debug/shader_language/warnings/enable", true);
+ GLOBAL_DEF("debug/shader_language/warnings/treat_warnings_as_errors", false);
+ for (int i = 0; i < (int)ShaderWarning::WARNING_MAX; i++) {
+ GLOBAL_DEF("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code((ShaderWarning::Code)i).to_lower(), true);
+ }
+ _update_warnings(false);
+
+ shader_editor = memnew(ShaderTextEditor);
+
+ shader_editor->connect("script_validated", callable_mp(this, &TextShaderEditor::_script_validated));
+
+ shader_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ shader_editor->add_theme_constant_override("separation", 0);
+ shader_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+
+ shader_editor->connect("show_warnings_panel", callable_mp(this, &TextShaderEditor::_show_warnings_panel));
+ shader_editor->connect("script_changed", callable_mp(this, &TextShaderEditor::apply_shaders));
+ EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &TextShaderEditor::_editor_settings_changed));
+ ProjectSettingsEditor::get_singleton()->connect("confirmed", callable_mp(this, &TextShaderEditor::_project_settings_changed));
+
+ shader_editor->get_text_editor()->set_code_hint_draw_below(EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line"));
+
+ shader_editor->get_text_editor()->set_symbol_lookup_on_click_enabled(true);
+ shader_editor->get_text_editor()->set_context_menu_enabled(false);
+ shader_editor->get_text_editor()->connect("gui_input", callable_mp(this, &TextShaderEditor::_text_edit_gui_input));
+
+ shader_editor->update_editor_settings();
+
+ context_menu = memnew(PopupMenu);
+ add_child(context_menu);
+ context_menu->connect("id_pressed", callable_mp(this, &TextShaderEditor::_menu_option));
+
+ VBoxContainer *main_container = memnew(VBoxContainer);
+ HBoxContainer *hbc = memnew(HBoxContainer);
+
+ edit_menu = memnew(MenuButton);
+ edit_menu->set_shortcut_context(this);
+ edit_menu->set_text(TTR("Edit"));
+ edit_menu->set_switch_on_hover(true);
+
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE);
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL);
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_selection"), EDIT_DUPLICATE_SELECTION);
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE);
+ edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &TextShaderEditor::_menu_option));
+
+ search_menu = memnew(MenuButton);
+ search_menu->set_shortcut_context(this);
+ search_menu->set_text(TTR("Search"));
+ search_menu->set_switch_on_hover(true);
+
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND);
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT);
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV);
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE);
+ search_menu->get_popup()->connect("id_pressed", callable_mp(this, &TextShaderEditor::_menu_option));
+
+ MenuButton *goto_menu = memnew(MenuButton);
+ goto_menu->set_shortcut_context(this);
+ goto_menu->set_text(TTR("Go To"));
+ goto_menu->set_switch_on_hover(true);
+ goto_menu->get_popup()->connect("id_pressed", callable_mp(this, &TextShaderEditor::_menu_option));
+
+ goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
+ goto_menu->get_popup()->add_separator();
+
+ bookmarks_menu = memnew(PopupMenu);
+ bookmarks_menu->set_name("Bookmarks");
+ goto_menu->get_popup()->add_child(bookmarks_menu);
+ goto_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "Bookmarks");
+ _update_bookmark_list();
+ bookmarks_menu->connect("about_to_popup", callable_mp(this, &TextShaderEditor::_update_bookmark_list));
+ bookmarks_menu->connect("index_pressed", callable_mp(this, &TextShaderEditor::_bookmark_item_pressed));
+
+ help_menu = memnew(MenuButton);
+ help_menu->set_text(TTR("Help"));
+ help_menu->set_switch_on_hover(true);
+ help_menu->get_popup()->add_item(TTR("Online Docs"), HELP_DOCS);
+ help_menu->get_popup()->connect("id_pressed", callable_mp(this, &TextShaderEditor::_menu_option));
+
+ add_child(main_container);
+ main_container->add_child(hbc);
+ hbc->add_child(search_menu);
+ hbc->add_child(edit_menu);
+ hbc->add_child(goto_menu);
+ hbc->add_child(help_menu);
+ hbc->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("ScriptEditorPanel"), SNAME("EditorStyles")));
+
+ VSplitContainer *editor_box = memnew(VSplitContainer);
+ main_container->add_child(editor_box);
+ editor_box->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+ editor_box->set_v_size_flags(SIZE_EXPAND_FILL);
+ editor_box->add_child(shader_editor);
+
+ FindReplaceBar *bar = memnew(FindReplaceBar);
+ main_container->add_child(bar);
+ bar->hide();
+ shader_editor->set_find_replace_bar(bar);
+
+ warnings_panel = memnew(RichTextLabel);
+ warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE));
+ warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL);
+ warnings_panel->set_meta_underline(true);
+ warnings_panel->set_selection_enabled(true);
+ warnings_panel->set_focus_mode(FOCUS_CLICK);
+ warnings_panel->hide();
+ warnings_panel->connect("meta_clicked", callable_mp(this, &TextShaderEditor::_warning_clicked));
+ editor_box->add_child(warnings_panel);
+ shader_editor->set_warnings_panel(warnings_panel);
+
+ goto_line_dialog = memnew(GotoLineDialog);
+ add_child(goto_line_dialog);
+
+ disk_changed = memnew(ConfirmationDialog);
+
+ VBoxContainer *vbc = memnew(VBoxContainer);
+ disk_changed->add_child(vbc);
+
+ Label *dl = memnew(Label);
+ dl->set_text(TTR("This shader has been modified on disk.\nWhat action should be taken?"));
+ vbc->add_child(dl);
+
+ disk_changed->connect("confirmed", callable_mp(this, &TextShaderEditor::_reload));
+ disk_changed->set_ok_button_text(TTR("Reload"));
+
+ disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
+ disk_changed->connect("custom_action", callable_mp(this, &TextShaderEditor::save_external_data));
+
+ add_child(disk_changed);
+
+ _editor_settings_changed();
+}
diff --git a/editor/plugins/text_shader_editor.h b/editor/plugins/text_shader_editor.h
new file mode 100644
index 0000000000..abeaff1fff
--- /dev/null
+++ b/editor/plugins/text_shader_editor.h
@@ -0,0 +1,199 @@
+/*************************************************************************/
+/* text_shader_editor.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEXT_SHADER_EDITOR_H
+#define TEXT_SHADER_EDITOR_H
+
+#include "editor/code_editor.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/panel_container.h"
+#include "scene/gui/rich_text_label.h"
+#include "servers/rendering/shader_warnings.h"
+
+class GDShaderSyntaxHighlighter : public CodeHighlighter {
+ GDCLASS(GDShaderSyntaxHighlighter, CodeHighlighter)
+
+private:
+ Vector<Point2i> disabled_branch_regions;
+ Color disabled_branch_color;
+
+public:
+ virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override;
+
+ void add_disabled_branch_region(const Point2i &p_region);
+ void clear_disabled_branch_regions();
+ void set_disabled_branch_color(const Color &p_color);
+};
+
+class ShaderTextEditor : public CodeTextEditor {
+ GDCLASS(ShaderTextEditor, CodeTextEditor);
+
+ Color marked_line_color = Color(1, 1, 1);
+
+ struct WarningsComparator {
+ _ALWAYS_INLINE_ bool operator()(const ShaderWarning &p_a, const ShaderWarning &p_b) const { return (p_a.get_line() < p_b.get_line()); }
+ };
+
+ Ref<GDShaderSyntaxHighlighter> syntax_highlighter;
+ RichTextLabel *warnings_panel = nullptr;
+ Ref<Shader> shader;
+ Ref<ShaderInclude> shader_inc;
+ List<ShaderWarning> warnings;
+ Error last_compile_result = Error::OK;
+
+ void _check_shader_mode();
+ void _update_warning_panel();
+
+ bool block_shader_changed = false;
+ void _shader_changed();
+
+ uint32_t dependencies_version = 0; // Incremented if deps changed
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+ virtual void _load_theme_settings() override;
+
+ virtual void _code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) override;
+
+public:
+ void set_block_shader_changed(bool p_block) { block_shader_changed = p_block; }
+ uint32_t get_dependencies_version() const { return dependencies_version; }
+
+ virtual void _validate_script() override;
+
+ void reload_text();
+ void set_warnings_panel(RichTextLabel *p_warnings_panel);
+
+ Ref<Shader> get_edited_shader() const;
+ Ref<ShaderInclude> get_edited_shader_include() const;
+
+ void set_edited_shader(const Ref<Shader> &p_shader);
+ void set_edited_shader(const Ref<Shader> &p_shader, const String &p_code);
+ void set_edited_shader_include(const Ref<ShaderInclude> &p_include);
+ void set_edited_shader_include(const Ref<ShaderInclude> &p_include, const String &p_code);
+ void set_edited_code(const String &p_code);
+
+ ShaderTextEditor();
+};
+
+class TextShaderEditor : public MarginContainer {
+ GDCLASS(TextShaderEditor, MarginContainer);
+
+ enum {
+ EDIT_UNDO,
+ EDIT_REDO,
+ EDIT_CUT,
+ EDIT_COPY,
+ EDIT_PASTE,
+ EDIT_SELECT_ALL,
+ EDIT_MOVE_LINE_UP,
+ EDIT_MOVE_LINE_DOWN,
+ EDIT_INDENT,
+ EDIT_UNINDENT,
+ EDIT_DELETE_LINE,
+ EDIT_DUPLICATE_SELECTION,
+ EDIT_TOGGLE_COMMENT,
+ EDIT_COMPLETE,
+ SEARCH_FIND,
+ SEARCH_FIND_NEXT,
+ SEARCH_FIND_PREV,
+ SEARCH_REPLACE,
+ SEARCH_GOTO_LINE,
+ BOOKMARK_TOGGLE,
+ BOOKMARK_GOTO_NEXT,
+ BOOKMARK_GOTO_PREV,
+ BOOKMARK_REMOVE_ALL,
+ HELP_DOCS,
+ };
+
+ MenuButton *edit_menu = nullptr;
+ MenuButton *search_menu = nullptr;
+ PopupMenu *bookmarks_menu = nullptr;
+ MenuButton *help_menu = nullptr;
+ PopupMenu *context_menu = nullptr;
+ RichTextLabel *warnings_panel = nullptr;
+ uint64_t idle = 0;
+
+ GotoLineDialog *goto_line_dialog = nullptr;
+ ConfirmationDialog *erase_tab_confirm = nullptr;
+ ConfirmationDialog *disk_changed = nullptr;
+
+ ShaderTextEditor *shader_editor = nullptr;
+ bool compilation_success = true;
+
+ void _menu_option(int p_option);
+ mutable Ref<Shader> shader;
+ mutable Ref<ShaderInclude> shader_inc;
+
+ void _editor_settings_changed();
+ void _project_settings_changed();
+
+ void _check_for_external_edit();
+ void _reload_shader_from_disk();
+ void _reload_shader_include_from_disk();
+ void _reload();
+ void _show_warnings_panel(bool p_show);
+ void _warning_clicked(Variant p_line);
+ void _update_warnings(bool p_validate);
+
+ void _script_validated(bool p_valid) {
+ compilation_success = p_valid;
+ emit_signal(SNAME("validation_changed"));
+ }
+
+ uint32_t dependencies_version = 0xFFFFFFFF;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+ void _make_context_menu(bool p_selection, Vector2 p_position);
+ void _text_edit_gui_input(const Ref<InputEvent> &p_ev);
+
+ void _update_bookmark_list();
+ void _bookmark_item_pressed(int p_idx);
+
+public:
+ bool was_compilation_successful() const { return compilation_success; }
+ void apply_shaders();
+ void ensure_select_current();
+ void edit(const Ref<Shader> &p_shader);
+ void edit(const Ref<ShaderInclude> &p_shader_inc);
+ void goto_line_selection(int p_line, int p_begin, int p_end);
+ void save_external_data(const String &p_str = "");
+ void validate_script();
+ bool is_unsaved() const;
+
+ virtual Size2 get_minimum_size() const override { return Size2(0, 200); }
+
+ TextShaderEditor();
+};
+
+#endif // TEXT_SHADER_EDITOR_H
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 4d54001b94..d0f8e1f32d 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -1977,6 +1977,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
}
}
drag_last_pos = mm->get_position();
+ accept_event();
} else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) {
int terrain_set = Dictionary(drag_painted_value)["terrain_set"];
int terrain = Dictionary(drag_painted_value)["terrain"];
@@ -2026,14 +2027,15 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
}
}
drag_last_pos = mm->get_position();
+ accept_event();
}
}
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MouseButton::LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) {
if (mb->is_pressed()) {
- if (picker_button->is_pressed()) {
+ if (mb->get_button_index() == MouseButton::LEFT && picker_button->is_pressed()) {
Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position());
coords = p_tile_set_atlas_source->get_tile_at_coords(coords);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
@@ -2060,6 +2062,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
terrain_set_property_editor->update_property();
_update_terrain_selector();
picker_button->set_pressed(false);
+ accept_event();
}
} else {
Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position());
@@ -2071,6 +2074,10 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
int terrain_set = int(dummy_object->get("terrain_set"));
int terrain = int(dummy_object->get("terrain"));
if (terrain_set == -1 || !tile_data || tile_data->get_terrain_set() != terrain_set) {
+ // Paint terrain sets.
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ terrain_set = -1;
+ }
if (mb->is_ctrl_pressed()) {
// Paint terrain set with rect.
drag_type = DRAG_TYPE_PAINT_TERRAIN_SET_RECT;
@@ -2106,9 +2113,14 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
}
drag_last_pos = mb->get_position();
}
+ accept_event();
} else if (tile_data->get_terrain_set() == terrain_set) {
+ // Paint terrain bits.
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ terrain = -1;
+ }
if (mb->is_ctrl_pressed()) {
- // Paint terrain set with rect.
+ // Paint terrain bits with rect.
drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS_RECT;
drag_modified.clear();
Dictionary painted_dict;
@@ -2163,6 +2175,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
}
drag_last_pos = mb->get_position();
}
+ accept_event();
}
}
} else {
@@ -2190,18 +2203,21 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
for (const TileMapCell &E : edited) {
Vector2i coords = E.get_atlas_coords();
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_set());
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), drag_painted_value);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain());
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_data->is_valid_terrain_peering_bit(bit)) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_peering_bit(bit));
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_set());
+ if (tile_data->get_terrain_set() >= 0) {
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain());
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_peering_bit(bit));
+ }
}
}
}
undo_redo->commit_action(true);
drag_type = DRAG_TYPE_NONE;
+ accept_event();
} else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) {
undo_redo->create_action(TTR("Painting Terrain Set"));
for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
@@ -2209,17 +2225,20 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Vector2i coords = E.key.get_atlas_coords();
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
- Array array = dict["terrain_peering_bits"];
- for (int i = 0; i < array.size(); i++) {
- TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set"], bit)) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
+ if (int(dict["terrain_set"]) >= 0) {
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
+ Array array = dict["terrain_peering_bits"];
+ for (int i = 0; i < array.size(); i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set"], bit)) {
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
+ }
}
}
}
undo_redo->commit_action(false);
drag_type = DRAG_TYPE_NONE;
+ accept_event();
} else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) {
Dictionary painted = Dictionary(drag_painted_value);
int terrain_set = int(painted["terrain_set"]);
@@ -2243,6 +2262,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
}
undo_redo->commit_action(false);
drag_type = DRAG_TYPE_NONE;
+ accept_event();
} else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS_RECT) {
Dictionary painted = Dictionary(drag_painted_value);
int terrain_set = int(painted["terrain_set"]);
@@ -2312,6 +2332,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
}
undo_redo->commit_action(true);
drag_type = DRAG_TYPE_NONE;
+ accept_event();
}
}
}
@@ -2348,6 +2369,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
}
drag_last_pos = mm->get_position();
+ accept_event();
} else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) {
Dictionary painted = Dictionary(drag_painted_value);
int terrain_set = int(painted["terrain_set"]);
@@ -2400,14 +2422,15 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
}
}
drag_last_pos = mm->get_position();
+ accept_event();
}
}
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MouseButton::LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) {
if (mb->is_pressed()) {
- if (picker_button->is_pressed()) {
+ if (mb->get_button_index() == MouseButton::LEFT && picker_button->is_pressed()) {
Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position());
Vector2i coords = Vector2i(tile.x, tile.y);
int alternative_tile = tile.z;
@@ -2437,6 +2460,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
terrain_set_property_editor->update_property();
_update_terrain_selector();
picker_button->set_pressed(false);
+ accept_event();
}
} else {
int terrain_set = int(dummy_object->get("terrain_set"));
@@ -2446,75 +2470,87 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
Vector2i coords = Vector2i(tile.x, tile.y);
int alternative_tile = tile.z;
- TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile);
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile);
- if (terrain_set == -1 || !tile_data || tile_data->get_terrain_set() != terrain_set) {
- drag_type = DRAG_TYPE_PAINT_TERRAIN_SET;
- drag_modified.clear();
- drag_painted_value = int(dummy_object->get("terrain_set"));
- if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
- TileMapCell cell;
- cell.source_id = 0;
- cell.set_atlas_coords(coords);
- cell.alternative_tile = alternative_tile;
- Dictionary dict;
- dict["terrain_set"] = tile_data->get_terrain_set();
- Array array;
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
+ if (terrain_set == -1 || !tile_data || tile_data->get_terrain_set() != terrain_set) {
+ // Paint terrain sets.
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ terrain_set = -1;
}
- dict["terrain_peering_bits"] = array;
- drag_modified[cell] = dict;
- tile_data->set_terrain_set(drag_painted_value);
- }
- drag_last_pos = mb->get_position();
- } else if (tile_data->get_terrain_set() == terrain_set) {
- // Paint terrain bits.
- drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS;
- drag_modified.clear();
- Dictionary painted_dict;
- painted_dict["terrain_set"] = terrain_set;
- painted_dict["terrain"] = terrain;
- drag_painted_value = painted_dict;
-
- if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
- TileMapCell cell;
- cell.source_id = 0;
- cell.set_atlas_coords(coords);
- cell.alternative_tile = alternative_tile;
-
- // Save the old terrain_set and terrains bits.
- Dictionary dict;
- dict["terrain_set"] = tile_data->get_terrain_set();
- dict["terrain"] = tile_data->get_terrain();
- Array array;
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
+ drag_type = DRAG_TYPE_PAINT_TERRAIN_SET;
+ drag_modified.clear();
+ drag_painted_value = int(dummy_object->get("terrain_set"));
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
+ TileMapCell cell;
+ cell.source_id = 0;
+ cell.set_atlas_coords(coords);
+ cell.alternative_tile = alternative_tile;
+ Dictionary dict;
+ dict["terrain_set"] = tile_data->get_terrain_set();
+ Array array;
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
+ }
+ dict["terrain_peering_bits"] = array;
+ drag_modified[cell] = dict;
+ tile_data->set_terrain_set(drag_painted_value);
}
- dict["terrain_peering_bits"] = array;
- drag_modified[cell] = dict;
+ drag_last_pos = mb->get_position();
+ accept_event();
+ } else if (tile_data->get_terrain_set() == terrain_set) {
+ // Paint terrain bits.
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ terrain = -1;
+ }
+ // Paint terrain bits.
+ drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS;
+ drag_modified.clear();
+ Dictionary painted_dict;
+ painted_dict["terrain_set"] = terrain_set;
+ painted_dict["terrain"] = terrain;
+ drag_painted_value = painted_dict;
- // Set the terrain bit.
- Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
- Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+ if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
+ TileMapCell cell;
+ cell.source_id = 0;
+ cell.set_atlas_coords(coords);
+ cell.alternative_tile = alternative_tile;
- Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
- if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
- tile_data->set_terrain(terrain);
- }
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
- polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
- if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
- tile_data->set_terrain_peering_bit(bit, terrain);
+ // Save the old terrain_set and terrains bits.
+ Dictionary dict;
+ dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
+ Array array;
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
+ }
+ dict["terrain_peering_bits"] = array;
+ drag_modified[cell] = dict;
+
+ // Set the terrain bit.
+ Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
+ tile_data->set_terrain(terrain);
+ }
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
+ if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
+ tile_data->set_terrain_peering_bit(bit, terrain);
+ }
}
}
}
+ drag_last_pos = mb->get_position();
+ accept_event();
}
- drag_last_pos = mb->get_position();
}
}
} else {
@@ -2523,16 +2559,19 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
Dictionary dict = E.value;
Vector2i coords = E.key.get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
- Array array = dict["terrain_peering_bits"];
- for (int i = 0; i < array.size(); i++) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
+ if (int(dict["terrain_set"]) >= 0) {
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
+ Array array = dict["terrain_peering_bits"];
+ for (int i = 0; i < array.size(); i++) {
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
+ }
}
}
undo_redo->commit_action(false);
drag_type = DRAG_TYPE_NONE;
+ accept_event();
} else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) {
Dictionary painted = Dictionary(drag_painted_value);
int terrain_set = int(painted["terrain_set"]);
@@ -2556,6 +2595,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
}
undo_redo->commit_action(false);
drag_type = DRAG_TYPE_NONE;
+ accept_event();
}
}
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 395c9b0248..acfa8b3d00 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -2428,20 +2428,16 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_line(Vector2i
return HashMap<Vector2i, TileMapCell>();
}
- if (selected_type == SELECTED_TYPE_CONNECT) {
- return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, true);
- } else if (selected_type == SELECTED_TYPE_PATH) {
- return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, false);
- } else { // SELECTED_TYPE_PATTERN
- TileSet::TerrainsPattern terrains_pattern;
- if (p_erase) {
- terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
- } else {
- terrains_pattern = selected_terrains_pattern;
+ if (p_erase) {
+ return _draw_terrain_pattern(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, TileSet::TerrainsPattern(*tile_set, selected_terrain_set));
+ } else {
+ if (selected_type == SELECTED_TYPE_CONNECT) {
+ return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, true);
+ } else if (selected_type == SELECTED_TYPE_PATH) {
+ return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, false);
+ } else { // SELECTED_TYPE_PATTERN
+ return _draw_terrain_pattern(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrains_pattern);
}
-
- Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell);
- return _draw_terrain_pattern(line, selected_terrain_set, terrains_pattern);
}
}
@@ -2468,16 +2464,14 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i
}
}
- if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
- return _draw_terrain_path_or_connect(to_draw, selected_terrain_set, selected_terrain, true);
- } else { // SELECTED_TYPE_PATTERN
- TileSet::TerrainsPattern terrains_pattern;
- if (p_erase) {
- terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
- } else {
- terrains_pattern = selected_terrains_pattern;
+ if (p_erase) {
+ return _draw_terrain_pattern(to_draw, selected_terrain_set, TileSet::TerrainsPattern(*tile_set, selected_terrain_set));
+ } else {
+ if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
+ return _draw_terrain_path_or_connect(to_draw, selected_terrain_set, selected_terrain, true);
+ } else { // SELECTED_TYPE_PATTERN
+ return _draw_terrain_pattern(to_draw, selected_terrain_set, selected_terrains_pattern);
}
- return _draw_terrain_pattern(to_draw, selected_terrain_set, terrains_pattern);
}
}
@@ -2609,16 +2603,14 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_bucket_fill(Ve
cells_to_draw_as_vector.append(cell);
}
- if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
- return _draw_terrain_path_or_connect(cells_to_draw_as_vector, selected_terrain_set, selected_terrain, true);
- } else { // SELECTED_TYPE_PATTERN
- TileSet::TerrainsPattern terrains_pattern;
- if (p_erase) {
- terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
- } else {
- terrains_pattern = selected_terrains_pattern;
+ if (p_erase) {
+ return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, TileSet::TerrainsPattern(*tile_set, selected_terrain_set));
+ } else {
+ if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
+ return _draw_terrain_path_or_connect(cells_to_draw_as_vector, selected_terrain_set, selected_terrain, true);
+ } else { // SELECTED_TYPE_PATTERN
+ return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, selected_terrains_pattern);
}
- return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, terrains_pattern);
}
}
diff --git a/editor/translations/extract.py b/editor/translations/extract.py
index 07026baee2..cecdb3939d 100755
--- a/editor/translations/extract.py
+++ b/editor/translations/extract.py
@@ -8,6 +8,7 @@ import re
import shutil
import subprocess
import sys
+from typing import Dict, Tuple
class Message:
@@ -42,7 +43,7 @@ class Message:
return "\n".join(lines)
-messages_map = {} # (id, context) -> Message.
+messages_map: Dict[Tuple[str, str], Message] = {} # (id, context) -> Message.
line_nb = False
@@ -51,11 +52,11 @@ for arg in sys.argv[1:]:
print("Enabling line numbers in the context locations.")
line_nb = True
else:
- os.sys.exit("Non supported argument '" + arg + "'. Aborting.")
+ sys.exit("Non supported argument '" + arg + "'. Aborting.")
if not os.path.exists("editor"):
- os.sys.exit("ERROR: This script should be started from the root of the git repo.")
+ sys.exit("ERROR: This script should be started from the root of the git repo.")
matches = []
diff --git a/gles3_builders.py b/gles3_builders.py
index eafe503dd5..84f11532e0 100644
--- a/gles3_builders.py
+++ b/gles3_builders.py
@@ -3,6 +3,10 @@
All such functions are invoked in a subprocess on Windows to prevent build flakiness.
"""
+import os.path
+
+from typing import Optional
+
from platform_methods import subprocess_main
@@ -30,7 +34,7 @@ class GLES3HeaderStruct:
self.specialization_values = []
-def include_file_in_gles3_header(filename, header_data, depth):
+def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct, depth: int):
fs = open(filename, "r")
line = fs.readline()
@@ -91,8 +95,6 @@ def include_file_in_gles3_header(filename, header_data, depth):
while line.find("#include ") != -1:
includeline = line.replace("#include ", "").strip()[1:-1]
- import os.path
-
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":
header_data.vertex_included_files += [included_file]
@@ -182,7 +184,7 @@ def include_file_in_gles3_header(filename, header_data, depth):
return header_data
-def build_gles3_header(filename, include, class_suffix, header_data=None):
+def build_gles3_header(filename: str, include: str, class_suffix: str, header_data: Optional[GLES3HeaderStruct] = None):
header_data = header_data or GLES3HeaderStruct()
include_file_in_gles3_header(filename, header_data, 0)
diff --git a/glsl_builders.py b/glsl_builders.py
index 8cb5807f21..888f541cf4 100644
--- a/glsl_builders.py
+++ b/glsl_builders.py
@@ -4,13 +4,15 @@ All such functions are invoked in a subprocess on Windows to prevent build flaki
"""
import os.path
+from typing import Optional, Iterable
+
from platform_methods import subprocess_main
-def generate_inline_code(input_lines, insert_newline=True):
+def generate_inline_code(input_lines: Iterable[str], insert_newline: bool = True):
"""Take header data and generate inline code
- :param: list input_lines: values for shared inline code
+ :param: input_lines: values for shared inline code
:return: str - generated inline value
"""
output = []
@@ -40,7 +42,7 @@ class RDHeaderStruct:
self.compute_offset = 0
-def include_file_in_rd_header(filename, header_data, depth):
+def include_file_in_rd_header(filename: str, header_data: RDHeaderStruct, depth: int) -> RDHeaderStruct:
fs = open(filename, "r")
line = fs.readline()
@@ -112,7 +114,7 @@ def include_file_in_rd_header(filename, header_data, depth):
return header_data
-def build_rd_header(filename, header_data=None):
+def build_rd_header(filename: str, header_data: Optional[RDHeaderStruct] = None) -> None:
header_data = header_data or RDHeaderStruct()
include_file_in_rd_header(filename, header_data, 0)
@@ -171,7 +173,7 @@ class RAWHeaderStruct:
self.code = ""
-def include_file_in_raw_header(filename, header_data, depth):
+def include_file_in_raw_header(filename: str, header_data: RAWHeaderStruct, depth: int) -> None:
fs = open(filename, "r")
line = fs.readline()
@@ -191,7 +193,7 @@ def include_file_in_raw_header(filename, header_data, depth):
fs.close()
-def build_raw_header(filename, header_data=None):
+def build_raw_header(filename: str, header_data: Optional[RAWHeaderStruct] = None):
header_data = header_data or RAWHeaderStruct()
include_file_in_raw_header(filename, header_data, 0)
diff --git a/methods.py b/methods.py
index 09012b8763..7feffb2848 100644
--- a/methods.py
+++ b/methods.py
@@ -1,6 +1,6 @@
import os
-import re
import sys
+import re
import glob
import subprocess
from collections import OrderedDict
@@ -663,7 +663,6 @@ def detect_visual_c_compiler_version(tools_env):
if vc_x86_amd64_compiler_detection_index > -1 and (
vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_x86_amd64_compiler_detection_index
):
- vc_chosen_compiler_index = vc_x86_amd64_compiler_detection_index
vc_chosen_compiler_str = "x86_amd64"
return vc_chosen_compiler_str
diff --git a/misc/scripts/mypy.ini b/misc/scripts/mypy.ini
new file mode 100644
index 0000000000..c1ea695ca5
--- /dev/null
+++ b/misc/scripts/mypy.ini
@@ -0,0 +1,11 @@
+[mypy]
+ignore_missing_imports = true
+disallow_any_generics = True
+pretty = True
+show_column_numbers = True
+warn_redundant_casts = True
+warn_return_any = True
+warn_unreachable = True
+
+namespace_packages = True
+explicit_package_bases = True
diff --git a/misc/scripts/mypy_check.sh b/misc/scripts/mypy_check.sh
new file mode 100755
index 0000000000..2a06486d67
--- /dev/null
+++ b/misc/scripts/mypy_check.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+set -uo pipefail
+
+echo -e "Python: mypy static analysis..."
+mypy --config-file=./misc/scripts/mypy.ini .
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index e0deea1106..8b27307d0c 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -43,26 +43,28 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
String prev_text = "";
int prev_column = 0;
-
bool prev_is_char = false;
bool prev_is_digit = false;
bool prev_is_binary_op = false;
+
bool in_keyword = false;
bool in_word = false;
bool in_number = false;
- bool in_function_name = false;
- bool in_lambda = false;
- bool in_variable_declaration = false;
- bool in_signal_declaration = false;
- bool in_function_args = false;
- bool in_member_variable = false;
bool in_node_path = false;
bool in_node_ref = false;
bool in_annotation = false;
bool in_string_name = false;
bool is_hex_notation = false;
bool is_bin_notation = false;
+ bool in_member_variable = false;
+ bool in_lambda = false;
+
+ bool in_function_name = false;
+ bool in_function_args = false;
+ bool in_variable_declaration = false;
+ bool in_signal_declaration = false;
bool expect_type = false;
+
Color keyword_color;
Color color;
@@ -241,16 +243,21 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
}
- // A bit of a hack, but couldn't come up with anything better.
+ // VERY hacky... but couldn't come up with anything better.
if (j > 0 && (str[j] == '&' || str[j] == '^' || str[j] == '%' || str[j] == '+' || str[j] == '-' || str[j] == '~' || str[j] == '.')) {
- if (prev_text == "true" || prev_text == "false" || prev_text == "self" || prev_text == "null" || prev_text == "PI" || prev_text == "TAU" || prev_text == "INF" || prev_text == "NAN") {
- is_binary_op = true;
- } else if (!keywords.has(prev_text)) {
- int k = j - 1;
- while (k > 0 && is_whitespace(str[k])) {
- k--;
- }
- if (!is_symbol(str[k]) || str[k] == '"' || str[k] == '\'' || str[k] == ')' || str[k] == ']' || str[k] == '}') {
+ int to = j - 1;
+ // Find what the last text was (prev_text won't work if there's no whitespace, so we need to do it manually).
+ while (to > 0 && is_whitespace(str[to])) {
+ to--;
+ }
+ int from = to;
+ while (from > 0 && !is_symbol(str[from])) {
+ from--;
+ }
+ String word = str.substr(from + 1, to - from);
+ // Keywords need to be exceptions, except for keywords that represent a value.
+ if (word == "true" || word == "false" || word == "null" || word == "PI" || word == "TAU" || word == "INF" || word == "NAN" || word == "self" || word == "super" || !keywords.has(word)) {
+ if (!is_symbol(str[to]) || str[to] == '"' || str[to] == '\'' || str[to] == ')' || str[to] == ']' || str[to] == '}') {
is_binary_op = true;
}
}
@@ -285,16 +292,18 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
is_hex_notation = true;
} else if (!((str[j] == '-' || str[j] == '+') && str[j - 1] == 'e' && !prev_is_digit) &&
!(str[j] == '_' && (prev_is_digit || str[j - 1] == 'b' || str[j - 1] == 'x' || str[j - 1] == '.')) &&
- !((str[j] == 'e' || str[j] == '.') && (prev_is_digit || str[j - 1] == '_')) &&
+ !(str[j] == 'e' && (prev_is_digit || str[j - 1] == '_')) &&
+ !(str[j] == '.' && (prev_is_digit || (!prev_is_binary_op && (j > 0 && (str[j - 1] == '-' || str[j - 1] == '+' || str[j - 1] == '~'))))) &&
!((str[j] == '-' || str[j] == '+' || str[j] == '~') && !prev_is_binary_op && str[j - 1] != 'e')) {
- /* 1st row of condition: '+' or '-' after scientific notation;
- 2nd row of condition: '_' as a numeric separator;
- 3rd row of condition: Scientific notation 'e' and floating points;
- 4th row of condition: Multiple unary operators. */
+ /* This condition continues Number highlighting in special cases.
+ 1st row: '+' or '-' after scientific notation;
+ 2nd row: '_' as a numeric separator;
+ 3rd row: Scientific notation 'e' and floating points;
+ 4th row: Floating points inside the number, or leading if after a unary mathematical operator;
+ 5th row: Multiple unary mathematical operators */
in_number = false;
}
- } else if ((str[j] == '-' || str[j] == '+' || str[j] == '~' || (str[j] == '.' && str[j + 1] != '.' && (j == 0 || (j > 0 && str[j - 1] != '.')))) && !is_binary_op) {
- // Start a number from unary mathematical operators and floating points, except for '..'
+ } else if (!is_binary_op && (str[j] == '-' || str[j] == '+' || str[j] == '~' || (str[j] == '.' && str[j + 1] != '.' && (j == 0 || (j > 0 && str[j - 1] != '.'))))) {
in_number = true;
}
@@ -316,7 +325,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
Color col = Color();
if (global_functions.has(word)) {
// "assert" and "preload" are reserved, so highlight even if not followed by a bracket.
- if (word == "assert" || word == "preload") {
+ if (word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::ASSERT) || word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::PRELOAD)) {
col = global_function_color;
} else {
// For other global functions, check if followed by bracket.
@@ -406,11 +415,11 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
in_function_args = false;
}
- if (expect_type && (prev_is_char || str[j] == '=')) {
+ if (expect_type && (prev_is_char || str[j] == '=') && str[j] != '[') {
expect_type = false;
}
- if (j > 0 && str[j] == '>' && str[j - 1] == '-') {
+ if (j > 0 && str[j - 1] == '-' && str[j] == '>') {
expect_type = true;
}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index b4da94e448..f1c841e9dc 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -2937,6 +2937,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
push_error(R"(Expected expression as the function argument.)");
} else {
call->arguments.push_back(argument);
+
+ if (argument->type == Node::IDENTIFIER && current.cursor_place == GDScriptTokenizer::CURSOR_BEGINNING) {
+ completion_context.type = COMPLETION_IDENTIFIER;
+ }
}
ct = COMPLETION_CALL_ARGUMENTS;
} while (match(GDScriptTokenizer::Token::COMMA));
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
index ab52761e17..f79731dd22 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.cpp
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -261,7 +261,7 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li
#define ADD_OPTION_ENUM(PATH, ENUM_HINT, VALUE) \
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, SNAME(PATH), PROPERTY_HINT_ENUM, ENUM_HINT), VALUE));
- ADD_OPTION_ENUM("blender/nodes/visible", "Visible Only,Renderable,All", BLEND_VISIBLE_ALL);
+ ADD_OPTION_ENUM("blender/nodes/visible", "All,Visible Only,Renderable", BLEND_VISIBLE_ALL);
ADD_OPTION_BOOL("blender/nodes/punctual_lights", true);
ADD_OPTION_BOOL("blender/nodes/cameras", true);
ADD_OPTION_BOOL("blender/nodes/custom_properties", true);
diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h
index dd1c1b9889..a1485ff82e 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.h
+++ b/modules/gltf/editor/editor_scene_importer_blend.h
@@ -45,9 +45,9 @@ class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter {
public:
enum {
+ BLEND_VISIBLE_ALL,
BLEND_VISIBLE_VISIBLE_ONLY,
- BLEND_VISIBLE_RENDERABLE,
- BLEND_VISIBLE_ALL
+ BLEND_VISIBLE_RENDERABLE
};
enum {
BLEND_BONE_INFLUENCES_NONE,
diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py
index d78a9c7db8..d28c3a0c3a 100755
--- a/modules/mono/build_scripts/build_assemblies.py
+++ b/modules/mono/build_scripts/build_assemblies.py
@@ -5,6 +5,7 @@ import os.path
import shlex
import subprocess
from dataclasses import dataclass
+from typing import Optional, List
def find_dotnet_cli():
@@ -150,10 +151,7 @@ def find_any_msbuild_tool(mono_prefix):
return None
-def run_msbuild(tools: ToolsLocation, sln: str, msbuild_args: [str] = None):
- if msbuild_args is None:
- msbuild_args = []
-
+def run_msbuild(tools: ToolsLocation, sln: str, msbuild_args: Optional[List[str]] = None):
using_msbuild_mono = False
# Preference order: dotnet CLI > Standalone MSBuild > Mono's MSBuild
@@ -169,7 +167,7 @@ def run_msbuild(tools: ToolsLocation, sln: str, msbuild_args: [str] = None):
args += [sln]
- if len(msbuild_args) > 0:
+ if msbuild_args:
args += msbuild_args
print("Running MSBuild: ", " ".join(shlex.quote(arg) for arg in args), flush=True)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index fbd59d649f..9c3bc51c44 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -4,6 +4,21 @@ using System.Runtime.InteropServices;
namespace Godot
{
/// <summary>
+ /// Specifies which order Euler angle rotations should be in.
+ /// When composing, the order is the same as the letters. When decomposing,
+ /// the order is reversed (ex: YXZ decomposes Z first, then X, and Y last).
+ /// </summary>
+ public enum EulerOrder
+ {
+ XYZ,
+ XZY,
+ YXZ,
+ YZX,
+ ZXY,
+ ZYX
+ };
+
+ /// <summary>
/// 3×3 matrix used for 3D rotation and scale.
/// Almost always used as an orthogonal basis for a Transform.
///
@@ -244,50 +259,258 @@ namespace Godot
}
/// <summary>
- /// Returns the basis's rotation in the form of Euler angles
- /// (in the YXZ convention: when *decomposing*, first Z, then X, and Y last).
- /// The returned vector contains the rotation angles in
- /// the format (X angle, Y angle, Z angle).
+ /// Returns the basis's rotation in the form of Euler angles.
+ /// The Euler order depends on the [param order] parameter,
+ /// by default it uses the YXZ convention: when decomposing,
+ /// first Z, then X, and Y last. The returned vector contains
+ /// the rotation angles in the format (X angle, Y angle, Z angle).
///
/// Consider using the <see cref="GetRotationQuaternion"/> method instead, which
/// returns a <see cref="Quaternion"/> quaternion instead of Euler angles.
/// </summary>
+ /// <param name="order">The Euler order to use. By default, use YXZ order (most common).</param>
/// <returns>A <see cref="Vector3"/> representing the basis rotation in Euler angles.</returns>
- public Vector3 GetEuler()
+ public Vector3 GetEuler(EulerOrder order = EulerOrder.YXZ)
{
- Basis m = Orthonormalized();
-
- Vector3 euler;
- euler.z = 0.0f;
-
- real_t mzy = m.Row1[2];
-
- if (mzy < 1.0f)
+ switch (order)
{
- if (mzy > -1.0f)
+ case EulerOrder.XYZ:
{
- euler.x = Mathf.Asin(-mzy);
- euler.y = Mathf.Atan2(m.Row0[2], m.Row2[2]);
- euler.z = Mathf.Atan2(m.Row1[0], m.Row1[1]);
+ // Euler angles in XYZ convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cy*cz -cy*sz sy
+ // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx
+ // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy
+ Vector3 euler;
+ real_t sy = Row0[2];
+ if (sy < (1.0f - Mathf.Epsilon))
+ {
+ if (sy > -(1.0f - Mathf.Epsilon))
+ {
+ // is this a pure Y rotation?
+ if (Row1[0] == 0 && Row0[1] == 0 && Row1[2] == 0 && Row2[1] == 0 && Row1[1] == 1)
+ {
+ // return the simplest form (human friendlier in editor and scripts)
+ euler.x = 0;
+ euler.y = Mathf.Atan2(Row0[2], Row0[0]);
+ euler.z = 0;
+ }
+ else
+ {
+ euler.x = Mathf.Atan2(-Row1[2], Row2[2]);
+ euler.y = Mathf.Asin(sy);
+ euler.z = Mathf.Atan2(-Row0[1], Row0[0]);
+ }
+ }
+ else
+ {
+ euler.x = Mathf.Atan2(Row2[1], Row1[1]);
+ euler.y = -Mathf.Tau / 4.0f;
+ euler.z = 0.0f;
+ }
+ }
+ else
+ {
+ euler.x = Mathf.Atan2(Row2[1], Row1[1]);
+ euler.y = Mathf.Tau / 4.0f;
+ euler.z = 0.0f;
+ }
+ return euler;
}
- else
+ case EulerOrder.XZY:
{
- euler.x = Mathf.Pi * 0.5f;
- euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]);
+ // Euler angles in XZY convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy -sz cz*sy
+ // sx*sy+cx*cy*sz cx*cz cx*sz*sy-cy*sx
+ // cy*sx*sz cz*sx cx*cy+sx*sz*sy
+ Vector3 euler;
+ real_t sz = Row0[1];
+ if (sz < (1.0f - Mathf.Epsilon))
+ {
+ if (sz > -(1.0f - Mathf.Epsilon))
+ {
+ euler.x = Mathf.Atan2(Row2[1], Row1[1]);
+ euler.y = Mathf.Atan2(Row0[2], Row0[0]);
+ euler.z = Mathf.Asin(-sz);
+ }
+ else
+ {
+ // It's -1
+ euler.x = -Mathf.Atan2(Row1[2], Row2[2]);
+ euler.y = 0.0f;
+ euler.z = Mathf.Tau / 4.0f;
+ }
+ }
+ else
+ {
+ // It's 1
+ euler.x = -Mathf.Atan2(Row1[2], Row2[2]);
+ euler.y = 0.0f;
+ euler.z = -Mathf.Tau / 4.0f;
+ }
+ return euler;
}
- }
- else
- {
- euler.x = -Mathf.Pi * 0.5f;
- euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]);
- }
+ case EulerOrder.YXZ:
+ {
+ // Euler angles in YXZ convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cy*cz+sy*sx*sz cz*sy*sx-cy*sz cx*sy
+ // cx*sz cx*cz -sx
+ // cy*sx*sz-cz*sy cy*cz*sx+sy*sz cy*cx
+ Vector3 euler;
+ real_t m12 = Row1[2];
+ if (m12 < (1 - Mathf.Epsilon))
+ {
+ if (m12 > -(1 - Mathf.Epsilon))
+ {
+ // is this a pure X rotation?
+ if (Row1[0] == 0 && Row0[1] == 0 && Row0[2] == 0 && Row2[0] == 0 && Row0[0] == 1)
+ {
+ // return the simplest form (human friendlier in editor and scripts)
+ euler.x = Mathf.Atan2(-m12, Row1[1]);
+ euler.y = 0;
+ euler.z = 0;
+ }
+ else
+ {
+ euler.x = Mathf.Asin(-m12);
+ euler.y = Mathf.Atan2(Row0[2], Row2[2]);
+ euler.z = Mathf.Atan2(Row1[0], Row1[1]);
+ }
+ }
+ else
+ { // m12 == -1
+ euler.x = Mathf.Tau / 4.0f;
+ euler.y = Mathf.Atan2(Row0[1], Row0[0]);
+ euler.z = 0;
+ }
+ }
+ else
+ { // m12 == 1
+ euler.x = -Mathf.Tau / 4.0f;
+ euler.y = -Mathf.Atan2(Row0[1], Row0[0]);
+ euler.z = 0;
+ }
- return euler;
+ return euler;
+ }
+ case EulerOrder.YZX:
+ {
+ // Euler angles in YZX convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cy*cz sy*sx-cy*cx*sz cx*sy+cy*sz*sx
+ // sz cz*cx -cz*sx
+ // -cz*sy cy*sx+cx*sy*sz cy*cx-sy*sz*sx
+ Vector3 euler;
+ real_t sz = Row1[0];
+ if (sz < (1.0f - Mathf.Epsilon))
+ {
+ if (sz > -(1.0f - Mathf.Epsilon))
+ {
+ euler.x = Mathf.Atan2(-Row1[2], Row1[1]);
+ euler.y = Mathf.Atan2(-Row2[0], Row0[0]);
+ euler.z = Mathf.Asin(sz);
+ }
+ else
+ {
+ // It's -1
+ euler.x = Mathf.Atan2(Row2[1], Row2[2]);
+ euler.y = 0.0f;
+ euler.z = -Mathf.Tau / 4.0f;
+ }
+ }
+ else
+ {
+ // It's 1
+ euler.x = Mathf.Atan2(Row2[1], Row2[2]);
+ euler.y = 0.0f;
+ euler.z = Mathf.Tau / 4.0f;
+ }
+ return euler;
+ }
+ case EulerOrder.ZXY:
+ {
+ // Euler angles in ZXY convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy-sz*sx*sy -cx*sz cz*sy+cy*sz*sx
+ // cy*sz+cz*sx*sy cz*cx sz*sy-cz*cy*sx
+ // -cx*sy sx cx*cy
+ Vector3 euler;
+ real_t sx = Row2[1];
+ if (sx < (1.0f - Mathf.Epsilon))
+ {
+ if (sx > -(1.0f - Mathf.Epsilon))
+ {
+ euler.x = Mathf.Asin(sx);
+ euler.y = Mathf.Atan2(-Row2[0], Row2[2]);
+ euler.z = Mathf.Atan2(-Row0[1], Row1[1]);
+ }
+ else
+ {
+ // It's -1
+ euler.x = -Mathf.Tau / 4.0f;
+ euler.y = Mathf.Atan2(Row0[2], Row0[0]);
+ euler.z = 0;
+ }
+ }
+ else
+ {
+ // It's 1
+ euler.x = Mathf.Tau / 4.0f;
+ euler.y = Mathf.Atan2(Row0[2], Row0[0]);
+ euler.z = 0;
+ }
+ return euler;
+ }
+ case EulerOrder.ZYX:
+ {
+ // Euler angles in ZYX convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy cz*sy*sx-cx*sz sz*sx+cz*cx*cy
+ // cy*sz cz*cx+sz*sy*sx cx*sz*sy-cz*sx
+ // -sy cy*sx cy*cx
+ Vector3 euler;
+ real_t sy = Row2[0];
+ if (sy < (1.0f - Mathf.Epsilon))
+ {
+ if (sy > -(1.0f - Mathf.Epsilon))
+ {
+ euler.x = Mathf.Atan2(Row2[1], Row2[2]);
+ euler.y = Mathf.Asin(-sy);
+ euler.z = Mathf.Atan2(Row1[0], Row0[0]);
+ }
+ else
+ {
+ // It's -1
+ euler.x = 0;
+ euler.y = Mathf.Tau / 4.0f;
+ euler.z = -Mathf.Atan2(Row0[1], Row1[1]);
+ }
+ }
+ else
+ {
+ // It's 1
+ euler.x = 0;
+ euler.y = -Mathf.Tau / 4.0f;
+ euler.z = -Mathf.Atan2(Row0[1], Row1[1]);
+ }
+ return euler;
+ }
+ default:
+ throw new ArgumentOutOfRangeException(nameof(order));
+ }
}
/// <summary>
/// Returns the basis's rotation in the form of a quaternion.
- /// See <see cref="GetEuler()"/> if you need Euler angles, but keep in
+ /// See <see cref="GetEuler"/> if you need Euler angles, but keep in
/// mind that quaternions should generally be preferred to Euler angles.
/// </summary>
/// <returns>A <see cref="Quaternion"/> representing the basis's rotation.</returns>
@@ -712,35 +935,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a pure rotation basis matrix from the given Euler angles
- /// (in the YXZ convention: when *composing*, first Y, then X, and Z last),
- /// given in the vector format as (X angle, Y angle, Z angle).
- ///
- /// Consider using the <see cref="Basis(Quaternion)"/> constructor instead, which
- /// uses a <see cref="Quaternion"/> quaternion instead of Euler angles.
- /// </summary>
- /// <param name="eulerYXZ">The Euler angles to create the basis from.</param>
- public Basis(Vector3 eulerYXZ)
- {
- real_t c;
- real_t s;
-
- c = Mathf.Cos(eulerYXZ.x);
- s = Mathf.Sin(eulerYXZ.x);
- var xmat = new Basis(1, 0, 0, 0, c, -s, 0, s, c);
-
- c = Mathf.Cos(eulerYXZ.y);
- s = Mathf.Sin(eulerYXZ.y);
- var ymat = new Basis(c, 0, s, 0, 1, 0, -s, 0, c);
-
- c = Mathf.Cos(eulerYXZ.z);
- s = Mathf.Sin(eulerYXZ.z);
- var zmat = new Basis(c, -s, 0, s, c, 0, 0, 0, 1);
-
- this = ymat * xmat * zmat;
- }
-
- /// <summary>
/// Constructs a pure rotation basis matrix, rotated around the given <paramref name="axis"/>
/// by <paramref name="angle"/> (in radians). The axis must be a normalized vector.
/// </summary>
@@ -800,6 +994,46 @@ namespace Godot
}
/// <summary>
+ /// Constructs a Basis matrix from Euler angles in the specified rotation order. By default, use YXZ order (most common).
+ /// </summary>
+ /// <param name="euler">The Euler angles to use.</param>
+ /// <param name="order">The order to compose the Euler angles.</param>
+ public static Basis FromEuler(Vector3 euler, EulerOrder order = EulerOrder.YXZ)
+ {
+ real_t c, s;
+
+ c = Mathf.Cos(euler.x);
+ s = Mathf.Sin(euler.x);
+ Basis xmat = new Basis(new Vector3(1, 0, 0), new Vector3(0, c, s), new Vector3(0, -s, c));
+
+ c = Mathf.Cos(euler.y);
+ s = Mathf.Sin(euler.y);
+ Basis ymat = new Basis(new Vector3(c, 0, -s), new Vector3(0, 1, 0), new Vector3(s, 0, c));
+
+ c = Mathf.Cos(euler.z);
+ s = Mathf.Sin(euler.z);
+ Basis zmat = new Basis(new Vector3(c, s, 0), new Vector3(-s, c, 0), new Vector3(0, 0, 1));
+
+ switch (order)
+ {
+ case EulerOrder.XYZ:
+ return xmat * ymat * zmat;
+ case EulerOrder.XZY:
+ return xmat * zmat * ymat;
+ case EulerOrder.YXZ:
+ return ymat * xmat * zmat;
+ case EulerOrder.YZX:
+ return ymat * zmat * xmat;
+ case EulerOrder.ZXY:
+ return zmat * xmat * ymat;
+ case EulerOrder.ZYX:
+ return zmat * ymat * xmat;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(order));
+ }
+ }
+
+ /// <summary>
/// Constructs a pure scale basis matrix with no rotation or shearing.
/// The scale values are set as the main diagonal of the matrix,
/// and all of the other parts of the matrix are zero.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
index 1b7f5158fd..bdedd2e87a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
@@ -72,7 +72,7 @@ namespace Godot
/// <param name="delegate">Delegate method that will be called.</param>
public Callable(Delegate @delegate)
{
- _target = null;
+ _target = @delegate?.Target as Object;
_method = null;
_delegate = @delegate;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
index 140fc167ba..76b186cd15 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
@@ -721,8 +721,9 @@ namespace Godot.NativeInterop
if (p_managed_callable.Delegate != null)
{
var gcHandle = CustomGCHandle.AllocStrong(p_managed_callable.Delegate);
+ IntPtr objectPtr = p_managed_callable.Target != null ? Object.GetPtr(p_managed_callable.Target) : IntPtr.Zero;
NativeFuncs.godotsharp_callable_new_with_delegate(
- GCHandle.ToIntPtr(gcHandle), out godot_callable callable);
+ GCHandle.ToIntPtr(gcHandle), objectPtr, out godot_callable callable);
return callable;
}
else
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
index bd00611383..20ede9f0dd 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -141,7 +141,7 @@ namespace Godot.NativeInterop
public static partial void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest,
in godot_string p_element);
- public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle,
+ public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, IntPtr p_object,
out godot_callable r_callable);
internal static partial godot_bool godotsharp_callable_get_data_for_marshalling(in godot_callable p_callable,
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index 276701cdaa..2717b945f6 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -447,9 +447,10 @@ void godotsharp_packed_string_array_add(PackedStringArray *r_dest, const String
r_dest->append(*p_element);
}
-void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, Callable *r_callable) {
+void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, const Object *p_object, Callable *r_callable) {
// TODO: Use pooling for ManagedCallable instances.
- CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle));
+ ObjectID objid = p_object ? p_object->get_instance_id() : ObjectID();
+ CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle, objid));
memnew_placement(r_callable, Callable(managed_callable));
}
diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp
index 9305dc645a..0c2c533090 100644
--- a/modules/mono/managed_callable.cpp
+++ b/modules/mono/managed_callable.cpp
@@ -79,7 +79,9 @@ CallableCustom::CompareLessFunc ManagedCallable::get_compare_less_func() const {
}
ObjectID ManagedCallable::get_object() const {
- // TODO: If the delegate target extends Godot.Object, use that instead!
+ if (object_id != ObjectID()) {
+ return object_id;
+ }
return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id();
}
@@ -104,7 +106,7 @@ void ManagedCallable::release_delegate_handle() {
// Why you do this clang-format...
/* clang-format off */
-ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle) : delegate_handle(p_delegate_handle) {
+ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle, ObjectID p_object_id) : delegate_handle(p_delegate_handle), object_id(p_object_id) {
#ifdef GD_MONO_HOT_RELOAD
{
MutexLock lock(instances_mutex);
diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h
index aa3344f4d5..26cd164fb6 100644
--- a/modules/mono/managed_callable.h
+++ b/modules/mono/managed_callable.h
@@ -40,6 +40,7 @@
class ManagedCallable : public CallableCustom {
friend class CSharpLanguage;
GCHandleIntPtr delegate_handle;
+ ObjectID object_id;
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable> self_instance = this;
@@ -66,7 +67,7 @@ public:
void release_delegate_handle();
- ManagedCallable(GCHandleIntPtr p_delegate_handle);
+ ManagedCallable(GCHandleIntPtr p_delegate_handle, ObjectID p_object_id);
~ManagedCallable();
};
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index 6e3dbfab47..53d8e82dfc 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -125,9 +125,12 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c
MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
+ const ObjectID oid = node->get_instance_id();
+ MultiplayerSpawner *spawner = rep_state->get_spawner(oid);
+ ERR_FAIL_COND_V_MSG(spawner && spawner->get_multiplayer_authority() != sync->get_multiplayer_authority(), ERR_INVALID_PARAMETER, "The authority of the MultiplayerSynchronizer \"" + String(sync->get_path()) + "\" differs from the authority of its \"root_node\" spawner and will not sync. Change the \"root_node\" of the MultiplayerSynchronizer to be a child of the scene root instead.");
+
// Add to synchronizer list and setup visibility.
rep_state->config_add_sync(node, sync);
- const ObjectID oid = node->get_instance_id();
sync->connect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed).bind(oid));
if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) {
_update_sync_visibility(0, oid);
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 394c32f20d..83862e1e34 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -238,6 +238,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
to_visit.clear();
to_visit.push_back(0);
least_cost_id = 0;
+ prev_least_cost_id = -1;
reachable_end = nullptr;
diff --git a/platform/android/detect.py b/platform/android/detect.py
index f3e3f80dd5..e541aa0373 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -3,6 +3,11 @@ import sys
import platform
import subprocess
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
def is_active():
return True
@@ -17,8 +22,6 @@ def can_build():
def get_opts():
- from SCons.Variables import BoolVariable, EnumVariable
-
return [
("ANDROID_SDK_ROOT", "Path to the Android SDK", get_env_android_sdk_root()),
("ndk_platform", 'Target platform (android-<api>, e.g. "android-24")', "android-24"),
@@ -74,7 +77,7 @@ def install_ndk_if_needed(env):
env["ANDROID_NDK_ROOT"] = get_android_ndk_root(env)
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
if env["arch"] not in supported_arches:
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index 74561e9fc5..38e62134b5 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -2,6 +2,11 @@ import os
import sys
from methods import detect_darwin_sdk_path
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
def is_active():
return True
@@ -42,7 +47,7 @@ def get_flags():
]
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
if env["arch"] not in supported_arches:
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 92af7e2d75..dfde0d249c 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -4,6 +4,11 @@ import sys
from methods import get_compiler_version, using_gcc
from platform_methods import detect_arch
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
def is_active():
return True
@@ -55,7 +60,7 @@ def get_flags():
]
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64"]
if env["arch"] not in supported_arches:
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 66dea6cf1b..81fc941608 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -3142,6 +3142,11 @@ void DisplayServerX11::_window_changed(XEvent *event) {
return;
}
+ // Query display server about a possible new window state.
+ wd.fullscreen = _window_fullscreen_check(window_id);
+ wd.minimized = _window_minimize_check(window_id);
+ wd.maximized = _window_maximize_check(window_id, "_NET_WM_STATE");
+
{
//the position in xconfigure is not useful here, obtain it manually
int x, y;
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index 58d209cc7b..511286d52b 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -3,6 +3,11 @@ import sys
from methods import detect_darwin_sdk_path
from platform_methods import detect_arch
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
def is_active():
return True
@@ -72,7 +77,7 @@ def get_mvk_sdk_path():
return os.path.join(os.path.join(dirname, ver_file), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/")
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
if env["arch"] not in supported_arches:
diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py
index 2c5746cb06..64fe5bc4a2 100644
--- a/platform/uwp/detect.py
+++ b/platform/uwp/detect.py
@@ -3,6 +3,11 @@ import os
import sys
from platform_methods import detect_arch
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
def is_active():
return True
@@ -39,7 +44,7 @@ def get_flags():
]
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32"]
if env["arch"] not in supported_arches:
@@ -83,7 +88,7 @@ def configure(env):
env.AppendUnique(CCFLAGS=["/utf-8"])
# ANGLE
- angle_root = os.getenv("ANGLE_SRC_PATH")
+ angle_root = os.environ["ANGLE_SRC_PATH"]
env.Prepend(CPPPATH=[angle_root + "/include"])
jobs = str(env.GetOption("num_jobs"))
angle_build_cmd = (
@@ -94,7 +99,7 @@ def configure(env):
+ " /p:Configuration=Release /p:Platform="
)
- if os.path.isfile(str(os.getenv("ANGLE_SRC_PATH")) + "/winrt/10/src/angle.sln"):
+ if os.path.isfile(f"{angle_root}/winrt/10/src/angle.sln"):
env["build_angle"] = True
## Architecture
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 77921847a8..08c1ff7b4a 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -11,6 +11,10 @@ from emscripten_helpers import (
)
from methods import get_compiler_version
from SCons.Util import WhereIs
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
def is_active():
@@ -60,7 +64,7 @@ def get_flags():
]
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["wasm32"]
if env["arch"] not in supported_arches:
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index b184da49e4..a5d8d0344b 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -4,6 +4,11 @@ import subprocess
import sys
from platform_methods import detect_arch
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
# To match other platforms
STACK_SIZE = 8388608
@@ -588,7 +593,7 @@ def configure_mingw(env):
env.Append(BUILDERS={"RES": env.Builder(action=build_res_file, suffix=".o", src_suffix=".rc")})
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
if env["arch"] not in supported_arches:
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 549cc19cb1..05b4f181c2 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -2799,7 +2799,7 @@ void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local
}
// Shortcut Input.
- if (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr) {
+ if (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr || Object::cast_to<InputEventJoypadButton>(*ev) != nullptr) {
get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, ev, this);
}
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index ae83e2136f..65f3767449 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -5275,6 +5275,7 @@ void TileData::set_terrain_set(int p_terrain_set) {
}
if (tile_set) {
ERR_FAIL_COND(p_terrain_set >= tile_set->get_terrain_sets_count());
+ terrain = -1;
for (int i = 0; i < 16; i++) {
terrain_peering_bits[i] = -1;
}
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
index cde241f231..1e70f6880c 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -230,6 +230,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
float sh[9 * 4];
};
+ // When changing any of these enums, remember to change the corresponding enums in the shader files as well.
enum {
INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4,
INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5,
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
index 1b31d2749d..a53872fc88 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -371,7 +371,7 @@ protected:
/* Geometry instance */
- // check which ones of these apply, probably all except GI and SDFGI
+ // When changing any of these enums, remember to change the corresponding enums in the shader files as well.
enum {
INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4,
INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5,
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
index e8e2dce990..0d36b98645 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
@@ -62,13 +62,14 @@ layout(set = 0, binding = 3) uniform sampler decal_sampler;
layout(set = 0, binding = 4) uniform sampler light_projector_sampler;
-#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 5)
-#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 6)
-#define INSTANCE_FLAGS_USE_SDFGI (1 << 7)
-#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 8)
-#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 9)
-#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 10)
-#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 11)
+#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4)
+#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5)
+#define INSTANCE_FLAGS_USE_SDFGI (1 << 6)
+#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 7)
+#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 8)
+#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 9)
+#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 10)
+#define INSTANCE_FLAGS_PARTICLES (1 << 11)
#define INSTANCE_FLAGS_MULTIMESH (1 << 12)
#define INSTANCE_FLAGS_MULTIMESH_FORMAT_2D (1 << 13)
#define INSTANCE_FLAGS_MULTIMESH_HAS_COLOR (1 << 14)
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl
index 5e4999fa0f..631ff0575b 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl
@@ -55,13 +55,14 @@ layout(set = 0, binding = 2) uniform sampler shadow_sampler;
layout(set = 0, binding = 3) uniform sampler decal_sampler;
layout(set = 0, binding = 4) uniform sampler light_projector_sampler;
-#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 5)
-#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 6)
-#define INSTANCE_FLAGS_USE_SDFGI (1 << 7)
-#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 8)
-#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 9)
-#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 10)
-#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 11)
+#define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4)
+#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5)
+#define INSTANCE_FLAGS_USE_SDFGI (1 << 6)
+#define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 7)
+#define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 8)
+#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 9)
+#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 10)
+#define INSTANCE_FLAGS_PARTICLES (1 << 11)
#define INSTANCE_FLAGS_MULTIMESH (1 << 12)
#define INSTANCE_FLAGS_MULTIMESH_FORMAT_2D (1 << 13)
#define INSTANCE_FLAGS_MULTIMESH_HAS_COLOR (1 << 14)
diff --git a/tests/core/math/test_vector3.h b/tests/core/math/test_vector3.h
index 52118fa943..be271bad1f 100644
--- a/tests/core/math/test_vector3.h
+++ b/tests/core/math/test_vector3.h
@@ -84,16 +84,12 @@ TEST_CASE("[Vector3] Axis methods") {
vector.min_axis_index() == Vector3::Axis::AXIS_X,
"Vector3 min_axis_index should work as expected.");
CHECK_MESSAGE(
- vector.get_axis(vector.max_axis_index()) == (real_t)5.6,
- "Vector3 get_axis should work as expected.");
+ vector[vector.max_axis_index()] == (real_t)5.6,
+ "Vector3 array operator should work as expected.");
CHECK_MESSAGE(
vector[vector.min_axis_index()] == (real_t)1.2,
"Vector3 array operator should work as expected.");
- vector.set_axis(Vector3::Axis::AXIS_Y, 4.7);
- CHECK_MESSAGE(
- vector.get_axis(Vector3::Axis::AXIS_Y) == (real_t)4.7,
- "Vector3 set_axis should work as expected.");
vector[Vector3::Axis::AXIS_Y] = 3.7;
CHECK_MESSAGE(
vector[Vector3::Axis::AXIS_Y] == (real_t)3.7,
diff --git a/tests/core/math/test_vector3i.h b/tests/core/math/test_vector3i.h
index 6c52781556..2050b222d0 100644
--- a/tests/core/math/test_vector3i.h
+++ b/tests/core/math/test_vector3i.h
@@ -53,16 +53,12 @@ TEST_CASE("[Vector3i] Axis methods") {
vector.min_axis_index() == Vector3i::Axis::AXIS_X,
"Vector3i min_axis_index should work as expected.");
CHECK_MESSAGE(
- vector.get_axis(vector.max_axis_index()) == 3,
- "Vector3i get_axis should work as expected.");
+ vector[vector.max_axis_index()] == 3,
+ "Vector3i array operator should work as expected.");
CHECK_MESSAGE(
vector[vector.min_axis_index()] == 1,
"Vector3i array operator should work as expected.");
- vector.set_axis(Vector3i::Axis::AXIS_Y, 4);
- CHECK_MESSAGE(
- vector.get_axis(Vector3i::Axis::AXIS_Y) == 4,
- "Vector3i set_axis should work as expected.");
vector[Vector3i::Axis::AXIS_Y] = 5;
CHECK_MESSAGE(
vector[Vector3i::Axis::AXIS_Y] == 5,
diff --git a/tests/core/math/test_vector4.h b/tests/core/math/test_vector4.h
index 25ec8929b8..3f50f16635 100644
--- a/tests/core/math/test_vector4.h
+++ b/tests/core/math/test_vector4.h
@@ -55,16 +55,12 @@ TEST_CASE("[Vector4] Axis methods") {
vector.min_axis_index() == Vector4::Axis::AXIS_W,
"Vector4 min_axis_index should work as expected.");
CHECK_MESSAGE(
- vector.get_axis(vector.max_axis_index()) == (real_t)5.6,
- "Vector4 get_axis should work as expected.");
+ vector[vector.max_axis_index()] == (real_t)5.6,
+ "Vector4 array operator should work as expected.");
CHECK_MESSAGE(
vector[vector.min_axis_index()] == (real_t)-0.9,
"Vector4 array operator should work as expected.");
- vector.set_axis(Vector4::Axis::AXIS_Y, 4.7);
- CHECK_MESSAGE(
- vector.get_axis(Vector4::Axis::AXIS_Y) == (real_t)4.7,
- "Vector4 set_axis should work as expected.");
vector[Vector4::Axis::AXIS_Y] = 3.7;
CHECK_MESSAGE(
vector[Vector4::Axis::AXIS_Y] == (real_t)3.7,
diff --git a/tests/core/math/test_vector4i.h b/tests/core/math/test_vector4i.h
index e106099914..309162c3f7 100644
--- a/tests/core/math/test_vector4i.h
+++ b/tests/core/math/test_vector4i.h
@@ -53,16 +53,12 @@ TEST_CASE("[Vector4i] Axis methods") {
vector.min_axis_index() == Vector4i::Axis::AXIS_X,
"Vector4i min_axis_index should work as expected.");
CHECK_MESSAGE(
- vector.get_axis(vector.max_axis_index()) == 4,
- "Vector4i get_axis should work as expected.");
+ vector[vector.max_axis_index()] == 4,
+ "Vector4i array operator should work as expected.");
CHECK_MESSAGE(
vector[vector.min_axis_index()] == 1,
"Vector4i array operator should work as expected.");
- vector.set_axis(Vector4i::Axis::AXIS_Y, 5);
- CHECK_MESSAGE(
- vector.get_axis(Vector4i::Axis::AXIS_Y) == 5,
- "Vector4i set_axis should work as expected.");
vector[Vector4i::Axis::AXIS_Y] = 5;
CHECK_MESSAGE(
vector[Vector4i::Axis::AXIS_Y] == 5,
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index d97da05c04..969f5fc096 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -740,6 +740,14 @@ TEST_CASE("[String] sprintf") {
REQUIRE(error == false);
CHECK(output == String("fish 99.990000 frog"));
+ // Real (infinity) left-padded
+ format = "fish %11f frog";
+ args.clear();
+ args.push_back(INFINITY);
+ output = format.sprintf(args, &error);
+ REQUIRE(error == false);
+ CHECK(output == String("fish inf frog"));
+
// Real right-padded.
format = "fish %-11f frog";
args.clear();
@@ -840,6 +848,14 @@ TEST_CASE("[String] sprintf") {
REQUIRE(error == false);
CHECK(output == String("fish ( 19.990000, 1.000000, -2.050000) frog"));
+ // Vector left-padded with inf/nan
+ format = "fish %11v frog";
+ args.clear();
+ args.push_back(Variant(Vector2(INFINITY, NAN)));
+ output = format.sprintf(args, &error);
+ REQUIRE(error == false);
+ CHECK(output == String("fish ( inf, nan) frog"));
+
// Vector right-padded.
format = "fish %-11v frog";
args.clear();