diff options
168 files changed, 1416 insertions, 492 deletions
diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 9503bd2575..58b2bc0492 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -613,27 +613,6 @@ void OS::_bind_methods() { BIND_ENUM_CONSTANT(RENDERING_DRIVER_VULKAN); BIND_ENUM_CONSTANT(RENDERING_DRIVER_OPENGL3); - BIND_ENUM_CONSTANT(DAY_SUNDAY); - BIND_ENUM_CONSTANT(DAY_MONDAY); - BIND_ENUM_CONSTANT(DAY_TUESDAY); - BIND_ENUM_CONSTANT(DAY_WEDNESDAY); - BIND_ENUM_CONSTANT(DAY_THURSDAY); - BIND_ENUM_CONSTANT(DAY_FRIDAY); - BIND_ENUM_CONSTANT(DAY_SATURDAY); - - BIND_ENUM_CONSTANT(MONTH_JANUARY); - BIND_ENUM_CONSTANT(MONTH_FEBRUARY); - BIND_ENUM_CONSTANT(MONTH_MARCH); - BIND_ENUM_CONSTANT(MONTH_APRIL); - BIND_ENUM_CONSTANT(MONTH_MAY); - BIND_ENUM_CONSTANT(MONTH_JUNE); - BIND_ENUM_CONSTANT(MONTH_JULY); - BIND_ENUM_CONSTANT(MONTH_AUGUST); - BIND_ENUM_CONSTANT(MONTH_SEPTEMBER); - BIND_ENUM_CONSTANT(MONTH_OCTOBER); - BIND_ENUM_CONSTANT(MONTH_NOVEMBER); - BIND_ENUM_CONSTANT(MONTH_DECEMBER); - BIND_ENUM_CONSTANT(SYSTEM_DIR_DESKTOP); BIND_ENUM_CONSTANT(SYSTEM_DIR_DCIM); BIND_ENUM_CONSTANT(SYSTEM_DIR_DOCUMENTS); diff --git a/core/core_bind.h b/core/core_bind.h index c863a8094c..e556b1878f 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -129,33 +129,6 @@ public: RENDERING_DRIVER_OPENGL3, }; - enum Weekday { - DAY_SUNDAY, - DAY_MONDAY, - DAY_TUESDAY, - DAY_WEDNESDAY, - DAY_THURSDAY, - DAY_FRIDAY, - DAY_SATURDAY - }; - - enum Month { - // Start at 1 to follow Windows SYSTEMTIME structure - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx - MONTH_JANUARY = 1, - MONTH_FEBRUARY, - MONTH_MARCH, - MONTH_APRIL, - MONTH_MAY, - MONTH_JUNE, - MONTH_JULY, - MONTH_AUGUST, - MONTH_SEPTEMBER, - MONTH_OCTOBER, - MONTH_NOVEMBER, - MONTH_DECEMBER - }; - virtual PackedStringArray get_connected_midi_inputs(); virtual void open_midi_inputs(); virtual void close_midi_inputs(); @@ -583,8 +556,6 @@ VARIANT_ENUM_CAST(core_bind::ResourceLoader::CacheMode); VARIANT_BITFIELD_CAST(core_bind::ResourceSaver::SaverFlags); VARIANT_ENUM_CAST(core_bind::OS::RenderingDriver); -VARIANT_ENUM_CAST(core_bind::OS::Weekday); -VARIANT_ENUM_CAST(core_bind::OS::Month); VARIANT_ENUM_CAST(core_bind::OS::SystemDir); VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyBooleanOperation); diff --git a/core/input/input.cpp b/core/input/input.cpp index cb65d2569c..554f8fad00 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -540,6 +540,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em drag_event->set_position(position); drag_event->set_relative(relative); + drag_event->set_tilt(mm->get_tilt()); + drag_event->set_pen_inverted(mm->get_pen_inverted()); + drag_event->set_pressure(mm->get_pressure()); drag_event->set_velocity(get_last_mouse_velocity()); event_dispatch_function(drag_event); @@ -605,6 +608,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em motion_event.instantiate(); motion_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE); + motion_event->set_tilt(sd->get_tilt()); + motion_event->set_pen_inverted(sd->get_pen_inverted()); + motion_event->set_pressure(sd->get_pressure()); motion_event->set_position(sd->get_position()); motion_event->set_global_position(sd->get_position()); motion_event->set_relative(sd->get_relative()); diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 0a32b4bf68..4859490230 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -1223,6 +1223,30 @@ int InputEventScreenDrag::get_index() const { return index; } +void InputEventScreenDrag::set_tilt(const Vector2 &p_tilt) { + tilt = p_tilt; +} + +Vector2 InputEventScreenDrag::get_tilt() const { + return tilt; +} + +void InputEventScreenDrag::set_pressure(float p_pressure) { + pressure = p_pressure; +} + +float InputEventScreenDrag::get_pressure() const { + return pressure; +} + +void InputEventScreenDrag::set_pen_inverted(bool p_inverted) { + pen_inverted = p_inverted; +} + +bool InputEventScreenDrag::get_pen_inverted() const { + return pen_inverted; +} + void InputEventScreenDrag::set_position(const Vector2 &p_pos) { pos = p_pos; } @@ -1256,6 +1280,9 @@ Ref<InputEvent> InputEventScreenDrag::xformed_by(const Transform2D &p_xform, con sd->set_window_id(get_window_id()); sd->set_index(index); + sd->set_pressure(get_pressure()); + sd->set_pen_inverted(get_pen_inverted()); + sd->set_tilt(get_tilt()); sd->set_position(p_xform.xform(pos + p_local_ofs)); sd->set_relative(p_xform.basis_xform(relative)); sd->set_velocity(p_xform.basis_xform(velocity)); @@ -1268,7 +1295,7 @@ String InputEventScreenDrag::as_text() const { } String InputEventScreenDrag::to_string() { - return vformat("InputEventScreenDrag: index=%d, position=(%s), relative=(%s), velocity=(%s)", index, String(get_position()), String(get_relative()), String(get_velocity())); + return vformat("InputEventScreenDrag: index=%d, position=(%s), relative=(%s), velocity=(%s), pressure=%.2f, tilt=(%s), pen_inverted=(%s)", index, String(get_position()), String(get_relative()), String(get_velocity()), get_pressure(), String(get_tilt()), get_pen_inverted()); } bool InputEventScreenDrag::accumulate(const Ref<InputEvent> &p_event) { @@ -1292,6 +1319,15 @@ void InputEventScreenDrag::_bind_methods() { ClassDB::bind_method(D_METHOD("set_index", "index"), &InputEventScreenDrag::set_index); ClassDB::bind_method(D_METHOD("get_index"), &InputEventScreenDrag::get_index); + ClassDB::bind_method(D_METHOD("set_tilt", "tilt"), &InputEventScreenDrag::set_tilt); + ClassDB::bind_method(D_METHOD("get_tilt"), &InputEventScreenDrag::get_tilt); + + ClassDB::bind_method(D_METHOD("set_pressure", "pressure"), &InputEventScreenDrag::set_pressure); + ClassDB::bind_method(D_METHOD("get_pressure"), &InputEventScreenDrag::get_pressure); + + ClassDB::bind_method(D_METHOD("set_pen_inverted", "pen_inverted"), &InputEventScreenDrag::set_pen_inverted); + ClassDB::bind_method(D_METHOD("get_pen_inverted"), &InputEventScreenDrag::get_pen_inverted); + ClassDB::bind_method(D_METHOD("set_position", "position"), &InputEventScreenDrag::set_position); ClassDB::bind_method(D_METHOD("get_position"), &InputEventScreenDrag::get_position); @@ -1302,6 +1338,9 @@ void InputEventScreenDrag::_bind_methods() { ClassDB::bind_method(D_METHOD("get_velocity"), &InputEventScreenDrag::get_velocity); ADD_PROPERTY(PropertyInfo(Variant::INT, "index"), "set_index", "get_index"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "tilt"), "set_tilt", "get_tilt"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pressure"), "set_pressure", "get_pressure"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pen_inverted"), "set_pen_inverted", "get_pen_inverted"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_NONE, "suffix:px"), "set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "relative", PROPERTY_HINT_NONE, "suffix:px"), "set_relative", "get_relative"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "suffix:px/s"), "set_velocity", "get_velocity"); diff --git a/core/input/input_event.h b/core/input/input_event.h index adbcb7cf68..06c3118abc 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -384,6 +384,9 @@ class InputEventScreenDrag : public InputEventFromWindow { Vector2 pos; Vector2 relative; Vector2 velocity; + Vector2 tilt; + float pressure = 0; + bool pen_inverted = false; protected: static void _bind_methods(); @@ -392,6 +395,15 @@ public: void set_index(int p_index); int get_index() const; + void set_tilt(const Vector2 &p_tilt); + Vector2 get_tilt() const; + + void set_pressure(float p_pressure); + float get_pressure() const; + + void set_pen_inverted(bool p_inverted); + bool get_pen_inverted() const; + void set_position(const Vector2 &p_pos); Vector2 get_position() const; diff --git a/core/os/os.h b/core/os/os.h index 07e9020a51..a5885b1aaa 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -34,6 +34,7 @@ #include "core/config/engine.h" #include "core/io/image.h" #include "core/io/logger.h" +#include "core/os/time_enums.h" #include "core/string/ustring.h" #include "core/templates/list.h" #include "core/templates/vector.h" @@ -185,33 +186,6 @@ public: virtual void yield(); - enum Weekday : uint8_t { - WEEKDAY_SUNDAY, - WEEKDAY_MONDAY, - WEEKDAY_TUESDAY, - WEEKDAY_WEDNESDAY, - WEEKDAY_THURSDAY, - WEEKDAY_FRIDAY, - WEEKDAY_SATURDAY, - }; - - enum Month : uint8_t { - /// Start at 1 to follow Windows SYSTEMTIME structure - /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx - MONTH_JANUARY = 1, - MONTH_FEBRUARY, - MONTH_MARCH, - MONTH_APRIL, - MONTH_MAY, - MONTH_JUNE, - MONTH_JULY, - MONTH_AUGUST, - MONTH_SEPTEMBER, - MONTH_OCTOBER, - MONTH_NOVEMBER, - MONTH_DECEMBER, - }; - struct DateTime { int64_t year; Month month; diff --git a/core/os/time.cpp b/core/os/time.cpp index a3c2c99b4c..3fc831d124 100644 --- a/core/os/time.cpp +++ b/core/os/time.cpp @@ -52,8 +52,8 @@ static const uint8_t MONTH_DAYS_TABLE[2][12] = { { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; -VARIANT_ENUM_CAST(Time::Month); -VARIANT_ENUM_CAST(Time::Weekday); +VARIANT_ENUM_CAST(Month); +VARIANT_ENUM_CAST(Weekday); #define UNIX_TIME_TO_HMS \ uint8_t hour, minute, second; \ diff --git a/core/os/time.h b/core/os/time.h index 4b4ce3526a..379a224f34 100644 --- a/core/os/time.h +++ b/core/os/time.h @@ -32,6 +32,7 @@ #define TIME_H #include "core/object/class_db.h" +#include "time_enums.h" // This Time class conforms with as many of the ISO 8601 standards as possible. // * As per ISO 8601:2004 4.3.2.1, all dates follow the Proleptic Gregorian @@ -51,33 +52,6 @@ class Time : public Object { public: static Time *get_singleton(); - enum Month { - /// Start at 1 to follow Windows SYSTEMTIME structure - /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx - MONTH_JANUARY = 1, - MONTH_FEBRUARY, - MONTH_MARCH, - MONTH_APRIL, - MONTH_MAY, - MONTH_JUNE, - MONTH_JULY, - MONTH_AUGUST, - MONTH_SEPTEMBER, - MONTH_OCTOBER, - MONTH_NOVEMBER, - MONTH_DECEMBER, - }; - - enum Weekday : uint8_t { - WEEKDAY_SUNDAY, - WEEKDAY_MONDAY, - WEEKDAY_TUESDAY, - WEEKDAY_WEDNESDAY, - WEEKDAY_THURSDAY, - WEEKDAY_FRIDAY, - WEEKDAY_SATURDAY, - }; - // Methods that convert times. Dictionary get_datetime_dict_from_unix_time(int64_t p_unix_time_val) const; Dictionary get_date_dict_from_unix_time(int64_t p_unix_time_val) const; diff --git a/core/os/time_enums.h b/core/os/time_enums.h new file mode 100644 index 0000000000..aa2d97a308 --- /dev/null +++ b/core/os/time_enums.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* time_enums.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 TIME_ENUMS_H +#define TIME_ENUMS_H + +#include <cstdint> + +enum Month { + /// Start at 1 to follow Windows SYSTEMTIME structure + /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx + MONTH_JANUARY = 1, + MONTH_FEBRUARY, + MONTH_MARCH, + MONTH_APRIL, + MONTH_MAY, + MONTH_JUNE, + MONTH_JULY, + MONTH_AUGUST, + MONTH_SEPTEMBER, + MONTH_OCTOBER, + MONTH_NOVEMBER, + MONTH_DECEMBER, +}; + +enum Weekday : uint8_t { + WEEKDAY_SUNDAY, + WEEKDAY_MONDAY, + WEEKDAY_TUESDAY, + WEEKDAY_WEDNESDAY, + WEEKDAY_THURSDAY, + WEEKDAY_FRIDAY, + WEEKDAY_SATURDAY, +}; + +#endif // TIME_ENUMS_H diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index ac569941bf..57d62dac91 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -65,11 +65,13 @@ static _FORCE_INLINE_ void vc_method_call(R (T::*method)(P...) const, Variant *b template <class T, class... P> static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { + VariantInternal::clear(&r_ret); call_with_variant_args_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals); } template <class T, class... P> static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { + VariantInternal::clear(&r_ret); call_with_variant_argsc_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals); } diff --git a/doc/classes/CharacterBody3D.xml b/doc/classes/CharacterBody3D.xml index 309e2231a4..821117122c 100644 --- a/doc/classes/CharacterBody3D.xml +++ b/doc/classes/CharacterBody3D.xml @@ -41,10 +41,16 @@ Returns a [KinematicCollision3D], which contains information about the latest collision that occurred during the last call to [method move_and_slide]. </description> </method> + <method name="get_platform_angular_velocity" qualifiers="const"> + <return type="Vector3" /> + <description> + Returns the angular velocity of the platform at the last collision point. Only valid after calling [method move_and_slide]. + </description> + </method> <method name="get_platform_velocity" qualifiers="const"> <return type="Vector3" /> <description> - Returns the linear velocity of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code]. + Returns the linear velocity of the platform at the last collision point. Only valid after calling [method move_and_slide]. </description> </method> <method name="get_position_delta" qualifiers="const"> diff --git a/doc/classes/InputEventScreenDrag.xml b/doc/classes/InputEventScreenDrag.xml index e5cc522b21..c1d126ca4a 100644 --- a/doc/classes/InputEventScreenDrag.xml +++ b/doc/classes/InputEventScreenDrag.xml @@ -13,12 +13,21 @@ <member name="index" type="int" setter="set_index" getter="get_index" default="0"> The drag event index in the case of a multi-drag event. </member> + <member name="pen_inverted" type="bool" setter="set_pen_inverted" getter="get_pen_inverted" default="false"> + Returns [code]true[/code] when using the eraser end of a stylus pen. + </member> <member name="position" type="Vector2" setter="set_position" getter="get_position" default="Vector2(0, 0)"> The drag position. </member> + <member name="pressure" type="float" setter="set_pressure" getter="get_pressure" default="0.0"> + Represents the pressure the user puts on the pen. Ranges from [code]0.0[/code] to [code]1.0[/code]. + </member> <member name="relative" type="Vector2" setter="set_relative" getter="get_relative" default="Vector2(0, 0)"> The drag position relative to the previous position (position at the last frame). </member> + <member name="tilt" type="Vector2" setter="set_tilt" getter="get_tilt" default="Vector2(0, 0)"> + Represents the angles of tilt of the pen. Positive X-coordinate value indicates a tilt to the right. Positive Y-coordinate value indicates a tilt toward the user. Ranges from [code]-1.0[/code] to [code]1.0[/code] for both axes. + </member> <member name="velocity" type="Vector2" setter="set_velocity" getter="get_velocity" default="Vector2(0, 0)"> The drag velocity. </member> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 0efc6ab399..ff4982e2fb 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -654,63 +654,6 @@ <constant name="RENDERING_DRIVER_OPENGL3" value="1" enum="RenderingDriver"> The OpenGL 3 rendering driver. It uses OpenGL 3.3 Core Profile on desktop platforms, OpenGL ES 3.0 on mobile devices, and WebGL 2.0 on Web. </constant> - <constant name="DAY_SUNDAY" value="0" enum="Weekday"> - Sunday. - </constant> - <constant name="DAY_MONDAY" value="1" enum="Weekday"> - Monday. - </constant> - <constant name="DAY_TUESDAY" value="2" enum="Weekday"> - Tuesday. - </constant> - <constant name="DAY_WEDNESDAY" value="3" enum="Weekday"> - Wednesday. - </constant> - <constant name="DAY_THURSDAY" value="4" enum="Weekday"> - Thursday. - </constant> - <constant name="DAY_FRIDAY" value="5" enum="Weekday"> - Friday. - </constant> - <constant name="DAY_SATURDAY" value="6" enum="Weekday"> - Saturday. - </constant> - <constant name="MONTH_JANUARY" value="1" enum="Month"> - January. - </constant> - <constant name="MONTH_FEBRUARY" value="2" enum="Month"> - February. - </constant> - <constant name="MONTH_MARCH" value="3" enum="Month"> - March. - </constant> - <constant name="MONTH_APRIL" value="4" enum="Month"> - April. - </constant> - <constant name="MONTH_MAY" value="5" enum="Month"> - May. - </constant> - <constant name="MONTH_JUNE" value="6" enum="Month"> - June. - </constant> - <constant name="MONTH_JULY" value="7" enum="Month"> - July. - </constant> - <constant name="MONTH_AUGUST" value="8" enum="Month"> - August. - </constant> - <constant name="MONTH_SEPTEMBER" value="9" enum="Month"> - September. - </constant> - <constant name="MONTH_OCTOBER" value="10" enum="Month"> - October. - </constant> - <constant name="MONTH_NOVEMBER" value="11" enum="Month"> - November. - </constant> - <constant name="MONTH_DECEMBER" value="12" enum="Month"> - December. - </constant> <constant name="SYSTEM_DIR_DESKTOP" value="0" enum="SystemDir"> Desktop directory path. </constant> diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml index 2d25965180..f1316fa991 100644 --- a/doc/classes/PhysicsServer2D.xml +++ b/doc/classes/PhysicsServer2D.xml @@ -805,6 +805,23 @@ Sets a joint parameter. See [enum JointParam] for a list of available parameters. </description> </method> + <method name="pin_joint_get_param" qualifiers="const"> + <return type="float" /> + <param index="0" name="joint" type="RID" /> + <param index="1" name="param" type="int" enum="PhysicsServer2D.PinJointParam" /> + <description> + Returns the value of a pin joint parameter. See [enum PinJointParam] for a list of available parameters. + </description> + </method> + <method name="pin_joint_set_param"> + <return type="void" /> + <param index="0" name="joint" type="RID" /> + <param index="1" name="param" type="int" enum="PhysicsServer2D.PinJointParam" /> + <param index="2" name="value" type="float" /> + <description> + Sets a pin joint parameter. See [enum PinJointParam] for a list of available parameters. + </description> + </method> <method name="rectangle_shape_create"> <return type="RID" /> <description> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 67b692a7a0..d4c42e36eb 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -464,9 +464,6 @@ <member name="debug/gdscript/warnings/unused_variable" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable is unused. </member> - <member name="debug/gdscript/warnings/void_assignment" type="int" setter="" getter="" default="1"> - When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when assigning the result of a function that returns [code]void[/code] to a variable. - </member> <member name="debug/settings/crash_handler/message" type="String" setter="" getter="" default=""Please include this when reporting the bug to the project developer.""> Message to be displayed before the backtrace when the engine crashes. By default, this message is only used in exported projects due to the editor-only override applied to this setting. </member> diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp index 8b31781c5e..351af4508f 100644 --- a/editor/debugger/editor_debugger_tree.cpp +++ b/editor/debugger/editor_debugger_tree.cpp @@ -118,6 +118,7 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position, Mou item_menu->add_icon_item(get_theme_icon(SNAME("CreateNewSceneFrom"), SNAME("EditorIcons")), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE); item_menu->add_icon_item(get_theme_icon(SNAME("CopyNodePath"), SNAME("EditorIcons")), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH); item_menu->set_position(get_screen_position() + get_local_mouse_position()); + item_menu->reset_size(); item_menu->popup(); } @@ -323,6 +324,8 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) { file_dialog->add_filter("*." + extensions[i], extensions[i].to_upper()); } + String filename = get_selected_path().get_file() + "." + extensions.front()->get().to_lower(); + file_dialog->set_current_path(filename); file_dialog->popup_file_dialog(); } break; case ITEM_MENU_COPY_NODE_PATH: { diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 55c512f77d..a9c46d4d94 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -1500,7 +1500,7 @@ void EditorFileDialog::_go_back() { } void EditorFileDialog::_go_forward() { - if (local_history_pos == local_history.size() - 1) { + if (local_history_pos >= local_history.size() - 1) { return; } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 88a1bbb4a9..9a3eb75416 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -3365,7 +3365,10 @@ void EditorInspector::set_keying(bool p_active) { return; } keying = p_active; - update_tree(); + // Propagate the keying state to its editor properties. + Array args; + args.append(keying); + main_vbox->propagate_call(SNAME("set_keying"), args, true); } void EditorInspector::set_read_only(bool p_read_only) { diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 4c9b18efe7..8c5db444ab 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -4121,6 +4121,8 @@ void EditorPropertyResource::update_property() { if (use_editor) { // Open editor directly and hide other such editors which are currently open. + // The opened editor is the one that edits the sub-resource, so keying state will be toggled to false. + sub_inspector->set_keying(false); _open_editor_pressed(); if (is_inside_tree()) { get_tree()->call_deferred(SNAME("call_group"), "_editor_resource_properties", "_fold_other_editors", this); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index aab64587ce..30322a94f1 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -135,7 +135,7 @@ public: label->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid_step_x = memnew(SpinBox); - grid_step_x->set_min(0.01); + grid_step_x->set_min(1); grid_step_x->set_max(SPIN_BOX_GRID_RANGE); grid_step_x->set_allow_greater(true); grid_step_x->set_suffix("px"); @@ -144,7 +144,7 @@ public: child_container->add_child(grid_step_x); grid_step_y = memnew(SpinBox); - grid_step_y->set_min(0.01); + grid_step_y->set_min(1); grid_step_y->set_max(SPIN_BOX_GRID_RANGE); grid_step_y->set_allow_greater(true); grid_step_y->set_suffix("px"); @@ -4977,7 +4977,7 @@ CanvasItemEditor::CanvasItemEditor() { SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created)); SceneTreeDock::get_singleton()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position)); - EditorNode::get_singleton()->call_deferred(SNAME("connect"), callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true)); + EditorNode::get_singleton()->call_deferred(SNAME("connect"), "play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true)); EditorNode::get_singleton()->call_deferred(SNAME("connect"), "stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false)); // A fluid container for all toolbars. diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 5f9446c3b1..cbe30f7e0f 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -436,6 +436,7 @@ EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() { } EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(sphere); RS::get_singleton()->free(sphere_instance); RS::get_singleton()->free(viewport); @@ -767,6 +768,7 @@ EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() { } EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); //RS::get_singleton()->free(sphere); RS::get_singleton()->free(mesh_instance); RS::get_singleton()->free(viewport); @@ -867,6 +869,7 @@ EditorFontPreviewPlugin::EditorFontPreviewPlugin() { } EditorFontPreviewPlugin::~EditorFontPreviewPlugin() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(canvas_item); RS::get_singleton()->free(canvas); RS::get_singleton()->free(viewport); diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 91c7fbc06a..0df53bddd1 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -100,6 +100,7 @@ bool EditorNode3DGizmo::is_editable() const { } void EditorNode3DGizmo::clear() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); for (int i = 0; i < instances.size(); i++) { if (instances[i].instance.is_valid()) { RS::get_singleton()->free(instances[i].instance); @@ -809,6 +810,7 @@ void EditorNode3DGizmo::transform() { } void EditorNode3DGizmo::free() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); ERR_FAIL_COND(!spatial_node); ERR_FAIL_COND(!valid); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 4976c8c750..8e2b9b4bce 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -3557,6 +3557,7 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) { } void Node3DEditorViewport::_finish_gizmo_instances() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); for (int i = 0; i < 3; i++) { RS::get_singleton()->free(move_gizmo_instance[i]); RS::get_singleton()->free(move_plane_gizmo_instance[i]); @@ -5536,6 +5537,7 @@ Node3DEditorViewportContainer::Node3DEditorViewportContainer() { Node3DEditor *Node3DEditor::singleton = nullptr; Node3DEditorSelectedItem::~Node3DEditorSelectedItem() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); if (sbox_instance.is_valid()) { RenderingServer::get_singleton()->free(sbox_instance); } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index bb5491fcb5..562eddbc64 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3003,6 +3003,7 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) { _go_to_tab(script_list->get_item_metadata(next_tab)); _update_script_names(); } + accept_event(); } if (ED_IS_SHORTCUT("script_editor/prev_script", p_event)) { if (script_list->get_item_count() > 1) { @@ -3011,12 +3012,15 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) { _go_to_tab(script_list->get_item_metadata(next_tab)); _update_script_names(); } + accept_event(); } if (ED_IS_SHORTCUT("script_editor/window_move_up", p_event)) { _menu_option(WINDOW_MOVE_UP); + accept_event(); } if (ED_IS_SHORTCUT("script_editor/window_move_down", p_event)) { _menu_option(WINDOW_MOVE_DOWN); + accept_event(); } } diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 5a1f214933..414c3a2759 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -1488,13 +1488,15 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() { } // Selection if needed. - for (RBSet<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) { + for (RBSet<TileMapCell>::Element *E = tile_set_selection.front(); E;) { + RBSet<TileMapCell>::Element *N = E->next(); const TileMapCell *selected = &(E->get()); if (!tile_set->has_source(selected->source_id) || !tile_set->get_source(selected->source_id)->has_tile(selected->get_atlas_coords()) || !tile_set->get_source(selected->source_id)->has_alternative_tile(selected->get_atlas_coords(), selected->alternative_tile)) { tile_set_selection.erase(E); } + E = N; } if (!tile_map_selection.is_empty()) { diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index c93b0019dc..3d0c1acff8 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -5383,6 +5383,10 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Binormal", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal", "BINORMAL"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Color", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color", "COLOR"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Custom0", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom0", "CUSTOM0"), { "custom0" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Custom1", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom1", "CUSTOM1"), { "custom1" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Custom2", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom2", "CUSTOM2"), { "custom2" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Custom3", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom3", "CUSTOM3"), { "custom3" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("InstanceId", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_id", "INSTANCE_ID"), { "instance_id" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("InstanceCustom", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom", "INSTANCE_CUSTOM"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("ModelViewMatrix", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "modelview_matrix", "MODELVIEW_MATRIX"), { "modelview_matrix" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp index 40683e2938..5243916bd2 100644 --- a/editor/rename_dialog.cpp +++ b/editor/rename_dialog.cpp @@ -338,7 +338,7 @@ void RenameDialog::_bind_methods() { } void RenameDialog::_update_substitute() { - LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(scene_tree_editor->get_viewport()->gui_get_focus_owner()); + LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(get_viewport()->gui_get_focus_owner()); bool is_main_field = _is_main_field(focus_owner_line_edit); but_insert_name->set_disabled(!is_main_field); @@ -636,7 +636,7 @@ bool RenameDialog::_is_main_field(LineEdit *line_edit) { } void RenameDialog::_insert_text(String text) { - LineEdit *focus_owner = Object::cast_to<LineEdit>(scene_tree_editor->get_viewport()->gui_get_focus_owner()); + LineEdit *focus_owner = Object::cast_to<LineEdit>(get_viewport()->gui_get_focus_owner()); if (_is_main_field(focus_owner)) { focus_owner->selection_delete(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index f91b571118..4b04fd1589 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -134,6 +134,8 @@ void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) { _tool_selected(TOOL_MOVE_DOWN); } else if (ED_IS_SHORTCUT("scene_tree/reparent", p_event)) { _tool_selected(TOOL_REPARENT); + } else if (ED_IS_SHORTCUT("scene_tree/reparent_to_new_node", p_event)) { + _tool_selected(TOOL_REPARENT_TO_NEW_NODE); } else if (ED_IS_SHORTCUT("scene_tree/save_branch_as_scene", p_event)) { _tool_selected(TOOL_NEW_SCENE_FROM); } else if (ED_IS_SHORTCUT("scene_tree/delete_no_confirm", p_event)) { @@ -3217,6 +3219,7 @@ List<Node *> SceneTreeDock::paste_nodes() { if (!paste_parent) { paste_parent = dup; owner = dup; + dup->set_scene_file_path(String()); // Make sure the scene path is empty, to avoid accidental references. ur->add_do_method(EditorNode::get_singleton(), "set_edited_scene", dup); } else { ur->add_do_method(paste_parent, "add_child", dup, true); diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 0ebdbf090d..9457583d09 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -115,10 +115,10 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l } if (from != line_length) { - /* check if we are in entering a region */ + // Check if we are in entering a region. if (in_region == -1) { for (int c = 0; c < color_regions.size(); c++) { - /* check there is enough room */ + // Check there is enough room. int chars_left = line_length - from; int start_key_length = color_regions[c].start_key.length(); int end_key_length = color_regions[c].end_key.length(); @@ -126,7 +126,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l continue; } - /* search the line */ + // Search the line. bool match = true; const char32_t *start_key = color_regions[c].start_key.get_data(); for (int k = 0; k < start_key_length; k++) { @@ -141,7 +141,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_region = c; from += start_key_length; - /* check if it's the whole line */ + // Check if it's the whole line. if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) { if (from + end_key_length > line_length) { // If it's key length and there is a '\', dont skip to highlight esc chars. @@ -166,7 +166,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l } } - /* if we are in one find the end key */ + // If we are in one, find the end key. if (in_region != -1) { Color region_color = color_regions[in_region].color; if (in_node_path && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) { @@ -183,7 +183,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l highlighter_info["color"] = region_color; color_map[j] = highlighter_info; - /* search the line */ + // Search the line. int region_end_index = -1; int end_key_length = color_regions[in_region].end_key.length(); const char32_t *end_key = color_regions[in_region].end_key.get_data(); @@ -267,14 +267,14 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_keyword = false; } - // allow ABCDEF in hex notation + // Allow ABCDEF in hex notation. if (is_hex_notation && (is_hex_digit(str[j]) || is_a_digit)) { is_a_digit = true; } else { is_hex_notation = false; } - // disallow anything not a 0 or 1 in binary notation + // Disallow anything not a 0 or 1 in binary notation. if (is_bin_notation && !is_binary_digit(str[j])) { is_a_digit = false; is_bin_notation = false; @@ -284,7 +284,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_number = true; } - // Special cases for numbers + // Special cases for numbers. if (in_number && !is_a_digit) { if (str[j] == 'b' && str[j - 1] == '0') { is_bin_notation = true; @@ -349,7 +349,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l if (col != Color()) { for (int k = j - 1; k >= 0; k--) { if (str[k] == '.') { - col = Color(); // keyword, member & global func indexing not allowed + col = Color(); // Keyword, member & global func indexing not allowed. break; } else if (str[k] > 32) { break; @@ -372,7 +372,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l k++; } - // check for space between name and bracket + // Check for space between name and bracket. while (k < line_length && is_whitespace(str[k])) { k++; } @@ -445,7 +445,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_member_variable = false; } - // Keep symbol color for binary '&&'. In the case of '&&&' use StringName color for the last ampersand + // Keep symbol color for binary '&&'. In the case of '&&&' use StringName color for the last ampersand. if (!in_string_name && in_region == -1 && str[j] == '&' && !is_binary_op) { if (j >= 2 && str[j - 1] == '&' && str[j - 2] != '&' && prev_is_binary_op) { is_binary_op = true; @@ -456,7 +456,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_string_name = false; } - // '^^' has no special meaning, so unlike StringName, when binary, use NodePath color for the last caret + // '^^' has no special meaning, so unlike StringName, when binary, use NodePath color for the last caret. if (!in_node_path && in_region == -1 && str[j] == '^' && !is_binary_op && (j == 0 || (j > 0 && str[j - 1] != '^') || prev_is_binary_op)) { in_node_path = true; } else if (in_region != -1 || is_a_symbol) { @@ -465,7 +465,8 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l if (!in_node_ref && in_region == -1 && (str[j] == '$' || (str[j] == '%' && !is_binary_op))) { in_node_ref = true; - } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%')) { + } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%') || (is_a_digit && j > 0 && (str[j - 1] == '$' || str[j - 1] == '/' || str[j - 1] == '%'))) { + // NodeRefs can't start with digits, so point out wrong syntax immediately. in_node_ref = false; } @@ -525,7 +526,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l prev_type = current_type; current_type = next_type; - // no need to store regions... + // No need to store regions... if (prev_type == REGION) { prev_text = ""; prev_column = j; @@ -533,7 +534,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l String text = str.substr(prev_column, j - prev_column).strip_edges(); prev_column = j; - // ignore if just whitespace + // Ignore if just whitespace. if (!text.is_empty()) { prev_text = text; } diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 1fe1561559..40bc2beb23 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -629,6 +629,10 @@ void GDScript::_update_doc() { } } + for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) { + E.value->_update_doc(); + } + _add_doc(doc); } #endif diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 4e92477a6e..ad20672e2d 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1751,12 +1751,6 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable type = p_variable->initializer->get_datatype(); -#ifdef DEBUG_ENABLED - if (p_variable->initializer->type == GDScriptParser::Node::CALL && type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) { - parser->push_warning(p_variable->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_variable->initializer)->function_name); - } -#endif - if (p_variable->infer_datatype) { type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; @@ -1846,12 +1840,6 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant } type = p_constant->initializer->get_datatype(); - -#ifdef DEBUG_ENABLED - if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) { - parser->push_warning(p_constant->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_constant->initializer)->function_name); - } -#endif } if (p_constant->datatype_specifier != nullptr) { @@ -2047,6 +2035,9 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { update_array_literal_element_type(expected_type, static_cast<GDScriptParser::ArrayNode *>(p_return->return_value)); } } + if (has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) { + push_error("A void function cannot return a value.", p_return); + } result = p_return->return_value->get_datatype(); } else { // Return type is null by default. @@ -2332,9 +2323,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig } #ifdef DEBUG_ENABLED - if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_value_type.is_hard_type() && assigned_value_type.kind == GDScriptParser::DataType::BUILTIN && assigned_value_type.builtin_type == Variant::NIL) { - parser->push_warning(p_assignment->assigned_value, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value)->function_name); - } else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) { + if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) { parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION); } #endif @@ -2649,6 +2638,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } else if (GDScriptUtilityFunctions::function_exists(function_name)) { MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name); + if (!p_is_root && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) { + push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call); + } + if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) { // Can call on compilation. Vector<const Variant *> args; @@ -2692,6 +2685,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } else if (Variant::has_utility_function(function_name)) { MethodInfo function_info = info_from_utility_func(function_name); + if (!p_is_root && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) { + push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call); + } + if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) { // Can call on compilation. Vector<const Variant *> args; @@ -2834,6 +2831,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a mark_lambda_use_self(); } + if (!p_is_root && return_type.is_hard_type() && return_type.kind == GDScriptParser::DataType::BUILTIN && return_type.builtin_type == Variant::NIL) { + push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", p_call->function_name), p_call); + } + #ifdef DEBUG_ENABLED if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL) { parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 6c7b5d534e..3c9387a837 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -538,20 +538,20 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Construct a built-in type. Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name); - gen->write_construct(result, vtype, arguments); + gen->write_construct(return_addr, vtype, arguments); } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) { // Variant utility function. - gen->write_call_utility(result, call->function_name, arguments); + gen->write_call_utility(return_addr, call->function_name, arguments); } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) { // GDScript utility function. - gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments); + gen->write_call_gdscript_utility(return_addr, GDScriptUtilityFunctions::get_function(call->function_name), arguments); } else { // Regular function. const GDScriptParser::ExpressionNode *callee = call->callee; if (call->is_super) { // Super call. - gen->write_super_call(result, call->function_name, arguments); + gen->write_super_call(return_addr, call->function_name, arguments); } else { if (callee->type == GDScriptParser::Node::IDENTIFIER) { // Self function call. @@ -563,22 +563,22 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (_have_exact_arguments(method, arguments)) { // Exact arguments, use ptrcall. - gen->write_call_ptrcall(result, self, method, arguments); + gen->write_call_ptrcall(return_addr, self, method, arguments); } else { // Not exact arguments, but still can use method bind call. - gen->write_call_method_bind(result, self, method, arguments); + gen->write_call_method_bind(return_addr, self, method, arguments); } } else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") { GDScriptCodeGenerator::Address self; self.mode = GDScriptCodeGenerator::Address::CLASS; if (within_await) { - gen->write_call_async(result, self, call->function_name, arguments); + gen->write_call_async(return_addr, self, call->function_name, arguments); } else { gen->write_call(return_addr, self, call->function_name, arguments); } } else { if (within_await) { - gen->write_call_self_async(result, call->function_name, arguments); + gen->write_call_self_async(return_addr, call->function_name, arguments); } else { gen->write_call_self(return_addr, call->function_name, arguments); } @@ -589,18 +589,18 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (subscript->is_attribute) { // May be static built-in method call. if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) { - gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments); + gen->write_call_builtin_type_static(return_addr, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments); } else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") && ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) { // It's a static native method call. - gen->write_call_native_static(result, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments); + gen->write_call_native_static(return_addr, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments); } else { GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base); if (r_error) { return GDScriptCodeGenerator::Address(); } if (within_await) { - gen->write_call_async(result, base, call->function_name, arguments); + gen->write_call_async(return_addr, base, call->function_name, arguments); } else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) { // Native method, use faster path. StringName class_name; @@ -613,16 +613,16 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code MethodBind *method = ClassDB::get_method(class_name, call->function_name); if (_have_exact_arguments(method, arguments)) { // Exact arguments, use ptrcall. - gen->write_call_ptrcall(result, base, method, arguments); + gen->write_call_ptrcall(return_addr, base, method, arguments); } else { // Not exact arguments, but still can use method bind call. - gen->write_call_method_bind(result, base, method, arguments); + gen->write_call_method_bind(return_addr, base, method, arguments); } } else { gen->write_call(return_addr, base, call->function_name, arguments); } } else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) { - gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments); + gen->write_call_builtin_type(return_addr, base, base.type.builtin_type, call->function_name, arguments); } else { gen->write_call(return_addr, base, call->function_name, arguments); } @@ -2636,10 +2636,6 @@ Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser: } } -#ifdef TOOLS_ENABLED - p_script->_update_doc(); -#endif - p_script->_init_rpc_methods_properties(); p_script->valid = true; @@ -2724,6 +2720,10 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri return err; } +#ifdef TOOLS_ENABLED + p_script->_update_doc(); +#endif + return GDScriptCache::finish_compiling(main_script->get_path()); } diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index fdcc0625d7..bc8e33b01a 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -1533,8 +1533,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Callable::CallError err; if (call_ret) { GET_INSTRUCTION_ARG(ret, argc + 1); +#ifdef DEBUG_ENABLED + Variant::Type base_type = base->get_type(); + Object *base_obj = base->get_validated_object(); + StringName base_class = base_obj ? base_obj->get_class_name() : StringName(); +#endif base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err); #ifdef DEBUG_ENABLED + if (ret->get_type() == Variant::NIL) { + if (base_type == Variant::OBJECT) { + if (base_obj) { + MethodBind *method = ClassDB::get_method(base_class, *methodname); + if (*methodname == CoreStringNames::get_singleton()->_free || (method && !method->has_return())) { + err_text = R"(Trying to get a return value of a method that returns "void")"; + OPCODE_BREAK; + } + } + } else if (Variant::has_builtin_method(base_type, *methodname) && !Variant::has_builtin_method_return_value(base_type, *methodname)) { + err_text = R"(Trying to get a return value of a method that returns "void")"; + OPCODE_BREAK; + } + } + if (!call_async && ret->get_type() == Variant::OBJECT) { // Check if getting a function state without await. bool was_freed = false; diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 36bc051643..9ae4bdae56 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -80,10 +80,6 @@ String GDScriptWarning::get_message() const { case STANDALONE_EXPRESSION: { return "Standalone expression (the line has no effect)."; } break; - case VOID_ASSIGNMENT: { - CHECK_SYMBOLS(1); - return "Assignment operation, but the function '" + symbols[0] + "()' returns void."; - } break; case NARROWING_CONVERSION: { return "Narrowing conversion (float is converted to int and loses precision)."; } break; @@ -202,7 +198,6 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "UNREACHABLE_CODE", "UNREACHABLE_PATTERN", "STANDALONE_EXPRESSION", - "VOID_ASSIGNMENT", "NARROWING_CONVERSION", "INCOMPATIBLE_TERNARY", "UNUSED_SIGNAL", diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index 7e4e975510..df6ee71425 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -57,7 +57,6 @@ public: UNREACHABLE_CODE, // Code after a return statement. UNREACHABLE_PATTERN, // Pattern in a match statement after a catch all pattern (wildcard or bind). STANDALONE_EXPRESSION, // Expression not assigned to a variable. - VOID_ASSIGNMENT, // Function returns void but it's assigned to a variable. NARROWING_CONVERSION, // Float value into an integer slot, precision is lost. INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible. UNUSED_SIGNAL, // Signal is defined but never emitted. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd new file mode 100644 index 0000000000..63587942f7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd @@ -0,0 +1,2 @@ +func test() -> void: + return null diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out new file mode 100644 index 0000000000..3c09f44ba9 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +A void function cannot return a value. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd new file mode 100644 index 0000000000..0ee4e7ea36 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd @@ -0,0 +1,4 @@ +func test() -> void: + var a + a = 1 + return a diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out new file mode 100644 index 0000000000..3c09f44ba9 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +A void function cannot return a value. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd new file mode 100644 index 0000000000..a3450966cc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd @@ -0,0 +1,3 @@ +func test(): + var builtin := [] + print(builtin.reverse()) # Built-in type method. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out new file mode 100644 index 0000000000..225c85e9c7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot get return value of call to "reverse()" because it returns "void". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd new file mode 100644 index 0000000000..2162a181ac --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd @@ -0,0 +1,5 @@ +func foo() -> void: + pass + +func test(): + print(foo()) # Custom method. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out new file mode 100644 index 0000000000..2b1a607883 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot get return value of call to "foo()" because it returns "void". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd new file mode 100644 index 0000000000..f3443d985e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd @@ -0,0 +1,2 @@ +func test(): + print(print_debug()) # GDScript utility function. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out new file mode 100644 index 0000000000..502c18ab9d --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot get return value of call to "print_debug()" because it returns "void". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd new file mode 100644 index 0000000000..b8e81b160a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd @@ -0,0 +1,3 @@ +func test(): + var obj := Node.new() + print(obj.free()) # Native type method. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out new file mode 100644 index 0000000000..88be39345b --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot get return value of call to "free()" because it returns "void". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd new file mode 100644 index 0000000000..8eabed4271 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd @@ -0,0 +1,2 @@ +func test(): + print(print()) # Built-in utility function. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out new file mode 100644 index 0000000000..ebf43186be --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot get return value of call to "print()" because it returns "void". diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd new file mode 100644 index 0000000000..c9caef7d7c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd @@ -0,0 +1,5 @@ +func variant() -> Variant: + return 'variant' + +func test(): + print(variant()) diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out new file mode 100644 index 0000000000..57fe608cc5 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out @@ -0,0 +1,2 @@ +GDTEST_OK +variant diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd deleted file mode 100644 index b4a42b3e3d..0000000000 --- a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd +++ /dev/null @@ -1,6 +0,0 @@ -func i_return_void() -> void: - return - - -func test(): - var __ = i_return_void() diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out deleted file mode 100644 index 84c9598f9a..0000000000 --- a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out +++ /dev/null @@ -1,5 +0,0 @@ -GDTEST_OK ->> WARNING ->> Line: 6 ->> VOID_ASSIGNMENT ->> Assignment operation, but the function 'i_return_void()' returns void. diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd new file mode 100644 index 0000000000..a3daf70627 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd @@ -0,0 +1,4 @@ +func test(): + var obj + obj = Node.new() + print(obj.free()) diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out new file mode 100644 index 0000000000..5edaf19442 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/use_return_value_of_free_call.gd +>> 4 +>> Trying to get a return value of a method that returns "void" diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd new file mode 100644 index 0000000000..49fb76ad1f --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd @@ -0,0 +1,4 @@ +func test(): + var value + value = [] + print(value.reverse()) diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out new file mode 100644 index 0000000000..128356ff8a --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/use_return_value_of_void_builtin_method_call.gd +>> 4 +>> Trying to get a return value of a method that returns "void" diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd new file mode 100644 index 0000000000..44f9aa467a --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd @@ -0,0 +1,4 @@ +func test(): + var obj + obj = RefCounted.new() + print(obj.notify_property_list_changed()) diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out new file mode 100644 index 0000000000..e02c206778 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/use_return_value_of_void_native_method_call.gd +>> 4 +>> Trying to get a return value of a method that returns "void" diff --git a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd index 5303fb04e2..9b64084fa6 100644 --- a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd +++ b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd @@ -15,10 +15,10 @@ func test(): var string_array: Array[String] = [] var stringname_array: Array[StringName] = [] - assert(!string_array.push_back(&"abc")) + string_array.push_back(&"abc") print("Array[String] insert converted: ", typeof(string_array[0]) == TYPE_STRING) - assert(!stringname_array.push_back("abc")) + stringname_array.push_back("abc") print("Array[StringName] insert converted: ", typeof(stringname_array[0]) == TYPE_STRING_NAME) print("StringName in Array[String]: ", &"abc" in string_array) @@ -28,8 +28,8 @@ func test(): assert(!packed_string_array.push_back("abc")) print("StringName in PackedStringArray: ", &"abc" in packed_string_array) - assert(!string_array.push_back("abc")) + string_array.push_back("abc") print("StringName finds String in Array: ", string_array.find(&"abc")) - assert(!stringname_array.push_back(&"abc")) + stringname_array.push_back(&"abc") print("String finds StringName in Array: ", stringname_array.find("abc")) diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp index cfa498af65..4b71511582 100644 --- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp +++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp @@ -59,6 +59,7 @@ Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFState> p_sta mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path()); node->replace_by(mesh_instance_node_3d); delete_queue.push_back(node); + node = mesh_instance_node_3d; } else { memdelete(mesh_instance_node_3d); } diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 89ef9ddd90..186e6a504f 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -1405,6 +1405,7 @@ GridMapEditor::GridMapEditor() { } GridMapEditor::~GridMapEditor() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); _clear_clipboard_data(); for (int i = 0; i < 3; i++) { diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index e396d77d86..d23d68ffba 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -796,6 +796,10 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) { } void GridMap::_octant_exit_world(const OctantKey &p_key) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); + ERR_FAIL_NULL(NavigationServer3D::get_singleton()); + ERR_FAIL_COND(!octant_map.has(p_key)); Octant &g = *octant_map[p_key]; PhysicsServer3D::get_singleton()->body_set_state(g.static_body, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform()); @@ -834,6 +838,10 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) { } void GridMap::_octant_clean_up(const OctantKey &p_key) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); + ERR_FAIL_NULL(NavigationServer3D::get_singleton()); + ERR_FAIL_COND(!octant_map.has(p_key)); Octant &g = *octant_map[p_key]; @@ -1186,6 +1194,7 @@ Vector3 GridMap::_get_offset() const { } void GridMap::clear_baked_meshes() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); for (int i = 0; i < baked_meshes.size(); i++) { RS::get_singleton()->free(baked_meshes[i].instance); } diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp index 8c785d7591..1b6f1d7897 100644 --- a/modules/noise/noise_texture_2d.cpp +++ b/modules/noise/noise_texture_2d.cpp @@ -40,6 +40,7 @@ NoiseTexture2D::NoiseTexture2D() { } NoiseTexture2D::~NoiseTexture2D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); if (texture.is_valid()) { RS::get_singleton()->free(texture); } diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index f3e7ae9b83..1c744afec0 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -385,11 +385,17 @@ void TextServerAdvanced::_free_rid(const RID &p_rid) { _THREAD_SAFE_METHOD_ if (font_owner.owns(p_rid)) { FontAdvanced *fd = font_owner.get_or_null(p_rid); - font_owner.free(p_rid); + { + MutexLock lock(fd->mutex); + font_owner.free(p_rid); + } memdelete(fd); } else if (shaped_owner.owns(p_rid)) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_rid); - shaped_owner.free(p_rid); + { + MutexLock lock(sd->mutex); + shaped_owner.free(p_rid); + } memdelete(sd); } } diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index ea7b7a271b..9fd8c8fb37 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -115,11 +115,17 @@ void TextServerFallback::_free_rid(const RID &p_rid) { _THREAD_SAFE_METHOD_ if (font_owner.owns(p_rid)) { FontFallback *fd = font_owner.get_or_null(p_rid); - font_owner.free(p_rid); + { + MutexLock lock(fd->mutex); + font_owner.free(p_rid); + } memdelete(fd); } else if (shaped_owner.owns(p_rid)) { ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_rid); - shaped_owner.free(p_rid); + { + MutexLock lock(sd->mutex); + shaped_owner.free(p_rid); + } memdelete(sd); } } diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 77cfa99aee..41d2579ac0 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -873,7 +873,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p int hand_tracking_frequency_index = p_preset->get("xr_features/hand_tracking_frequency"); bool backup_allowed = p_preset->get("user_data_backup/allow"); - bool classify_as_game = p_preset->get("package/classify_as_game"); + int app_category = p_preset->get("package/app_category"); bool retain_data_on_uninstall = p_preset->get("package/retain_data_on_uninstall"); bool exclude_from_recents = p_preset->get("package/exclude_from_recents"); bool is_resizeable = bool(GLOBAL_GET("display/window/size/resizable")); @@ -972,8 +972,12 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]); } + if (tname == "application" && attrname == "appCategory") { + encode_uint32(_get_app_category_value(app_category), &p_manifest.write[iofs + 16]); + } + if (tname == "application" && attrname == "isGame") { - encode_uint32(classify_as_game, &p_manifest.write[iofs + 16]); + encode_uint32(app_category == APP_CATEGORY_GAME, &p_manifest.write[iofs + 16]); } if (tname == "application" && attrname == "hasFragileUserData") { @@ -1731,7 +1735,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/classify_as_game"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "package/app_category", PROPERTY_HINT_ENUM, "Accessibility,Audio,Game,Image,Maps,News,Productivity,Social,Video"), APP_CATEGORY_GAME)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false)); diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index 8d016d3fac..1cbed4b3bb 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -72,6 +72,54 @@ String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_or } } +int _get_app_category_value(int category_index) { + switch (category_index) { + case APP_CATEGORY_ACCESSIBILITY: + return 8; + case APP_CATEGORY_AUDIO: + return 1; + case APP_CATEGORY_IMAGE: + return 3; + case APP_CATEGORY_MAPS: + return 6; + case APP_CATEGORY_NEWS: + return 5; + case APP_CATEGORY_PRODUCTIVITY: + return 7; + case APP_CATEGORY_SOCIAL: + return 4; + case APP_CATEGORY_VIDEO: + return 2; + case APP_CATEGORY_GAME: + default: + return 0; + } +} + +String _get_app_category_label(int category_index) { + switch (category_index) { + case APP_CATEGORY_ACCESSIBILITY: + return "accessibility"; + case APP_CATEGORY_AUDIO: + return "audio"; + case APP_CATEGORY_IMAGE: + return "image"; + case APP_CATEGORY_MAPS: + return "maps"; + case APP_CATEGORY_NEWS: + return "news"; + case APP_CATEGORY_PRODUCTIVITY: + return "productivity"; + case APP_CATEGORY_SOCIAL: + return "social"; + case APP_CATEGORY_VIDEO: + return "video"; + case APP_CATEGORY_GAME: + default: + return "game"; + } +} + // Utility method used to create a directory. Error create_directory(const String &p_dir) { if (!DirAccess::exists(p_dir)) { @@ -253,21 +301,27 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { } String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission) { + int app_category_index = (int)(p_preset->get("package/app_category")); + bool is_game = app_category_index == APP_CATEGORY_GAME; + int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); bool uses_xr = xr_mode_index == XR_MODE_OPENXR; + String manifest_application_text = vformat( " <application android:label=\"@string/godot_project_name_string\"\n" " android:allowBackup=\"%s\"\n" " android:icon=\"@mipmap/icon\"\n" + " android:appCategory=\"%s\"\n" " android:isGame=\"%s\"\n" " android:hasFragileUserData=\"%s\"\n" " android:requestLegacyExternalStorage=\"%s\"\n" - " tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n" + " tools:replace=\"android:allowBackup,android:appCategory,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n" " tools:ignore=\"GoogleAppIndexingWarning\">\n\n" " <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_version_name\" />\n" " <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_metadata_name\" />\n", bool_to_string(p_preset->get("user_data_backup/allow")), - bool_to_string(p_preset->get("package/classify_as_game")), + _get_app_category_label(app_category_index), + bool_to_string(is_game), bool_to_string(p_preset->get("package/retain_data_on_uninstall")), bool_to_string(p_has_read_write_storage_permission)); diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h index 232b4458c6..9c9c5923f7 100644 --- a/platform/android/export/gradle_export_util.h +++ b/platform/android/export/gradle_export_util.h @@ -44,6 +44,18 @@ const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="ut </resources> )"; +// Application category. +// See https://developer.android.com/guide/topics/manifest/application-element#appCategory for standards +static const int APP_CATEGORY_ACCESSIBILITY = 0; +static const int APP_CATEGORY_AUDIO = 1; +static const int APP_CATEGORY_GAME = 2; +static const int APP_CATEGORY_IMAGE = 3; +static const int APP_CATEGORY_MAPS = 4; +static const int APP_CATEGORY_NEWS = 5; +static const int APP_CATEGORY_PRODUCTIVITY = 6; +static const int APP_CATEGORY_SOCIAL = 7; +static const int APP_CATEGORY_VIDEO = 8; + // Supported XR modes. // This should match the entries in 'platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java' static const int XR_MODE_REGULAR = 0; @@ -73,6 +85,10 @@ int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orien String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation); +int _get_app_category_value(int category_index); + +String _get_app_category_label(int category_index); + // Utility method used to create a directory. Error create_directory(const String &p_dir); diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index 1db135826a..8c8608cbbb 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -20,6 +20,7 @@ android:label="@string/godot_project_name_string" android:allowBackup="false" android:icon="@mipmap/icon" + android:appCategory="game" android:isGame="true" android:hasFragileUserData="false" android:requestLegacyExternalStorage="false" diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index 0ca0d3d1fe..82351a536b 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -105,10 +105,10 @@ public: // MARK: - Input - // MARK: Touches + // MARK: Touches and Apple Pencil void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click); - void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y); + void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, float p_pressure, Vector2 p_tilt); void touches_cancelled(int p_idx); // MARK: Keyboard diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index ce78b8d20f..789364494d 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -237,10 +237,12 @@ void DisplayServerIOS::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, perform_event(ev); } -void DisplayServerIOS::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) { +void DisplayServerIOS::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, float p_pressure, Vector2 p_tilt) { Ref<InputEventScreenDrag> ev; ev.instantiate(); ev->set_index(p_idx); + ev->set_pressure(p_pressure); + ev->set_tilt(p_tilt); ev->set_position(Vector2(p_x, p_y)); ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y)); perform_event(ev); diff --git a/platform/ios/godot_view.mm b/platform/ios/godot_view.mm index 19cb914521..5aff75ea04 100644 --- a/platform/ios/godot_view.mm +++ b/platform/ios/godot_view.mm @@ -363,7 +363,9 @@ static const float earth_gravity = 9.80665; ERR_FAIL_COND(tid == -1); CGPoint touchPoint = [touch locationInView:self]; CGPoint prev_point = [touch previousLocationInView:self]; - DisplayServerIOS::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor); + CGFloat alt = [touch altitudeAngle]; + CGVector azim = [touch azimuthUnitVectorInView:self]; + DisplayServerIOS::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, [touch force] / [touch maximumPossibleForce], Vector2(azim.dx, azim.dy) * Math::cos(alt)); } } } diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 844b15e9fb..747dcbd76c 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -339,9 +339,6 @@ def configure(env: "Environment"): env.Prepend(CPPPATH=["#platform/linuxbsd"]) if env["x11"]: - if not env["vulkan"]: - print("Error: X11 support requires vulkan=yes") - env.Exit(255) env.Append(CPPDEFINES=["X11_ENABLED"]) env.Append(CPPDEFINES=["UNIX_ENABLED"]) diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 57f125a754..c44c3635cf 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -1235,10 +1235,10 @@ Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const { return ret; } -DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen) { _THREAD_SAFE_METHOD_ - WindowID id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect); + WindowID id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect, p_screen); for (int i = 0; i < WINDOW_FLAG_MAX; i++) { if (p_flags & (1 << i)) { window_set_flag(WindowFlags(i), true, id); @@ -1257,6 +1257,8 @@ void DisplayServerX11::show_window(WindowID p_id) { DEBUG_LOG_X11("show_window: %lu (%u) \n", wd.x11_window, p_id); XMapWindow(x11_display, wd.x11_window); + XSync(x11_display, False); + _validate_mode_on_map(p_id); } void DisplayServerX11::delete_sub_window(WindowID p_id) { @@ -1505,16 +1507,24 @@ void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window // Check if screen is valid ERR_FAIL_INDEX(p_screen, get_screen_count()); + if (window_get_current_screen(p_window) == p_screen) { + return; + } + if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN) { Point2i position = screen_get_position(p_screen); Size2i size = screen_get_size(p_screen); XMoveResizeWindow(x11_display, wd.x11_window, position.x, position.y, size.x, size.y); } else { - if (p_screen != window_get_current_screen(p_window)) { - Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); - window_set_position(ofs + screen_get_position(p_screen), p_window); - } + Rect2i srect = screen_get_usable_rect(p_screen); + Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); + Size2i wsize = window_get_size(p_window); + wpos += srect.position; + + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - wsize.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - wsize.height / 3); + window_set_position(wpos, p_window); } } @@ -4531,7 +4541,7 @@ DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, W return ds; } -DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen) { //Create window XVisualInfo visualInfo; @@ -4607,8 +4617,38 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V valuemask |= CWOverrideRedirect | CWSaveUnder; } + Rect2i win_rect = p_rect; + if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + Rect2i screen_rect = Rect2i(screen_get_position(p_screen), screen_get_size(p_screen)); + + win_rect = screen_rect; + } else { + int nearest_area = 0; + int pos_screen = -1; + for (int i = 0; i < get_screen_count(); i++) { + Rect2i r; + r.position = screen_get_position(i); + r.size = screen_get_size(i); + Rect2 inters = r.intersection(p_rect); + + int area = inters.size.width * inters.size.height; + if (area > nearest_area) { + pos_screen = i; + nearest_area = area; + } + } + + Rect2i srect = screen_get_usable_rect(p_screen); + Point2i wpos = p_rect.position - ((pos_screen >= 0) ? screen_get_position(pos_screen) : Vector2i()); + wpos += srect.position; + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - p_rect.size.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - p_rect.size.height / 3); + + win_rect.position = wpos; + } + { - wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo.screen), p_rect.position.x, p_rect.position.y, p_rect.size.width > 0 ? p_rect.size.width : 1, p_rect.size.height > 0 ? p_rect.size.height : 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes); + wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo.screen), win_rect.position.x, win_rect.position.y, win_rect.size.width > 0 ? win_rect.size.width : 1, win_rect.size.height > 0 ? win_rect.size.height : 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes); // Enable receiving notification when the window is initialized (MapNotify) // so the focus can be set at the right time. @@ -4729,13 +4769,13 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V #if defined(VULKAN_ENABLED) if (context_vulkan) { - Error err = context_vulkan->window_create(id, p_vsync_mode, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height); + Error err = context_vulkan->window_create(id, p_vsync_mode, wd.x11_window, x11_display, win_rect.size.width, win_rect.size.height); ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan window"); } #endif #ifdef GLES3_ENABLED if (gl_manager) { - Error err = gl_manager->window_create(id, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height); + Error err = gl_manager->window_create(id, wd.x11_window, x11_display, win_rect.size.width, win_rect.size.height); ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL window"); window_set_vsync_mode(p_vsync_mode, id); } @@ -5038,12 +5078,13 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode Point2i window_position( (screen_get_size(0).width - p_resolution.width) / 2, (screen_get_size(0).height - p_resolution.height) / 2); + window_position += screen_get_position(0); if (p_position != nullptr) { window_position = *p_position; } - WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution)); + WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution), 0); if (main_window == INVALID_WINDOW_ID) { r_error = ERR_CANT_CREATE; return; @@ -5054,8 +5095,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode } } show_window(main_window); - XSync(x11_display, False); - _validate_mode_on_map(main_window); #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index 2578ca06fd..d8d914bffc 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -185,7 +185,7 @@ class DisplayServerX11 : public DisplayServer { WindowID last_focused_window = INVALID_WINDOW_ID; WindowID window_id_counter = MAIN_WINDOW_ID; - WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect); + WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen); String internal_clipboard; String internal_clipboard_primary; @@ -365,7 +365,7 @@ public: virtual Vector<DisplayServer::WindowID> get_window_list() const override; - virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()) override; + virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), int p_screen = 0) override; virtual void show_window(WindowID p_id) override; virtual void delete_sub_window(WindowID p_id) override; diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index bd26f6e417..4a2d6032b9 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -99,6 +99,8 @@ public: Callable drop_files_callback; ObjectID instance_id; + bool fs_transition = false; + bool initial_size = true; WindowID transient_parent = INVALID_WINDOW_ID; bool exclusive = false; @@ -189,7 +191,7 @@ private: const NSMenu *_get_menu_root(const String &p_menu_root) const; NSMenu *_get_menu_root(const String &p_menu_root); - WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect); + WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect, int p_screen); void _update_window_style(WindowData p_wd); void _set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window); @@ -334,7 +336,7 @@ public: virtual Vector<int> get_window_list() const override; - virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()) override; + virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), int p_screen = 0) override; virtual void show_window(WindowID p_id) override; virtual void delete_sub_window(WindowID p_id) override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 5c979dbf22..5421739971 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -109,7 +109,7 @@ NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) { return menu; } -DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect) { +DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect, int p_screen) { WindowID id; const float scale = screen_get_max_scale(); { @@ -119,19 +119,36 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod ERR_FAIL_COND_V_MSG(wd.window_delegate == nil, INVALID_WINDOW_ID, "Can't create a window delegate"); [wd.window_delegate setWindowID:window_id_counter]; - Point2i position = p_rect.position; + int nearest_area = 0; + int pos_screen = -1; + for (int i = 0; i < get_screen_count(); i++) { + Rect2i r = screen_get_usable_rect(i); + Rect2 inters = r.intersection(p_rect); + int area = inters.size.width * inters.size.height; + if (area > nearest_area && area > 0) { + pos_screen = i; + nearest_area = area; + } + } + + Rect2i srect = screen_get_usable_rect(p_screen); + Point2i wpos = p_rect.position - ((pos_screen >= 0) ? screen_get_position(pos_screen) : Vector2i()); + wpos += srect.position; + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - p_rect.size.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - p_rect.size.height / 3); // OS X native y-coordinate relative to _get_screens_origin() is negative, // Godot passes a positive value. - position.y *= -1; - position += _get_screens_origin(); + wpos.y *= -1; + wpos += _get_screens_origin(); // initWithContentRect uses bottom-left corner of the window’s frame as origin. wd.window_object = [[GodotWindow alloc] - initWithContentRect:NSMakeRect(position.x / scale, (position.y - p_rect.size.height) / scale, p_rect.size.width / scale, p_rect.size.height / scale) + initWithContentRect:NSMakeRect(0, 0, p_rect.size.width / scale, p_rect.size.height / scale) styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO]; ERR_FAIL_COND_V_MSG(wd.window_object == nil, INVALID_WINDOW_ID, "Can't create a window"); + [wd.window_object setFrameTopLeftPoint:NSMakePoint(wpos.x / scale, wpos.y / scale)]; [wd.window_object setWindowID:window_id_counter]; wd.window_view = [[GodotContentView alloc] init]; @@ -2208,10 +2225,10 @@ Vector<DisplayServer::WindowID> DisplayServerMacOS::get_window_list() const { return ret; } -DisplayServer::WindowID DisplayServerMacOS::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServer::WindowID DisplayServerMacOS::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen) { _THREAD_SAFE_METHOD_ - WindowID id = _create_window(p_mode, p_vsync_mode, p_rect); + WindowID id = _create_window(p_mode, p_vsync_mode, p_rect, p_screen); for (int i = 0; i < WINDOW_FLAG_MAX; i++) { if (p_flags & (1 << i)) { window_set_flag(WindowFlags(i), true, id); @@ -2328,8 +2345,14 @@ void DisplayServerMacOS::window_set_current_screen(int p_screen, WindowID p_wind was_fullscreen = true; } + Rect2i srect = screen_get_usable_rect(p_screen); Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); - window_set_position(wpos + screen_get_position(p_screen), p_window); + Size2i wsize = window_get_size(p_window); + wpos += srect.position; + + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - wsize.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - wsize.height / 3); + window_set_position(wpos, p_window); if (was_fullscreen) { // Re-enter fullscreen mode. @@ -3781,7 +3804,7 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM window_position = *p_position; } - WindowID main_window = _create_window(p_mode, p_vsync_mode, Rect2i(window_position, p_resolution)); + WindowID main_window = _create_window(p_mode, p_vsync_mode, Rect2i(window_position, p_resolution), 0); ERR_FAIL_COND(main_window == INVALID_WINDOW_ID); for (int i = 0; i < WINDOW_FLAG_MAX; i++) { if (p_flags & (1 << i)) { diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm index cb70a5db86..27afa60bed 100644 --- a/platform/macos/godot_content_view.mm +++ b/platform/macos/godot_content_view.mm @@ -65,16 +65,28 @@ if (ds && ds->has_window(window_id)) { DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); NSRect frameRect = [wd.window_object frame]; - bool left = (wd.last_frame_rect.origin.x != frameRect.origin.x); - bool top = (wd.last_frame_rect.origin.y == frameRect.origin.y); - if (left && top) { - self.layerContentsPlacement = NSViewLayerContentsPlacementBottomRight; - } else if (left && !top) { - self.layerContentsPlacement = NSViewLayerContentsPlacementTopRight; - } else if (!left && top) { - self.layerContentsPlacement = NSViewLayerContentsPlacementBottomLeft; + if (wd.fs_transition || wd.initial_size) { + self.layerContentsPlacement = NSViewLayerContentsPlacementScaleAxesIndependently; + wd.initial_size = false; } else { - self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft; + bool left = (wd.last_frame_rect.origin.x != frameRect.origin.x); + bool bottom = (wd.last_frame_rect.origin.y != frameRect.origin.y); + bool right = (wd.last_frame_rect.origin.x + wd.last_frame_rect.size.width != frameRect.origin.x + frameRect.size.width); + bool top = (wd.last_frame_rect.origin.y + wd.last_frame_rect.size.height != frameRect.origin.y + frameRect.size.height); + + if (left && top) { + self.layerContentsPlacement = NSViewLayerContentsPlacementBottomRight; + } else if (left && bottom) { + self.layerContentsPlacement = NSViewLayerContentsPlacementTopRight; + } else if (left) { + self.layerContentsPlacement = NSViewLayerContentsPlacementRight; + } else if (right && top) { + self.layerContentsPlacement = NSViewLayerContentsPlacementBottomLeft; + } else if (right && bottom) { + self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft; + } else if (right) { + self.layerContentsPlacement = NSViewLayerContentsPlacementLeft; + } } wd.last_frame_rect = frameRect; } @@ -440,7 +452,7 @@ NSEventSubtype subtype = [event subtype]; if (subtype == NSEventSubtypeTabletPoint) { const NSPoint p = [event tilt]; - mm->set_tilt(Vector2(p.x, p.y)); + mm->set_tilt(Vector2(p.x, -p.y)); mm->set_pen_inverted(last_pen_inverted); } else if (subtype == NSEventSubtypeTabletProximity) { // Check if using the eraser end of pen only on proximity event. diff --git a/platform/macos/godot_window_delegate.h b/platform/macos/godot_window_delegate.h index 01cc13a016..98c226aa2f 100644 --- a/platform/macos/godot_window_delegate.h +++ b/platform/macos/godot_window_delegate.h @@ -38,8 +38,6 @@ @interface GodotWindowDelegate : NSObject <NSWindowDelegate> { DisplayServer::WindowID window_id; - NSRect old_frame; - NSWindowStyleMask old_style_mask; } - (void)setWindowID:(DisplayServer::WindowID)wid; diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm index 27efd3ebb2..aa5da14651 100644 --- a/platform/macos/godot_window_delegate.mm +++ b/platform/macos/godot_window_delegate.mm @@ -70,24 +70,24 @@ ds->window_destroy(window_id); } -- (NSArray<NSWindow *> *)customWindowsToEnterFullScreenForWindow:(NSWindow *)window { +- (void)windowWillEnterFullScreen:(NSNotification *)notification { DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); if (!ds || !ds->has_window(window_id)) { - return nullptr; + return; } - old_frame = [window frame]; - old_style_mask = [window styleMask]; - - NSMutableArray<NSWindow *> *windows = [[NSMutableArray alloc] init]; - [windows addObject:window]; - - return windows; + DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); + wd.fs_transition = true; } -- (void)window:(NSWindow *)window startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration { - [(GodotWindow *)window setAnimDuration:duration]; - [window setFrame:[[window screen] frame] display:YES animate:YES]; +- (void)windowDidFailToEnterFullScreen:(NSWindow *)window { + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + if (!ds || !ds->has_window(window_id)) { + return; + } + + DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); + wd.fs_transition = false; } - (void)windowDidEnterFullScreen:(NSNotification *)notification { @@ -98,29 +98,31 @@ DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); wd.fullscreen = true; + wd.fs_transition = false; // Reset window size limits. [wd.window_object setContentMinSize:NSMakeSize(0, 0)]; [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; - [(GodotWindow *)wd.window_object setAnimDuration:-1.0f]; // Reset custom window buttons. if ([wd.window_object styleMask] & NSWindowStyleMaskFullSizeContentView) { ds->window_set_custom_window_buttons(wd, false); } - // Force window resize event. - [self windowDidResize:notification]; ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE); + + // Force window resize event and redraw. + [self windowDidResize:notification]; } -- (NSArray<NSWindow *> *)customWindowsToExitFullScreenForWindow:(NSWindow *)window { +- (void)windowWillExitFullScreen:(NSNotification *)notification { DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); if (!ds || !ds->has_window(window_id)) { - return nullptr; + return; } DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); + wd.fs_transition = true; // Restore custom window buttons. if ([wd.window_object styleMask] & NSWindowStyleMaskFullSizeContentView) { @@ -128,16 +130,22 @@ } ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE); - - NSMutableArray<NSWindow *> *windows = [[NSMutableArray alloc] init]; - [windows addObject:wd.window_object]; - return windows; } -- (void)window:(NSWindow *)window startCustomAnimationToExitFullScreenWithDuration:(NSTimeInterval)duration { - [(GodotWindow *)window setAnimDuration:duration]; - [window setStyleMask:old_style_mask]; - [window setFrame:old_frame display:YES animate:YES]; +- (void)windowDidFailToExitFullScreen:(NSWindow *)window { + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + if (!ds || !ds->has_window(window_id)) { + return; + } + + DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); + wd.fs_transition = false; + + if ([wd.window_object styleMask] & NSWindowStyleMaskFullSizeContentView) { + ds->window_set_custom_window_buttons(wd, false); + } + + ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE); } - (void)windowDidExitFullScreen:(NSNotification *)notification { @@ -153,8 +161,7 @@ wd.fullscreen = false; wd.exclusive_fullscreen = false; - - [(GodotWindow *)wd.window_object setAnimDuration:-1.0f]; + wd.fs_transition = false; // Set window size limits. const float scale = ds->screen_get_max_scale(); @@ -177,7 +184,7 @@ [wd.window_object setLevel:NSFloatingWindowLevel]; } - // Force window resize event. + // Force window resize event and redraw. [self windowDidResize:notification]; } @@ -305,6 +312,8 @@ ds->update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); } + [self windowDidResize:notification]; // Emit resize event, to ensure content is resized if the window was resized while it was hidden. + ds->set_last_focused_window(window_id); ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_FOCUS_IN); } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e7864ebac0..fb996ec1ff 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -625,10 +625,10 @@ DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(cons return INVALID_WINDOW_ID; } -DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen) { _THREAD_SAFE_METHOD_ - WindowID window_id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect); + WindowID window_id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect, p_screen); ERR_FAIL_COND_V_MSG(window_id == INVALID_WINDOW_ID, INVALID_WINDOW_ID, "Failed to create sub window."); WindowData &wd = windows[window_id]; @@ -865,19 +865,24 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi ERR_FAIL_COND(!windows.has(p_window)); ERR_FAIL_INDEX(p_screen, get_screen_count()); + if (window_get_current_screen(p_window) == p_screen) { + return; + } const WindowData &wd = windows[p_window]; if (wd.fullscreen) { - int cs = window_get_current_screen(p_window); - if (cs == p_screen) { - return; - } Point2 pos = screen_get_position(p_screen); Size2 size = screen_get_size(p_screen); MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE); } else { - Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); - window_set_position(ofs + screen_get_position(p_screen), p_window); + Rect2i srect = screen_get_usable_rect(p_screen); + Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); + Size2i wsize = window_get_size(p_window); + wpos += srect.position; + + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - wsize.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - wsize.height / 3); + window_set_position(wpos, p_window); } // Don't let the mouse leave the window when resizing to a smaller resolution. @@ -3534,7 +3539,7 @@ void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const } } -DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen) { DWORD dwExStyle; DWORD dwStyle; @@ -3548,24 +3553,37 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, WindowRect.bottom = p_rect.position.y + p_rect.size.y; if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + Rect2i screen_rect = Rect2i(screen_get_position(p_screen), screen_get_size(p_screen)); + + WindowRect.left = screen_rect.position.x; + WindowRect.right = screen_rect.position.x + screen_rect.size.x; + WindowRect.top = screen_rect.position.y; + WindowRect.bottom = screen_rect.position.y + screen_rect.size.y; + } else { int nearest_area = 0; - Rect2i screen_rect; + int pos_screen = -1; for (int i = 0; i < get_screen_count(); i++) { Rect2i r; r.position = screen_get_position(i); r.size = screen_get_size(i); Rect2 inters = r.intersection(p_rect); int area = inters.size.width * inters.size.height; - if (area >= nearest_area) { - screen_rect = r; + if (area > nearest_area) { + pos_screen = i; nearest_area = area; } } - WindowRect.left = screen_rect.position.x; - WindowRect.right = screen_rect.position.x + screen_rect.size.x; - WindowRect.top = screen_rect.position.y; - WindowRect.bottom = screen_rect.position.y + screen_rect.size.y; + Rect2i srect = screen_get_usable_rect(p_screen); + Point2i wpos = p_rect.position - ((pos_screen >= 0) ? screen_get_position(pos_screen) : Vector2i()); + wpos += srect.position; + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - p_rect.size.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - p_rect.size.height / 3); + + WindowRect.left = wpos.x; + WindowRect.right = wpos.x + p_rect.size.x; + WindowRect.top = wpos.y; + WindowRect.bottom = wpos.y + p_rect.size.y; } AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); @@ -3709,7 +3727,6 @@ WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr; // UXTheme API. bool DisplayServerWindows::dark_title_available = false; bool DisplayServerWindows::ux_theme_available = false; -IsDarkModeAllowedForAppPtr DisplayServerWindows::IsDarkModeAllowedForApp = nullptr; ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr; GetImmersiveColorFromColorSetExPtr DisplayServerWindows::GetImmersiveColorFromColorSetEx = nullptr; GetImmersiveColorTypeFromNamePtr DisplayServerWindows::GetImmersiveColorTypeFromName = nullptr; @@ -3727,7 +3744,7 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS { } SHC_PROCESS_DPI_AWARENESS; bool DisplayServerWindows::is_dark_mode_supported() const { - return ux_theme_available && IsDarkModeAllowedForApp(); + return ux_theme_available; } bool DisplayServerWindows::is_dark_mode() const { @@ -3817,13 +3834,12 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win // Load UXTheme. HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll"); if (ux_theme_lib) { - IsDarkModeAllowedForApp = (IsDarkModeAllowedForAppPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136)); ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(132)); GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95)); GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96)); GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98)); - ux_theme_available = IsDarkModeAllowedForApp && ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference; + ux_theme_available = ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference; if (os_ver.dwBuildNumber >= 22000) { dark_title_available = true; } @@ -3933,7 +3949,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win window_position = *p_position; } - WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution)); + WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution), 0); ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); joypad = new JoypadWindows(&windows[MAIN_WINDOW_ID].hWnd); diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 4702bb7765..fe67febe40 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -152,7 +152,6 @@ typedef UINT(WINAPI *WTInfoPtr)(UINT p_category, UINT p_index, LPVOID p_output); typedef BOOL(WINAPI *WTPacketPtr)(HANDLE p_ctx, UINT p_param, LPVOID p_packets); typedef BOOL(WINAPI *WTEnablePtr)(HANDLE p_ctx, BOOL p_enable); -typedef bool(WINAPI *IsDarkModeAllowedForAppPtr)(); typedef bool(WINAPI *ShouldAppsUseDarkModePtr)(); typedef DWORD(WINAPI *GetImmersiveColorFromColorSetExPtr)(UINT dwImmersiveColorSet, UINT dwImmersiveColorType, bool bIgnoreHighContrast, UINT dwHighContrastCacheMode); typedef int(WINAPI *GetImmersiveColorTypeFromNamePtr)(const WCHAR *name); @@ -288,7 +287,6 @@ class DisplayServerWindows : public DisplayServer { // UXTheme API static bool dark_title_available; static bool ux_theme_available; - static IsDarkModeAllowedForAppPtr IsDarkModeAllowedForApp; static ShouldAppsUseDarkModePtr ShouldAppsUseDarkMode; static GetImmersiveColorFromColorSetExPtr GetImmersiveColorFromColorSetEx; static GetImmersiveColorTypeFromNamePtr GetImmersiveColorTypeFromName; @@ -428,7 +426,7 @@ class DisplayServerWindows : public DisplayServer { uint64_t time_since_popup = 0; Ref<Image> icon; - WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect); + WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen); WindowID window_id_counter = MAIN_WINDOW_ID; RBMap<WindowID, WindowData> windows; @@ -524,7 +522,7 @@ public: virtual Vector<DisplayServer::WindowID> get_window_list() const override; - virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()) override; + virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), int p_screen = 0) override; virtual void show_window(WindowID p_window) override; virtual void delete_sub_window(WindowID p_window) override; diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index df75d34e0f..fccaf63319 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -661,5 +661,6 @@ CollisionObject2D::CollisionObject2D() { } CollisionObject2D::~CollisionObject2D() { + ERR_FAIL_NULL(PhysicsServer2D::get_singleton()); PhysicsServer2D::get_singleton()->free(rid); } diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index c6296f4732..68dc85486d 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -1507,6 +1507,7 @@ CPUParticles2D::CPUParticles2D() { } CPUParticles2D::~CPUParticles2D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(multimesh); RS::get_singleton()->free(mesh); } diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index ccbc080768..95e24028a6 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -688,6 +688,7 @@ GPUParticles2D::GPUParticles2D() { } GPUParticles2D::~GPUParticles2D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(particles); RS::get_singleton()->free(mesh); } diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp index 3ec744ff8e..9e12c7aa3b 100644 --- a/scene/2d/joint_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -246,6 +246,7 @@ Joint2D::Joint2D() { } Joint2D::~Joint2D() { + ERR_FAIL_NULL(PhysicsServer2D::get_singleton()); PhysicsServer2D::get_singleton()->free(joint); } diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 78b5199358..e07393a9c7 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -321,6 +321,7 @@ Light2D::Light2D() { } Light2D::~Light2D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(canvas_light); } diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 67e82140e4..e7997c6eff 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -148,6 +148,7 @@ OccluderPolygon2D::OccluderPolygon2D() { } OccluderPolygon2D::~OccluderPolygon2D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(occ_polygon); } @@ -291,5 +292,7 @@ LightOccluder2D::LightOccluder2D() { } LightOccluder2D::~LightOccluder2D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); + RS::get_singleton()->free(occluder); } diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index 2bbe88b0e0..3936724dd4 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -380,7 +380,7 @@ void LineBuilder::build() { current_distance1 += pos0.distance_to(pos1); } if (_interpolate_color) { - color1 = gradient->get_color(gradient->get_points_count() - 1); + color1 = gradient->get_color(gradient->get_point_count() - 1); } if (retrieve_curve) { width_factor = curve->sample_baked(1.f); @@ -406,7 +406,7 @@ void LineBuilder::build() { // End cap (round) if (end_cap_mode == Line2D::LINE_CAP_ROUND) { // Note: color is not used in case we don't interpolate... - Color color = _interpolate_color ? gradient->get_color(gradient->get_points_count() - 1) : Color(0, 0, 0); + Color color = _interpolate_color ? gradient->get_color(gradient->get_point_count() - 1) : Color(0, 0, 0); float dist = 0; if (texture_mode == Line2D::LINE_TEXTURE_TILE) { dist = width_factor / tile_aspect; diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 62afe0d89b..4455142a6c 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -197,6 +197,7 @@ NavigationAgent2D::NavigationAgent2D() { } NavigationAgent2D::~NavigationAgent2D() { + ERR_FAIL_NULL(NavigationServer2D::get_singleton()); NavigationServer2D::get_singleton()->free(agent); agent = RID(); // Pointless } diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp index d639e1cc89..34005dbd9e 100644 --- a/scene/2d/navigation_link_2d.cpp +++ b/scene/2d/navigation_link_2d.cpp @@ -285,6 +285,7 @@ NavigationLink2D::NavigationLink2D() { } NavigationLink2D::~NavigationLink2D() { + ERR_FAIL_NULL(NavigationServer2D::get_singleton()); NavigationServer2D::get_singleton()->free(link); link = RID(); } diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index cbec4db4c5..4d9d46fb06 100644 --- a/scene/2d/navigation_obstacle_2d.cpp +++ b/scene/2d/navigation_obstacle_2d.cpp @@ -116,6 +116,7 @@ NavigationObstacle2D::NavigationObstacle2D() { } NavigationObstacle2D::~NavigationObstacle2D() { + ERR_FAIL_NULL(NavigationServer2D::get_singleton()); NavigationServer2D::get_singleton()->free(agent); agent = RID(); // Pointless } diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 675ef7c780..78c65256e8 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -343,6 +343,7 @@ NavigationRegion2D::NavigationRegion2D() { } NavigationRegion2D::~NavigationRegion2D() { + ERR_FAIL_NULL(NavigationServer2D::get_singleton()); NavigationServer2D::get_singleton()->free(region); #ifdef DEBUG_ENABLED diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index e41664b006..5b00a3a2dc 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -664,6 +664,7 @@ Polygon2D::Polygon2D() { Polygon2D::~Polygon2D() { // This will free the internally-allocated mesh instance, if allocated. + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID()); RS::get_singleton()->free(mesh); } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 62787d4488..fa0358fabc 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -529,6 +529,7 @@ Bone2D::Bone2D() { Bone2D::~Bone2D() { #ifdef TOOLS_ENABLED if (!editor_gizmo_rid.is_null()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(editor_gizmo_rid); } #endif // TOOLS_ENABLED @@ -803,5 +804,6 @@ Skeleton2D::Skeleton2D() { } Skeleton2D::~Skeleton2D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(skeleton); } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 3136752a2e..f52dbd1c53 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1108,6 +1108,7 @@ void TileMap::_rendering_update_layer(int p_layer) { void TileMap::_rendering_cleanup_layer(int p_layer) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer *rs = RenderingServer::get_singleton(); if (layers[p_layer].canvas_item.is_valid()) { rs->free(layers[p_layer].canvas_item); @@ -1280,6 +1281,7 @@ void TileMap::_rendering_create_quadrant(TileMapQuadrant *p_quadrant) { } void TileMap::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); // Free the canvas items. for (const RID &ci : p_quadrant->canvas_items) { RenderingServer::get_singleton()->free(ci); @@ -1596,6 +1598,7 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) { // Remove a quadrant. + ERR_FAIL_NULL(PhysicsServer2D::get_singleton()); for (RID body : p_quadrant->bodies) { bodies_coords.erase(body); PhysicsServer2D::get_singleton()->free(body); @@ -1754,6 +1757,7 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) { // Clear navigation shapes in the quadrant. + ERR_FAIL_NULL(NavigationServer2D::get_singleton()); for (const KeyValue<Vector2i, Vector<RID>> &E : p_quadrant->navigation_regions) { for (int i = 0; i < E.value.size(); i++) { RID region = E.value[i]; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 2a50575749..03c24e63ef 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -744,8 +744,10 @@ Camera3D::Camera3D() { } Camera3D::~Camera3D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(camera); if (pyramid_shape.is_valid()) { + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); PhysicsServer3D::get_singleton()->free(pyramid_shape); } } diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 66546092f2..1b94ff822c 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -352,6 +352,8 @@ void CollisionObject3D::_shape_changed(const Ref<Shape3D> &p_shape) { } void CollisionObject3D::_update_debug_shapes() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); + if (!is_inside_tree()) { debug_shapes_to_update.clear(); return; @@ -393,6 +395,8 @@ void CollisionObject3D::_update_debug_shapes() { } void CollisionObject3D::_clear_debug_shapes() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); + for (KeyValue<uint32_t, ShapeData> &E : shapes) { ShapeData &shapedata = E.value; ShapeData::ShapeBase *shape_bases = shapedata.shapes.ptrw(); @@ -627,6 +631,7 @@ int CollisionObject3D::shape_owner_get_shape_index(uint32_t p_owner, int p_shape } void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); ERR_FAIL_COND(!shapes.has(p_owner)); ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size()); @@ -722,5 +727,6 @@ CollisionObject3D::CollisionObject3D() { } CollisionObject3D::~CollisionObject3D() { + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); PhysicsServer3D::get_singleton()->free(rid); } diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 5ac8535bb6..6d47375589 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -1701,5 +1701,6 @@ CPUParticles3D::CPUParticles3D() { } CPUParticles3D::~CPUParticles3D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(multimesh); } diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index fc442986a8..ee1086fb88 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -254,5 +254,6 @@ Decal::Decal() { } Decal::~Decal() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(decal); } diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp index 4606e70310..d9912c47b1 100644 --- a/scene/3d/fog_volume.cpp +++ b/scene/3d/fog_volume.cpp @@ -118,5 +118,6 @@ FogVolume::FogVolume() { } FogVolume::~FogVolume() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(volume); } diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 17dfe2610e..6302a9e1b8 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -634,5 +634,6 @@ GPUParticles3D::GPUParticles3D() { } GPUParticles3D::~GPUParticles3D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(particles); } diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 476820b1c4..f6546b2d1f 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -58,6 +58,7 @@ GPUParticlesCollision3D::GPUParticlesCollision3D(RS::ParticlesCollisionType p_ty } GPUParticlesCollision3D::~GPUParticlesCollision3D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(collision); } @@ -819,6 +820,7 @@ GPUParticlesAttractor3D::GPUParticlesAttractor3D(RS::ParticlesCollisionType p_ty set_base(collision); } GPUParticlesAttractor3D::~GPUParticlesAttractor3D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(collision); } diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp index 1a18f43e7b..d86c156d99 100644 --- a/scene/3d/joint_3d.cpp +++ b/scene/3d/joint_3d.cpp @@ -234,6 +234,7 @@ Joint3D::Joint3D() { } Joint3D::~Joint3D() { + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); PhysicsServer3D::get_singleton()->free(joint); } diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 262dc0db37..d3020fc5b7 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -966,6 +966,7 @@ Label3D::~Label3D() { TS->free_rid(text_rid); + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(mesh); for (KeyValue<SurfaceKey, SurfaceData> E : surfaces) { RenderingServer::get_singleton()->free(E.value.material); diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 3f43e718b8..23a08f0292 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -476,6 +476,7 @@ Light3D::Light3D() { } Light3D::~Light3D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->instance_set_base(get_instance(), RID()); if (light.is_valid()) { diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 1b5427c80d..108bbe072e 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -313,6 +313,7 @@ LightmapGIData::LightmapGIData() { } LightmapGIData::~LightmapGIData() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(lightmap); } diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 741f7397ad..3acb33f42a 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -204,6 +204,7 @@ NavigationAgent3D::NavigationAgent3D() { } NavigationAgent3D::~NavigationAgent3D() { + ERR_FAIL_NULL(NavigationServer3D::get_singleton()); NavigationServer3D::get_singleton()->free(agent); agent = RID(); // Pointless } diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp index bee7c7f39b..009abfc8fd 100644 --- a/scene/3d/navigation_link_3d.cpp +++ b/scene/3d/navigation_link_3d.cpp @@ -227,10 +227,12 @@ NavigationLink3D::NavigationLink3D() { } NavigationLink3D::~NavigationLink3D() { + ERR_FAIL_NULL(NavigationServer3D::get_singleton()); NavigationServer3D::get_singleton()->free(link); link = RID(); #ifdef DEBUG_ENABLED + ERR_FAIL_NULL(RenderingServer::get_singleton()); if (debug_instance.is_valid()) { RenderingServer::get_singleton()->free(debug_instance); } diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index f241d65649..6431bd2b77 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -122,6 +122,7 @@ NavigationObstacle3D::NavigationObstacle3D() { } NavigationObstacle3D::~NavigationObstacle3D() { + ERR_FAIL_NULL(NavigationServer3D::get_singleton()); NavigationServer3D::get_singleton()->free(agent); agent = RID(); // Pointless } diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 86b78f847e..0e0233c0cd 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -375,12 +375,15 @@ NavigationRegion3D::~NavigationRegion3D() { if (navigation_mesh.is_valid()) { navigation_mesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed)); } + ERR_FAIL_NULL(NavigationServer3D::get_singleton()); NavigationServer3D::get_singleton()->free(region); #ifdef DEBUG_ENABLED NavigationServer3D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion3D::_navigation_map_changed)); NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_mesh)); NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh)); + + ERR_FAIL_NULL(RenderingServer::get_singleton()); if (debug_instance.is_valid()) { RenderingServer::get_singleton()->free(debug_instance); } diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index f150a602b3..7166d99b92 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -139,6 +139,7 @@ Occluder3D::Occluder3D() { Occluder3D::~Occluder3D() { if (occluder.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(occluder); } } diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 02ab297d8e..ea0cfce987 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -40,6 +40,7 @@ Path3D::Path3D() { } Path3D::~Path3D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); if (debug_instance.is_valid()) { RS::get_singleton()->free(debug_instance); } diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index d29f337fc8..fd0b7912ae 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -1246,6 +1246,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo platform_rid = RID(); platform_object_id = ObjectID(); platform_velocity = Vector3(); + platform_angular_velocity = Vector3(); platform_ceiling_velocity = Vector3(); floor_normal = Vector3(); wall_normal = Vector3(); @@ -1506,6 +1507,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { platform_object_id = ObjectID(); floor_normal = Vector3(); platform_velocity = Vector3(); + platform_angular_velocity = Vector3(); bool first_slide = true; for (int iteration = 0; iteration < max_slides; ++iteration) { @@ -1708,6 +1710,7 @@ void CharacterBody3D::_set_platform_data(const PhysicsServer3D::MotionCollision platform_rid = p_collision.collider; platform_object_id = p_collision.collider_id; platform_velocity = p_collision.collider_velocity; + platform_angular_velocity = p_collision.collider_angular_velocity; platform_layer = PhysicsServer3D::get_singleton()->body_get_collision_layer(platform_rid); } @@ -1780,6 +1783,10 @@ const Vector3 &CharacterBody3D::get_platform_velocity() const { return platform_velocity; } +const Vector3 &CharacterBody3D::get_platform_angular_velocity() const { + return platform_angular_velocity; +} + Vector3 CharacterBody3D::get_linear_velocity() const { return get_real_velocity(); } @@ -1932,6 +1939,7 @@ void CharacterBody3D::_notification(int p_what) { platform_object_id = ObjectID(); motion_results.clear(); platform_velocity = Vector3(); + platform_angular_velocity = Vector3(); } break; } } @@ -1986,6 +1994,7 @@ void CharacterBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_real_velocity"), &CharacterBody3D::get_real_velocity); ClassDB::bind_method(D_METHOD("get_floor_angle", "up_direction"), &CharacterBody3D::get_floor_angle, DEFVAL(Vector3(0.0, 1.0, 0.0))); ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody3D::get_platform_velocity); + ClassDB::bind_method(D_METHOD("get_platform_angular_velocity"), &CharacterBody3D::get_platform_angular_velocity); ClassDB::bind_method(D_METHOD("get_slide_collision_count"), &CharacterBody3D::get_slide_collision_count); ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody3D::_get_slide_collision); ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody3D::_get_last_slide_collision); @@ -3358,6 +3367,7 @@ PhysicalBone3D::~PhysicalBone3D() { if (joint_data) { memdelete(joint_data); } + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); PhysicsServer3D::get_singleton()->free(joint); } diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 36b7ce774c..9c5cb48762 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -371,6 +371,7 @@ public: const Vector3 &get_real_velocity() const; real_t get_floor_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const; const Vector3 &get_platform_velocity() const; + const Vector3 &get_platform_angular_velocity() const; virtual Vector3 get_linear_velocity() const override; @@ -423,6 +424,7 @@ private: Vector3 ceiling_normal; Vector3 last_motion; Vector3 platform_velocity; + Vector3 platform_angular_velocity; Vector3 platform_ceiling_velocity; Vector3 previous_position; Vector3 real_velocity; diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index bc3cc31963..f8164ee1aa 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -257,5 +257,6 @@ ReflectionProbe::ReflectionProbe() { } ReflectionProbe::~ReflectionProbe() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(probe); } diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index b205c2cde0..27310e7cee 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -58,10 +58,10 @@ Ref<Skin> SkinReference::get_skin() const { } SkinReference::~SkinReference() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); if (skeleton_node) { skeleton_node->skin_bindings.erase(this); } - RS::get_singleton()->free(skeleton); } diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index 1814baa9e9..31d3f1aeb7 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -704,6 +704,7 @@ SoftBody3D::SoftBody3D() : SoftBody3D::~SoftBody3D() { memdelete(rendering_server_handler); + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); PhysicsServer3D::get_singleton()->free(physics_rid); } diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index e488ff3059..3629464cd1 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -611,6 +611,7 @@ SpriteBase3D::SpriteBase3D() { } SpriteBase3D::~SpriteBase3D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(mesh); RenderingServer::get_singleton()->free(material); } diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp index 6d4c18a69e..70c9321338 100644 --- a/scene/3d/visible_on_screen_notifier_3d.cpp +++ b/scene/3d/visible_on_screen_notifier_3d.cpp @@ -99,6 +99,7 @@ VisibleOnScreenNotifier3D::VisibleOnScreenNotifier3D() { VisibleOnScreenNotifier3D::~VisibleOnScreenNotifier3D() { RID base_old = get_base(); set_base(RID()); + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(base_old); } diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 3b21f5c03a..07ca63dcd6 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -157,6 +157,7 @@ VisualInstance3D::VisualInstance3D() { } VisualInstance3D::~VisualInstance3D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(instance); } diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 7caf2f4874..2cb15715c6 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -242,6 +242,7 @@ VoxelGIData::VoxelGIData() { } VoxelGIData::~VoxelGIData() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(probe); } @@ -516,5 +517,6 @@ VoxelGI::VoxelGI() { } VoxelGI::~VoxelGI() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(voxel_gi); } diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index d54740e9b0..4c677b6a9f 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -415,9 +415,6 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s fading_pos += p_time; } fade_blend = MIN(1.0, fading_pos / fading_time); - if (fade_blend > 1.0) { - fading_from = StringName(); - } } } @@ -426,9 +423,12 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } double rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. - double fade_blend_inv = 1.0 - fade_blend; if (fading_from != StringName()) { + double fade_blend_inv = 1.0 - fade_blend; p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend_inv) ? CMP_EPSILON : fade_blend_inv, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + if (fade_blend >= 1.0) { + fading_from = StringName(); + } } //guess playback position diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 11a3803b35..a846ab9485 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -411,7 +411,7 @@ void FileDialog::_go_back() { } void FileDialog::_go_forward() { - if (local_history_pos == local_history.size() - 1) { + if (local_history_pos >= local_history.size() - 1) { return; } diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 82ef53e317..3c404c65b5 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -345,6 +345,7 @@ void MenuBar::_notification(int p_what) { } break; case NOTIFICATION_MOUSE_EXIT: { focused_menu = -1; + selected_menu = -1; queue_redraw(); } break; case NOTIFICATION_TRANSLATION_CHANGED: diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index f87829cf71..beaffc14b0 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -154,7 +154,9 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { queue_redraw(); } - _update_hover(); + if (!tabs.is_empty()) { + _update_hover(); + } return; } @@ -362,12 +364,19 @@ void TabBar::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { + bool rtl = is_layout_rtl(); + Vector2 size = get_size(); + if (tabs.is_empty()) { + // Draw the drop indicator where the first tab would be if there are no tabs. + if (dragging_valid_tab) { + int x = rtl ? size.x : 0; + theme_cache.drop_mark_icon->draw(get_canvas_item(), Point2(x - (theme_cache.drop_mark_icon->get_width() / 2), (size.height - theme_cache.drop_mark_icon->get_height()) / 2), theme_cache.drop_mark_color); + } + return; } - bool rtl = is_layout_rtl(); - Vector2 size = get_size(); int limit_minus_buttons = size.width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); int ofs = tabs[offset].ofs_cache; @@ -1092,7 +1101,8 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { hover_now += 1; } } else { - hover_now = is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x) ? 0 : get_tab_count() - 1; + int x = tabs.is_empty() ? 0 : get_tab_rect(0).position.x; + hover_now = is_layout_rtl() ^ (p_point.x < x) ? 0 : get_tab_count() - 1; } move_tab(tab_from_id, hover_now); @@ -1118,7 +1128,7 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { hover_now += 1; } } else { - hover_now = is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x) ? 0 : get_tab_count(); + hover_now = tabs.is_empty() || (is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x)) ? 0 : get_tab_count(); } Tab moving_tab = from_tabs->tabs[tab_from_id]; @@ -1154,10 +1164,13 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { int TabBar::get_tab_idx_at_point(const Point2 &p_point) const { int hover_now = -1; - for (int i = offset; i <= max_drawn_tab; i++) { - Rect2 rect = get_tab_rect(i); - if (rect.has_point(p_point)) { - hover_now = i; + + if (!tabs.is_empty()) { + for (int i = offset; i <= max_drawn_tab; i++) { + Rect2 rect = get_tab_rect(i); + if (rect.has_point(p_point)) { + hover_now = i; + } } } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 79bad44e15..b105ad5165 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -2580,8 +2580,8 @@ void Tree::_range_click_timeout() { mb.instantiate(); int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width; - if (h_scroll->is_visible()) { - x_limit -= h_scroll->get_minimum_size().width; + if (v_scroll->is_visible()) { + x_limit -= v_scroll->get_minimum_size().width; } cache.rtl = is_layout_rtl(); @@ -3640,8 +3640,8 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { propagate_mouse_activated = false; int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width; - if (h_scroll->is_visible()) { - x_limit -= h_scroll->get_minimum_size().width; + if (v_scroll->is_visible()) { + x_limit -= v_scroll->get_minimum_size().width; } cache.rtl = is_layout_rtl(); @@ -3847,41 +3847,38 @@ Size2 Tree::get_internal_min_size() const { } void Tree::update_scrollbars() { - Size2 size = get_size(); - int tbh; - if (show_column_titles) { - tbh = _get_title_button_height(); - } else { - tbh = 0; - } - - Size2 hmin = h_scroll->get_combined_minimum_size(); - Size2 vmin = v_scroll->get_combined_minimum_size(); - - v_scroll->set_begin(Point2(size.width - vmin.width, theme_cache.panel_style->get_margin(SIDE_TOP))); - v_scroll->set_end(Point2(size.width, size.height - theme_cache.panel_style->get_margin(SIDE_TOP) - theme_cache.panel_style->get_margin(SIDE_BOTTOM))); - - h_scroll->set_begin(Point2(0, size.height - hmin.height)); - h_scroll->set_end(Point2(size.width - vmin.width, size.height)); - - Size2 internal_min_size = get_internal_min_size(); - - bool display_vscroll = internal_min_size.height + theme_cache.panel_style->get_margin(SIDE_TOP) > size.height; - bool display_hscroll = internal_min_size.width + theme_cache.panel_style->get_margin(SIDE_LEFT) > size.width; + const Size2 size = get_size(); + const Size2 hmin = h_scroll->get_combined_minimum_size(); + const Size2 vmin = v_scroll->get_combined_minimum_size(); + + const Rect2 content_rect = Rect2(theme_cache.panel_style->get_offset(), size - theme_cache.panel_style->get_minimum_size()); + v_scroll->set_begin(content_rect.get_position() + Vector2(content_rect.get_size().x - vmin.width, 0)); + v_scroll->set_end(content_rect.get_end() - Vector2(0, hmin.height)); + h_scroll->set_begin(content_rect.get_position() + Vector2(0, content_rect.get_size().y - hmin.height)); + h_scroll->set_end(content_rect.get_end() - Vector2(vmin.width, 0)); + + const Size2 internal_min_size = get_internal_min_size(); + const int title_button_height = _get_title_button_height(); + + Size2 tree_content_size = content_rect.get_size() - Vector2(0, title_button_height); + bool display_vscroll = internal_min_size.height > tree_content_size.height; + bool display_hscroll = internal_min_size.width > tree_content_size.width; for (int i = 0; i < 2; i++) { // Check twice, as both values are dependent on each other. if (display_hscroll) { - display_vscroll = internal_min_size.height + theme_cache.panel_style->get_margin(SIDE_TOP) + hmin.height > size.height; + tree_content_size.height = content_rect.get_size().height - title_button_height - hmin.height; + display_vscroll = internal_min_size.height > tree_content_size.height; } if (display_vscroll) { - display_hscroll = internal_min_size.width + theme_cache.panel_style->get_margin(SIDE_LEFT) + vmin.width > size.width; + tree_content_size.width = content_rect.get_size().width - vmin.width; + display_hscroll = internal_min_size.width > tree_content_size.width; } } if (display_vscroll) { v_scroll->show(); v_scroll->set_max(internal_min_size.height); - v_scroll->set_page(size.height - hmin.height - tbh); + v_scroll->set_page(tree_content_size.height); theme_cache.offset.y = v_scroll->get_value(); } else { v_scroll->hide(); @@ -3891,7 +3888,7 @@ void Tree::update_scrollbars() { if (display_hscroll) { h_scroll->show(); h_scroll->set_max(internal_min_size.width); - h_scroll->set_page(size.width - vmin.width); + h_scroll->set_page(tree_content_size.width); theme_cache.offset.x = h_scroll->get_value(); } else { h_scroll->hide(); @@ -4015,8 +4012,8 @@ void Tree::_notification(int p_what) { Point2 draw_ofs; draw_ofs += bg->get_offset(); Size2 draw_size = get_size() - bg->get_minimum_size(); - if (h_scroll->is_visible()) { - draw_size.width -= h_scroll->get_minimum_size().width; + if (v_scroll->is_visible()) { + draw_size.width -= v_scroll->get_minimum_size().width; } bg->draw(ci, Rect2(Point2(), get_size())); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index f3812eb497..404c289a6a 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -1305,6 +1305,7 @@ CanvasItem::CanvasItem() : } CanvasItem::~CanvasItem() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(canvas_item); } @@ -1459,5 +1460,6 @@ CanvasTexture::CanvasTexture() { canvas_texture = RS::get_singleton()->canvas_texture_create(); } CanvasTexture::~CanvasTexture() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(canvas_texture); } diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 97b784e9d0..2aa8df10ab 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -360,5 +360,6 @@ CanvasLayer::CanvasLayer() { } CanvasLayer::~CanvasLayer() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(canvas); } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index afc31ae480..c0b81c9b9a 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -80,6 +80,7 @@ void ViewportTexture::setup_local_to_scene() { vp->viewport_textures.insert(this); + ERR_FAIL_NULL(RenderingServer::get_singleton()); if (proxy_ph.is_valid()) { RS::get_singleton()->texture_proxy_update(proxy, vp->texture_rid); RS::get_singleton()->free(proxy_ph); @@ -153,6 +154,8 @@ ViewportTexture::~ViewportTexture() { vp->viewport_textures.erase(this); } + ERR_FAIL_NULL(RenderingServer::get_singleton()); + if (proxy_ph.is_valid()) { RS::get_singleton()->free(proxy_ph); } @@ -301,6 +304,8 @@ void Viewport::_sub_window_remove(Window *p_window) { int index = _sub_window_find(p_window); ERR_FAIL_COND(index == -1); + ERR_FAIL_NULL(RenderingServer::get_singleton()); + RS::get_singleton()->free(gui.sub_windows[index].canvas_item); gui.sub_windows.remove_at(index); @@ -4117,6 +4122,7 @@ Viewport::~Viewport() { for (ViewportTexture *E : viewport_textures) { E->vp = nullptr; } + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(viewport); } diff --git a/scene/main/window.cpp b/scene/main/window.cpp index c71c3e195b..f18b712153 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -462,9 +462,8 @@ void Window::_make_window() { } DisplayServer::VSyncMode vsync_mode = DisplayServer::get_singleton()->window_get_vsync_mode(DisplayServer::MAIN_WINDOW_ID); - window_id = DisplayServer::get_singleton()->create_sub_window(DisplayServer::WindowMode(mode), vsync_mode, f, Rect2i(position, size)); + window_id = DisplayServer::get_singleton()->create_sub_window(DisplayServer::WindowMode(mode), vsync_mode, f, Rect2i(position, size), current_screen); ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID); - DisplayServer::get_singleton()->window_set_current_screen(current_screen, window_id); DisplayServer::get_singleton()->window_set_max_size(Size2i(), window_id); DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id); String tr_title = atr(title); diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp index 8e4876e01f..292accddc1 100644 --- a/scene/resources/camera_attributes.cpp +++ b/scene/resources/camera_attributes.cpp @@ -135,6 +135,7 @@ CameraAttributes::CameraAttributes() { } CameraAttributes::~CameraAttributes() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(camera_attributes); } diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp index b16059c218..0cc5faffb1 100644 --- a/scene/resources/canvas_item_material.cpp +++ b/scene/resources/canvas_item_material.cpp @@ -294,6 +294,8 @@ CanvasItemMaterial::CanvasItemMaterial() : CanvasItemMaterial::~CanvasItemMaterial() { MutexLock lock(material_mutex); + ERR_FAIL_NULL(RenderingServer::get_singleton()); + if (shader_map.has(current_key)) { shader_map[current_key].users--; if (shader_map[current_key].users == 0) { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 571ddee763..a2638c39ca 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -509,14 +509,15 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Texture2D> empty_icon = memnew(ImageTexture); - const Ref<StyleBoxFlat> style_scrollbar = make_flat_stylebox(style_normal_color, 4, 4, 4, 4, 10); + const Ref<StyleBoxFlat> style_h_scrollbar = make_flat_stylebox(style_normal_color, 0, 4, 0, 4, 10); + const Ref<StyleBoxFlat> style_v_scrollbar = make_flat_stylebox(style_normal_color, 4, 0, 4, 0, 10); Ref<StyleBoxFlat> style_scrollbar_grabber = make_flat_stylebox(style_progress_color, 4, 4, 4, 4, 10); Ref<StyleBoxFlat> style_scrollbar_grabber_highlight = make_flat_stylebox(style_focus_color, 4, 4, 4, 4, 10); Ref<StyleBoxFlat> style_scrollbar_grabber_pressed = make_flat_stylebox(style_focus_color * Color(0.75, 0.75, 0.75), 4, 4, 4, 4, 10); // HScrollBar - theme->set_stylebox("scroll", "HScrollBar", style_scrollbar); + theme->set_stylebox("scroll", "HScrollBar", style_h_scrollbar); theme->set_stylebox("scroll_focus", "HScrollBar", focus); theme->set_stylebox("grabber", "HScrollBar", style_scrollbar_grabber); theme->set_stylebox("grabber_highlight", "HScrollBar", style_scrollbar_grabber_highlight); @@ -531,7 +532,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // VScrollBar - theme->set_stylebox("scroll", "VScrollBar", style_scrollbar); + theme->set_stylebox("scroll", "VScrollBar", style_v_scrollbar); theme->set_stylebox("scroll_focus", "VScrollBar", focus); theme->set_stylebox("grabber", "VScrollBar", style_scrollbar_grabber); theme->set_stylebox("grabber_highlight", "VScrollBar", style_scrollbar_grabber_highlight); diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 23bd8a4be4..f97cffc3fc 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1550,5 +1550,6 @@ Environment::Environment() { } Environment::~Environment() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(environment); } diff --git a/scene/resources/fog_material.cpp b/scene/resources/fog_material.cpp index 46b44d681f..2aca552716 100644 --- a/scene/resources/fog_material.cpp +++ b/scene/resources/fog_material.cpp @@ -132,6 +132,7 @@ void FogMaterial::_bind_methods() { void FogMaterial::cleanup_shader() { if (shader.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(shader); } } diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 584a7e7eac..78b4edfcf7 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -270,24 +270,18 @@ void Font::set_cache_capacity(int p_single_line, int p_multi_line) { } Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { - uint64_t hash = p_text.hash64(); - hash = hash_djb2_one_64(p_font_size, hash); - if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { - hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); - } - hash = hash_djb2_one_64(p_direction, hash); - hash = hash_djb2_one_64(p_orientation, hash); + bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL); + ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation); Ref<TextLine> buffer; - if (cache.has(hash)) { - buffer = cache.get(hash); + if (cache.has(key)) { + buffer = cache.get(key); } else { buffer.instantiate(); buffer->set_direction(p_direction); buffer->set_orientation(p_orientation); buffer->add_string(p_text, Ref<Font>(this), p_font_size); - cache.insert(hash, buffer); + cache.insert(key, buffer); } buffer->set_width(p_width); @@ -300,17 +294,11 @@ Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignmen } Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { - uint64_t hash = p_text.hash64(); - hash = hash_djb2_one_64(p_font_size, hash); - hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash); - hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); - hash = hash_djb2_one_64(p_direction, hash); - hash = hash_djb2_one_64(p_orientation, hash); + ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation); Ref<TextParagraph> lines_buffer; - if (cache_wrap.has(hash)) { - lines_buffer = cache_wrap.get(hash); + if (cache_wrap.has(key)) { + lines_buffer = cache_wrap.get(key); } else { lines_buffer.instantiate(); lines_buffer->set_direction(p_direction); @@ -319,7 +307,7 @@ Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment lines_buffer->set_width(p_width); lines_buffer->set_break_flags(p_brk_flags); lines_buffer->set_justification_flags(p_jst_flags); - cache_wrap.insert(hash, lines_buffer); + cache_wrap.insert(key, lines_buffer); } lines_buffer->set_alignment(p_alignment); @@ -329,24 +317,18 @@ Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment } void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { - uint64_t hash = p_text.hash64(); - hash = hash_djb2_one_64(p_font_size, hash); - if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { - hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); - } - hash = hash_djb2_one_64(p_direction, hash); - hash = hash_djb2_one_64(p_orientation, hash); + bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL); + ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation); Ref<TextLine> buffer; - if (cache.has(hash)) { - buffer = cache.get(hash); + if (cache.has(key)) { + buffer = cache.get(key); } else { buffer.instantiate(); buffer->set_direction(p_direction); buffer->set_orientation(p_orientation); buffer->add_string(p_text, Ref<Font>(this), p_font_size); - cache.insert(hash, buffer); + cache.insert(key, buffer); } Vector2 ofs = p_pos; @@ -366,17 +348,11 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t } void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { - uint64_t hash = p_text.hash64(); - hash = hash_djb2_one_64(p_font_size, hash); - hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash); - hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); - hash = hash_djb2_one_64(p_direction, hash); - hash = hash_djb2_one_64(p_orientation, hash); + ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation); Ref<TextParagraph> lines_buffer; - if (cache_wrap.has(hash)) { - lines_buffer = cache_wrap.get(hash); + if (cache_wrap.has(key)) { + lines_buffer = cache_wrap.get(key); } else { lines_buffer.instantiate(); lines_buffer->set_direction(p_direction); @@ -385,7 +361,7 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S lines_buffer->set_width(p_width); lines_buffer->set_break_flags(p_brk_flags); lines_buffer->set_justification_flags(p_jst_flags); - cache_wrap.insert(hash, lines_buffer); + cache_wrap.insert(key, lines_buffer); } Vector2 ofs = p_pos; @@ -402,24 +378,18 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S } void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { - uint64_t hash = p_text.hash64(); - hash = hash_djb2_one_64(p_font_size, hash); - if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { - hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); - } - hash = hash_djb2_one_64(p_direction, hash); - hash = hash_djb2_one_64(p_orientation, hash); + bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL); + ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation); Ref<TextLine> buffer; - if (cache.has(hash)) { - buffer = cache.get(hash); + if (cache.has(key)) { + buffer = cache.get(key); } else { buffer.instantiate(); buffer->set_direction(p_direction); buffer->set_orientation(p_orientation); buffer->add_string(p_text, Ref<Font>(this), p_font_size); - cache.insert(hash, buffer); + cache.insert(key, buffer); } Vector2 ofs = p_pos; @@ -439,17 +409,11 @@ void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const Str } void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { - uint64_t hash = p_text.hash64(); - hash = hash_djb2_one_64(p_font_size, hash); - hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash); - hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); - hash = hash_djb2_one_64(p_direction, hash); - hash = hash_djb2_one_64(p_orientation, hash); + ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation); Ref<TextParagraph> lines_buffer; - if (cache_wrap.has(hash)) { - lines_buffer = cache_wrap.get(hash); + if (cache_wrap.has(key)) { + lines_buffer = cache_wrap.get(key); } else { lines_buffer.instantiate(); lines_buffer->set_direction(p_direction); @@ -458,7 +422,7 @@ void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, lines_buffer->set_width(p_width); lines_buffer->set_break_flags(p_brk_flags); lines_buffer->set_justification_flags(p_jst_flags); - cache_wrap.insert(hash, lines_buffer); + cache_wrap.insert(key, lines_buffer); } Vector2 ofs = p_pos; diff --git a/scene/resources/font.h b/scene/resources/font.h index e9f7507652..44198a3111 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -47,9 +47,44 @@ class TextParagraph; class Font : public Resource { GDCLASS(Font, Resource); + struct ShapedTextKey { + String text; + int font_size = 14; + float width = 0.f; + BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_NONE; + BitField<TextServer::LineBreakFlag> brk_flags = TextServer::BREAK_MANDATORY; + TextServer::Direction direction = TextServer::DIRECTION_AUTO; + TextServer::Orientation orientation = TextServer::ORIENTATION_HORIZONTAL; + + bool operator==(const ShapedTextKey &p_b) const { + return (font_size == p_b.font_size) && (width == p_b.width) && (jst_flags == p_b.jst_flags) && (brk_flags == p_b.brk_flags) && (direction == p_b.direction) && (orientation == p_b.orientation) && (text == p_b.text); + } + + ShapedTextKey() {} + ShapedTextKey(const String &p_text, int p_font_size, float p_width, BitField<TextServer::JustificationFlag> p_jst_flags, BitField<TextServer::LineBreakFlag> p_brk_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { + text = p_text; + font_size = p_font_size; + width = p_width; + jst_flags = p_jst_flags; + brk_flags = p_brk_flags; + direction = p_direction; + orientation = p_orientation; + } + }; + + struct ShapedTextKeyHasher { + _FORCE_INLINE_ static uint32_t hash(const ShapedTextKey &p_a) { + uint32_t hash = p_a.text.hash(); + hash = hash_murmur3_one_32(p_a.font_size, hash); + hash = hash_murmur3_one_float(p_a.width, hash); + hash = hash_murmur3_one_32(p_a.brk_flags | (p_a.jst_flags << 6) | (p_a.direction << 12) | (p_a.orientation << 15), hash); + return hash_fmix32(hash); + } + }; + // Shaped string cache. - mutable LRUCache<uint64_t, Ref<TextLine>> cache; - mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap; + mutable LRUCache<ShapedTextKey, Ref<TextLine>, ShapedTextKeyHasher> cache; + mutable LRUCache<ShapedTextKey, Ref<TextParagraph>, ShapedTextKeyHasher> cache_wrap; protected: // Output. diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp index f04eb75d86..4890bb32c4 100644 --- a/scene/resources/gradient.cpp +++ b/scene/resources/gradient.cpp @@ -58,7 +58,7 @@ void Gradient::_bind_methods() { ClassDB::bind_method(D_METHOD("sample", "offset"), &Gradient::get_color_at_offset); - ClassDB::bind_method(D_METHOD("get_point_count"), &Gradient::get_points_count); + ClassDB::bind_method(D_METHOD("get_point_count"), &Gradient::get_point_count); ClassDB::bind_method(D_METHOD("set_offsets", "offsets"), &Gradient::set_offsets); ClassDB::bind_method(D_METHOD("get_offsets"), &Gradient::get_offsets); @@ -190,6 +190,6 @@ Color Gradient::get_color(int pos) { return points[pos].color; } -int Gradient::get_points_count() const { +int Gradient::get_point_count() const { return points.size(); } diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h index 2b91331ab0..16ddf5df2a 100644 --- a/scene/resources/gradient.h +++ b/scene/resources/gradient.h @@ -171,7 +171,7 @@ public: } } - int get_points_count() const; + int get_point_count() const; }; VARIANT_ENUM_CAST(Gradient::InterpolationMode); diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index 90cc3ea5f4..2defe729cc 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -410,5 +410,6 @@ ImmediateMesh::ImmediateMesh() { mesh = RS::get_singleton()->mesh_create(); } ImmediateMesh::~ImmediateMesh() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(mesh); } diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index a16d2c2072..44ce90cc4a 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -141,6 +141,7 @@ Material::Material() { } Material::~Material() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(material); } @@ -3005,6 +3006,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : } BaseMaterial3D::~BaseMaterial3D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); MutexLock lock(material_mutex); if (shader_map.has(current_key)) { diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index af770ddede..a610290a11 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -2117,6 +2117,7 @@ ArrayMesh::ArrayMesh() { ArrayMesh::~ArrayMesh() { if (mesh.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(mesh); } } @@ -2132,5 +2133,6 @@ PlaceholderMesh::PlaceholderMesh() { } PlaceholderMesh::~PlaceholderMesh() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(rid); } diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index 8afb0563b2..2ea357d814 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -365,5 +365,6 @@ MultiMesh::MultiMesh() { } MultiMesh::~MultiMesh() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(multimesh); } diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index b77430c154..a07a8ac7cd 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -1894,6 +1894,7 @@ ParticleProcessMaterial::ParticleProcessMaterial() : } ParticleProcessMaterial::~ParticleProcessMaterial() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); MutexLock lock(material_mutex); if (shader_map.has(current_key)) { diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 54d3676c15..aeb62d6709 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -340,6 +340,7 @@ PrimitiveMesh::PrimitiveMesh() { } PrimitiveMesh::~PrimitiveMesh() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(mesh); } diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 48ec084b02..c6621ce482 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -208,6 +208,7 @@ Shader::Shader() { } Shader::~Shader() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(shader); } diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp index 735134e27b..32ce0abf7a 100644 --- a/scene/resources/sky.cpp +++ b/scene/resources/sky.cpp @@ -106,5 +106,6 @@ Sky::Sky() { } Sky::~Sky() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(sky); } diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 48eaf98aa3..6193c62788 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -324,6 +324,7 @@ ImageTexture::ImageTexture() {} ImageTexture::~ImageTexture() { if (texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(texture); } } @@ -630,6 +631,7 @@ PortableCompressedTexture2D::PortableCompressedTexture2D() {} PortableCompressedTexture2D::~PortableCompressedTexture2D() { if (texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(texture); } } @@ -1041,6 +1043,7 @@ CompressedTexture2D::CompressedTexture2D() {} CompressedTexture2D::~CompressedTexture2D() { if (texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(texture); } } @@ -1225,6 +1228,7 @@ ImageTexture3D::ImageTexture3D() { ImageTexture3D::~ImageTexture3D() { if (texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(texture); } } @@ -1386,6 +1390,7 @@ CompressedTexture3D::CompressedTexture3D() {} CompressedTexture3D::~CompressedTexture3D() { if (texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(texture); } } @@ -1911,6 +1916,7 @@ CurveTexture::CurveTexture() {} CurveTexture::~CurveTexture() { if (_texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(_texture); } } @@ -2109,6 +2115,7 @@ CurveXYZTexture::CurveXYZTexture() {} CurveXYZTexture::~CurveXYZTexture() { if (_texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(_texture); } } @@ -2121,6 +2128,7 @@ GradientTexture1D::GradientTexture1D() { GradientTexture1D::~GradientTexture1D() { if (texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(texture); } } @@ -2263,6 +2271,7 @@ GradientTexture2D::GradientTexture2D() { GradientTexture2D::~GradientTexture2D() { if (texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(texture); } } @@ -2303,9 +2312,9 @@ void GradientTexture2D::_update() { Ref<Image> image; image.instantiate(); - if (gradient->get_points_count() <= 1) { // No need to interpolate. + if (gradient->get_point_count() <= 1) { // No need to interpolate. image->initialize_data(width, height, false, (use_hdr) ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8); - image->fill((gradient->get_points_count() == 1) ? gradient->get_color(0) : Color(0, 0, 0, 1)); + image->fill((gradient->get_point_count() == 1) ? gradient->get_color(0) : Color(0, 0, 0, 1)); } else { if (use_hdr) { image->initialize_data(width, height, false, Image::FORMAT_RGBAF); @@ -2521,6 +2530,7 @@ void ProxyTexture::set_base(const Ref<Texture2D> &p_texture) { base = p_texture; if (base.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); if (proxy_ph.is_valid()) { RS::get_singleton()->texture_proxy_update(proxy, base->get_rid()); RS::get_singleton()->free(proxy_ph); @@ -2571,6 +2581,7 @@ ProxyTexture::ProxyTexture() { } ProxyTexture::~ProxyTexture() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); if (proxy_ph.is_valid()) { RS::get_singleton()->free(proxy_ph); } @@ -2828,6 +2839,7 @@ AnimatedTexture::AnimatedTexture() { } AnimatedTexture::~AnimatedTexture() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(proxy); RS::get_singleton()->free(proxy_ph); } @@ -3031,6 +3043,7 @@ ImageTextureLayered::ImageTextureLayered(LayeredType p_layered_type) { ImageTextureLayered::~ImageTextureLayered() { if (texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(texture); } } @@ -3198,6 +3211,7 @@ CompressedTextureLayered::CompressedTextureLayered(LayeredType p_type) { CompressedTextureLayered::~CompressedTextureLayered() { if (texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(texture); } } @@ -3353,6 +3367,7 @@ CameraTexture::CameraTexture() {} CameraTexture::~CameraTexture() { if (_texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(_texture); } } @@ -3394,6 +3409,7 @@ PlaceholderTexture2D::PlaceholderTexture2D() { } PlaceholderTexture2D::~PlaceholderTexture2D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(rid); } @@ -3441,6 +3457,7 @@ PlaceholderTexture3D::PlaceholderTexture3D() { rid = RS::get_singleton()->texture_3d_placeholder_create(); } PlaceholderTexture3D::~PlaceholderTexture3D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(rid); } @@ -3499,5 +3516,6 @@ PlaceholderTextureLayered::PlaceholderTextureLayered(LayeredType p_type) { rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type)); } PlaceholderTextureLayered::~PlaceholderTextureLayered() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(rid); } diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 6b8f8097a8..fc2366a78c 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -2650,6 +2650,10 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_direction_world", "CAMERA_DIRECTION_WORLD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "camera_visible_layers", "CAMERA_VISIBLE_LAYERS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_view", "NODE_POSITION_VIEW" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom0", "CUSTOM0" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom1", "CUSTOM1" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom2", "CUSTOM2" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom3", "CUSTOM3" }, // Node3D, Fragment { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 75deb1e60b..85ce47f06c 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -89,6 +89,9 @@ World2D::World2D() { } World2D::~World2D() { + ERR_FAIL_NULL(RenderingServer::get_singleton()); + ERR_FAIL_NULL(PhysicsServer2D::get_singleton()); + ERR_FAIL_NULL(NavigationServer2D::get_singleton()); RenderingServer::get_singleton()->free(canvas); PhysicsServer2D::get_singleton()->free(space); NavigationServer2D::get_singleton()->free(navigation_map); diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index ae8c9a182f..8808da95d7 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -157,6 +157,9 @@ World3D::World3D() { } World3D::~World3D() { + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); + ERR_FAIL_NULL(RenderingServer::get_singleton()); + ERR_FAIL_NULL(NavigationServer3D::get_singleton()); PhysicsServer3D::get_singleton()->free(space); RenderingServer::get_singleton()->free(scenario); NavigationServer3D::get_singleton()->free(navigation_map); diff --git a/servers/camera/camera_feed.cpp b/servers/camera/camera_feed.cpp index 624c06ecf1..e5b160ae27 100644 --- a/servers/camera/camera_feed.cpp +++ b/servers/camera/camera_feed.cpp @@ -165,6 +165,7 @@ CameraFeed::CameraFeed(String p_name, FeedPosition p_position) { CameraFeed::~CameraFeed() { // Free our textures + ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer::get_singleton()->free(texture[CameraServer::FEED_Y_IMAGE]); RenderingServer::get_singleton()->free(texture[CameraServer::FEED_CBCR_IMAGE]); } diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 523cc714f0..68d625b083 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -379,7 +379,7 @@ bool DisplayServer::screen_is_kept_on() const { return false; } -DisplayServer::WindowID DisplayServer::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServer::WindowID DisplayServer::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen) { ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Sub-windows not supported by this display server."); } diff --git a/servers/display_server.h b/servers/display_server.h index 4c36b0acb9..67e7a82e25 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -312,7 +312,7 @@ public: WINDOW_FLAG_EXTEND_TO_TITLE_BIT = (1 << WINDOW_FLAG_EXTEND_TO_TITLE), }; - virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()); + virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), int p_screen = 0); virtual void show_window(WindowID p_id); virtual void delete_sub_window(WindowID p_id); diff --git a/servers/display_server_headless.h b/servers/display_server_headless.h index 94b5f3c1e9..c4f6c7f165 100644 --- a/servers/display_server_headless.h +++ b/servers/display_server_headless.h @@ -66,7 +66,7 @@ public: Vector<DisplayServer::WindowID> get_window_list() const override { return Vector<DisplayServer::WindowID>(); } - WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()) override { return 0; } + WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), int p_screen = 0) override { return 0; } void show_window(WindowID p_id) override {} void delete_sub_window(WindowID p_id) override {} diff --git a/servers/physics_3d/godot_collision_solver_3d.cpp b/servers/physics_3d/godot_collision_solver_3d.cpp index ca76a819ec..47f767425b 100644 --- a/servers/physics_3d/godot_collision_solver_3d.cpp +++ b/servers/physics_3d/godot_collision_solver_3d.cpp @@ -513,10 +513,6 @@ bool GodotCollisionSolver3D::solve_distance_world_boundary(const GodotShape3D *p } bool GodotCollisionSolver3D::solve_distance(const GodotShape3D *p_shape_A, const Transform3D &p_transform_A, const GodotShape3D *p_shape_B, const Transform3D &p_transform_B, Vector3 &r_point_A, Vector3 &r_point_B, const AABB &p_concave_hint, Vector3 *r_sep_axis) { - if (p_shape_A->is_concave()) { - return false; - } - if (p_shape_B->get_type() == PhysicsServer3D::SHAPE_WORLD_BOUNDARY) { Vector3 a, b; bool col = solve_distance_world_boundary(p_shape_B, p_transform_B, p_shape_A, p_transform_A, a, b); diff --git a/servers/physics_3d/godot_space_3d.cpp b/servers/physics_3d/godot_space_3d.cpp index c98409e2c4..813970848a 100644 --- a/servers/physics_3d/godot_space_3d.cpp +++ b/servers/physics_3d/godot_space_3d.cpp @@ -992,6 +992,7 @@ bool GodotSpace3D::test_body_motion(GodotBody3D *p_body, const PhysicsServer3D:: Vector3 rel_vec = result.contact - (body->get_transform().origin + body->get_center_of_mass()); collision.collider_velocity = body->get_linear_velocity() + (body->get_angular_velocity()).cross(rel_vec); + collision.collider_angular_velocity = body->get_angular_velocity(); } r_result->travel = safe * p_parameters.motion; diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 20f23ae031..bd4512a3d5 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -772,6 +772,9 @@ void PhysicsServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("joint_make_groove", "joint", "groove1_a", "groove2_a", "anchor_b", "body_a", "body_b"), &PhysicsServer2D::joint_make_groove, DEFVAL(RID()), DEFVAL(RID())); ClassDB::bind_method(D_METHOD("joint_make_damped_spring", "joint", "anchor_a", "anchor_b", "body_a", "body_b"), &PhysicsServer2D::joint_make_damped_spring, DEFVAL(RID())); + ClassDB::bind_method(D_METHOD("pin_joint_set_param", "joint", "param", "value"), &PhysicsServer2D::pin_joint_set_param); + ClassDB::bind_method(D_METHOD("pin_joint_get_param", "joint", "param"), &PhysicsServer2D::pin_joint_get_param); + ClassDB::bind_method(D_METHOD("damped_spring_joint_set_param", "joint", "param", "value"), &PhysicsServer2D::damped_spring_joint_set_param); ClassDB::bind_method(D_METHOD("damped_spring_joint_get_param", "joint", "param"), &PhysicsServer2D::damped_spring_joint_get_param); diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index ca1ff57c99..922baad663 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -541,6 +541,7 @@ public: Vector3 position; Vector3 normal; Vector3 collider_velocity; + Vector3 collider_angular_velocity; real_t depth = 0.0; int local_shape = 0; ObjectID collider_id; diff --git a/servers/rendering/renderer_scene_occlusion_cull.cpp b/servers/rendering/renderer_scene_occlusion_cull.cpp index e1ca5a7103..627e6941cf 100644 --- a/servers/rendering/renderer_scene_occlusion_cull.cpp +++ b/servers/rendering/renderer_scene_occlusion_cull.cpp @@ -60,6 +60,8 @@ void RendererSceneOcclusionCull::HZBuffer::clear() { if (debug_image.is_valid()) { debug_image.unref(); } + + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(debug_texture); } diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index ec4ae98397..b437ed1512 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -123,6 +123,7 @@ XRInterface::XRInterface() {} XRInterface::~XRInterface() { if (vrs.vrs_texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); RS::get_singleton()->free(vrs.vrs_texture); vrs.vrs_texture = RID(); } diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h index f5c5de7fdf..5feb3a4054 100644 --- a/tests/core/object/test_object.h +++ b/tests/core/object/test_object.h @@ -281,6 +281,84 @@ TEST_CASE("[Object] Absent name getter") { actual_value == Variant(), "The returned value should equal nil variant."); } + +TEST_CASE("[Object] Signals") { + Object object; + + CHECK_FALSE(object.has_signal("my_custom_signal")); + + List<MethodInfo> signals_before; + object.get_signal_list(&signals_before); + + object.add_user_signal(MethodInfo("my_custom_signal")); + + CHECK(object.has_signal("my_custom_signal")); + + List<MethodInfo> signals_after; + object.get_signal_list(&signals_after); + + // Should be one more signal. + CHECK_EQ(signals_before.size() + 1, signals_after.size()); + + SUBCASE("Adding the same user signal again should not have any effect") { + CHECK(object.has_signal("my_custom_signal")); + ERR_PRINT_OFF; + object.add_user_signal(MethodInfo("my_custom_signal")); + ERR_PRINT_ON; + CHECK(object.has_signal("my_custom_signal")); + + List<MethodInfo> signals_after_existing_added; + object.get_signal_list(&signals_after_existing_added); + + CHECK_EQ(signals_after.size(), signals_after_existing_added.size()); + } + + SUBCASE("Trying to add a preexisting signal should not have any effect") { + CHECK(object.has_signal("script_changed")); + ERR_PRINT_OFF; + object.add_user_signal(MethodInfo("script_changed")); + ERR_PRINT_ON; + CHECK(object.has_signal("script_changed")); + + List<MethodInfo> signals_after_existing_added; + object.get_signal_list(&signals_after_existing_added); + + CHECK_EQ(signals_after.size(), signals_after_existing_added.size()); + } + + SUBCASE("Adding an empty signal should not have any effect") { + CHECK_FALSE(object.has_signal("")); + ERR_PRINT_OFF; + object.add_user_signal(MethodInfo("")); + ERR_PRINT_ON; + CHECK_FALSE(object.has_signal("")); + + List<MethodInfo> signals_after_empty_added; + object.get_signal_list(&signals_after_empty_added); + + CHECK_EQ(signals_after.size(), signals_after_empty_added.size()); + } + + SUBCASE("Emitting a non existing signal will return an error") { + Error err = object.emit_signal("some_signal"); + CHECK(err == ERR_UNAVAILABLE); + } + + SUBCASE("Emitting an existing signal should call the connected method") { + Array empty_signal_args; + empty_signal_args.push_back(Array()); + + SIGNAL_WATCH(&object, "my_custom_signal"); + SIGNAL_CHECK_FALSE("my_custom_signal"); + + Error err = object.emit_signal("my_custom_signal"); + CHECK(err == OK); + + SIGNAL_CHECK("my_custom_signal", empty_signal_args); + SIGNAL_UNWATCH(&object, "my_custom_signal"); + } +} + } // namespace TestObject #endif // TEST_OBJECT_H diff --git a/tests/core/test_time.h b/tests/core/test_time.h index 177512c832..ae3d624fe8 100644 --- a/tests/core/test_time.h +++ b/tests/core/test_time.h @@ -91,7 +91,7 @@ TEST_CASE("[Time] Datetime dictionary conversion methods") { datetime[YEAR_KEY] = 2014; datetime[MONTH_KEY] = 2; datetime[DAY_KEY] = 9; - datetime[WEEKDAY_KEY] = Time::Weekday::WEEKDAY_SUNDAY; + datetime[WEEKDAY_KEY] = Weekday::WEEKDAY_SUNDAY; datetime[HOUR_KEY] = 22; datetime[MINUTE_KEY] = 10; datetime[SECOND_KEY] = 30; @@ -100,7 +100,7 @@ TEST_CASE("[Time] Datetime dictionary conversion methods") { date_only[YEAR_KEY] = 2014; date_only[MONTH_KEY] = 2; date_only[DAY_KEY] = 9; - date_only[WEEKDAY_KEY] = Time::Weekday::WEEKDAY_SUNDAY; + date_only[WEEKDAY_KEY] = Weekday::WEEKDAY_SUNDAY; Dictionary time_only; time_only[HOUR_KEY] = 22; @@ -115,8 +115,8 @@ TEST_CASE("[Time] Datetime dictionary conversion methods") { CHECK_MESSAGE(time->get_date_dict_from_unix_time(1391904000).hash() == date_only.hash(), "Time get_date_dict_from_unix_time: The date timestamp for GODOT IS OPEN SOURCE is converted to a dictionary as expected."); CHECK_MESSAGE(time->get_time_dict_from_unix_time(79830).hash() == time_only.hash(), "Time get_time_dict_from_unix_time: The time timestamp for GODOT IS OPEN SOURCE is converted to a dictionary as expected."); - CHECK_MESSAGE((Time::Weekday)(int)time->get_datetime_dict_from_unix_time(0)[WEEKDAY_KEY] == Time::Weekday::WEEKDAY_THURSDAY, "Time get_datetime_dict_from_unix_time: The weekday for the Unix epoch is a Thursday as expected."); - CHECK_MESSAGE((Time::Weekday)(int)time->get_datetime_dict_from_unix_time(1391983830)[WEEKDAY_KEY] == Time::Weekday::WEEKDAY_SUNDAY, "Time get_datetime_dict_from_unix_time: The weekday for GODOT IS OPEN SOURCE is a Sunday as expected."); + CHECK_MESSAGE((Weekday)(int)time->get_datetime_dict_from_unix_time(0)[WEEKDAY_KEY] == Weekday::WEEKDAY_THURSDAY, "Time get_datetime_dict_from_unix_time: The weekday for the Unix epoch is a Thursday as expected."); + CHECK_MESSAGE((Weekday)(int)time->get_datetime_dict_from_unix_time(1391983830)[WEEKDAY_KEY] == Weekday::WEEKDAY_SUNDAY, "Time get_datetime_dict_from_unix_time: The weekday for GODOT IS OPEN SOURCE is a Sunday as expected."); CHECK_MESSAGE(time->get_datetime_dict_from_datetime_string("2014-02-09T22:10:30").hash() == datetime.hash(), "Time get_datetime_dict_from_string: The dictionary from string for GODOT IS OPEN SOURCE works as expected."); CHECK_MESSAGE(!time->get_datetime_dict_from_datetime_string("2014-02-09T22:10:30", false).has(WEEKDAY_KEY), "Time get_datetime_dict_from_string: The dictionary from string for GODOT IS OPEN SOURCE without weekday doesn't contain the weekday key as expected."); diff --git a/tests/scene/test_gradient.h b/tests/scene/test_gradient.h index b0e6128932..6049abbec2 100644 --- a/tests/scene/test_gradient.h +++ b/tests/scene/test_gradient.h @@ -42,7 +42,7 @@ TEST_CASE("[Gradient] Default gradient") { Ref<Gradient> gradient = memnew(Gradient); CHECK_MESSAGE( - gradient->get_points_count() == 2, + gradient->get_point_count() == 2, "Default gradient should contain the expected number of points."); CHECK_MESSAGE( @@ -77,7 +77,7 @@ TEST_CASE("[Gradient] Custom gradient (points specified in order)") { gradient->set_points(points); CHECK_MESSAGE( - gradient->get_points_count() == 3, + gradient->get_point_count() == 3, "Custom gradient should contain the expected number of points."); CHECK_MESSAGE( @@ -98,7 +98,7 @@ TEST_CASE("[Gradient] Custom gradient (points specified in order)") { gradient->remove_point(1); CHECK_MESSAGE( - gradient->get_points_count() == 2, + gradient->get_point_count() == 2, "Custom gradient should contain the expected number of points after removing one point."); CHECK_MESSAGE( gradient->get_color_at_offset(0.5).is_equal_approx(Color(0.5, 1, 0)), @@ -119,7 +119,7 @@ TEST_CASE("[Gradient] Custom gradient (points specified out-of-order)") { gradient->set_points(points); CHECK_MESSAGE( - gradient->get_points_count() == 6, + gradient->get_point_count() == 6, "Custom out-of-order gradient should contain the expected number of points."); CHECK_MESSAGE( @@ -137,7 +137,7 @@ TEST_CASE("[Gradient] Custom gradient (points specified out-of-order)") { gradient->remove_point(0); CHECK_MESSAGE( - gradient->get_points_count() == 5, + gradient->get_point_count() == 5, "Custom out-of-order gradient should contain the expected number of points after removing one point."); // The color will be clamped to the nearest point (which is at offset 0.2). CHECK_MESSAGE( diff --git a/tests/scene/test_node.h b/tests/scene/test_node.h new file mode 100644 index 0000000000..68ec41f07a --- /dev/null +++ b/tests/scene/test_node.h @@ -0,0 +1,415 @@ +/*************************************************************************/ +/* test_node.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 TEST_NODE_H +#define TEST_NODE_H + +#include "scene/main/node.h" + +#include "tests/test_macros.h" + +namespace TestNode { + +TEST_CASE("[SceneTree][Node] Simple Add/Remove/Move/Find") { + Node *node = memnew(Node); + + // Check initial scene tree setup. + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 0); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 1); + + // Check initial node setup. + CHECK(node->get_name() == StringName()); + CHECK_FALSE(node->is_inside_tree()); + CHECK_EQ(node->get_parent(), nullptr); + ERR_PRINT_OFF; + CHECK(node->get_path().is_empty()); + ERR_PRINT_ON; + CHECK_EQ(node->get_child_count(), 0); + + SceneTree::get_singleton()->get_root()->add_child(node); + + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 1); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 2); + + CHECK(node->get_name() != StringName()); + CHECK(node->is_inside_tree()); + CHECK_EQ(SceneTree::get_singleton()->get_root(), node->get_parent()); + CHECK_FALSE(node->get_path().is_empty()); + CHECK_EQ(node->get_child_count(), 0); + + SUBCASE("Node should be accessible as first child") { + Node *child = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child, node); + } + + SUBCASE("Node should be accessible via the node path") { + Node *child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node->get_path()); + CHECK_EQ(child_by_path, node); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node")); + CHECK_EQ(child_by_path, nullptr); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node")); + CHECK_EQ(child_by_path, nullptr); + + node->set_name("Node"); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node->get_path()); + CHECK_EQ(child_by_path, node); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node")); + CHECK_EQ(child_by_path, node); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node")); + CHECK_EQ(child_by_path, node); + } + + SUBCASE("Node should be accessible via group") { + List<Node *> nodes; + SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes); + CHECK(nodes.is_empty()); + + node->add_to_group("nodes"); + + SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes); + CHECK_EQ(nodes.size(), 1); + List<Node *>::Element *E = nodes.front(); + CHECK_EQ(E->get(), node); + } + + SUBCASE("Node should be possible to find") { + Node *child = SceneTree::get_singleton()->get_root()->find_child("Node", true, false); + CHECK_EQ(child, nullptr); + + node->set_name("Node"); + + child = SceneTree::get_singleton()->get_root()->find_child("Node", true, false); + CHECK_EQ(child, node); + } + + SUBCASE("Node should be possible to remove") { + SceneTree::get_singleton()->get_root()->remove_child(node); + + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 0); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 1); + + CHECK_FALSE(node->is_inside_tree()); + CHECK_EQ(node->get_parent(), nullptr); + ERR_PRINT_OFF; + CHECK(node->get_path().is_empty()); + ERR_PRINT_ON; + } + + SUBCASE("Node should be possible to move") { + SceneTree::get_singleton()->get_root()->move_child(node, 0); + + Node *child = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child, node); + CHECK(node->is_inside_tree()); + } + + memdelete(node); +} + +TEST_CASE("[SceneTree][Node] Nested Add/Remove/Move/Find") { + Node *node1 = memnew(Node); + Node *node2 = memnew(Node); + Node *node1_1 = memnew(Node); + + SceneTree::get_singleton()->get_root()->add_child(node1); + SceneTree::get_singleton()->get_root()->add_child(node2); + + node1->add_child(node1_1); + + CHECK(node1_1->is_inside_tree()); + CHECK_EQ(node1_1->get_parent(), node1); + CHECK_EQ(node1->get_child_count(), 1); + + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 2); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4); + + SUBCASE("Nodes should be accessible via get_child(..)") { + Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child1, node1); + + Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1); + CHECK_EQ(child2, node2); + + Node *child1_1 = node1->get_child(0); + CHECK_EQ(child1_1, node1_1); + } + + SUBCASE("Removed nodes should also remove their children from the scene tree") { + // Should also remove node1_1 from the scene tree. + SceneTree::get_singleton()->get_root()->remove_child(node1); + + CHECK_EQ(node1->get_child_count(), 1); + + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 1); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 2); + + // First child should now be the second node. + Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child1, node2); + } + + SUBCASE("Removed children nodes should not affect their parent in the scene tree") { + node1->remove_child(node1_1); + + CHECK_EQ(node1_1->get_parent(), nullptr); + CHECK_EQ(node1->get_child_count(), 0); + + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 3); + } + + SUBCASE("Nodes should be in the expected order when a node is moved to the back") { + SceneTree::get_singleton()->get_root()->move_child(node1, 1); + + Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child1, node2); + + Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1); + CHECK_EQ(child2, node1); + } + + SUBCASE("Nodes should be in the expected order when a node is moved to the front") { + SceneTree::get_singleton()->get_root()->move_child(node2, 0); + + Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child1, node2); + + Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1); + CHECK_EQ(child2, node1); + } + + SUBCASE("Nodes should be in the expected order when reparented") { + CHECK_EQ(node2->get_child_count(), 0); + + node1->remove_child(node1_1); + CHECK_EQ(node1->get_child_count(), 0); + CHECK_EQ(node1_1->get_parent(), nullptr); + + node2->add_child(node1_1); + CHECK_EQ(node2->get_child_count(), 1); + CHECK_EQ(node1_1->get_parent(), node2); + + Node *child = node2->get_child(0); + CHECK_EQ(child, node1_1); + + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 2); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4); + } + + SUBCASE("Nodes should be possible to find") { + Node *child = SceneTree::get_singleton()->get_root()->find_child("NestedNode", true, false); + CHECK_EQ(child, nullptr); + + node1->set_name("Node1"); + node2->set_name("Node2"); + node1_1->set_name("NestedNode"); + + child = SceneTree::get_singleton()->get_root()->find_child("NestedNode", true, false); + CHECK_EQ(child, node1_1); + + // First node that matches with the name is node1. + child = SceneTree::get_singleton()->get_root()->find_child("Node?", true, false); + CHECK_EQ(child, node1); + + SceneTree::get_singleton()->get_root()->move_child(node2, 0); + + // It should be node2, as it is now the first one in the tree. + child = SceneTree::get_singleton()->get_root()->find_child("Node?", true, false); + CHECK_EQ(child, node2); + } + + SUBCASE("Nodes should be accessible via their node path") { + Node *child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node1->get_path()); + CHECK_EQ(child_by_path, node1); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node2->get_path()); + CHECK_EQ(child_by_path, node2); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node1_1->get_path()); + CHECK_EQ(child_by_path, node1_1); + + node1->set_name("Node1"); + node1_1->set_name("NestedNode"); + + child_by_path = node1->get_node_or_null(NodePath("NestedNode")); + CHECK_EQ(child_by_path, node1_1); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node1/NestedNode")); + CHECK_EQ(child_by_path, node1_1); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node1/NestedNode")); + CHECK_EQ(child_by_path, node1_1); + } + + SUBCASE("Nodes should be accessible via their groups") { + List<Node *> nodes; + SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes); + CHECK(nodes.is_empty()); + + SceneTree::get_singleton()->get_nodes_in_group("other_nodes", &nodes); + CHECK(nodes.is_empty()); + + node1->add_to_group("nodes"); + node2->add_to_group("other_nodes"); + node1_1->add_to_group("nodes"); + node1_1->add_to_group("other_nodes"); + + SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes); + CHECK_EQ(nodes.size(), 2); + + List<Node *>::Element *E = nodes.front(); + CHECK_EQ(E->get(), node1); + E = E->next(); + CHECK_EQ(E->get(), node1_1); + + // Clear and try again with the other group. + nodes.clear(); + + SceneTree::get_singleton()->get_nodes_in_group("other_nodes", &nodes); + CHECK_EQ(nodes.size(), 2); + + E = nodes.front(); + CHECK_EQ(E->get(), node1_1); + E = E->next(); + CHECK_EQ(E->get(), node2); + + // Clear and try again with the other group and one node removed. + nodes.clear(); + + node1->remove_from_group("nodes"); + SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes); + CHECK_EQ(nodes.size(), 1); + + E = nodes.front(); + CHECK_EQ(E->get(), node1_1); + } + + memdelete(node1_1); + memdelete(node1); + memdelete(node2); +} + +TEST_CASE("[Node] Processing") { + Node *node = memnew(Node); + + SUBCASE("Processing") { + CHECK_FALSE(node->is_processing()); + + node->set_process(true); + + CHECK(node->is_processing()); + + node->set_process(false); + + CHECK_FALSE(node->is_processing()); + } + + SUBCASE("Physics processing") { + CHECK_FALSE(node->is_physics_processing()); + + node->set_physics_process(true); + + CHECK(node->is_physics_processing()); + + node->set_physics_process(false); + + CHECK_FALSE(node->is_physics_processing()); + } + + SUBCASE("Unhandled input processing") { + CHECK_FALSE(node->is_processing_unhandled_input()); + + node->set_process_unhandled_input(true); + + CHECK(node->is_processing_unhandled_input()); + + node->set_process_unhandled_input(false); + + CHECK_FALSE(node->is_processing_unhandled_input()); + } + + SUBCASE("Input processing") { + CHECK_FALSE(node->is_processing_input()); + + node->set_process_input(true); + + CHECK(node->is_processing_input()); + + node->set_process_input(false); + + CHECK_FALSE(node->is_processing_input()); + } + + SUBCASE("Unhandled key input processing") { + CHECK_FALSE(node->is_processing_unhandled_key_input()); + + node->set_process_unhandled_key_input(true); + + CHECK(node->is_processing_unhandled_key_input()); + + node->set_process_unhandled_key_input(false); + + CHECK_FALSE(node->is_processing_unhandled_key_input()); + } + + SUBCASE("Shortcut input processing") { + CHECK_FALSE(node->is_processing_shortcut_input()); + + node->set_process_shortcut_input(true); + + CHECK(node->is_processing_shortcut_input()); + + node->set_process_shortcut_input(false); + + CHECK_FALSE(node->is_processing_shortcut_input()); + } + + SUBCASE("Internal processing") { + CHECK_FALSE(node->is_processing_internal()); + + node->set_process_internal(true); + + CHECK(node->is_processing_internal()); + + node->set_process_internal(false); + + CHECK_FALSE(node->is_processing_internal()); + } + + memdelete(node); +} + +} // namespace TestNode + +#endif // TEST_NODE_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index f51d7c8e4d..3ef8e2de6f 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -92,6 +92,7 @@ #include "tests/scene/test_code_edit.h" #include "tests/scene/test_curve.h" #include "tests/scene/test_gradient.h" +#include "tests/scene/test_node.h" #include "tests/scene/test_path_2d.h" #include "tests/scene/test_path_3d.h" #include "tests/scene/test_primitives.h" |