summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/CODEOWNERS4
-rw-r--r--README.md1
-rw-r--r--SConstruct2
-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--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_map_editor.cpp54
-rw-r--r--methods.py1
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp61
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp2
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.h4
-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/navigation/nav_map.cpp1
-rw-r--r--platform/linuxbsd/display_server_x11.cpp5
-rw-r--r--scene/main/viewport.cpp2
-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
50 files changed, 1873 insertions, 1610 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/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/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/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_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/methods.py b/methods.py
index dadac37cb5..7feffb2848 100644
--- a/methods.py
+++ b/methods.py
@@ -1,4 +1,5 @@
import os
+import sys
import re
import glob
import subprocess
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/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/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/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/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/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/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();