diff --git a/SConstruct b/SConstruct index 11e7649ea1..bb4b2a72ee 100644 --- a/SConstruct +++ b/SConstruct @@ -498,8 +498,9 @@ if selected_platform in platform_list: if env["target"] == "release": if env["tools"]: - print("Tools can only be built with targets 'debug' and 'release_debug'.") - sys.exit(255) + print("ERROR: The editor can only be built with `target=debug` or `target=release_debug`.") + print(" Use `tools=no target=release` to build a release export template.") + Exit(255) suffix += ".opt" env.Append(CPPDEFINES=["NDEBUG"]) diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index 3db743ff1f..9b00a910dc 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -82,11 +82,6 @@ bool AABB::create_from_points(const Vector &p_points) { } void AABB::merge_with(const AABB &p_aabb) { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { - ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); - } -#endif Vector3 beg_1, beg_2; Vector3 end_1, end_2; Vector3 min, max; @@ -113,11 +108,6 @@ bool AABB::is_equal_approx(const AABB &p_aabb) const { } AABB AABB::intersection(const AABB &p_aabb) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { - ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); - } -#endif Vector3 src_min = position; Vector3 src_max = position + size; Vector3 dst_min = p_aabb.position; @@ -150,11 +140,6 @@ AABB AABB::intersection(const AABB &p_aabb) const { } bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip, Vector3 *r_normal) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { - ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); - } -#endif Vector3 c1, c2; Vector3 end = position + size; real_t near = -1e20; @@ -198,11 +183,6 @@ bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 * } bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector3 *r_clip, Vector3 *r_normal) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { - ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); - } -#endif real_t min = 0, max = 1; int axis = 0; real_t sign = 0; diff --git a/core/math/aabb.h b/core/math/aabb.h index b6a7f45bab..14c0ebf695 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -115,11 +115,6 @@ public: }; inline bool AABB::intersects(const AABB &p_aabb) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { - ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); - } -#endif if (position.x >= (p_aabb.position.x + p_aabb.size.x)) { return false; } @@ -143,11 +138,6 @@ inline bool AABB::intersects(const AABB &p_aabb) const { } inline bool AABB::intersects_inclusive(const AABB &p_aabb) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { - ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); - } -#endif if (position.x > (p_aabb.position.x + p_aabb.size.x)) { return false; } @@ -171,11 +161,6 @@ inline bool AABB::intersects_inclusive(const AABB &p_aabb) const { } inline bool AABB::encloses(const AABB &p_aabb) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { - ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); - } -#endif Vector3 src_min = position; Vector3 src_max = position + size; Vector3 dst_min = p_aabb.position; @@ -286,11 +271,6 @@ bool AABB::inside_convex_shape(const Plane *p_planes, int p_plane_count) const { } bool AABB::has_point(const Vector3 &p_point) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { - ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); - } -#endif if (p_point.x < position.x) { return false; } @@ -314,11 +294,6 @@ bool AABB::has_point(const Vector3 &p_point) const { } inline void AABB::expand_to(const Vector3 &p_vector) { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { - ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); - } -#endif Vector3 begin = position; Vector3 end = position + size; @@ -385,11 +360,6 @@ inline real_t AABB::get_shortest_axis_size() const { } bool AABB::smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t t0, real_t t1) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { - ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); - } -#endif real_t divx = 1.0 / p_dir.x; real_t divy = 1.0 / p_dir.y; real_t divz = 1.0 / p_dir.z; diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp index 4c44c75083..60c44999f7 100644 --- a/core/math/rect2.cpp +++ b/core/math/rect2.cpp @@ -35,11 +35,6 @@ bool Rect2::is_equal_approx(const Rect2 &p_rect) const { } bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); - } -#endif real_t min = 0, max = 1; int axis = 0; real_t sign = 0; @@ -100,11 +95,6 @@ bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 } bool Rect2::intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { - ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); - } -#endif //SAT intersection between local and transformed rect2 Vector2 xf_points[4] = { diff --git a/core/math/rect2.h b/core/math/rect2.h index 19295c13a5..48a04f9491 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -49,11 +49,6 @@ struct Rect2 { _FORCE_INLINE_ Vector2 get_center() const { return position + (size * 0.5); } inline bool intersects(const Rect2 &p_rect, const bool p_include_borders = false) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { - ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); - } -#endif if (p_include_borders) { if (position.x > (p_rect.position.x + p_rect.size.width)) { return false; @@ -86,11 +81,6 @@ struct Rect2 { } inline real_t distance_to(const Vector2 &p_point) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); - } -#endif real_t dist = 0.0; bool inside = true; @@ -127,11 +117,6 @@ struct Rect2 { bool intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos = nullptr, Point2 *r_normal = nullptr) const; inline bool encloses(const Rect2 &p_rect) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { - ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); - } -#endif return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) && ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y)); @@ -161,11 +146,7 @@ struct Rect2 { } inline Rect2 merge(const Rect2 &p_rect) const { ///< return a merged rect -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { - ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); - } -#endif + Rect2 new_rect; new_rect.position.x = MIN(p_rect.position.x, position.x); @@ -179,11 +160,6 @@ struct Rect2 { return new_rect; }; inline bool has_point(const Point2 &p_point) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); - } -#endif if (p_point.x < position.x) { return false; } @@ -206,11 +182,6 @@ struct Rect2 { bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } inline Rect2 grow(real_t p_by) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); - } -#endif Rect2 g = *this; g.grow_by(p_by); return g; @@ -233,11 +204,6 @@ struct Rect2 { } inline Rect2 grow_individual(real_t p_left, real_t p_top, real_t p_right, real_t p_bottom) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); - } -#endif Rect2 g = *this; g.position.x -= p_left; g.position.y -= p_top; @@ -254,11 +220,7 @@ struct Rect2 { } inline void expand_to(const Vector2 &p_vector) { //in place function for speed -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); - } -#endif + Vector2 begin = position; Vector2 end = position + size; @@ -311,11 +273,6 @@ struct Rect2i { _FORCE_INLINE_ Vector2i get_center() const { return position + (size / 2); } inline bool intersects(const Rect2i &p_rect) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif if (position.x > (p_rect.position.x + p_rect.size.width)) { return false; } @@ -333,11 +290,6 @@ struct Rect2i { } inline bool encloses(const Rect2i &p_rect) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); @@ -360,18 +312,14 @@ struct Rect2i { Point2 p_rect_end = p_rect.position + p_rect.size; Point2 end = position + size; - new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; - new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; + new_rect.size.x = (int)(MIN(p_rect_end.x, end.x) - new_rect.position.x); + new_rect.size.y = (int)(MIN(p_rect_end.y, end.y) - new_rect.position.y); return new_rect; } inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif + Rect2i new_rect; new_rect.position.x = MIN(p_rect.position.x, position.x); @@ -383,13 +331,8 @@ struct Rect2i { new_rect.size = new_rect.size - new_rect.position; //make relative again return new_rect; - } + }; bool has_point(const Point2 &p_point) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif if (p_point.x < position.x) { return false; } @@ -411,11 +354,6 @@ struct Rect2i { bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; } Rect2i grow(int p_by) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif Rect2i g = *this; g.position.x -= p_by; g.position.y -= p_by; @@ -434,11 +372,6 @@ struct Rect2i { } inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif Rect2i g = *this; g.position.x -= p_left; g.position.y -= p_top; @@ -455,11 +388,6 @@ struct Rect2i { } inline void expand_to(const Point2i &p_vector) { -#ifdef MATH_CHECKS - if (unlikely(size.x < 0 || size.y < 0)) { - ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size."); - } -#endif Point2i begin = position; Point2i end = position + size; diff --git a/core/os/input_event.h b/core/os/input_event.h index 4b20240797..f0af4ee4be 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -85,7 +85,7 @@ enum JoystickList { JOY_BUTTON_20 = 20, JOY_BUTTON_21 = 21, JOY_BUTTON_22 = 22, - JOY_BUTTON_MAX = 23, + JOY_BUTTON_MAX = 128, // Android supports up to 36 buttons. DirectInput supports up to 128 buttons. JOY_L = JOY_BUTTON_4, JOY_R = JOY_BUTTON_5, diff --git a/core/set.h b/core/set.h index eb2014f36d..d0eeb8a0f9 100644 --- a/core/set.h +++ b/core/set.h @@ -509,6 +509,9 @@ public: } Element *lower_bound(const T &p_value) const { + if (!_data._root) { + return nullptr; + } return _lower_bound(p_value); } diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index d1b3e3882c..1ba70d06d3 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1015,8 +1015,11 @@ Gamepad button 22. - - Represents the maximum number of joystick buttons supported. + + The maximum number of game controller buttons supported by the engine. The actual limit may be lower on specific platforms: + - Android: Up to 36 buttons. + - Linux: Up to 80 buttons. + - Windows and macOS: Up to 128 buttons. DualShock circle button. diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml index 617c483017..502e7b59a9 100644 --- a/doc/classes/AABB.xml +++ b/doc/classes/AABB.xml @@ -6,7 +6,6 @@ [AABB] consists of a position, a size, and several utility functions. It is typically used for fast overlap tests. It uses floating-point coordinates. The 2D counterpart to [AABB] is [Rect2]. - Negative values for [member size] are not supported and will not work for most methods. Use [method abs] to get an AABB with a positive size. [b]Note:[/b] Unlike [Rect2], [AABB] does not have a variant that uses integer coordinates. diff --git a/doc/classes/AudioEffectRecord.xml b/doc/classes/AudioEffectRecord.xml index 316e3aabfe..b0c525b64a 100644 --- a/doc/classes/AudioEffectRecord.xml +++ b/doc/classes/AudioEffectRecord.xml @@ -1,10 +1,12 @@ - Audio effect used for recording sound from a microphone. + Audio effect used for recording the sound from an audio bus. - Allows the user to record sound from a microphone. It sets and gets the format in which the audio file will be recorded (8-bit, 16-bit, or compressed). It checks whether or not the recording is active, and if it is, records the sound. It then returns the recorded sample. + Allows the user to record the sound from an audio bus. This can include all audio output by Godot when used on the "Master" audio bus. + Can be used (with an [AudioStreamMicrophone]) to record from a microphone. + It sets and gets the format in which the audio file will be recorded (8-bit, 16-bit, or compressed). It checks whether or not the recording is active, and if it is, records the sound. It then returns the recorded sample. $DOCS_URL/tutorials/audio/recording_with_microphone.html diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index a4b41906a6..bf93b7814c 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -893,6 +893,12 @@ Emitted when the mouse leaves the control's [code]Rect[/code] area, provided its [member mouse_filter] lets the event reach it. [b]Note:[/b] [signal mouse_exited] will be emitted if the mouse enters a child [Control] node, even if the mouse cursor is still inside the parent's [code]Rect[/code] area. + If you want to check whether the mouse truly left the area, ignoring any top nodes, you can use code like this: + [codeblock] + func _on_mouse_exited(): + if not Rect2(Vector2(), rect_size).has_point(get_local_mouse_position()): + # Not hovering over area. + [/codeblock] diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml index 28cf336b51..b2b3c7d8ae 100644 --- a/doc/classes/FileDialog.xml +++ b/doc/classes/FileDialog.xml @@ -13,7 +13,9 @@ - Adds [code]filter[/code] as a custom filter; [code]filter[/code] should be of the form [code]"filename.extension ; Description"[/code]. For example, [code]"*.png ; PNG Images"[/code]. + Adds [code]filter[/code] to the list of filters, which restricts what files can be picked. + A [code]filter[/code] should be of the form [code]"filename.extension ; Description"[/code], where filename and extension can be [code]*[/code] to match any string. Filters starting with [code].[/code] (i.e. empty filenames) are not allowed. + Example filters: [code]"*.png ; PNG Images"[/code], [code]"project.godot ; Godot Project"[/code]. @@ -65,7 +67,7 @@ - The available file type filters. For example, this shows only [code].png[/code] and [code].gd[/code] files: [code]set_filters(PoolStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"]))[/code]. + The available file type filters. For example, this shows only [code].png[/code] and [code].gd[/code] files: [code]set_filters(PoolStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"]))[/code]. Multiple file types can also be specified in a single filter. [code]"*.png, *.jpg, *.jpeg ; Supported Images"[/code] will show both PNG and JPEG files when selected. The dialog's open or save mode, which affects the selection behavior. See enum [code]Mode[/code] constants. diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 21d5ea4954..2402dc6742 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -283,7 +283,7 @@ The horizontal range within which a port can be grabbed (on both sides). - + The vertical range within which a port can be grabbed (on both sides). diff --git a/doc/classes/MeshDataTool.xml b/doc/classes/MeshDataTool.xml index c207b59ac7..b1b347f87a 100644 --- a/doc/classes/MeshDataTool.xml +++ b/doc/classes/MeshDataTool.xml @@ -93,7 +93,7 @@ Returns specified edge associated with given face. - Edge argument must 2 or less because a face only has three edges. + Edge argument must be either 0, 1, or 2 because a face only has three edges. @@ -116,7 +116,7 @@ Returns the specified vertex of the given face. - Vertex argument must be 2 or less because faces contain three vertices. + Vertex argument must be either 0, 1, or 2 because faces contain three vertices. diff --git a/doc/classes/MeshInstance2D.xml b/doc/classes/MeshInstance2D.xml index 0c0a020761..663f55c996 100644 --- a/doc/classes/MeshInstance2D.xml +++ b/doc/classes/MeshInstance2D.xml @@ -17,7 +17,7 @@ The normal map that will be used if using the default [CanvasItemMaterial]. - [b]Note:[/b] Godot expects the normal map to use X+, Y-, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines. + [b]Note:[/b] Godot expects the normal map to use X+, Y+, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines. The [Texture] that will be used if using the default [CanvasItemMaterial]. Can be accessed as [code]TEXTURE[/code] in CanvasItem shader. diff --git a/doc/classes/MultiMeshInstance2D.xml b/doc/classes/MultiMeshInstance2D.xml index 2a9bdc9839..159ce6441a 100644 --- a/doc/classes/MultiMeshInstance2D.xml +++ b/doc/classes/MultiMeshInstance2D.xml @@ -17,7 +17,7 @@ The normal map that will be used if using the default [CanvasItemMaterial]. - [b]Note:[/b] Godot expects the normal map to use X+, Y-, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines. + [b]Note:[/b] Godot expects the normal map to use X+, Y+, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines. The [Texture] that will be used if using the default [CanvasItemMaterial]. Can be accessed as [code]TEXTURE[/code] in CanvasItem shader. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 4b2dbe11cf..bea86b40d8 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -163,6 +163,7 @@ [codeblock] ProjectSettings.set_setting("application/config/name", "Example") [/codeblock] + This can also be used to erase custom project settings. To do this change the setting value to [code]null[/code]. diff --git a/doc/classes/RayCast.xml b/doc/classes/RayCast.xml index 760368fe26..1d301ceaa5 100644 --- a/doc/classes/RayCast.xml +++ b/doc/classes/RayCast.xml @@ -122,7 +122,7 @@ The custom color to use to draw the shape in the editor and at run-time if [b]Visible Collision Shapes[/b] is enabled in the [b]Debug[/b] menu. This color will be highlighted at run-time if the [RayCast] is colliding with something. If set to [code]Color(0.0, 0.0, 0.0)[/code] (by default), the color set in [member ProjectSettings.debug/shapes/collision/shape_color] is used. - + If set to [code]1[/code], a line is used as the debug shape. Otherwise, a truncated pyramid is drawn to represent the [RayCast]. Requires [b]Visible Collision Shapes[/b] to be enabled in the [b]Debug[/b] menu for the debug shape to be visible at run-time. diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml index d10339c7f9..7f1a9fe72c 100644 --- a/doc/classes/Rect2.xml +++ b/doc/classes/Rect2.xml @@ -7,7 +7,6 @@ [Rect2] consists of a position, a size, and several utility functions. It is typically used for fast overlap tests. It uses floating-point coordinates. The 3D counterpart to [Rect2] is [AABB]. - Negative values for [member size] are not supported and will not work for most methods. Use [method abs] to get a Rect2 with a positive size. $DOCS_URL/tutorials/math/index.html diff --git a/doc/classes/Slider.xml b/doc/classes/Slider.xml index 1434aedcb0..cc2b7fc24d 100644 --- a/doc/classes/Slider.xml +++ b/doc/classes/Slider.xml @@ -27,6 +27,19 @@ If [code]true[/code], the slider will display ticks for minimum and maximum values. + + + + + Emitted when dragging stops. If [code]value_changed[/code] is true, [member Range.value] is different from the value when you started the dragging. + + + + + Emitted when dragging is started. + + + diff --git a/doc/classes/SpatialMaterial.xml b/doc/classes/SpatialMaterial.xml index 9a96b9baed..b49ee4c774 100644 --- a/doc/classes/SpatialMaterial.xml +++ b/doc/classes/SpatialMaterial.xml @@ -146,7 +146,7 @@ Texture that specifies the per-pixel normal of the detail overlay. - [b]Note:[/b] Godot expects the normal map to use X+, Y-, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines. + [b]Note:[/b] Godot expects the normal map to use X+, Y+, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines. Specifies whether to use [code]UV[/code] or [code]UV2[/code] for the detail layer. See [enum DetailUV] for options. @@ -239,7 +239,7 @@ Texture used to specify the normal at a given pixel. The [code]normal_texture[/code] only uses the red and green channels; the blue and alpha channels are ignored. The normal read from [code]normal_texture[/code] is oriented around the surface normal provided by the [Mesh]. [b]Note:[/b] The mesh must have both normals and tangents defined in its vertex data. Otherwise, the normal map won't render correctly and will only appear to darken the whole surface. If creating geometry with [SurfaceTool], you can use [method SurfaceTool.generate_normals] and [method SurfaceTool.generate_tangents] to automatically generate normals and tangents respectively. - [b]Note:[/b] Godot expects the normal map to use X+, Y-, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines. + [b]Note:[/b] Godot expects the normal map to use X+, Y+, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines. Threshold at which the alpha scissor will discard values. diff --git a/doc/classes/SpriteFrames.xml b/doc/classes/SpriteFrames.xml index 237892a731..507bba5c8e 100644 --- a/doc/classes/SpriteFrames.xml +++ b/doc/classes/SpriteFrames.xml @@ -1,10 +1,10 @@ - Sprite frame library for AnimatedSprite. + Sprite frame library for AnimatedSprite and AnimatedSprite3D. - Sprite frame library for [AnimatedSprite]. Contains frames and animation data for playback. + Sprite frame library for an [AnimatedSprite] or [AnimatedSprite3D] node. Contains frames and animation data for playback. [b]Note:[/b] You can associate a set of normal maps by creating additional [SpriteFrames] resources with a [code]_normal[/code] suffix. For example, having 2 [SpriteFrames] resources [code]run[/code] and [code]run_normal[/code] will make it so the [code]run[/code] animation uses the normal map. diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index 6ac1e751da..e1c7f42db5 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -86,7 +86,9 @@ - Returns the cross product of this vector and [code]with[/code]. + Returns the 2D analog of the cross product for this vector and [code]with[/code]. + This is the signed area of the parallelogram formed by the two vectors. If the second vector is clockwise from the first vector, then the cross product is the positive area. If counter-clockwise, the cross product is the negative area. + [b]Note:[/b] Cross product is not defined in 2D mathematically. This method embeds the 2D vectors in the XY plane of 3D space and uses their cross product's Z component as the analog. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 4d2e52a0bb..3b03e632f5 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2665,8 +2665,8 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } } } break; - case RUN_PROJECT_DATA_FOLDER: { - // ensure_user_data_dir() to prevent the edge case: "Open Project Data Folder" won't work after the project was renamed in ProjectSettingsEditor unless the project is saved + case RUN_USER_DATA_FOLDER: { + // ensure_user_data_dir() to prevent the edge case: "Open User Data Folder" won't work after the project was renamed in ProjectSettingsEditor unless the project is saved OS::get_singleton()->ensure_user_data_dir(); OS::get_singleton()->shell_open(String("file://") + OS::get_singleton()->get_user_data_dir()); } break; @@ -6333,7 +6333,7 @@ EditorNode::EditorNode() { p->add_separator(); p->add_shortcut(ED_SHORTCUT("editor/export", TTR("Export...")), FILE_EXPORT_PROJECT); p->add_item(TTR("Install Android Build Template..."), FILE_INSTALL_ANDROID_SOURCE); - p->add_item(TTR("Open Project Data Folder"), RUN_PROJECT_DATA_FOLDER); + p->add_item(TTR("Open User Data Folder"), RUN_USER_DATA_FOLDER); plugin_config_dialog = memnew(PluginConfigDialog); plugin_config_dialog->connect("plugin_ready", this, "_on_plugin_ready"); diff --git a/editor/editor_node.h b/editor/editor_node.h index 50356132fb..2b714c746f 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -163,7 +163,7 @@ private: RUN_PLAY_CUSTOM_SCENE, RUN_SCENE_SETTINGS, RUN_SETTINGS, - RUN_PROJECT_DATA_FOLDER, + RUN_USER_DATA_FOLDER, RUN_RELOAD_CURRENT_PROJECT, RUN_PROJECT_MANAGER, RUN_FILE_SERVER, diff --git a/main/input_default.cpp b/main/input_default.cpp index 7ca580f8ed..c2c874076f 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -775,7 +775,8 @@ InputDefault::InputDefault() { void InputDefault::joy_button(int p_device, int p_button, bool p_pressed) { _THREAD_SAFE_METHOD_; Joypad &joy = joy_names[p_device]; - //printf("got button %i, mapping is %i\n", p_button, joy.mapping); + ERR_FAIL_INDEX(p_button, JOY_BUTTON_MAX); + if (joy.last_buttons[p_button] == p_pressed) { return; } diff --git a/modules/fbx/data/fbx_mesh_data.cpp b/modules/fbx/data/fbx_mesh_data.cpp index 75755efeaf..ac883bafc9 100644 --- a/modules/fbx/data/fbx_mesh_data.cpp +++ b/modules/fbx/data/fbx_mesh_data.cpp @@ -1104,7 +1104,7 @@ HashMap FBXMeshData::extract_per_vertex_data( const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index); ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap()), "FBX file corrupted: #ERR05"); ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap()), "FBX file corrupted: #ERR06"); - const int index_to_direct = p_mapping_data.index[polygon_vertex_index]; + const int index_to_direct = get_vertex_from_polygon_vertex(p_mapping_data.index, polygon_vertex_index); T value = p_mapping_data.data[index_to_direct]; aggregate_vertex_data[vertex_index].push_back({ polygon_id, value }); } @@ -1309,7 +1309,7 @@ HashMap FBXMeshData::extract_per_polygon( } else { ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.index.size(), (HashMap()), "FBX file is corrupted: #ERR62"); - const int index_to_direct = p_fbx_data.index[polygon_index]; + const int index_to_direct = get_vertex_from_polygon_vertex(p_fbx_data.index, polygon_index); T value = p_fbx_data.data[index_to_direct]; aggregate_polygon_data[polygon_index].push_back(value); } diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 7716faae79..fda0379982 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -6586,103 +6586,110 @@ Error GLTFDocument::parse(Ref state, String p_path, bool p_read_binar state->major_version = version.get_slice(".", 0).to_int(); state->minor_version = version.get_slice(".", 1).to_int(); - /* STEP 0 PARSE SCENE */ + /* PARSE EXTENSIONS */ + + err = _parse_gltf_extensions(state); + if (err != OK) { + return Error::FAILED; + } + + /* PARSE SCENE */ err = _parse_scenes(state); if (err != OK) { return Error::FAILED; } - /* STEP 1 PARSE NODES */ + /* PARSE NODES */ err = _parse_nodes(state); if (err != OK) { return Error::FAILED; } - /* STEP 2 PARSE BUFFERS */ + /* PARSE BUFFERS */ err = _parse_buffers(state, p_path.get_base_dir()); if (err != OK) { return Error::FAILED; } - /* STEP 3 PARSE BUFFER VIEWS */ + /* PARSE BUFFER VIEWS */ err = _parse_buffer_views(state); if (err != OK) { return Error::FAILED; } - /* STEP 4 PARSE ACCESSORS */ + /* PARSE ACCESSORS */ err = _parse_accessors(state); if (err != OK) { return Error::FAILED; } - /* STEP 5 PARSE IMAGES */ + /* PARSE IMAGES */ err = _parse_images(state, p_path.get_base_dir()); if (err != OK) { return Error::FAILED; } - /* STEP 6 PARSE TEXTURES */ + /* PARSE TEXTURES */ err = _parse_textures(state); if (err != OK) { return Error::FAILED; } - /* STEP 7 PARSE TEXTURES */ + /* PARSE TEXTURES */ err = _parse_materials(state); if (err != OK) { return Error::FAILED; } - /* STEP 9 PARSE SKINS */ + /* PARSE SKINS */ err = _parse_skins(state); if (err != OK) { return Error::FAILED; } - /* STEP 10 DETERMINE SKELETONS */ + /* DETERMINE SKELETONS */ err = _determine_skeletons(state); if (err != OK) { return Error::FAILED; } - /* STEP 11 CREATE SKELETONS */ + /* CREATE SKELETONS */ err = _create_skeletons(state); if (err != OK) { return Error::FAILED; } - /* STEP 12 CREATE SKINS */ + /* CREATE SKINS */ err = _create_skins(state); if (err != OK) { return Error::FAILED; } - /* STEP 13 PARSE MESHES (we have enough info now) */ + /* PARSE MESHES (we have enough info now) */ err = _parse_meshes(state); if (err != OK) { return Error::FAILED; } - /* STEP 14 PARSE LIGHTS */ + /* PARSE LIGHTS */ err = _parse_lights(state); if (err != OK) { return Error::FAILED; } - /* STEP 15 PARSE CAMERAS */ + /* PARSE CAMERAS */ err = _parse_cameras(state); if (err != OK) { return Error::FAILED; } - /* STEP 16 PARSE ANIMATIONS */ + /* PARSE ANIMATIONS */ err = _parse_animations(state); if (err != OK) { return Error::FAILED; } - /* STEP 17 ASSIGN SCENE NAMES */ + /* ASSIGN SCENE NAMES */ _assign_scene_names(state); return OK; @@ -6802,3 +6809,15 @@ Error GLTFDocument::_serialize_file(Ref state, const String p_path) { } return err; } + +Error GLTFDocument::_parse_gltf_extensions(Ref state) { + ERR_FAIL_COND_V(!state.is_valid(), ERR_PARSE_ERROR); + if (state->json.has("extensionsRequired") && state->json["extensionsRequired"].get_type() == Variant::ARRAY) { + Array extensions_required = state->json["extensionsRequired"]; + if (extensions_required.find("KHR_draco_mesh_compression") != -1) { + ERR_PRINT("glTF2 extension KHR_draco_mesh_compression is not supported."); + return ERR_UNAVAILABLE; + } + } + return OK; +} diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 4a1666838e..dfc4eeb14f 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -373,6 +373,8 @@ public: String _sanitize_scene_name(Ref state, const String &p_name); String _legacy_validate_node_name(const String &p_name); + Error _parse_gltf_extensions(Ref state); + void _process_mesh_instances(Ref state, Node *scene_root); void _generate_scene_node(Ref state, Node *scene_parent, Spatial *scene_root, diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index 69c53db6d8..27e19ac676 100644 --- a/modules/websocket/websocket_server.cpp +++ b/modules/websocket/websocket_server.cpp @@ -67,7 +67,7 @@ void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_handshake_timeout"), &WebSocketServer::get_handshake_timeout); ClassDB::bind_method(D_METHOD("set_handshake_timeout", "timeout"), &WebSocketServer::set_handshake_timeout); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handshake_timeout"), "set_handshake_timeout", "get_handshake_timeout"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "handshake_timeout"), "set_handshake_timeout", "get_handshake_timeout"); ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close"))); diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index e53797f8f4..e06f880757 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -170,9 +170,9 @@ static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) { @implementation GodotApplicationDelegate - (void)forceUnbundledWindowActivationHackStep1 { - // Step1: Switch focus to macOS Dock. + // Step 1: Switch focus to macOS SystemUIServer process. // Required to perform step 2, TransformProcessType will fail if app is already the in focus. - for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) { + for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.systemuiserver"]) { [app activateWithOptions:NSApplicationActivateIgnoringOtherApps]; break; } @@ -194,8 +194,8 @@ static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) { - (void)applicationDidFinishLaunching:(NSNotification *)notice { NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; - if (nsappname == nil) { - // If executable is not a bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored). + if (nsappname == nil || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO)) { + // If the executable is started from terminal or is not bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored). [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02]; } } diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 1023973404..1c7066f60e 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -137,12 +137,8 @@ void OS_UWP::initialize_core() { NetSocketPosix::make_default(); // We need to know how often the clock is updated - if (!QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second)) - ticks_per_second = 1000; - // If timeAtGameStart is 0 then we get the time since - // the start of the computer when we call GetGameTime() - ticks_start = 0; - ticks_start = get_ticks_usec(); + QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second); + QueryPerformanceCounter((LARGE_INTEGER *)&ticks_start); IP_Unix::make_default(); @@ -564,6 +560,9 @@ uint64_t OS_UWP::get_ticks_usec() const { // This is the number of clock ticks since start QueryPerformanceCounter((LARGE_INTEGER *)&ticks); + // Subtract the ticks at game start to get + // the ticks since the game started + ticks -= ticks_start; // Divide by frequency to get the time in seconds // original calculation shown below is subject to overflow @@ -583,9 +582,6 @@ uint64_t OS_UWP::get_ticks_usec() const { // seconds time += seconds * 1000000L; - // Subtract the time at game start to get - // the time since the game started - time -= ticks_start; return time; } diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 85081ebcb5..3d6183769c 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -174,12 +174,8 @@ void OS_Windows::initialize_core() { NetSocketPosix::make_default(); // We need to know how often the clock is updated - if (!QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second)) - ticks_per_second = 1000; - // If timeAtGameStart is 0 then we get the time since - // the start of the computer when we call GetGameTime() - ticks_start = 0; - ticks_start = get_ticks_usec(); + QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second); + QueryPerformanceCounter((LARGE_INTEGER *)&ticks_start); // set minimum resolution for periodic timers, otherwise Sleep(n) may wait at least as // long as the windows scheduler resolution (~16-30ms) even for calls like Sleep(1) @@ -2460,8 +2456,10 @@ uint64_t OS_Windows::get_ticks_usec() const { uint64_t ticks; // This is the number of clock ticks since start - if (!QueryPerformanceCounter((LARGE_INTEGER *)&ticks)) - ticks = (UINT64)timeGetTime(); + QueryPerformanceCounter((LARGE_INTEGER *)&ticks); + // Subtract the ticks at game start to get + // the ticks since the game started + ticks -= ticks_start; // Divide by frequency to get the time in seconds // original calculation shown below is subject to overflow @@ -2481,9 +2479,6 @@ uint64_t OS_Windows::get_ticks_usec() const { // seconds time += seconds * 1000000L; - // Subtract the time at game start to get - // the time since the game started - time -= ticks_start; return time; } diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 6d3113fbe0..ec262e95e9 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -94,6 +94,10 @@ void Path2D::_notification(int p_what) { return; } + if (curve->get_point_count() < 2) { + return; + } + #ifdef TOOLS_ENABLED const float line_width = 2 * EDSCALE; #else diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index e86f35ab98..77721a8899 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -636,7 +636,7 @@ void Polygon2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv"); ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "vertex_colors"), "set_vertex_colors", "get_vertex_colors"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons"), "set_polygons", "get_polygons"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_bones", "_get_bones"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_bones", "_get_bones"); ADD_PROPERTY(PropertyInfo(Variant::INT, "internal_vertex_count", PROPERTY_HINT_RANGE, "0,1000"), "set_internal_vertex_count", "get_internal_vertex_count"); } diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp index f6fdc51d26..8db3b94af9 100644 --- a/scene/3d/ray_cast.cpp +++ b/scene/3d/ray_cast.cpp @@ -332,7 +332,7 @@ void RayCast::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_shape_thickness", PROPERTY_HINT_RANGE, "1,5"), "set_debug_shape_thickness", "get_debug_shape_thickness"); } -float RayCast::get_debug_shape_thickness() const { +int RayCast::get_debug_shape_thickness() const { return debug_shape_thickness; } @@ -361,7 +361,7 @@ void RayCast::_update_debug_shape_vertices() { } } -void RayCast::set_debug_shape_thickness(const float p_debug_shape_thickness) { +void RayCast::set_debug_shape_thickness(const int p_debug_shape_thickness) { debug_shape_thickness = p_debug_shape_thickness; update_gizmo(); diff --git a/scene/3d/ray_cast.h b/scene/3d/ray_cast.h index 150595ed4b..f7639bdce6 100644 --- a/scene/3d/ray_cast.h +++ b/scene/3d/ray_cast.h @@ -100,8 +100,8 @@ public: Ref get_debug_material(); - float get_debug_shape_thickness() const; - void set_debug_shape_thickness(const float p_debug_thickness); + int get_debug_shape_thickness() const; + void set_debug_shape_thickness(const int p_debug_thickness); void force_raycast_update(); bool is_colliding() const; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 31b7a0a566..99bd403f65 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -567,6 +567,7 @@ void FileDialog::clear_filters() { invalidate(); } void FileDialog::add_filter(const String &p_filter) { + ERR_FAIL_COND_MSG(p_filter.begins_with("."), "Filter must be \"filename.extension\", can't start with dot."); filters.push_back(p_filter); update_filters(); invalidate(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 2cc2483d63..36d981eded 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1667,6 +1667,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub _remove_item(p_item->subitems.front()->get(), p_line, p_subitem_line); } } + memdelete(p_item); } void RichTextLabel::add_image(const Ref &p_image, const int p_width, const int p_height) { @@ -2164,6 +2165,12 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { push_strikethrough(); pos = brk_end + 1; tag_stack.push_front(tag); + } else if (tag == "lb") { + add_text("["); + pos = brk_end + 1; + } else if (tag == "rb") { + add_text("]"); + pos = brk_end + 1; } else if (tag == "center") { push_align(ALIGN_CENTER); pos = brk_end + 1; diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index dd460cdfdc..b8c8275f9e 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -68,8 +68,13 @@ void Slider::_gui_input(Ref p_event) { } grab.active = true; grab.uvalue = get_as_ratio(); + + emit_signal("drag_started"); } else { grab.active = false; + + const bool value_changed = !Math::is_equal_approx((double)grab.uvalue, get_as_ratio()); + emit_signal("drag_ended", value_changed); } } else if (scrollable) { if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) { @@ -263,6 +268,9 @@ void Slider::_bind_methods() { ClassDB::bind_method(D_METHOD("set_scrollable", "scrollable"), &Slider::set_scrollable); ClassDB::bind_method(D_METHOD("is_scrollable"), &Slider::is_scrollable); + ADD_SIGNAL(MethodInfo("drag_started")); + ADD_SIGNAL(MethodInfo("drag_ended", PropertyInfo(Variant::BOOL, "value_changed"))); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrollable"), "set_scrollable", "is_scrollable"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tick_count", PROPERTY_HINT_RANGE, "0,4096,1"), "set_ticks", "get_ticks"); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 86d73c61ad..f6c8b1242b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -382,6 +382,7 @@ void TextEdit::_update_scrollbars() { cursor.line_ofs = 0; cursor.wrap_ofs = 0; v_scroll->set_value(0); + v_scroll->set_max(0); v_scroll->hide(); } @@ -399,6 +400,7 @@ void TextEdit::_update_scrollbars() { } else { cursor.x_ofs = 0; h_scroll->set_value(0); + h_scroll->set_max(0); h_scroll->hide(); } diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index e44497721e..49ccaf4d25 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -168,6 +168,12 @@ void TextureButton::_notification(int p_what) { Point2 ofs; Size2 size; + bool draw_focus = (has_focus() && focused.is_valid()); + + // If no other texture is valid, try using focused texture. + if (!texdraw.is_valid() && draw_focus) { + texdraw = focused; + } if (texdraw.is_valid()) { size = texdraw->get_size(); @@ -224,7 +230,9 @@ void TextureButton::_notification(int p_what) { size.width *= hflip ? -1.0f : 1.0f; size.height *= vflip ? -1.0f : 1.0f; - if (_tile) { + if (texdraw == focused) { + // Do nothing, we only needed to calculate the rectangle. + } else if (_tile) { draw_texture_rect(texdraw, Rect2(ofs, size), _tile); } else { draw_texture_rect_region(texdraw, Rect2(ofs, size), _texture_region); @@ -233,7 +241,7 @@ void TextureButton::_notification(int p_what) { _position_rect = Rect2(); } - if (has_focus() && focused.is_valid()) { + if (draw_focus) { draw_texture_rect(focused, Rect2(ofs, size), false); }; } break; diff --git a/scene/property_utils.cpp b/scene/property_utils.cpp index bed00e72e3..b58a9a4ccb 100644 --- a/scene/property_utils.cpp +++ b/scene/property_utils.cpp @@ -109,10 +109,37 @@ Variant PropertyUtils::get_property_default_value(const Object *p_object, const } // Fall back to the default from the native class - if (r_is_class_default) { - *r_is_class_default = true; + { + if (r_is_class_default) { + *r_is_class_default = true; + } + bool valid = false; + Variant value = ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property, &valid); + if (valid) { + if (r_is_valid) { + *r_is_valid = true; + } + return value; + } else { + // Heuristically check if this is a synthetic property (whatever/0, whatever/1, etc.) + // because they are not in the class DB yet must have a default (null). + String prop_str = String(p_property); + int p = prop_str.rfind("/"); + if (p != -1 && p < prop_str.length() - 1) { + bool all_digits = true; + for (int i = p + 1; i < prop_str.length(); i++) { + if (prop_str[i] < '0' || prop_str[i] > '9') { + all_digits = false; + break; + } + } + if (r_is_valid) { + *r_is_valid = all_digits; + } + } + return Variant(); + } } - return ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property, r_is_valid); } // Like SceneState::PackState, but using a raw pointer to avoid the cost of diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 85474a0735..93e72faca1 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -903,7 +903,7 @@ void fill_default_theme(Ref &theme, const Ref &default_font, const // Visual Node Ports theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 24 * scale); - theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale); + theme->set_constant("port_grab_distance_vertical", "GraphEdit", 26 * scale); theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0)); Ref style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0);