diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml index ce7ad101fc..ab581f31a3 100644 --- a/.github/workflows/static_checks.yml +++ b/.github/workflows/static_checks.yml @@ -13,7 +13,7 @@ jobs: run: | sudo apt-get update -qq sudo apt-get install -qq dos2unix recode clang-format - sudo pip3 install black pygments + sudo pip3 install black==20.8b1 pygments - name: File formatting checks (file_format.sh) run: | diff --git a/SConstruct b/SConstruct index 26019e6c13..7ccd036646 100644 --- a/SConstruct +++ b/SConstruct @@ -316,31 +316,6 @@ if selected_platform in platform_list: env["warnings"] = "extra" env["werror"] = True - if env["vsproj"]: - env.vs_incs = [] - env.vs_srcs = [] - - def AddToVSProject(sources): - for x in sources: - if type(x) == type(""): - fname = env.File(x).path - else: - fname = env.File(x)[0].path - pieces = fname.split(".") - if len(pieces) > 0: - basename = pieces[0] - basename = basename.replace("\\\\", "/") - if os.path.isfile(basename + ".h"): - env.vs_incs = env.vs_incs + [basename + ".h"] - elif os.path.isfile(basename + ".hpp"): - env.vs_incs = env.vs_incs + [basename + ".hpp"] - if os.path.isfile(basename + ".c"): - env.vs_srcs = env.vs_srcs + [basename + ".c"] - elif os.path.isfile(basename + ".cpp"): - env.vs_srcs = env.vs_srcs + [basename + ".cpp"] - - env.AddToVSProject = AddToVSProject - env.extra_suffix = "" if env["extra_suffix"] != "": @@ -604,6 +579,10 @@ if selected_platform in platform_list: CacheDir(scons_cache_path) print("Scons cache enabled... (path: '" + scons_cache_path + "')") + if env["vsproj"]: + env.vs_incs = [] + env.vs_srcs = [] + Export("env") # build subdirs, the build order is dependent on link order. diff --git a/core/message_queue.h b/core/message_queue.h index e9a92ff5b7..9eb4b0261c 100644 --- a/core/message_queue.h +++ b/core/message_queue.h @@ -39,8 +39,7 @@ class MessageQueue { _THREAD_SAFE_CLASS_ enum { - - DEFAULT_QUEUE_SIZE_KB = 1024 + DEFAULT_QUEUE_SIZE_KB = 4096 }; enum { diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp index 1aa8298aff..80e845ad6e 100644 --- a/core/variant_parser.cpp +++ b/core/variant_parser.cpp @@ -1750,11 +1750,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); p_store_string_func(p_store_string_ud, ": "); write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); - if (E->next()) + if (E->next()) { p_store_string_func(p_store_string_ud, ",\n"); + } else { + p_store_string_func(p_store_string_ud, "\n"); + } } - p_store_string_func(p_store_string_ud, "\n}"); + p_store_string_func(p_store_string_ud, "}"); } break; case Variant::ARRAY: { diff --git a/doc/classes/AudioEffectHighShelfFilter.xml b/doc/classes/AudioEffectHighShelfFilter.xml index 4b631d68dc..0a17bfd9a5 100644 --- a/doc/classes/AudioEffectHighShelfFilter.xml +++ b/doc/classes/AudioEffectHighShelfFilter.xml @@ -1,10 +1,12 @@ + Reduces all frequencies above the [member AudioEffectFilter.cutoff_hz]. + https://docs.godotengine.org/en/latest/tutorials/audio/audio_buses.html diff --git a/doc/classes/AudioEffectLowShelfFilter.xml b/doc/classes/AudioEffectLowShelfFilter.xml index b1e919c445..da54c57ef7 100644 --- a/doc/classes/AudioEffectLowShelfFilter.xml +++ b/doc/classes/AudioEffectLowShelfFilter.xml @@ -1,10 +1,12 @@ + Reduces all frequencies below the [member AudioEffectFilter.cutoff_hz]. + https://docs.godotengine.org/en/latest/tutorials/audio/audio_buses.html diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index 37b9a4903b..bfedeb212f 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -8,6 +8,7 @@ You can also create a color from standardized color names by using [method @GDScript.ColorN] or directly using the color constants defined here. The standardized color set is based on the [url=https://en.wikipedia.org/wiki/X11_color_names]X11 color names[/url]. If you want to supply values in a range of 0 to 255, you should use [method @GDScript.Color8]. [b]Note:[/b] In a boolean context, a Color will evaluate to [code]false[/code] if it's equal to [code]Color(0, 0, 0, 1)[/code] (opaque black). Otherwise, a Color will always evaluate to [code]true[/code]. + [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/color_constants.png]Color constants cheatsheet[/url] diff --git a/doc/classes/DynamicFont.xml b/doc/classes/DynamicFont.xml index b67f0319b3..7fd397a864 100644 --- a/doc/classes/DynamicFont.xml +++ b/doc/classes/DynamicFont.xml @@ -96,10 +96,12 @@ Extra spacing at the bottom in pixels. - Extra character spacing in pixels. + Extra spacing for each character in pixels. + This can be a negative number to make the distance between characters smaller. - Extra space spacing in pixels. + Extra spacing for the space character (in addition to [member extra_spacing_char]) in pixels. + This can be a negative number to make the distance between words smaller. Extra spacing at the top in pixels. @@ -132,10 +134,10 @@ Spacing at the bottom. - Character spacing. + Spacing for each character. - Space spacing. + Spacing for the space character. diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index aadbeb0708..0dea8a4729 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -125,8 +125,10 @@ + + - Shows the given property on the given [code]object[/code] in the editor's Inspector dock. + Shows the given property on the given [code]object[/code] in the editor's Inspector dock. If [code]inspector_only[/code] is [code]true[/code], plugins will not attempt to edit [code]object[/code]. diff --git a/doc/classes/File.xml b/doc/classes/File.xml index ea1973bcc0..b972301fe3 100644 --- a/doc/classes/File.xml +++ b/doc/classes/File.xml @@ -21,6 +21,7 @@ return content [/codeblock] In the example above, the file will be saved in the user data folder as specified in the [url=https://docs.godotengine.org/en/latest/tutorials/io/data_paths.html]Data paths[/url] documentation. + [b]Note:[/b] To access project resources once exported, it is recommended to use [ResourceLoader] instead of the [File] API, as some files are converted to engine-specific formats and their original source files might not be present in the exported PCK package. https://docs.godotengine.org/en/latest/getting_started/step_by_step/filesystem.html @@ -48,7 +49,7 @@ Returns [code]true[/code] if the file exists in the given path. - [b]Note:[/b] Many resources types are imported (e.g. textures or sound files), and that their source asset will not be included in the exported game, as only the imported version is used (in the [code]res://.import[/code] folder). To check for the existence of such resources while taking into account the remapping to their imported location, use [method ResourceLoader.exists]. Typically, using [code]File.file_exists[/code] on an imported resource would work while you are developing in the editor (the source asset is present in [code]res://[/code], but fail when exported). + [b]Note:[/b] Many resources types are imported (e.g. textures or sound files), and their source asset will not be included in the exported game, as only the imported version is used. See [method ResourceLoader.exists] for an alternative approach that takes resource remapping into account. diff --git a/doc/classes/KinematicBody.xml b/doc/classes/KinematicBody.xml index 62a2f623b1..30ebe3660a 100644 --- a/doc/classes/KinematicBody.xml +++ b/doc/classes/KinematicBody.xml @@ -6,7 +6,7 @@ Kinematic bodies are special types of bodies that are meant to be user-controlled. They are not affected by physics at all; to other types of bodies, such as a character or a rigid body, these are the same as a static body. However, they have two main uses: [b]Simulated motion:[/b] When these bodies are moved manually, either from code or from an [AnimationPlayer] (with [member AnimationPlayer.playback_process_mode] set to "physics"), the physics will automatically compute an estimate of their linear and angular velocity. This makes them very useful for moving platforms or other AnimationPlayer-controlled objects (like a door, a bridge that opens, etc). - [b]Kinematic characters:[/b] KinematicBody also has an API for moving objects (the [method move_and_collide] and [method move_and_slide] methods) while performing collision tests. This makes them really useful to implement characters that collide against a world, but that don't require advanced physics. + [b]Kinematic characters:[/b] KinematicBody also has an API for moving objects (the [method move_and_collide] and [method move_and_slide] methods) while performing collision tests. This makes them really useful to implement characters that collide against a world, but don't require advanced physics. https://docs.godotengine.org/en/latest/tutorials/physics/kinematic_character_2d.html @@ -41,35 +41,35 @@ - Returns a [KinematicCollision], which contains information about a collision that occurred during the last [method move_and_slide] call. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1). + Returns a [KinematicCollision], which contains information about a collision that occurred during the last call to [method move_and_slide] or [method move_and_slide_with_snap]. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1). - Returns the number of times the body collided and changed direction during the last call to [method move_and_slide]. + Returns the number of times the body collided and changed direction during the last call to [method move_and_slide] or [method move_and_slide_with_snap]. - Returns [code]true[/code] if the body is on the ceiling. Only updates when calling [method move_and_slide]. + Returns [code]true[/code] if the body is on the ceiling. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap]. - Returns [code]true[/code] if the body is on the floor. Only updates when calling [method move_and_slide]. + Returns [code]true[/code] if the body is on the floor. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap]. - Returns [code]true[/code] if the body is on a wall. Only updates when calling [method move_and_slide]. + Returns [code]true[/code] if the body is on a wall. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap]. @@ -104,7 +104,7 @@ - Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [KinematicBody] or [RigidBody], it will also be affected by the motion of the other body. You can use this to make moving or rotating platforms, or to make nodes push other nodes. + Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [KinematicBody] or [RigidBody], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed. [code]linear_velocity[/code] is the velocity vector (typically meters per second). Unlike in [method move_and_collide], you should [i]not[/i] multiply it by [code]delta[/code] — the physics engine handles applying the velocity. [code]up_direction[/code] is the up direction, used to determine what is a wall and what is a floor or a ceiling. If set to the default value of [code]Vector3(0, 0, 0)[/code], everything is considered a wall. diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml index 5f6edae536..60e9e195a5 100644 --- a/doc/classes/KinematicBody2D.xml +++ b/doc/classes/KinematicBody2D.xml @@ -6,7 +6,7 @@ Kinematic bodies are special types of bodies that are meant to be user-controlled. They are not affected by physics at all; to other types of bodies, such as a character or a rigid body, these are the same as a static body. However, they have two main uses: [b]Simulated motion:[/b] When these bodies are moved manually, either from code or from an [AnimationPlayer] (with [member AnimationPlayer.playback_process_mode] set to "physics"), the physics will automatically compute an estimate of their linear and angular velocity. This makes them very useful for moving platforms or other AnimationPlayer-controlled objects (like a door, a bridge that opens, etc). - [b]Kinematic characters:[/b] KinematicBody2D also has an API for moving objects (the [method move_and_collide] and [method move_and_slide] methods) while performing collision tests. This makes them really useful to implement characters that collide against a world, but that don't require advanced physics. + [b]Kinematic characters:[/b] KinematicBody2D also has an API for moving objects (the [method move_and_collide] and [method move_and_slide] methods) while performing collision tests. This makes them really useful to implement characters that collide against a world, but don't require advanced physics. https://docs.godotengine.org/en/latest/tutorials/physics/kinematic_character_2d.html @@ -33,7 +33,7 @@ - Returns a [KinematicCollision2D], which contains information about a collision that occurred during the last [method move_and_slide] call. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1). + Returns a [KinematicCollision2D], which contains information about a collision that occurred during the last call to [method move_and_slide] or [method move_and_slide_with_snap]. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1). [b]Example usage:[/b] [codeblock] for i in get_slide_count(): @@ -46,28 +46,28 @@ - Returns the number of times the body collided and changed direction during the last call to [method move_and_slide]. + Returns the number of times the body collided and changed direction during the last call to [method move_and_slide] or [method move_and_slide_with_snap]. - Returns [code]true[/code] if the body is on the ceiling. Only updates when calling [method move_and_slide]. + Returns [code]true[/code] if the body is on the ceiling. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap]. - Returns [code]true[/code] if the body is on the floor. Only updates when calling [method move_and_slide]. + Returns [code]true[/code] if the body is on the floor. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap]. - Returns [code]true[/code] if the body is on a wall. Only updates when calling [method move_and_slide]. + Returns [code]true[/code] if the body is on a wall. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap]. @@ -102,7 +102,7 @@ - Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [KinematicBody2D] or [RigidBody2D], it will also be affected by the motion of the other body. You can use this to make moving or rotating platforms, or to make nodes push other nodes. + Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [KinematicBody2D] or [RigidBody2D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed. [code]linear_velocity[/code] is the velocity vector in pixels per second. Unlike in [method move_and_collide], you should [i]not[/i] multiply it by [code]delta[/code] — the physics engine handles applying the velocity. [code]up_direction[/code] is the up direction, used to determine what is a wall and what is a floor or a ceiling. If set to the default value of [code]Vector2(0, 0)[/code], everything is considered a wall. This is useful for topdown games. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index e7d3207107..fd88d83cc6 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -824,7 +824,7 @@ Specifies the maximum amount of log files allowed (used for rotation). - + Godot uses a message queue to defer some function calls. If you run out of space on it (you will see an error), you can increase the size here. diff --git a/doc/classes/SpatialMaterial.xml b/doc/classes/SpatialMaterial.xml index 5a39a83dcb..0edfaedc49 100644 --- a/doc/classes/SpatialMaterial.xml +++ b/doc/classes/SpatialMaterial.xml @@ -158,10 +158,12 @@ Specifies whether to use [code]UV[/code] or [code]UV2[/code] for the detail layer. See [enum DetailUV] for options. - Distance at which the object fades fully and is no longer visible. + Distance at which the object appears fully opaque. + [b]Note:[/b] If [code]distance_fade_max_distance[/code] is less than [code]distance_fade_min_distance[/code], the behavior will be reversed. The object will start to fade away at [code]distance_fade_max_distance[/code] and will fully disappear once it reaches [code]distance_fade_min_distance[/code]. - Distance at which the object starts to fade. If the object is less than this distance away it will appear normal. + Distance at which the object starts to become visible. If the object is less than this distance away, it will be invisible. + [b]Note:[/b] If [code]distance_fade_min_distance[/code] is greater than [code]distance_fade_max_distance[/code], the behavior will be reversed. The object will start to fade away at [code]distance_fade_max_distance[/code] and will fully disappear once it reaches [code]distance_fade_min_distance[/code]. Specifies which type of fade to use. Can be any of the [enum DistanceFadeMode]s. @@ -252,6 +254,7 @@ Controls how the object faces the camera. See [enum BillboardMode]. + [b]Note:[/b] Billboard mode is not suitable for VR because the left-right vector of the camera is not horizontal when the screen is attached to your head instead of on the table. See [url=https://github.com/godotengine/godot/issues/41567]GitHub issue #41567[/url] for details. The material's blend mode. diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index 26f8d721bc..9f2db05f0e 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -172,7 +172,7 @@ - Returns [code]true[/code] if the vector is normalized, and false otherwise. + Returns [code]true[/code] if the vector is normalized, [code]false[/code] otherwise. diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index f78295bd7e..872280261c 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -147,7 +147,7 @@ - Returns [code]true[/code] if the vector is normalized, and false otherwise. + Returns [code]true[/code] if the vector is normalized, [code]false[/code] otherwise. diff --git a/drivers/SCsub b/drivers/SCsub index 35524868bb..6cd3150741 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -33,15 +33,6 @@ else: # Core dependencies SConscript("png/SCsub") -if env["vsproj"]: - import os - - path = os.getcwd() - # Change directory so the path resolves correctly in the function call. - os.chdir("..") - env.AddToVSProject(env.drivers_sources) - os.chdir(path) - env.add_source_files(env.drivers_sources, "*.cpp") lib = env.add_library("drivers", env.drivers_sources) diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index 66c76e9bec..ed3c3f5f59 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -154,7 +154,7 @@ String DirAccessUnix::get_next() { _cisdir = (entry->d_type == DT_DIR); } - _cishidden = (fname != "." && fname != ".." && fname.begins_with(".")); + _cishidden = is_hidden(fname); return fname; } @@ -408,6 +408,10 @@ String DirAccessUnix::get_filesystem_type() const { return ""; //TODO this should be implemented } +bool DirAccessUnix::is_hidden(const String &p_name) { + return p_name != "." && p_name != ".." && p_name.begins_with("."); +} + DirAccessUnix::DirAccessUnix() { dir_stream = 0; diff --git a/drivers/unix/dir_access_unix.h b/drivers/unix/dir_access_unix.h index 4237dd5691..b6d31bdc8f 100644 --- a/drivers/unix/dir_access_unix.h +++ b/drivers/unix/dir_access_unix.h @@ -52,6 +52,7 @@ class DirAccessUnix : public DirAccess { protected: virtual String fix_unicode_name(const char *p_name) const { return String::utf8(p_name); } + virtual bool is_hidden(const String &p_name); public: virtual Error list_dir_begin(); ///< This starts dir listing diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index d80ccbd9a0..2a59bbc3fb 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -82,8 +82,15 @@ void EditorLog::_clear_request() { } void EditorLog::_copy_request() { + String text = log->get_selected_text(); - log->selection_copy(); + if (text == "") { + text = log->get_text(); + } + + if (text != "") { + OS::get_singleton()->set_clipboard(text); + } } void EditorLog::clear() { diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index a441acd607..a34148086a 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -242,9 +242,9 @@ String EditorInterface::get_current_path() const { return EditorNode::get_singleton()->get_filesystem_dock()->get_current_path(); } -void EditorInterface::inspect_object(Object *p_obj, const String &p_for_property) { +void EditorInterface::inspect_object(Object *p_obj, const String &p_for_property, bool p_inspector_only) { - EditorNode::get_singleton()->push_item(p_obj, p_for_property); + EditorNode::get_singleton()->push_item(p_obj, p_for_property, p_inspector_only); } EditorFileSystem *EditorInterface::get_resource_file_system() { @@ -311,7 +311,7 @@ bool EditorInterface::is_distraction_free_mode_enabled() const { void EditorInterface::_bind_methods() { - ClassDB::bind_method(D_METHOD("inspect_object", "object", "for_property"), &EditorInterface::inspect_object, DEFVAL(String())); + ClassDB::bind_method(D_METHOD("inspect_object", "object", "for_property", "inspector_only"), &EditorInterface::inspect_object, DEFVAL(String()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_selection"), &EditorInterface::get_selection); ClassDB::bind_method(D_METHOD("get_editor_settings"), &EditorInterface::get_editor_settings); ClassDB::bind_method(D_METHOD("get_script_editor"), &EditorInterface::get_script_editor); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index c5b9744261..5fb03ccef3 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -88,7 +88,7 @@ public: String get_selected_path() const; String get_current_path() const; - void inspect_object(Object *p_obj, const String &p_for_property = String()); + void inspect_object(Object *p_obj, const String &p_for_property = String(), bool p_inspector_only = false); EditorSelection *get_selection(); //EditorImportExport *get_import_export(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 89ffb25bf7..9d0a9165b9 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -987,19 +987,27 @@ void EditorPropertyEasing::_drag_easing(const Ref &p_ev) { rel = -rel; float val = get_edited_object()->get(get_edited_property()); - if (val == 0) - return; bool sg = val < 0; val = Math::absf(val); val = Math::log(val) / Math::log((float)2.0); - //logspace + // Logarithmic space. val += rel * 0.05; val = Math::pow(2.0f, val); if (sg) val = -val; + // 0 is a singularity, but both positive and negative values + // are otherwise allowed. Enforce 0+ as workaround. + if (Math::is_zero_approx(val)) { + val = 0.00001; + } + + // Limit to a reasonable value to prevent the curve going into infinity, + // which can cause crashes and other issues. + val = CLAMP(val, -1000000, 1000000); + emit_changed(get_edited_property(), val); easing_draw->update(); } @@ -1044,7 +1052,18 @@ void EditorPropertyEasing::_draw_easing() { } easing_draw->draw_multiline(lines, line_color, 1.0, true); - f->draw(ci, Point2(10, 10 + f->get_ascent()), String::num(exp, 2), font_color); + // Draw more decimals for small numbers since higher precision is usually required for fine adjustments. + int decimals; + if (Math::abs(exp) < 0.1 - CMP_EPSILON) { + decimals = 4; + } else if (Math::abs(exp) < 1 - CMP_EPSILON) { + decimals = 3; + } else if (Math::abs(exp) < 10 - CMP_EPSILON) { + decimals = 2; + } else { + decimals = 1; + } + f->draw(ci, Point2(10, 10 + f->get_ascent()), rtos(exp).pad_decimals(decimals), font_color); } void EditorPropertyEasing::update_property() { @@ -1075,6 +1094,11 @@ void EditorPropertyEasing::_spin_value_changed(double p_value) { if (Math::is_zero_approx(p_value)) { p_value = 0.00001; } + + // Limit to a reasonable value to prevent the curve going into infinity, + // which can cause crashes and other issues. + p_value = CLAMP(p_value, -1000000, 1000000); + emit_changed(get_edited_property(), p_value); _spin_focus_exited(); } diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 64e6f874a6..5affdc49f3 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -376,7 +376,12 @@ String EditorSpinSlider::get_label() const { } void EditorSpinSlider::_evaluate_input_text() { - String text = value_input->get_text(); + // Replace comma with dot to support it as decimal separator (GH-6028). + // This prevents using functions like `pow()`, but using functions + // in EditorSpinSlider is a barely known (and barely used) feature. + // Instead, we'd rather support German/French keyboard layouts out of the box. + const String text = value_input->get_text().replace(",", "."); + Ref expr; expr.instance(); Error err = expr->parse(text); diff --git a/editor/icons/icon_auto_key.svg b/editor/icons/icon_auto_key.svg index 9852d1360e..acc6665baf 100644 --- a/editor/icons/icon_auto_key.svg +++ b/editor/icons/icon_auto_key.svg @@ -1 +1 @@ - + diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index c6e919512f..f8084e9168 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -1226,6 +1226,12 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { ERR_FAIL_INDEX_V(material, state.materials.size(), ERR_FILE_CORRUPT); const Ref &mat = state.materials[material]; + mesh.mesh->surface_set_material(mesh.mesh->get_surface_count() - 1, mat); + } else { + Ref mat; + mat.instance(); + mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + mesh.mesh->surface_set_material(mesh.mesh->get_surface_count() - 1, mat); } } @@ -1391,6 +1397,7 @@ Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { if (d.has("name")) { material->set_name(d["name"]); } + material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); if (d.has("pbrMetallicRoughness")) { diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 4f73a5eaea..bc355c2d1f 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -951,8 +951,11 @@ void EditorAssetLibrary::_search(int p_page) { _api_request("asset", REQUESTING_SEARCH, args); } -void EditorAssetLibrary::_search_text_entered(const String &p_text) { +void EditorAssetLibrary::_search_text_changed(const String &p_text) { + filter_debounce_timer->start(); +} +void EditorAssetLibrary::_filter_debounce_timer_timeout() { _search(); } @@ -1328,7 +1331,8 @@ void EditorAssetLibrary::_bind_methods() { ClassDB::bind_method("_select_category", &EditorAssetLibrary::_select_category); ClassDB::bind_method("_image_request_completed", &EditorAssetLibrary::_image_request_completed); ClassDB::bind_method("_search", &EditorAssetLibrary::_search, DEFVAL(0)); - ClassDB::bind_method("_search_text_entered", &EditorAssetLibrary::_search_text_entered); + ClassDB::bind_method("_search_text_changed", &EditorAssetLibrary::_search_text_changed); + ClassDB::bind_method("_filter_debounce_timer_timeout", &EditorAssetLibrary::_filter_debounce_timer_timeout); ClassDB::bind_method("_install_asset", &EditorAssetLibrary::_install_asset); ClassDB::bind_method("_manage_plugins", &EditorAssetLibrary::_manage_plugins); ClassDB::bind_method("_asset_open", &EditorAssetLibrary::_asset_open); @@ -1359,10 +1363,15 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { filter = memnew(LineEdit); search_hb->add_child(filter); filter->set_h_size_flags(SIZE_EXPAND_FILL); - filter->connect("text_entered", this, "_search_text_entered"); - search = memnew(Button(TTR("Search"))); - search->connect("pressed", this, "_search"); - search_hb->add_child(search); + filter->connect("text_changed", this, "_search_text_changed"); + + // Perform a search automatically if the user hasn't entered any text for a certain duration. + // This way, the user doesn't need to press Enter to initiate their search. + filter_debounce_timer = memnew(Timer); + filter_debounce_timer->set_one_shot(true); + filter_debounce_timer->set_wait_time(0.25); + filter_debounce_timer->connect("timeout", this, "_filter_debounce_timer_timeout"); + search_hb->add_child(filter_debounce_timer); if (!p_templates_only) search_hb->add_child(memnew(VSeparator)); diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index aa3c735810..12df2950d4 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -186,10 +186,10 @@ class EditorAssetLibrary : public PanelContainer { Label *library_loading; Label *library_error; LineEdit *filter; + Timer *filter_debounce_timer; OptionButton *categories; OptionButton *repository; OptionButton *sort; - Button *search; HBoxContainer *error_hb; TextureRect *error_tr; Label *error_label; @@ -284,10 +284,12 @@ class EditorAssetLibrary : public PanelContainer { void _search(int p_page = 0); void _rerun_search(int p_ignore); + void _search_text_changed(const String &p_text = ""); void _search_text_entered(const String &p_text = ""); void _api_request(const String &p_request, RequestType p_request_type, const String &p_arguments = ""); void _http_request_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data); void _http_download_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data); + void _filter_debounce_timer_timeout(); void _repository_changed(int p_repository_id); void _support_toggled(int p_support); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 09c6f0c234..60c06f9299 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -844,10 +844,11 @@ Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2 ERR_FAIL_COND_V(!p_control, Vector2()); Rect2 parent_rect = p_control->get_parent_anchorable_rect(); - ERR_FAIL_COND_V(parent_rect.size.x == 0, Vector2()); - ERR_FAIL_COND_V(parent_rect.size.y == 0, Vector2()); - return (p_control->get_transform().xform(position) - parent_rect.position) / parent_rect.size; + Vector2 output = Vector2(); + output.x = (parent_rect.size.x == 0) ? 0.0 : (p_control->get_transform().xform(position).x - parent_rect.position.x) / parent_rect.size.x; + output.y = (parent_rect.size.y == 0) ? 0.0 : (p_control->get_transform().xform(position).y - parent_rect.position.y) / parent_rect.size.y; + return output; } void CanvasItemEditor::_save_canvas_item_ik_chain(const CanvasItem *p_canvas_item, List *p_bones_length, List *p_bones_state) { @@ -3694,12 +3695,12 @@ void CanvasItemEditor::_draw_viewport() { _draw_grid(); _draw_ruler_tool(); - _draw_selection(); _draw_axis(); if (editor->get_edited_scene()) { _draw_locks_and_groups(editor->get_edited_scene()); _draw_invisible_nodes_positions(editor->get_edited_scene()); } + _draw_selection(); RID ci = viewport->get_canvas_item(); VisualServer::get_singleton()->canvas_item_add_set_transform(ci, Transform2D()); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 0f402fe47f..74db897b02 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -1856,7 +1856,7 @@ void ProjectManager::_update_project_buttons() { rename_btn->set_disabled(empty_selection || is_missing_project_selected); run_btn->set_disabled(empty_selection || is_missing_project_selected); - erase_missing_btn->set_visible(_project_list->is_any_project_missing()); + erase_missing_btn->set_disabled(!_project_list->is_any_project_missing()); } void ProjectManager::_unhandled_input(const Ref &p_ev) { @@ -2525,6 +2525,7 @@ ProjectManager::ProjectManager() { _project_list->set_enable_h_scroll(false); VBoxContainer *tree_vb = memnew(VBoxContainer); + tree_vb->set_custom_minimum_size(Size2(120, 120)); tree_hb->add_child(tree_vb); Button *open = memnew(Button); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 52e7672118..b09358c077 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1229,8 +1229,6 @@ void SceneTreeDock::_node_selected() { Node *node = scene_tree->get_selected(); if (!node) { - - editor->push_item(NULL); return; } @@ -1933,11 +1931,10 @@ void SceneTreeDock::_selection_changed() { if (selection_size > 1) { //automatically turn on multi-edit _tool_selected(TOOL_MULTI_EDIT); - } else if (selection_size == 1) { - editor->push_item(EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list()[0]); - } else { + } else if (selection_size == 0) { editor->push_item(NULL); } + _update_script_button(); } diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 49c2f2b8b5..d71f837851 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -794,6 +794,9 @@ void SceneTreeEditor::_renamed() { if (new_name == n->get_name()) return; + // Trim leading/trailing whitespace to prevent node names from containing accidental whitespace, which would make it more difficult to get the node via `get_node()`. + new_name = new_name.strip_edges(); + if (!undo_redo) { n->set_name(new_name); which->set_metadata(0, n->get_path()); diff --git a/editor/translations/pt_PT.po b/editor/translations/pt.po similarity index 99% rename from editor/translations/pt_PT.po rename to editor/translations/pt.po index 66b7252aaf..6b6a15dda7 100644 --- a/editor/translations/pt_PT.po +++ b/editor/translations/pt.po @@ -1,4 +1,4 @@ -# Portuguese (Portugal) translation of the Godot Engine editor +# Portuguese translation of the Godot Engine editor # Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. # Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). # This file is distributed under the same license as the Godot source code. @@ -22,9 +22,9 @@ msgstr "" "POT-Creation-Date: \n" "PO-Revision-Date: 2020-09-24 12:43+0000\n" "Last-Translator: ssantos \n" -"Language-Team: Portuguese (Portugal) \n" -"Language: pt_PT\n" +"Language-Team: Portuguese \n" +"Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/methods.py b/methods.py index 9615f6764f..97dc5c8c50 100644 --- a/methods.py +++ b/methods.py @@ -5,6 +5,9 @@ import subprocess from collections import OrderedDict from compat import iteritems, isbasestring, open_utf8, decode_utf8, qualname +from SCons import Node +from SCons.Script import Glob + def add_source_files(self, sources, files, warn_duplicates=True): # Convert string to list of absolute paths (including expanding wildcard) @@ -564,6 +567,35 @@ def generate_cpp_hint_file(filename): print("Could not write cpp.hint file.") +def glob_recursive(pattern, node="."): + results = [] + for f in Glob(str(node) + "/*", source=True): + if type(f) is Node.FS.Dir: + results += glob_recursive(pattern, f) + results += Glob(str(node) + "/" + pattern, source=True) + return results + + +def add_to_vs_project(env, sources): + for x in sources: + if type(x) == type(""): + fname = env.File(x).path + else: + fname = env.File(x)[0].path + pieces = fname.split(".") + if len(pieces) > 0: + basename = pieces[0] + basename = basename.replace("\\\\", "/") + if os.path.isfile(basename + ".h"): + env.vs_incs += [basename + ".h"] + elif os.path.isfile(basename + ".hpp"): + env.vs_incs += [basename + ".hpp"] + if os.path.isfile(basename + ".c"): + env.vs_srcs += [basename + ".c"] + elif os.path.isfile(basename + ".cpp"): + env.vs_srcs += [basename + ".cpp"] + + def generate_vs_project(env, num_jobs): batch_file = find_visual_c_batch_file(env) if batch_file: @@ -596,12 +628,16 @@ def generate_vs_project(env, num_jobs): result = " ^& ".join(common_build_prefix + [" ".join([commands] + common_build_postfix)]) return result - env.AddToVSProject(env.core_sources) - env.AddToVSProject(env.main_sources) - env.AddToVSProject(env.modules_sources) - env.AddToVSProject(env.scene_sources) - env.AddToVSProject(env.servers_sources) - env.AddToVSProject(env.editor_sources) + add_to_vs_project(env, env.core_sources) + add_to_vs_project(env, env.drivers_sources) + add_to_vs_project(env, env.main_sources) + add_to_vs_project(env, env.modules_sources) + add_to_vs_project(env, env.scene_sources) + add_to_vs_project(env, env.servers_sources) + add_to_vs_project(env, env.editor_sources) + + for header in glob_recursive("**/*.h"): + env.vs_incs.append(str(header)) env["MSVSBUILDCOM"] = build_commandline("scons") env["MSVSREBUILDCOM"] = build_commandline("scons vsproj=yes") diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 93ce9870c9..1866af282c 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -817,6 +817,7 @@ a = [1, 2, 3] print("a", "b", a) # Prints ab[1, 2, 3] [/codeblock] + [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed. @@ -890,6 +891,7 @@ [codeblock] push_error("test error") # Prints "test error" to debugger and terminal as error call [/codeblock] + [b]Note:[/b] Errors printed this way will not pause project execution. To print an error message and pause project execution in debug builds, use [code]assert(false, "test error")[/code] instead. diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 513ea42804..1cf301d4d7 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2246,8 +2246,15 @@ GDScriptLanguage::~GDScriptLanguage() { } // Clear dependencies between scripts, to ensure cyclic references are broken (to avoid leaks at exit). - while (script_list.first()) { - GDScript *script = script_list.first()->self(); + SelfList *s = script_list.first(); + while (s) { + GDScript *script = s->self(); + // This ensures the current script is not released before we can check what's the next one + // in the list (we can't get the next upfront because we don't know if the reference breaking + // will cause it -or any other after it, for that matter- to be released so the next one + // is not the same as before). + script->reference(); + for (Map::Element *E = script->member_functions.front(); E; E = E->next()) { GDScriptFunction *func = E->get(); for (int i = 0; i < func->argument_types.size(); i++) { @@ -2258,6 +2265,9 @@ GDScriptLanguage::~GDScriptLanguage() { for (Map::Element *E = script->member_indices.front(); E; E = E->next()) { E->get().data_type.script_type_ref = Ref