diff options
99 files changed, 1285 insertions, 792 deletions
diff --git a/SConstruct b/SConstruct index 128c5b0e92..cdbb47faea 100644 --- a/SConstruct +++ b/SConstruct @@ -189,7 +189,7 @@ Help(opts.GenerateHelpText(env_base))  # generate help  # add default include paths -env_base.Prepend(CPPPATH=['#', '#editor']) +env_base.Prepend(CPPPATH=['#'])  # configure ENV for platform  env_base.platform_exporters = platform_exporters diff --git a/core/cowdata.h b/core/cowdata.h index 3e40ad0f4b..c92e20920c 100644 --- a/core/cowdata.h +++ b/core/cowdata.h @@ -33,6 +33,7 @@  #include <string.h> +#include "core/error_macros.h"  #include "core/os/memory.h"  #include "core/safe_refcount.h" diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 271e5c5a0b..5684c82d1c 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -123,6 +123,13 @@ void ConfigFile::erase_section(const String &p_section) {  	values.erase(p_section);  } +void ConfigFile::erase_section_key(const String &p_section, const String &p_key) { + +	ERR_FAIL_COND_MSG(!values.has(p_section), "Cannot erase key from nonexistent section '" + p_section + "'."); + +	values[p_section].erase(p_key); +} +  Error ConfigFile::save(const String &p_path) {  	Error err; @@ -293,6 +300,7 @@ void ConfigFile::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_section_keys", "section"), &ConfigFile::_get_section_keys);  	ClassDB::bind_method(D_METHOD("erase_section", "section"), &ConfigFile::erase_section); +	ClassDB::bind_method(D_METHOD("erase_section_key", "section", "key"), &ConfigFile::erase_section_key);  	ClassDB::bind_method(D_METHOD("load", "path"), &ConfigFile::load);  	ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save); diff --git a/core/io/config_file.h b/core/io/config_file.h index 3ab6fef868..d927779f9c 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -60,6 +60,7 @@ public:  	void get_section_keys(const String &p_section, List<String> *r_keys) const;  	void erase_section(const String &p_section); +	void erase_section_key(const String &p_section, const String &p_key);  	Error save(const String &p_path);  	Error load(const String &p_path); diff --git a/core/list.h b/core/list.h index d1b528562d..c46888e01c 100644 --- a/core/list.h +++ b/core/list.h @@ -31,6 +31,7 @@  #ifndef GLOBALS_LIST_H  #define GLOBALS_LIST_H +#include "core/error_macros.h"  #include "core/os/memory.h"  #include "core/sort_array.h" diff --git a/core/map.h b/core/map.h index c87ee42e1b..77e73d70cb 100644 --- a/core/map.h +++ b/core/map.h @@ -31,6 +31,7 @@  #ifndef MAP_H  #define MAP_H +#include "core/error_macros.h"  #include "core/set.h"  // based on the very nice implementation of rb-trees by: diff --git a/core/math/basis.h b/core/math/basis.h index 053effda69..4be4ea4cd3 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -28,13 +28,11 @@  /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */  /*************************************************************************/ -// Circular dependency between Vector3 and Basis :/ -#include "core/math/vector3.h" -  #ifndef BASIS_H  #define BASIS_H  #include "core/math/quat.h" +#include "core/math/vector3.h"  class Basis {  public: diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index f04e40cb6c..50fcdb2c13 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -30,6 +30,8 @@  #include "math_funcs.h" +#include "core/error_macros.h" +  RandomPCG Math::default_rand(RandomPCG::DEFAULT_SEED, RandomPCG::DEFAULT_INC);  #define PHI 0x9e3779b9 diff --git a/core/math/rect2.h b/core/math/rect2.h index d636aa223f..f58756ee40 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -99,8 +99,8 @@ struct Rect2 {  	inline bool encloses(const Rect2 &p_rect) const {  		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)); +			   ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) && +			   ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y));  	}  	_FORCE_INLINE_ bool has_no_area() const { diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 73927821cf..ebc1599820 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -134,6 +134,21 @@ Vector3 Vector3::move_toward(const Vector3 &p_to, const real_t p_delta) const {  	return len <= p_delta || len < CMP_EPSILON ? p_to : v + vd / len * p_delta;  } +Basis Vector3::outer(const Vector3 &p_b) const { + +	Vector3 row0(x * p_b.x, x * p_b.y, x * p_b.z); +	Vector3 row1(y * p_b.x, y * p_b.y, y * p_b.z); +	Vector3 row2(z * p_b.x, z * p_b.y, z * p_b.z); + +	return Basis(row0, row1, row2); +} + +Basis Vector3::to_diagonal_matrix() const { +	return Basis(x, 0, 0, +			0, y, 0, +			0, 0, z); +} +  Vector3::operator String() const {  	return (rtos(x) + ", " + rtos(y) + ", " + rtos(z)); diff --git a/core/math/vector3.h b/core/math/vector3.h index c68b075613..de1743d88f 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -96,8 +96,8 @@ struct Vector3 {  	_FORCE_INLINE_ Vector3 cross(const Vector3 &p_b) const;  	_FORCE_INLINE_ real_t dot(const Vector3 &p_b) const; -	_FORCE_INLINE_ Basis outer(const Vector3 &p_b) const; -	_FORCE_INLINE_ Basis to_diagonal_matrix() const; +	Basis outer(const Vector3 &p_b) const; +	Basis to_diagonal_matrix() const;  	_FORCE_INLINE_ Vector3 abs() const;  	_FORCE_INLINE_ Vector3 floor() const; @@ -154,9 +154,6 @@ struct Vector3 {  	_FORCE_INLINE_ Vector3() { x = y = z = 0; }  }; -// Should be included after class definition, otherwise we get circular refs -#include "core/math/basis.h" -  Vector3 Vector3::cross(const Vector3 &p_b) const {  	Vector3 ret( @@ -172,21 +169,6 @@ real_t Vector3::dot(const Vector3 &p_b) const {  	return x * p_b.x + y * p_b.y + z * p_b.z;  } -Basis Vector3::outer(const Vector3 &p_b) const { - -	Vector3 row0(x * p_b.x, x * p_b.y, x * p_b.z); -	Vector3 row1(y * p_b.x, y * p_b.y, y * p_b.z); -	Vector3 row2(z * p_b.x, z * p_b.y, z * p_b.z); - -	return Basis(row0, row1, row2); -} - -Basis Vector3::to_diagonal_matrix() const { -	return Basis(x, 0, 0, -			0, y, 0, -			0, 0, z); -} -  Vector3 Vector3::abs() const {  	return Vector3(Math::abs(x), Math::abs(y), Math::abs(z)); diff --git a/core/node_path.cpp b/core/node_path.cpp index b43f76f680..970ed100fe 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -269,7 +269,7 @@ NodePath NodePath::rel_path_to(const NodePath &p_np) const {  NodePath NodePath::get_as_property_path() const { -	if (!data->path.size()) { +	if (!data || !data->path.size()) {  		return *this;  	} else {  		Vector<StringName> new_path = data->subpath; diff --git a/core/os/memory.h b/core/os/memory.h index 8778cb63ad..a68a359546 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -31,6 +31,7 @@  #ifndef MEMORY_H  #define MEMORY_H +#include "core/error_macros.h"  #include "core/safe_refcount.h"  #include <stddef.h> diff --git a/core/self_list.h b/core/self_list.h index 314d440977..07abcd1d53 100644 --- a/core/self_list.h +++ b/core/self_list.h @@ -31,6 +31,7 @@  #ifndef SELF_LIST_H  #define SELF_LIST_H +#include "core/error_macros.h"  #include "core/typedefs.h"  template <class T> diff --git a/core/sort_array.h b/core/sort_array.h index 8660ee3333..d330e0c647 100644 --- a/core/sort_array.h +++ b/core/sort_array.h @@ -31,6 +31,7 @@  #ifndef SORT_ARRAY_H  #define SORT_ARRAY_H +#include "core/error_macros.h"  #include "core/typedefs.h"  #define ERR_BAD_COMPARE(cond)                                         \ diff --git a/core/typedefs.h b/core/typedefs.h index 660139b90a..767a97ac38 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -108,7 +108,6 @@ T *_nullptr() {  #include "core/int_types.h"  #include "core/error_list.h" -#include "core/error_macros.h"  /** Generic ABS function, for math uses please use Math::abs */ diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 1b5ca9d3e5..8a64ee4e2f 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -591,9 +591,9 @@ struct _VariantCall {  		int buffer_size = (int)(*p_args[0]); -		if (buffer_size < 0) { +		if (buffer_size <= 0) {  			r_ret = decompressed; -			ERR_FAIL_MSG("Decompression buffer size is less than zero."); +			ERR_FAIL_MSG("Decompression buffer size must be greater than zero.");  		}  		decompressed.resize(buffer_size); diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 9acddb3115..9e1b25abe1 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -326,7 +326,7 @@  			<argument index="0" name="screen" type="int" default="-1">  			</argument>  			<description> -				Returns the dots per inch density of the specified screen. +				Returns the dots per inch density of the specified screen. If [code]screen[/code] is [/code]-1[/code] (the default value), the current screen will be used.  				On Android devices, the actual screen densities are grouped into six generalized densities:  				[codeblock]  				   ldpi - 120 dpi @@ -344,7 +344,7 @@  			<argument index="0" name="screen" type="int" default="-1">  			</argument>  			<description> -				Returns the position of the specified screen by index. If no screen index is provided, the current screen will be used. +				Returns the position of the specified screen by index. If [code]screen[/code] is [/code]-1[/code] (the default value), the current screen will be used.  			</description>  		</method>  		<method name="get_screen_size" qualifiers="const"> @@ -353,7 +353,7 @@  			<argument index="0" name="screen" type="int" default="-1">  			</argument>  			<description> -				Returns the dimensions in pixels of the specified screen. +				Returns the dimensions in pixels of the specified screen. If [code]screen[/code] is [/code]-1[/code] (the default value), the current screen will be used.  			</description>  		</method>  		<method name="get_splash_tick_msec" qualifiers="const"> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index d9400088dd..50b300d216 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -89,6 +89,36 @@  				An [code]id[/code] can optionally be provided, as well as an accelerator ([code]accel[/code]). If no [code]id[/code] is provided, one will be created from the index. If no [code]accel[/code] is provided then the default [code]0[/code] will be assigned to it. See [method get_item_accelerator] for more info on accelerators.  			</description>  		</method> +		<method name="add_icon_radio_check_item"> +			<return type="void"> +			</return> +			<argument index="0" name="texture" type="Texture"> +			</argument> +			<argument index="1" name="label" type="String"> +			</argument> +			<argument index="2" name="id" type="int" default="-1"> +			</argument> +			<argument index="3" name="accel" type="int" default="0"> +			</argument> +			<description> +				Same as [method add_icon_check_item], but uses a radio check button. +			</description> +		</method> +		<method name="add_icon_radio_check_shortcut"> +			<return type="void"> +			</return> +			<argument index="0" name="texture" type="Texture"> +			</argument> +			<argument index="1" name="shortcut" type="ShortCut"> +			</argument> +			<argument index="2" name="id" type="int" default="-1"> +			</argument> +			<argument index="3" name="global" type="bool" default="false"> +			</argument> +			<description> +				Same as [method add_icon_check_shortcut], but uses a radio check button. +			</description> +		</method>  		<method name="add_icon_shortcut">  			<return type="void">  			</return> @@ -119,6 +149,25 @@  				An [code]id[/code] can optionally be provided, as well as an accelerator ([code]accel[/code]). If no [code]id[/code] is provided, one will be created from the index. If no [code]accel[/code] is provided then the default [code]0[/code] will be assigned to it. See [method get_item_accelerator] for more info on accelerators.  			</description>  		</method> +		<method name="add_multistate_item"> +			<return type="void"> +			</return> +			<argument index="0" name="label" type="String"> +			</argument> +			<argument index="1" name="max_states" type="int"> +			</argument> +			<argument index="2" name="default_state" type="int"> +			</argument> +			<argument index="3" name="id" type="int" default="-1"> +			</argument> +			<argument index="4" name="accel" type="int" default="0"> +			</argument> +			<description> +				Adds a new multistate item with text [code]label[/code]. +				Contrarily to normal binary items, multistate items can have more than two states, as defined by [code]max_states[/code]. Each press or activate of the item will increase the state by one. The default value is defined by [code]default_state[/code]. +				An [code]id[/code] can optionally be provided, as well as an accelerator ([code]accel[/code]). If no [code]id[/code] is provided, one will be created from the index. If no [code]accel[/code] is provided then the default [code]0[/code] will be assigned to it. See [method get_item_accelerator] for more info on accelerators. +			</description> +		</method>  		<method name="add_radio_check_item">  			<return type="void">  			</return> @@ -129,7 +178,7 @@  			<argument index="2" name="accel" type="int" default="0">  			</argument>  			<description> -				Adds a new radio button with text [code]label[/code]. +				Adds a new radio check button with text [code]label[/code].  				An [code]id[/code] can optionally be provided, as well as an accelerator ([code]accel[/code]). If no [code]id[/code] is provided, one will be created from the index. If no [code]accel[/code] is provided then the default [code]0[/code] will be assigned to it. See [method get_item_accelerator] for more info on accelerators.  				[b]Note:[/b] Checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually. See [method set_item_checked] for more info on how to control it.  			</description> diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml index 68b9110b50..0a1cf962c8 100644 --- a/doc/classes/ReflectionProbe.xml +++ b/doc/classes/ReflectionProbe.xml @@ -1,8 +1,10 @@  <?xml version="1.0" encoding="UTF-8" ?>  <class name="ReflectionProbe" inherits="VisualInstance" category="Core" version="3.2">  	<brief_description> +		Captures its surroundings to create reflections.  	</brief_description>  	<description> +		Capture its surroundings as a dual parabolid image, and stores versions of it with increasing levels of blur to simulate different material roughnesses.  	</description>  	<tutorials>  		<link>https://docs.godotengine.org/en/latest/tutorials/3d/reflection_probes.html</link> @@ -11,14 +13,17 @@  	</methods>  	<members>  		<member name="box_projection" type="bool" setter="set_enable_box_projection" getter="is_box_projection_enabled" default="false"> +			If [code]true[/code], enables box projection. This makes reflections look more correct in rectangle-shaped rooms by offsetting the reflection center depending on the camera's location.  		</member>  		<member name="cull_mask" type="int" setter="set_cull_mask" getter="get_cull_mask" default="1048575">  		</member>  		<member name="enable_shadows" type="bool" setter="set_enable_shadows" getter="are_shadows_enabled" default="false"> +			If [code]true[/code], computes shadows in the reflection probe. This makes the reflection probe slower to render; you may want to disable this if using the [constant UPDATE_ALWAYS] [member update_mode].  		</member>  		<member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3( 1, 1, 1 )">  		</member>  		<member name="intensity" type="float" setter="set_intensity" getter="get_intensity" default="1.0"> +			Defines the reflection intensity.  		</member>  		<member name="interior_ambient_color" type="Color" setter="set_interior_ambient" getter="get_interior_ambient" default="Color( 0, 0, 0, 1 )">  		</member> diff --git a/doc/classes/Skeleton2D.xml b/doc/classes/Skeleton2D.xml index 064a7266bd..886e8244a2 100644 --- a/doc/classes/Skeleton2D.xml +++ b/doc/classes/Skeleton2D.xml @@ -1,6 +1,7 @@  <?xml version="1.0" encoding="UTF-8" ?>  <class name="Skeleton2D" inherits="Node2D" category="Core" version="3.2">  	<brief_description> +		Skeleton for 2D characters and animated objects.  	</brief_description>  	<description>  	</description> @@ -20,6 +21,7 @@  			<return type="int">  			</return>  			<description> +				Returns the amount of bones in the skeleton.  			</description>  		</method>  		<method name="get_skeleton" qualifiers="const"> diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml index 7d7002e752..f0f03b6c21 100644 --- a/doc/classes/VisualShader.xml +++ b/doc/classes/VisualShader.xml @@ -145,12 +145,6 @@  			<description>  			</description>  		</method> -		<method name="rebuild"> -			<return type="void"> -			</return> -			<description> -			</description> -		</method>  		<method name="remove_node">  			<return type="void">  			</return> diff --git a/doc/classes/VisualShaderNodeCubeMapUniform.xml b/doc/classes/VisualShaderNodeCubeMapUniform.xml index ad34e7d30c..453af81009 100644 --- a/doc/classes/VisualShaderNodeCubeMapUniform.xml +++ b/doc/classes/VisualShaderNodeCubeMapUniform.xml @@ -1,5 +1,5 @@  <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualShaderNodeCubeMapUniform" inherits="VisualShaderNode" category="Core" version="3.2"> +<class name="VisualShaderNodeCubeMapUniform" inherits="VisualShaderNodeTextureUniform" category="Core" version="3.2">  	<brief_description>  	</brief_description>  	<description> diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py index 974ac2d05c..6e34cffc05 100644 --- a/doc/tools/doc_status.py +++ b/doc/tools/doc_status.py @@ -259,7 +259,8 @@ class ClassStatus:                      status.progresses[tag.tag].increment(len(descr.text.strip()) > 0)              elif tag.tag in ['constants', 'members']:                  for sub_tag in list(tag): -                    status.progresses[tag.tag].increment(len(sub_tag.text.strip()) > 0) +                    if not sub_tag.text is None: +                        status.progresses[tag.tag].increment(len(sub_tag.text.strip()) > 0)              elif tag.tag in ['tutorials']:                  pass  # Ignore those tags for now diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 6be48a4c58..e34705f7b7 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -761,6 +761,14 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur  					source.size.y = tex->height;  				} +				float screen_scale = 1.0; + +				if (source.size.x != 0 && source.size.y != 0) { + +					screen_scale = MIN(np->rect.size.x / source.size.x, np->rect.size.y / source.size.y); +					screen_scale = MIN(1.0, screen_scale); +				} +  				// prepare vertex buffer  				// this buffer contains [ POS POS UV UV ] * @@ -777,13 +785,13 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur  					buffer[(0 * 4 * 4) + 2] = source.position.x * texpixel_size.x;  					buffer[(0 * 4 * 4) + 3] = source.position.y * texpixel_size.y; -					buffer[(0 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; +					buffer[(0 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT] * screen_scale;  					buffer[(0 * 4 * 4) + 5] = np->rect.position.y;  					buffer[(0 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;  					buffer[(0 * 4 * 4) + 7] = source.position.y * texpixel_size.y; -					buffer[(0 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; +					buffer[(0 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT] * screen_scale;  					buffer[(0 * 4 * 4) + 9] = np->rect.position.y;  					buffer[(0 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; @@ -798,25 +806,25 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur  					// second row  					buffer[(1 * 4 * 4) + 0] = np->rect.position.x; -					buffer[(1 * 4 * 4) + 1] = np->rect.position.y + np->margin[MARGIN_TOP]; +					buffer[(1 * 4 * 4) + 1] = np->rect.position.y + np->margin[MARGIN_TOP] * screen_scale;  					buffer[(1 * 4 * 4) + 2] = source.position.x * texpixel_size.x;  					buffer[(1 * 4 * 4) + 3] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; -					buffer[(1 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; -					buffer[(1 * 4 * 4) + 5] = np->rect.position.y + np->margin[MARGIN_TOP]; +					buffer[(1 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT] * screen_scale; +					buffer[(1 * 4 * 4) + 5] = np->rect.position.y + np->margin[MARGIN_TOP] * screen_scale;  					buffer[(1 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;  					buffer[(1 * 4 * 4) + 7] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; -					buffer[(1 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; -					buffer[(1 * 4 * 4) + 9] = np->rect.position.y + np->margin[MARGIN_TOP]; +					buffer[(1 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT] * screen_scale; +					buffer[(1 * 4 * 4) + 9] = np->rect.position.y + np->margin[MARGIN_TOP] * screen_scale;  					buffer[(1 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;  					buffer[(1 * 4 * 4) + 11] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;  					buffer[(1 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; -					buffer[(1 * 4 * 4) + 13] = np->rect.position.y + np->margin[MARGIN_TOP]; +					buffer[(1 * 4 * 4) + 13] = np->rect.position.y + np->margin[MARGIN_TOP] * screen_scale;  					buffer[(1 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x;  					buffer[(1 * 4 * 4) + 15] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; @@ -824,25 +832,25 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur  					// third row  					buffer[(2 * 4 * 4) + 0] = np->rect.position.x; -					buffer[(2 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; +					buffer[(2 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM] * screen_scale;  					buffer[(2 * 4 * 4) + 2] = source.position.x * texpixel_size.x;  					buffer[(2 * 4 * 4) + 3] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; -					buffer[(2 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; -					buffer[(2 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; +					buffer[(2 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT] * screen_scale; +					buffer[(2 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM] * screen_scale;  					buffer[(2 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;  					buffer[(2 * 4 * 4) + 7] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; -					buffer[(2 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; -					buffer[(2 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; +					buffer[(2 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT] * screen_scale; +					buffer[(2 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM] * screen_scale;  					buffer[(2 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;  					buffer[(2 * 4 * 4) + 11] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;  					buffer[(2 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; -					buffer[(2 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; +					buffer[(2 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM] * screen_scale;  					buffer[(2 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x;  					buffer[(2 * 4 * 4) + 15] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; @@ -855,13 +863,13 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur  					buffer[(3 * 4 * 4) + 2] = source.position.x * texpixel_size.x;  					buffer[(3 * 4 * 4) + 3] = (source.position.y + source.size.y) * texpixel_size.y; -					buffer[(3 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; +					buffer[(3 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT] * screen_scale;  					buffer[(3 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y;  					buffer[(3 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;  					buffer[(3 * 4 * 4) + 7] = (source.position.y + source.size.y) * texpixel_size.y; -					buffer[(3 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; +					buffer[(3 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT] * screen_scale;  					buffer[(3 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y;  					buffer[(3 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index e83f53d648..7255b0425c 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -380,14 +380,16 @@ uniform bool np_draw_center;  // left top right bottom in pixel coordinates  uniform vec4 np_margins; -float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) { +float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, float s_ratio, int np_repeat, inout int draw_center) {  	float tex_size = 1.0 / tex_pixel_size; -	if (pixel < margin_begin) { -		return pixel * tex_pixel_size; -	} else if (pixel >= draw_size - margin_end) { -		return (tex_size - (draw_size - pixel)) * tex_pixel_size; +	float screen_margin_begin = margin_begin / s_ratio; +	float screen_margin_end = margin_end / s_ratio; +	if (pixel < screen_margin_begin) { +		return pixel * s_ratio * tex_pixel_size; +	} else if (pixel >= draw_size - screen_margin_end) { +		return (tex_size - (draw_size - pixel) * s_ratio) * tex_pixel_size;  	} else {  		if (!np_draw_center) {  			draw_center--; @@ -395,22 +397,22 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo  		if (np_repeat == 0) { //stretch  			//convert to ratio -			float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end); +			float ratio = (pixel - screen_margin_begin) / (draw_size - screen_margin_begin - screen_margin_end);  			//scale to source texture  			return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;  		} else if (np_repeat == 1) { //tile  			//convert to ratio -			float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end); +			float ofs = mod((pixel - screen_margin_begin), tex_size - margin_begin - margin_end);  			//scale to source texture  			return (margin_begin + ofs) * tex_pixel_size;  		} else if (np_repeat == 2) { //tile fit  			//convert to ratio -			float src_area = draw_size - margin_begin - margin_end; +			float src_area = draw_size - screen_margin_begin - screen_margin_end;  			float dst_area = tex_size - margin_begin - margin_end;  			float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5));  			//convert to ratio -			float ratio = (pixel - margin_begin) / src_area; +			float ratio = (pixel - screen_margin_begin) / src_area;  			ratio = mod(ratio * scale, 1.0);  			return (margin_begin + ratio * dst_area) * tex_pixel_size;  		} @@ -432,9 +434,11 @@ void main() {  #ifdef USE_NINEPATCH  	int draw_center = 2; +	float s_ratio = max((1.0 / color_texpixel_size.x) / abs(dst_rect.z), (1.0 / color_texpixel_size.y) / abs(dst_rect.w)); +	s_ratio = max(1.0, s_ratio);  	uv = vec2( -			map_ninepatch_axis(pixel_size_interp.x, abs(dst_rect.z), color_texpixel_size.x, np_margins.x, np_margins.z, np_repeat_h, draw_center), -			map_ninepatch_axis(pixel_size_interp.y, abs(dst_rect.w), color_texpixel_size.y, np_margins.y, np_margins.w, np_repeat_v, draw_center)); +			map_ninepatch_axis(pixel_size_interp.x, abs(dst_rect.z), color_texpixel_size.x, np_margins.x, np_margins.z, s_ratio, np_repeat_h, draw_center), +			map_ninepatch_axis(pixel_size_interp.y, abs(dst_rect.w), color_texpixel_size.y, np_margins.y, np_margins.w, s_ratio, np_repeat_v, draw_center));  	if (draw_center == 0) {  		color.a = 0.0; diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index 6a57a2e562..da46b393c6 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -30,6 +30,7 @@  #include "net_socket_posix.h" +#ifndef UNIX_SOCKET_UNAVAILABLE  #if defined(UNIX_ENABLED)  #include <errno.h> @@ -280,6 +281,21 @@ void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_  	_sock = p_sock;  	_ip_type = p_ip_type;  	_is_stream = p_is_stream; +	// Disable descriptor sharing with subprocesses. +	_set_close_exec_enabled(true); +} + +void NetSocketPosix::_set_close_exec_enabled(bool p_enabled) { +#ifndef WINDOWS_ENABLED +	// Enable close on exec to avoid sharing with subprocesses. Off by default on Windows. +#if defined(NO_FCNTL) +	unsigned long par = p_enabled ? 1 : 0; +	SOCK_IOCTL(_sock, FIOCLEX, &par); +#else +	int opts = fcntl(_sock, F_GETFD); +	fcntl(_sock, F_SETFD, opts | FD_CLOEXEC); +#endif +#endif  }  Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { @@ -320,6 +336,9 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {  	_is_stream = p_sock_type == TYPE_TCP; +	// Disable descriptor sharing with subprocesses. +	_set_close_exec_enabled(true); +  #if defined(WINDOWS_ENABLED)  	if (!_is_stream) {  		// Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when @@ -691,3 +710,4 @@ Error NetSocketPosix::join_multicast_group(const IP_Address &p_multi_address, St  Error NetSocketPosix::leave_multicast_group(const IP_Address &p_multi_address, String p_if_name) {  	return _change_multicast_group(p_multi_address, p_if_name, false);  } +#endif diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h index 40406b241a..e549ea1d6a 100644 --- a/drivers/unix/net_socket_posix.h +++ b/drivers/unix/net_socket_posix.h @@ -61,6 +61,7 @@ private:  	NetError _get_socket_error();  	void _set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream);  	_FORCE_INLINE_ Error _change_multicast_group(IP_Address p_ip, String p_if_name, bool p_add); +	_FORCE_INLINE_ void _set_close_exec_enabled(bool p_enabled);  protected:  	static NetSocket *_create_func(); diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 90f54df485..9510092a86 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -1606,6 +1606,9 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr  			da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);  			for (int i = 0; i < so_files.size() && err == OK; i++) {  				err = da->copy(so_files[i].path, p_path.get_base_dir().plus_file(so_files[i].path.get_file())); +				if (err == OK) { +					err = sign_shared_object(p_preset, p_debug, p_path.get_base_dir().plus_file(so_files[i].path.get_file())); +				}  			}  			memdelete(da);  		} @@ -1614,6 +1617,10 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr  	return err;  } +Error EditorExportPlatformPC::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { +	return OK; +} +  void EditorExportPlatformPC::set_extension(const String &p_extension, const String &p_feature_key) {  	extensions[p_feature_key] = p_extension;  } diff --git a/editor/editor_export.h b/editor/editor_export.h index 3152e249bd..11dc464b5a 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -423,6 +423,7 @@ public:  	virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;  	virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const;  	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); +	virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);  	void set_extension(const String &p_extension, const String &p_feature_key = "default");  	void set_name(const String &p_name); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 83434a6d9f..4a1e93eaad 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -72,9 +72,12 @@ void EditorHelp::_unhandled_key_input(const Ref<InputEvent> &p_ev) {  	}  } -void EditorHelp::_search(const String &) { +void EditorHelp::_search(bool p_search_previous) { -	find_bar->search_next(); +	if (p_search_previous) +		find_bar->search_prev(); +	else +		find_bar->search_next();  }  void EditorHelp::_class_list_select(const String &p_select) { @@ -1502,8 +1505,8 @@ String EditorHelp::get_class() {  	return edited_class;  } -void EditorHelp::search_again() { -	_search(prev_search); +void EditorHelp::search_again(bool p_search_previous) { +	_search(p_search_previous);  }  int EditorHelp::get_scroll() const { diff --git a/editor/editor_help.h b/editor/editor_help.h index 1019cafffc..23a6e005a0 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -158,7 +158,7 @@ class EditorHelp : public VBoxContainer {  	void _update_doc();  	void _request_help(const String &p_string); -	void _search(const String &p_str); +	void _search(bool p_search_previous = false);  	void _unhandled_key_input(const Ref<InputEvent> &p_ev); @@ -179,7 +179,7 @@ public:  	void scroll_to_section(int p_section_index);  	void popup_search(); -	void search_again(); +	void search_again(bool p_search_previous = false);  	String get_class(); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index b29417b4c3..78e058eeaa 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1566,11 +1566,11 @@ void EditorInspector::update_tree() {  			if (dot != -1) {  				String ov = name.right(dot);  				name = name.substr(0, dot); -				name = name.camelcase_to_underscore().capitalize(); +				name = name.capitalize();  				name += ov;  			} else { -				name = name.camelcase_to_underscore().capitalize(); +				name = name.capitalize();  			}  		} diff --git a/editor/editor_sub_scene.cpp b/editor/editor_sub_scene.cpp index 987033b123..a77051c80b 100644 --- a/editor/editor_sub_scene.cpp +++ b/editor/editor_sub_scene.cpp @@ -97,8 +97,14 @@ void EditorSubScene::_fill_tree(Node *p_node, TreeItem *p_parent) {  }  void EditorSubScene::_selected_changed() { -	selection.clear(); -	is_root = false; +	TreeItem *item = tree->get_selected(); +	ERR_FAIL_COND(!item); +	Node *n = item->get_metadata(0); + +	if (!n || !selection.find(n)) { +		selection.clear(); +		is_root = false; +	}  }  void EditorSubScene::_item_multi_selected(Object *p_object, int p_cell, bool p_selected) { @@ -116,6 +122,11 @@ void EditorSubScene::_item_multi_selected(Object *p_object, int p_cell, bool p_s  				selection.clear();  			}  			selection.push_back(n); +		} else { +			List<Node *>::Element *E = selection.find(n); + +			if (E) +				selection.erase(E);  		}  	}  } diff --git a/editor/import/resource_importer_obj.h b/editor/import/resource_importer_obj.h index b96bc1b656..adad21da61 100644 --- a/editor/import/resource_importer_obj.h +++ b/editor/import/resource_importer_obj.h @@ -31,7 +31,7 @@  #ifndef RESOURCEIMPORTEROBJ_H  #define RESOURCEIMPORTEROBJ_H -#include "import/resource_importer_scene.h" +#include "resource_importer_scene.h"  class EditorOBJImporter : public EditorSceneImporter { diff --git a/editor/output_strings.cpp b/editor/output_strings.cpp deleted file mode 100644 index baabaff9a8..0000000000 --- a/editor/output_strings.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/*************************************************************************/ -/*  output_strings.cpp                                                   */ -/*************************************************************************/ -/*                       This file is part of:                           */ -/*                           GODOT ENGINE                                */ -/*                      https://godotengine.org                          */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */ -/*                                                                       */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the       */ -/* "Software"), to deal in the Software without restriction, including   */ -/* without limitation the rights to use, copy, modify, merge, publish,   */ -/* distribute, sublicense, and/or sell copies of the Software, and to    */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions:                                             */ -/*                                                                       */ -/* The above copyright notice and this permission notice shall be        */ -/* included in all copies or substantial portions of the Software.       */ -/*                                                                       */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ -/*************************************************************************/ - -#include "output_strings.h" - -void OutputStrings::update_scrollbars() { - -	Size2 hmin = h_scroll->get_combined_minimum_size(); -	Size2 vmin = v_scroll->get_combined_minimum_size(); - -	v_scroll->set_anchor(MARGIN_LEFT, ANCHOR_END); -	v_scroll->set_anchor(MARGIN_RIGHT, ANCHOR_END); -	v_scroll->set_anchor(MARGIN_BOTTOM, ANCHOR_END); - -	v_scroll->set_begin(Point2(-vmin.width, 0)); -	v_scroll->set_end(Point2(0, 0)); - -	h_scroll->set_anchor(MARGIN_RIGHT, ANCHOR_END); -	h_scroll->set_anchor(MARGIN_TOP, ANCHOR_END); -	h_scroll->set_anchor(MARGIN_BOTTOM, ANCHOR_END); - -	h_scroll->set_begin(Point2(0, -hmin.y)); -	h_scroll->set_end(Point2(-vmin.x, 0)); - -	margin.y = hmin.y; -	margin.x = vmin.x; - -	Ref<StyleBox> tree_st = get_stylebox("bg", "Tree"); -	int page = ((size_height - (int)margin.y - tree_st->get_margin(MARGIN_TOP)) / font_height); -	v_scroll->set_page(page); -} - -void OutputStrings::_notification(int p_what) { - -	switch (p_what) { - -		case NOTIFICATION_DRAW: { - -			if (following) { - -				updating = true; -				v_scroll->set_value(v_scroll->get_max() - v_scroll->get_page()); -				updating = false; -			} - -			RID ci = get_canvas_item(); -			Size2 size = get_size(); - -			Ref<Font> font = get_font("font", "Tree"); -			Ref<StyleBox> tree_st = get_stylebox("bg", "Tree"); -			tree_st->draw(ci, Rect2(Point2(), size)); -			Color color = get_color("font_color", "Tree"); -			Ref<Texture> icon_error = get_icon("Error", "EditorIcons"); -			Ref<Texture> icon_warning = get_icon("Warning", "EditorIcons"); - -			//int lines = (size_height-(int)margin.y) / font_height; -			Point2 ofs = tree_st->get_offset(); - -			LineMap::Element *E = line_map.find(v_scroll->get_value()); -			float h_ofs = (int)h_scroll->get_value(); -			Point2 icon_ofs = Point2(0, (font_height - (int)icon_error->get_height()) / 2); - -			FontDrawer drawer(font, Color(1, 1, 1)); -			while (E && ofs.y < (size_height - (int)margin.y)) { - -				String str = E->get().text; -				Point2 line_ofs = ofs; - -				switch (E->get().type) { - -					case LINE_WARNING: { -						icon_warning->draw(ci, line_ofs + icon_ofs); - -					} break; -					case LINE_ERROR: { -						icon_error->draw(ci, line_ofs + icon_ofs); -					} break; -					case LINE_LINK: { - -					} break; -					default: { -					} -				} - -				line_ofs.y += font->get_ascent(); -				line_ofs.x += icon_error->get_width() + 4; - -				for (int i = 0; i < str.length(); i++) { -					if (line_ofs.x - h_ofs < 0) { -						line_ofs.x += font->get_char_size(str[i], str[i + 1]).width; -					} else if (line_ofs.x - h_ofs > size.width - margin.width) { -						break; -					} else { -						line_ofs.x += font->draw_char(ci, Point2(line_ofs.x - h_ofs, line_ofs.y), str[i], str[i + 1], color); -					} -				} - -				ofs.y += font_height; -				E = E->next(); -			} - -		} break; - -		case NOTIFICATION_ENTER_TREE: -		case NOTIFICATION_RESIZED: { - -			font_height = get_font("font", "Tree")->get_height(); -			size_height = get_size().height; -			update_scrollbars(); -		} break; -	} -} - -void OutputStrings::_hscroll_changed(float p_value) { - -	if (updating) -		return; - -	update(); -} -void OutputStrings::_vscroll_changed(float p_value) { - -	if (updating) -		return; -	//user changed scroll -	following = (p_value + v_scroll->get_page()) >= v_scroll->get_max(); -	update(); -} - -void OutputStrings::add_line(const String &p_text, const Variant &p_meta, const LineType p_type) { - -	Vector<String> strings = p_text.split("\n"); - -	for (int i = 0; i < strings.size(); i++) { - -		if (strings[i].length() == 0) -			continue; - -		int last = line_map.empty() ? 0 : (line_map.back()->key() + 1); - -		Line l; -		l.text = strings[i]; -		l.meta = p_meta; -		l.type = p_type; -		line_map.insert(last, l); - -		updating = true; -		v_scroll->set_max(last + 1); -		v_scroll->set_min(line_map.front()->key()); -		updating = false; -	} - -	while (line_map.size() > line_max_count) { - -		line_map.erase(line_map.front()); -	} - -	update(); -} - -void OutputStrings::_bind_methods() { - -	ClassDB::bind_method("_vscroll_changed", &OutputStrings::_vscroll_changed); -	ClassDB::bind_method("_hscroll_changed", &OutputStrings::_hscroll_changed); -} - -OutputStrings::OutputStrings() { - -	following = true; -	updating = false; -	line_max_count = 4096; -	h_scroll = memnew(HScrollBar); -	v_scroll = memnew(VScrollBar); -	add_child(h_scroll); -	add_child(v_scroll); -	size_height = 1; -	font_height = 1; -	update_scrollbars(); -	h_scroll->connect("value_changed", this, "_hscroll_changed"); -	v_scroll->connect("value_changed", this, "_vscroll_changed"); -} diff --git a/editor/output_strings.h b/editor/output_strings.h deleted file mode 100644 index 4fd3f7d836..0000000000 --- a/editor/output_strings.h +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************/ -/*  output_strings.h                                                     */ -/*************************************************************************/ -/*                       This file is part of:                           */ -/*                           GODOT ENGINE                                */ -/*                      https://godotengine.org                          */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */ -/* Copyright (c) 2014-2019 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 OUTPUT_STRINGS_H -#define OUTPUT_STRINGS_H - -#include "core/map.h" -#include "scene/gui/control.h" -#include "scene/gui/scroll_bar.h" - -class OutputStrings : public Control { - -	GDCLASS(OutputStrings, Control); - -public: -	enum LineType { - -		LINE_NORMAL, -		LINE_WARNING, -		LINE_ERROR, -		LINE_LINK -	}; - -private: -	struct Line { - -		LineType type; -		Variant meta; -		String text; -	}; - -	int font_height; -	int size_height; - -	Size2 margin; -	typedef Map<int, Line> LineMap; -	Map<int, Line> line_map; - -	VScrollBar *v_scroll; -	HScrollBar *h_scroll; - -	bool following; -	int line_max_count; -	bool updating; - -	void _vscroll_changed(float p_value); -	void _hscroll_changed(float p_value); -	void update_scrollbars(); - -protected: -	static void _bind_methods(); -	void _notification(int p_what); - -public: -	void add_line(const String &p_text, const Variant &p_meta = Variant(), const LineType p_type = LINE_NORMAL); - -	OutputStrings(); -}; - -#endif // OUTPUT_STRINGS_H diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 3d161dc5b9..d5b1f46333 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -32,8 +32,8 @@  #include "core/io/json.h"  #include "core/version.h" -#include "editor_node.h" -#include "editor_settings.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h"  void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, const String &p_cost) { diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index 7e934ac6cb..94289f3b49 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -31,24 +31,22 @@  #ifndef ASSET_LIBRARY_EDITOR_PLUGIN_H  #define ASSET_LIBRARY_EDITOR_PLUGIN_H -#include "editor_plugin.h" +#include "editor/editor_asset_installer.h" +#include "editor/editor_plugin.h" +#include "editor/editor_plugin_settings.h"  #include "scene/gui/box_container.h"  #include "scene/gui/check_box.h" +#include "scene/gui/grid_container.h"  #include "scene/gui/line_edit.h"  #include "scene/gui/link_button.h"  #include "scene/gui/option_button.h"  #include "scene/gui/panel_container.h"  #include "scene/gui/progress_bar.h" -#include "scene/gui/separator.h" -#include "scene/gui/tab_container.h" - -#include "editor_plugin_settings.h" -#include "scene/gui/grid_container.h"  #include "scene/gui/rich_text_label.h"  #include "scene/gui/scroll_container.h" +#include "scene/gui/separator.h" +#include "scene/gui/tab_container.h"  #include "scene/gui/texture_button.h" - -#include "editor_asset_installer.h"  #include "scene/main/http_request.h"  class EditorAssetLibraryItem : public PanelContainer { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 7055abd643..f54411c72b 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -72,7 +72,7 @@ class SnapDialog : public ConfirmationDialog {  public:  	SnapDialog() { -		const int SPIN_BOX_GRID_RANGE = 256; +		const int SPIN_BOX_GRID_RANGE = 16384;  		const int SPIN_BOX_ROTATION_RANGE = 360;  		Label *label;  		VBoxContainer *container; @@ -96,6 +96,8 @@ public:  		grid_offset_x = memnew(SpinBox);  		grid_offset_x->set_min(-SPIN_BOX_GRID_RANGE);  		grid_offset_x->set_max(SPIN_BOX_GRID_RANGE); +		grid_offset_x->set_allow_lesser(true); +		grid_offset_x->set_allow_greater(true);  		grid_offset_x->set_suffix("px");  		grid_offset_x->set_h_size_flags(SIZE_EXPAND_FILL);  		child_container->add_child(grid_offset_x); @@ -103,6 +105,8 @@ public:  		grid_offset_y = memnew(SpinBox);  		grid_offset_y->set_min(-SPIN_BOX_GRID_RANGE);  		grid_offset_y->set_max(SPIN_BOX_GRID_RANGE); +		grid_offset_y->set_allow_lesser(true); +		grid_offset_y->set_allow_greater(true);  		grid_offset_y->set_suffix("px");  		grid_offset_y->set_h_size_flags(SIZE_EXPAND_FILL);  		child_container->add_child(grid_offset_y); @@ -115,6 +119,7 @@ public:  		grid_step_x = memnew(SpinBox);  		grid_step_x->set_min(0.01);  		grid_step_x->set_max(SPIN_BOX_GRID_RANGE); +		grid_step_x->set_allow_greater(true);  		grid_step_x->set_suffix("px");  		grid_step_x->set_h_size_flags(SIZE_EXPAND_FILL);  		child_container->add_child(grid_step_x); @@ -122,6 +127,7 @@ public:  		grid_step_y = memnew(SpinBox);  		grid_step_y->set_min(0.01);  		grid_step_y->set_max(SPIN_BOX_GRID_RANGE); +		grid_step_y->set_allow_greater(true);  		grid_step_y->set_suffix("px");  		grid_step_y->set_h_size_flags(SIZE_EXPAND_FILL);  		child_container->add_child(grid_step_y); @@ -5353,7 +5359,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {  	p->set_hide_on_checkable_item_selection(false);  	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Show Grid"), KEY_G), SHOW_GRID);  	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show Helpers"), KEY_H), SHOW_HELPERS); -	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show Rulers"), KEY_MASK_CMD | KEY_R), SHOW_RULERS); +	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show Rulers")), SHOW_RULERS);  	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show Guides"), KEY_Y), SHOW_GUIDES);  	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_origin", TTR("Show Origin")), SHOW_ORIGIN);  	p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_viewport", TTR("Show Viewport")), SHOW_VIEWPORT); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 80b0f0738a..f7e997a269 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -43,6 +43,7 @@  #include "editor/plugins/shader_editor_plugin.h"  #include "editor/script_editor_debugger.h"  #include "scene/main/viewport.h" +#include "scene/scene_string_names.h"  #include "script_text_editor.h"  #include "text_editor.h" @@ -1325,6 +1326,9 @@ void ScriptEditor::_menu_option(int p_option) {  				case HELP_SEARCH_FIND_NEXT: {  					help->search_again();  				} break; +				case HELP_SEARCH_FIND_PREVIOUS: { +					help->search_again(true); +				} break;  				case FILE_CLOSE: {  					_close_current_tab();  				} break; @@ -1417,16 +1421,6 @@ void ScriptEditor::_notification(int p_what) {  			members_overview->connect("item_selected", this, "_members_overview_selected");  			help_overview->connect("item_selected", this, "_help_overview_selected");  			script_split->connect("dragged", this, "_script_split_dragged"); -			autosave_timer->connect("timeout", this, "_autosave_scripts"); -			{ -				float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs"); -				if (autosave_time > 0) { -					autosave_timer->set_wait_time(autosave_time); -					autosave_timer->start(); -				} else { -					autosave_timer->stop(); -				} -			}  			EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed");  			FALLTHROUGH; @@ -2332,13 +2326,7 @@ void ScriptEditor::_editor_settings_changed() {  	_update_members_overview_visibility();  	_update_help_overview_visibility(); -	float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs"); -	if (autosave_time > 0) { -		autosave_timer->set_wait_time(autosave_time); -		autosave_timer->start(); -	} else { -		autosave_timer->stop(); -	} +	_update_autosave_timer();  	if (current_theme == "") {  		current_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme"); @@ -2366,6 +2354,21 @@ void ScriptEditor::_autosave_scripts() {  	save_all_scripts();  } +void ScriptEditor::_update_autosave_timer() { + +	if (!autosave_timer->is_inside_tree()) { +		return; +	} + +	float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs"); +	if (autosave_time > 0) { +		autosave_timer->set_wait_time(autosave_time); +		autosave_timer->start(); +	} else { +		autosave_timer->stop(); +	} +} +  void ScriptEditor::_tree_changed() {  	if (waiting_update_names) @@ -2827,6 +2830,7 @@ void ScriptEditor::_update_selected_editor_menu() {  		script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND);  		script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT); +		script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3), HELP_SEARCH_FIND_PREVIOUS);  		script_search_menu->get_popup()->add_separator();  		script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES);  		script_search_menu->show(); @@ -3088,6 +3092,7 @@ void ScriptEditor::_bind_methods() {  	ClassDB::bind_method("_show_debugger", &ScriptEditor::_show_debugger);  	ClassDB::bind_method("_get_debug_tooltip", &ScriptEditor::_get_debug_tooltip);  	ClassDB::bind_method("_autosave_scripts", &ScriptEditor::_autosave_scripts); +	ClassDB::bind_method("_update_autosave_timer", &ScriptEditor::_update_autosave_timer);  	ClassDB::bind_method("_editor_settings_changed", &ScriptEditor::_editor_settings_changed);  	ClassDB::bind_method("_update_script_names", &ScriptEditor::_update_script_names);  	ClassDB::bind_method("_update_script_connections", &ScriptEditor::_update_script_connections); @@ -3424,6 +3429,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {  	autosave_timer = memnew(Timer);  	autosave_timer->set_one_shot(false); +	autosave_timer->connect(SceneStringNames::get_singleton()->tree_entered, this, "_update_autosave_timer"); +	autosave_timer->connect("timeout", this, "_autosave_scripts");  	add_child(autosave_timer);  	grab_focus_block = false; diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 0c876108a5..294294fc56 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -168,6 +168,7 @@ class ScriptEditor : public PanelContainer {  		REQUEST_DOCS,  		HELP_SEARCH_FIND,  		HELP_SEARCH_FIND_NEXT, +		HELP_SEARCH_FIND_PREVIOUS,  		WINDOW_MOVE_UP,  		WINDOW_MOVE_DOWN,  		WINDOW_NEXT, @@ -342,6 +343,7 @@ class ScriptEditor : public PanelContainer {  	void _save_layout();  	void _editor_settings_changed();  	void _autosave_scripts(); +	void _update_autosave_timer();  	void _update_members_overview_visibility();  	void _update_members_overview(); diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp index 69fd592652..40734cffc4 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -178,6 +178,7 @@ void SpriteEditor::_update_mesh_data() {  		err_dialog->popup_centered_minsize();  		return;  	} +  	Ref<Image> image = texture->get_data();  	ERR_FAIL_COND(image.is_null());  	Rect2 rect; @@ -190,7 +191,12 @@ void SpriteEditor::_update_mesh_data() {  	bm.instance();  	bm->create_from_image_alpha(image); -	int grow = island_merging->get_value(); +	int shrink = shrink_pixels->get_value(); +	if (shrink > 0) { +		bm->shrink_mask(shrink, rect); +	} + +	int grow = grow_pixels->get_value();  	if (grow > 0) {  		bm->grow_mask(grow, rect);  	} @@ -338,6 +344,13 @@ void SpriteEditor::_convert_to_mesh_2d_node() {  }  void SpriteEditor::_convert_to_polygon_2d_node() { + +	if (computed_outline_lines.empty()) { +		err_dialog->set_text(TTR("Invalid geometry, can't create polygon.")); +		err_dialog->popup_centered_minsize(); +		return; +	} +  	Polygon2D *polygon_2d_instance = memnew(Polygon2D);  	int total_point_count = 0; @@ -362,12 +375,6 @@ void SpriteEditor::_convert_to_polygon_2d_node() {  		Vector<Vector2> outline = computed_outline_lines[i];  		Vector<Vector2> uv_outline = outline_lines[i]; -		if (outline.size() < 3) { -			err_dialog->set_text(TTR("Invalid geometry, can't create polygon.")); -			err_dialog->popup_centered_minsize(); -			return; -		} -  		PoolIntArray pia;  		pia.resize(outline.size());  		PoolIntArray::Write pia_write = pia.write(); @@ -396,16 +403,17 @@ void SpriteEditor::_convert_to_polygon_2d_node() {  }  void SpriteEditor::_create_collision_polygon_2d_node() { + +	if (computed_outline_lines.empty()) { +		err_dialog->set_text(TTR("Invalid geometry, can't create collision polygon.")); +		err_dialog->popup_centered_minsize(); +		return; +	} +  	for (int i = 0; i < computed_outline_lines.size(); i++) {  		Vector<Vector2> outline = computed_outline_lines[i]; -		if (outline.size() < 3) { -			err_dialog->set_text(TTR("Invalid geometry, can't create collision polygon.")); -			err_dialog->popup_centered_minsize(); -			continue; -		} -  		CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D);  		collision_polygon_2d_instance->set_polygon(outline); @@ -419,16 +427,17 @@ void SpriteEditor::_create_collision_polygon_2d_node() {  }  void SpriteEditor::_create_light_occluder_2d_node() { + +	if (computed_outline_lines.empty()) { +		err_dialog->set_text(TTR("Invalid geometry, can't create light occluder.")); +		err_dialog->popup_centered_minsize(); +		return; +	} +  	for (int i = 0; i < computed_outline_lines.size(); i++) {  		Vector<Vector2> outline = computed_outline_lines[i]; -		if (outline.size() < 3) { -			err_dialog->set_text(TTR("Invalid geometry, can't create light occluder.")); -			err_dialog->popup_centered_minsize(); -			continue; -		} -  		Ref<OccluderPolygon2D> polygon;  		polygon.instance(); @@ -531,10 +540,14 @@ void SpriteEditor::_debug_uv_draw() {  	Ref<Texture> tex = node->get_texture();  	ERR_FAIL_COND(!tex.is_valid()); + +	Point2 draw_pos_offset = Point2(1.0, 1.0); +	Size2 draw_size_offset = Size2(2.0, 2.0); +  	debug_uv->set_clip_contents(true); -	debug_uv->draw_texture(tex, Point2()); -	debug_uv->set_custom_minimum_size(tex->get_size()); -	//debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size()); +	debug_uv->draw_texture(tex, draw_pos_offset); +	debug_uv->set_custom_minimum_size(tex->get_size() + draw_size_offset); +	debug_uv->draw_set_transform(draw_pos_offset, 0, Size2(1.0, 1.0));  	Color color = Color(1.0, 0.8, 0.7); @@ -604,13 +617,21 @@ SpriteEditor::SpriteEditor() {  	simplification->set_value(2);  	hb->add_child(simplification);  	hb->add_spacer(); +	hb->add_child(memnew(Label(TTR("Shrink (Pixels): ")))); +	shrink_pixels = memnew(SpinBox); +	shrink_pixels->set_min(0); +	shrink_pixels->set_max(10); +	shrink_pixels->set_step(1); +	shrink_pixels->set_value(0); +	hb->add_child(shrink_pixels); +	hb->add_spacer();  	hb->add_child(memnew(Label(TTR("Grow (Pixels): ")))); -	island_merging = memnew(SpinBox); -	island_merging->set_min(0); -	island_merging->set_max(10); -	island_merging->set_step(1); -	island_merging->set_value(2); -	hb->add_child(island_merging); +	grow_pixels = memnew(SpinBox); +	grow_pixels->set_min(0); +	grow_pixels->set_max(10); +	grow_pixels->set_step(1); +	grow_pixels->set_value(2); +	hb->add_child(grow_pixels);  	hb->add_spacer();  	update_preview = memnew(Button);  	update_preview->set_text(TTR("Update Preview")); diff --git a/editor/plugins/sprite_editor_plugin.h b/editor/plugins/sprite_editor_plugin.h index 81be4a19e9..4ca7bca1a8 100644 --- a/editor/plugins/sprite_editor_plugin.h +++ b/editor/plugins/sprite_editor_plugin.h @@ -67,7 +67,8 @@ class SpriteEditor : public Control {  	Vector<int> computed_indices;  	SpinBox *simplification; -	SpinBox *island_merging; +	SpinBox *grow_pixels; +	SpinBox *shrink_pixels;  	Button *update_preview;  	void _menu_option(int p_option); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index d0320bcd8b..0cef5a8b6f 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -31,7 +31,7 @@  #include "text_editor.h"  #include "core/os/keyboard.h" -#include "editor_node.h" +#include "editor/editor_node.h"  void TextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) {  	highlighters[p_highlighter->get_name()] = p_highlighter; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index c962751c7a..3a9e48cfdb 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -178,6 +178,9 @@ bool VisualShaderEditor::_is_available(int p_mode) {  }  void VisualShaderEditor::update_custom_nodes() { +	if (members_dialog->is_visible()) { +		return; +	}  	clear_custom_types();  	List<StringName> class_list;  	ScriptServer::get_global_class_list(&class_list); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index f70dcab931..d903e153a7 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -1753,6 +1753,12 @@ void ProjectManager::_notification(int p_what) {  			Engine::get_singleton()->set_editor_hint(false);  		} break; +		case NOTIFICATION_RESIZED: { + +			if (open_templates->is_visible()) { +				open_templates->popup_centered_minsize(); +			} +		} break;  		case NOTIFICATION_READY: {  			if (_project_list->get_project_count() == 0 && StreamPeerSSL::is_available()) diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index f177e634a6..35187214a6 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -845,13 +845,9 @@ void ProjectSettingsEditor::_item_adds(String) {  void ProjectSettingsEditor::_item_add() { -	Variant value; -	switch (type->get_selected()) { -		case 0: value = false; break; -		case 1: value = 0; break; -		case 2: value = 0.0; break; -		case 3: value = ""; break; -	} +	// Initialize the property with the default value for the given type +	Variant::CallError ce; +	const Variant value = Variant::construct(Variant::Type(type->get_selected()), NULL, 0, ce);  	String catname = category->get_text().strip_edges();  	String propname = property->get_text().strip_edges(); @@ -1834,10 +1830,11 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {  	type = memnew(OptionButton);  	type->set_h_size_flags(Control::SIZE_EXPAND_FILL);  	add_prop_bar->add_child(type); -	type->add_item("bool"); -	type->add_item("int"); -	type->add_item("float"); -	type->add_item("string"); + +	// Start at 1 to avoid adding "Nil" as an option +	for (int i = 1; i < Variant::VARIANT_MAX; i++) { +		type->add_item(Variant::get_type_name(Variant::Type(i)), i); +	}  	Button *add = memnew(Button);  	add_prop_bar->add_child(add); diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp index f2f51d9dd3..1ea9399c02 100644 --- a/modules/assimp/editor_scene_importer_assimp.cpp +++ b/modules/assimp/editor_scene_importer_assimp.cpp @@ -29,11 +29,12 @@  /*************************************************************************/  #include "editor_scene_importer_assimp.h" +  #include "core/bind/core_bind.h"  #include "core/io/image_loader.h"  #include "editor/editor_file_system.h" +#include "editor/editor_settings.h"  #include "editor/import/resource_importer_scene.h" -#include "editor_settings.h"  #include "import_utils.h"  #include "scene/3d/camera.h"  #include "scene/3d/light.h" @@ -1350,4 +1351,4 @@ void EditorSceneImporterAssimp::_generate_node(  		_generate_node(state, recursive_state.skeleton, recursive_state.assimp_node->mChildren[i],  				recursive_state.new_node != NULL ? recursive_state.new_node : recursive_state.parent_node);  	} -}
\ No newline at end of file +} diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index ae9a00543c..aa48ab44f2 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -39,7 +39,7 @@  			[b]Note:[/b] If the library defines tool scripts that run inside the editor, [code]reloadable[/code] must be [code]false[/code]. Otherwise, the editor will attempt to unload the tool scripts while they're in use and crash.  		</member>  		<member name="singleton" type="bool" setter="set_singleton" getter="is_singleton" default="false"> -			If [code]true[/code], Godot loads the library at startup rather than the first time a script uses the library, calling [code]gdnative_singleton[/code] after initializing the library. The library remains loaded as long as Godot is running. +			If [code]true[/code], Godot loads the library at startup rather than the first time a script uses the library, calling [code]{prefix}gdnative_singleton[/code] after initializing the library (where [code]{prefix}[/code] is the value of [member symbol_prefix]). The library remains loaded as long as Godot is running.  			[b]Note:[/b] A singleton library cannot be [member reloadable].  		</member>  		<member name="symbol_prefix" type="String" setter="set_symbol_prefix" getter="get_symbol_prefix" default=""godot_""> diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index 783ad4e147..ee9e71d4a0 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -339,7 +339,7 @@ bool GDNative::initialize() {  	if (err || !library_init) {  		OS::get_singleton()->close_dynamic_library(native_handle);  		native_handle = NULL; -		ERR_PRINT("Failed to obtain godot_gdnative_init symbol"); +		ERR_PRINTS("Failed to obtain " + library->get_symbol_prefix() + "gdnative_init symbol");  		return false;  	} diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index 6ff6262b56..0194199133 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -277,7 +277,7 @@ void register_gdnative_types() {  				proc_ptr);  		if (err != OK) { -			ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton->get_library()->get_current_library_path()) + "\" found").utf8().get_data()); +			ERR_PRINTS("No " + lib->get_symbol_prefix() + "gdnative_singleton in \"" + singleton->get_library()->get_current_library_path() + "\" found");  		} else {  			singleton_gdnatives.push_back(singleton);  			((void (*)())proc_ptr)(); diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index ae2aaf6aee..90646f73ba 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -134,6 +134,22 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {  }  void GDScriptLanguageProtocol::initialized(const Variant &p_params) { + +	lsp::GodotCapabilities capabilities; + +	DocData *doc = EditorHelp::get_doc_data(); +	for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) { + +		lsp::GodotNativeClassInfo gdclass; +		gdclass.name = E->get().name; +		gdclass.class_doc = &(E->get()); +		if (ClassDB::ClassInfo *ptr = ClassDB::classes.getptr(StringName(E->get().name))) { +			gdclass.class_info = ptr; +		} +		capabilities.native_classes.push_back(gdclass); +	} + +	notify_client("gdscript/capabilities", capabilities.to_json());  }  void GDScriptLanguageProtocol::poll() { diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 62c212a2bd..8d58b99e02 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -56,8 +56,9 @@ void GDScriptLanguageServer::_notification(int p_what) {  void GDScriptLanguageServer::thread_main(void *p_userdata) {  	GDScriptLanguageServer *self = static_cast<GDScriptLanguageServer *>(p_userdata);  	while (!self->thread_exit) { +		// Poll 20 times per second  		self->protocol.poll(); -		OS::get_singleton()->delay_usec(10); +		OS::get_singleton()->delay_usec(50000);  	}  } diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 6baa7e4219..c289ff6c07 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -269,7 +269,11 @@ Error GDScriptWorkspace::initialize() {  				params += params.empty() ? "..." : ", ...";  			} -			symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + data.return_type; +			String return_type = data.return_type; +			if (return_type.empty()) { +				return_type = "void"; +			} +			symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + return_type;  			symbol.documentation = data.description;  			class_symbol.children.push_back(symbol);  		} diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 61a0980c41..cf360b5291 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -31,7 +31,9 @@  #ifndef GODOT_LSP_H  #define GODOT_LSP_H -#include "core/variant.h" +#include "core/class_db.h" +#include "core/list.h" +#include "editor/doc/doc_data.h"  namespace lsp { @@ -1567,6 +1569,39 @@ struct InitializeResult {  	}  }; +struct GodotNativeClassInfo { + +	String name; +	const DocData::ClassDoc *class_doc = NULL; +	const ClassDB::ClassInfo *class_info = NULL; + +	Dictionary to_json() { +		Dictionary dict; +		dict["name"] = name; +		dict["inherits"] = class_doc->inherits; +		return dict; +	} +}; + +/** Features not included in the standart lsp specifications */ +struct GodotCapabilities { + +	/** +	 * Native class list +	*/ +	List<GodotNativeClassInfo> native_classes; + +	Dictionary to_json() { +		Dictionary dict; +		Array classes; +		for (List<GodotNativeClassInfo>::Element *E = native_classes.front(); E; E = E->next()) { +			classes.push_back(E->get().to_json()); +		} +		dict["native_classes"] = classes; +		return dict; +	} +}; +  /** Format BBCode documentation from DocData to markdown */  static String marked_documentation(const String &p_bbcode) { diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp index ca656b4b9b..204f4e8905 100644 --- a/modules/mbedtls/crypto_mbedtls.cpp +++ b/modules/mbedtls/crypto_mbedtls.cpp @@ -237,6 +237,7 @@ Ref<CryptoKey> CryptoMbedTLS::generate_rsa(int p_bytes) {  Ref<X509Certificate> CryptoMbedTLS::generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after) {  	Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS> >(p_key); +	ERR_FAIL_COND_V_MSG(key.is_null(), NULL, "Invalid private key argument.");  	mbedtls_x509write_cert crt;  	mbedtls_x509write_crt_init(&crt); diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs index 417032da54..ab37d89955 100644 --- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs @@ -160,9 +160,16 @@ namespace GodotTools              if (!File.Exists(GodotSharpDirs.ProjectSlnPath))                  return true; // No solution to build -            // Make sure to update the API assemblies if they happen to be missing. Just in -            // case the user decided to delete them at some point after they were loaded. -            Internal.UpdateApiAssembliesFromPrebuilt(); +            // Make sure the API assemblies are up to date before building the project. +            // We may not have had the chance to update the release API assemblies, and the debug ones +            // may have been deleted by the user at some point after they were loaded by the Godot editor. +            string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(config == "Release" ? "Release" : "Debug"); + +            if (!string.IsNullOrEmpty(apiAssembliesUpdateError)) +            { +                ShowBuildErrorDialog("Failed to update the Godot API assemblies"); +                return false; +            }              var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();              var buildTool = (BuildTool) editorSettings.GetSetting("mono/builds/build_tool"); diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 7da7cff933..12edd651df 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -34,7 +34,7 @@ namespace GodotTools          private bool CreateProjectSolution()          { -            using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2)) +            using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3))              {                  pr.Step("Generating C# project...".TTR()); @@ -73,9 +73,23 @@ namespace GodotTools                          return false;                      } -                    // Make sure to update the API assemblies if they happen to be missing. Just in -                    // case the user decided to delete them at some point after they were loaded. -                    Internal.UpdateApiAssembliesFromPrebuilt(); +                    pr.Step("Updating Godot API assemblies...".TTR()); + +                    string debugApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Debug"); + +                    if (!string.IsNullOrEmpty(debugApiAssembliesError)) +                    { +                        ShowErrorDialog("Failed to update the Godot API assemblies: " + debugApiAssembliesError); +                        return false; +                    } + +                    string releaseApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Release"); + +                    if (!string.IsNullOrEmpty(releaseApiAssembliesError)) +                    { +                        ShowErrorDialog("Failed to update the Godot API assemblies: " + releaseApiAssembliesError); +                        return false; +                    }                      pr.Step("Done".TTR()); diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs index 9e24138143..01aa0d0ab1 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs @@ -40,8 +40,7 @@ namespace GodotTools.Ides          protected ILogger Logger          { -            get => logger ?? (logger = new ConsoleLogger()); -            set => logger = value; +            get => logger ?? (logger = new GodotLogger());          }          private void StartServer() diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs index 7783576910..836c9c11e4 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -10,8 +10,8 @@ namespace GodotTools.Internals          public const string CSharpLanguageType = "CSharpScript";          public const string CSharpLanguageExtension = "cs"; -        public static string UpdateApiAssembliesFromPrebuilt() => -            internal_UpdateApiAssembliesFromPrebuilt(); +        public static string UpdateApiAssembliesFromPrebuilt(string config) => +            internal_UpdateApiAssembliesFromPrebuilt(config);          public static string FullTemplatesDir =>              internal_FullTemplatesDir(); @@ -55,7 +55,7 @@ namespace GodotTools.Internals          // Internal Calls          [MethodImpl(MethodImplOptions.InternalCall)] -        private static extern string internal_UpdateApiAssembliesFromPrebuilt(); +        private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config);          [MethodImpl(MethodImplOptions.InternalCall)]          private static extern string internal_FullTemplatesDir(); diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index 0e6c58c9d7..748447005f 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -75,7 +75,7 @@ bool generate_api_solution(const String &p_solution_dir, const String &p_core_pr  				p_editor_proj_dir, p_editor_compile_items,  				GDMono::get_singleton()->get_tools_project_editor_assembly());  	} else { -		MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.ApiSolutionGenerationDomain"); +		MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ApiSolutionGeneration");  		CRASH_COND(temp_domain == NULL);  		_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain); diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 5a84d9e3b8..1564d73c2a 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -230,31 +230,9 @@ uint32_t godot_icall_GodotSharpExport_GetExportedAssemblyDependencies(MonoString  	return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_lib_dir, dependencies);  } -float godot_icall_Globals_EditorScale() { -	return EDSCALE; -} - -MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { -	String setting = GDMonoMarshal::mono_string_to_godot(p_setting); -	Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); -	Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed); -	return GDMonoMarshal::variant_to_mono_object(result); -} - -MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { -	String setting = GDMonoMarshal::mono_string_to_godot(p_setting); -	Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); -	Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed); -	return GDMonoMarshal::variant_to_mono_object(result); -} - -MonoString *godot_icall_Globals_TTR(MonoString *p_text) { -	String text = GDMonoMarshal::mono_string_to_godot(p_text); -	return GDMonoMarshal::mono_string_from_godot(TTR(text)); -} - -MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt() { -	String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt(); +MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) { +	String config = GDMonoMarshal::mono_string_to_godot(p_config); +	String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt(config);  	return GDMonoMarshal::mono_string_from_godot(error_str);  } @@ -365,6 +343,29 @@ void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {  	}  } +float godot_icall_Globals_EditorScale() { +	return EDSCALE; +} + +MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { +	String setting = GDMonoMarshal::mono_string_to_godot(p_setting); +	Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); +	Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed); +	return GDMonoMarshal::variant_to_mono_object(result); +} + +MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { +	String setting = GDMonoMarshal::mono_string_to_godot(p_setting); +	Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); +	Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed); +	return GDMonoMarshal::variant_to_mono_object(result); +} + +MonoString *godot_icall_Globals_TTR(MonoString *p_text) { +	String text = GDMonoMarshal::mono_string_to_godot(p_text); +	return GDMonoMarshal::mono_string_from_godot(TTR(text)); +} +  MonoString *godot_icall_Utils_OS_GetPlatformName() {  	String os_name = OS::get_singleton()->get_name();  	return GDMonoMarshal::mono_string_from_godot(os_name); diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index 80a7335b1d..e83152d668 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -32,9 +32,13 @@  #include <mono/metadata/image.h> +#include "core/os/os.h" +  #include "../mono_gd/gd_mono.h"  #include "../mono_gd/gd_mono_assembly.h" +namespace GodotSharpExport { +  String get_assemblyref_name(MonoImage *p_image, int index) {  	const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF); @@ -45,7 +49,7 @@ String get_assemblyref_name(MonoImage *p_image, int index) {  	return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME]));  } -Error GodotSharpExport::get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) { +Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) {  	MonoImage *image = p_assembly->get_image();  	for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) { @@ -96,8 +100,8 @@ Error GodotSharpExport::get_assembly_dependencies(GDMonoAssembly *p_assembly, co  	return OK;  } -Error GodotSharpExport::get_exported_assembly_dependencies(const String &p_project_dll_name, const String &p_project_dll_src_path, const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_dependencies) { -	MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain"); +Error get_exported_assembly_dependencies(const String &p_project_dll_name, const String &p_project_dll_src_path, const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_dependencies) { +	MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport");  	ERR_FAIL_NULL_V(export_domain, FAILED);  	_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain); @@ -110,7 +114,9 @@ Error GodotSharpExport::get_exported_assembly_dependencies(const String &p_proje  	ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + p_project_dll_name + "'.");  	Vector<String> search_dirs; -	GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_lib_dir); +	GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);  	return get_assembly_dependencies(scripts_assembly, search_dirs, r_dependencies);  } + +} // namespace GodotSharpExport diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h index 8d121a6bc3..58e46e2f2d 100644 --- a/modules/mono/editor/godotsharp_export.h +++ b/modules/mono/editor/godotsharp_export.h @@ -39,10 +39,11 @@  namespace GodotSharpExport { +Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies); +  Error get_exported_assembly_dependencies(const String &p_project_dll_name,  		const String &p_project_dll_src_path, const String &p_build_config,  		const String &p_custom_lib_dir, Dictionary &r_dependencies); -Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);  } // namespace GodotSharpExport diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 4b2525c692..5fa8aed5a9 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -43,6 +43,8 @@  #include "utils/android_utils.h"  #endif +#include "mono_gd/gd_mono.h" +  namespace GodotSharpDirs {  String _get_expected_build_config() { @@ -59,20 +61,6 @@ String _get_expected_build_config() {  #endif  } -String _get_expected_api_build_config() { -#ifdef TOOLS_ENABLED -	return "Debug"; -#else - -#ifdef DEBUG_ENABLED -	return "Debug"; -#else -	return "Release"; -#endif - -#endif -} -  String _get_mono_user_dir() {  #ifdef TOOLS_ENABLED  	if (EditorSettings::get_singleton()) { @@ -134,7 +122,7 @@ private:  		res_data_dir = "res://.mono";  		res_metadata_dir = res_data_dir.plus_file("metadata");  		res_assemblies_base_dir = res_data_dir.plus_file("assemblies"); -		res_assemblies_dir = res_assemblies_base_dir.plus_file(_get_expected_api_build_config()); +		res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config());  		res_config_dir = res_data_dir.plus_file("etc").plus_file("mono");  		// TODO use paths from csproj diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 544bfc4615..0a34404154 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -381,10 +381,10 @@ void GDMono::initialize_load_assemblies() {  }  bool GDMono::_are_api_assemblies_out_of_sync() { -	bool out_of_sync = core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated); +	bool out_of_sync = core_api_assembly.assembly && (core_api_assembly.out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated);  #ifdef TOOLS_ENABLED  	if (!out_of_sync) -		out_of_sync = editor_api_assembly && editor_api_assembly_out_of_sync; +		out_of_sync = editor_api_assembly.assembly && editor_api_assembly.out_of_sync;  #endif  	return out_of_sync;  } @@ -523,10 +523,10 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMo  	return true;  } -APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) { -	APIAssembly::Version api_assembly_version; +ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, ApiAssemblyInfo::Type p_api_type) { +	ApiAssemblyInfo::Version api_assembly_version; -	const char *nativecalls_name = p_api_type == APIAssembly::API_CORE ? +	const char *nativecalls_name = p_api_type == ApiAssemblyInfo::API_CORE ?  										   BINDINGS_CLASS_NATIVECALLS :  										   BINDINGS_CLASS_NATIVECALLS_EDITOR; @@ -549,8 +549,8 @@ APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssemb  	return api_assembly_version;  } -String APIAssembly::to_string(APIAssembly::Type p_type) { -	return p_type == APIAssembly::API_CORE ? "API_CORE" : "API_EDITOR"; +String ApiAssemblyInfo::to_string(ApiAssemblyInfo::Type p_type) { +	return p_type == ApiAssemblyInfo::API_CORE ? "API_CORE" : "API_EDITOR";  }  bool GDMono::_load_corlib_assembly() { @@ -567,16 +567,12 @@ bool GDMono::_load_corlib_assembly() {  }  #ifdef TOOLS_ENABLED -bool GDMono::copy_prebuilt_api_assembly(APIAssembly::Type p_api_type, const String &p_config) { - -	bool &api_assembly_out_of_sync = (p_api_type == APIAssembly::API_CORE) ? -											 GDMono::get_singleton()->core_api_assembly_out_of_sync : -											 GDMono::get_singleton()->editor_api_assembly_out_of_sync; +bool GDMono::copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config) {  	String src_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);  	String dst_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config); -	String assembly_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; +	String assembly_name = p_api_type == ApiAssemblyInfo::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;  	// Create destination directory if needed  	if (!DirAccess::exists(dst_dir)) { @@ -590,35 +586,102 @@ bool GDMono::copy_prebuilt_api_assembly(APIAssembly::Type p_api_type, const Stri  		}  	} +	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + +	String xml_file = assembly_name + ".xml"; +	if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK) +		WARN_PRINTS("Failed to copy '" + xml_file + "'."); + +	String pdb_file = assembly_name + ".pdb"; +	if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK) +		WARN_PRINTS("Failed to copy '" + pdb_file + "'."); +  	String assembly_file = assembly_name + ".dll"; -	String assembly_src = src_dir.plus_file(assembly_file); -	String assembly_dst = dst_dir.plus_file(assembly_file); +	if (da->copy(src_dir.plus_file(assembly_file), dst_dir.plus_file(assembly_file)) != OK) { +		ERR_PRINTS("Failed to copy '" + assembly_file + "'."); +		return false; +	} -	if (!FileAccess::exists(assembly_dst) || api_assembly_out_of_sync) { -		DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); +	return true; +} -		String xml_file = assembly_name + ".xml"; -		if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK) -			WARN_PRINTS("Failed to copy '" + xml_file + "'."); +static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool &r_out_of_sync) { +	String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); +	String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); -		String pdb_file = assembly_name + ".pdb"; -		if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK) -			WARN_PRINTS("Failed to copy '" + pdb_file + "'."); +	if (!FileAccess::exists(core_api_assembly_path) || !FileAccess::exists(editor_api_assembly_path)) +		return false; -		Error err = da->copy(assembly_src, assembly_dst); +	String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg"); -		if (err != OK) { -			ERR_PRINTS("Failed to copy '" + assembly_file + "'."); -			return false; -		} +	if (!FileAccess::exists(cached_api_hash_path)) +		return false; + +	Ref<ConfigFile> cfg; +	cfg.instance(); +	Error cfg_err = cfg->load(cached_api_hash_path); +	ERR_FAIL_COND_V(cfg_err != OK, false); -		api_assembly_out_of_sync = false; +	// Checking the modified time is good enough +	if (FileAccess::get_modified_time(core_api_assembly_path) != (uint64_t)cfg->get_value("core", "modified_time") || +			FileAccess::get_modified_time(editor_api_assembly_path) != (uint64_t)cfg->get_value("editor", "modified_time")) { +		return false;  	} +	r_out_of_sync = GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("core", "bindings_version") || +					GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("core", "cs_glue_version") || +					GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("editor", "bindings_version") || +					GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("editor", "cs_glue_version") || +					GodotSharpBindings::get_core_api_hash() != (uint64_t)cfg->get_value("core", "api_hash") || +					GodotSharpBindings::get_editor_api_hash() != (uint64_t)cfg->get_value("editor", "api_hash"); +  	return true;  } -String GDMono::update_api_assemblies_from_prebuilt() { +static void create_cached_api_hash_for(const String &p_api_assemblies_dir) { + +	String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); +	String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); +	String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg"); + +	Ref<ConfigFile> cfg; +	cfg.instance(); + +	cfg->set_value("core", "modified_time", FileAccess::get_modified_time(core_api_assembly_path)); +	cfg->set_value("editor", "modified_time", FileAccess::get_modified_time(editor_api_assembly_path)); + +	cfg->set_value("core", "bindings_version", GodotSharpBindings::get_bindings_version()); +	cfg->set_value("core", "cs_glue_version", GodotSharpBindings::get_cs_glue_version()); +	cfg->set_value("editor", "bindings_version", GodotSharpBindings::get_bindings_version()); +	cfg->set_value("editor", "cs_glue_version", GodotSharpBindings::get_cs_glue_version()); + +	// This assumes the prebuilt api assemblies we copied to the project are not out of sync +	cfg->set_value("core", "api_hash", GodotSharpBindings::get_core_api_hash()); +	cfg->set_value("editor", "api_hash", GodotSharpBindings::get_editor_api_hash()); + +	Error err = cfg->save(cached_api_hash_path); +	ERR_FAIL_COND(err != OK); +} + +bool GDMono::_temp_domain_load_are_assemblies_out_of_sync(const String &p_config) { +	MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.Domain.CheckApiAssemblies"); +	ERR_FAIL_NULL_V(temp_domain, "Failed to create temporary domain to check API assemblies"); +	_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain); + +	_GDMONO_SCOPE_DOMAIN_(temp_domain); + +	GDMono::LoadedApiAssembly temp_core_api_assembly; +	GDMono::LoadedApiAssembly temp_editor_api_assembly; + +	if (!_try_load_api_assemblies(temp_core_api_assembly, temp_editor_api_assembly, +				p_config, /* refonly: */ true, /* loaded_callback: */ NULL)) { +		return temp_core_api_assembly.out_of_sync || temp_editor_api_assembly.out_of_sync; +	} + +	return true; // Failed to load, assume they're outdated assemblies +} + +String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync, const bool *p_editor_api_out_of_sync) {  #define FAIL_REASON(m_out_of_sync, m_prebuilt_exists)                            \  	(                                                                            \ @@ -629,46 +692,55 @@ String GDMono::update_api_assemblies_from_prebuilt() {  							String("and the prebuilt assemblies are missing.") : \  							String("and we failed to copy the prebuilt assemblies."))) -	bool api_assembly_out_of_sync = core_api_assembly_out_of_sync || editor_api_assembly_out_of_sync; +	String dst_assemblies_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config); -	String core_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll"); -	String editor_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); +	String core_assembly_path = dst_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); +	String editor_assembly_path = dst_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); -	if (!api_assembly_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) -		return String(); // No update needed +	bool api_assemblies_out_of_sync = false; -	const int CONFIGS_LEN = 2; -	String configs[CONFIGS_LEN] = { String("Debug"), String("Release") }; +	if (p_core_api_out_of_sync && p_editor_api_out_of_sync) { +		api_assemblies_out_of_sync = p_core_api_out_of_sync || p_editor_api_out_of_sync; +	} else if (FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) { +		// Determine if they're out of sync +		if (!try_get_cached_api_hash_for(dst_assemblies_dir, api_assemblies_out_of_sync)) { +			api_assemblies_out_of_sync = _temp_domain_load_are_assemblies_out_of_sync(p_config); +		} +	} -	for (int i = 0; i < CONFIGS_LEN; i++) { -		String config = configs[i]; +	// Note: Even if only one of the assemblies if missing or out of sync, we update both -		print_verbose("Updating '" + config + "' API assemblies"); +	if (!api_assemblies_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) +		return String(); // No update needed -		String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(config); -		String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); -		String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); +	print_verbose("Updating '" + p_config + "' API assemblies"); -		if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path)) { -			return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exists: */ false); -		} +	String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); +	String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); +	String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); -		// Copy the prebuilt Api -		if (!copy_prebuilt_api_assembly(APIAssembly::API_CORE, config) || -				!copy_prebuilt_api_assembly(APIAssembly::API_EDITOR, config)) { -			return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exists: */ true); -		} +	if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path)) { +		return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ false);  	} +	// Copy the prebuilt Api +	if (!copy_prebuilt_api_assembly(ApiAssemblyInfo::API_CORE, p_config) || +			!copy_prebuilt_api_assembly(ApiAssemblyInfo::API_EDITOR, p_config)) { +		return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ true); +	} + +	// Cache the api hash of the assemblies we just copied +	create_cached_api_hash_for(dst_assemblies_dir); +  	return String(); // Updated successfully  #undef FAIL_REASON  }  #endif -bool GDMono::_load_core_api_assembly() { +bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) { -	if (core_api_assembly) +	if (r_loaded_api_assembly.assembly)  		return true;  #ifdef TOOLS_ENABLED @@ -676,101 +748,115 @@ bool GDMono::_load_core_api_assembly() {  	// If running the project manager, load it from the prebuilt API directory  	String assembly_dir = !Main::is_project_manager() ? -								  GodotSharpDirs::get_res_assemblies_dir() : -								  GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"); +								  GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) : +								  GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);  	String assembly_path = assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");  	bool success = FileAccess::exists(assembly_path) && -				   load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly); +				   load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);  #else -	bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly); +	bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly, p_refonly);  #endif  	if (success) { -		APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE); -		core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || -										GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || -										GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; -		if (!core_api_assembly_out_of_sync) { -			GDMonoUtils::update_godot_api_cache(); - -			_install_trace_listener(); -		} +		ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_CORE); +		r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || +											GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || +											GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;  	} else { -		core_api_assembly_out_of_sync = false; +		r_loaded_api_assembly.out_of_sync = false;  	}  	return success;  }  #ifdef TOOLS_ENABLED -bool GDMono::_load_editor_api_assembly() { +bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) { -	if (editor_api_assembly) +	if (r_loaded_api_assembly.assembly)  		return true;  	// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date  	// If running the project manager, load it from the prebuilt API directory  	String assembly_dir = !Main::is_project_manager() ? -								  GodotSharpDirs::get_res_assemblies_dir() : -								  GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"); +								  GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) : +								  GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);  	String assembly_path = assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");  	bool success = FileAccess::exists(assembly_path) && -				   load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly); +				   load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);  	if (success) { -		APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR); -		editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || -										  GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || -										  GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; +		ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_EDITOR); +		r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || +											GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || +											GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;  	} else { -		editor_api_assembly_out_of_sync = false; +		r_loaded_api_assembly.out_of_sync = false;  	}  	return success;  }  #endif -bool GDMono::_try_load_api_assemblies() { - -	if (!_load_core_api_assembly()) { +bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly, +		const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback) { +	if (!_load_core_api_assembly(r_core_api_assembly, p_config, p_refonly)) {  		if (OS::get_singleton()->is_stdout_verbose())  			print_error("Mono: Failed to load Core API assembly");  		return false;  	}  #ifdef TOOLS_ENABLED -	if (!_load_editor_api_assembly()) { +	if (!_load_editor_api_assembly(r_editor_api_assembly, p_config, p_refonly)) {  		if (OS::get_singleton()->is_stdout_verbose())  			print_error("Mono: Failed to load Editor API assembly");  		return false;  	} -	if (editor_api_assembly_out_of_sync) +	if (r_editor_api_assembly.out_of_sync)  		return false;  #endif  	// Check if the core API assembly is out of sync only after trying to load the  	// editor API assembly. Otherwise, if both assemblies are out of sync, we would  	// only update the former as we won't know the latter also needs to be updated. -	if (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated) +	if (r_core_api_assembly.out_of_sync)  		return false; +	if (p_callback) +		return p_callback(); +  	return true;  } +bool GDMono::_on_core_api_assembly_loaded() { +	GDMonoUtils::update_godot_api_cache(); + +	if (!GDMonoUtils::mono_cache.godot_api_cache_updated) +		return false; + +	get_singleton()->_install_trace_listener(); + +	return true; +} + +bool GDMono::_try_load_api_assemblies_preset() { +	return _try_load_api_assemblies(core_api_assembly, editor_api_assembly, +			get_expected_api_build_config(), /* refonly: */ false, _on_core_api_assembly_loaded); +} +  void GDMono::_load_api_assemblies() { -	bool api_assemblies_loaded = _try_load_api_assemblies(); +	bool api_assemblies_loaded = _try_load_api_assemblies_preset();  	if (!api_assemblies_loaded) {  #ifdef TOOLS_ENABLED -		// The API assemblies are out of sync. Fine, try one more time, but this time -		// update them from the prebuilt assemblies directory before trying to load them. +		// The API assemblies are out of sync or some other error happened. Fine, try one more time, but +		// this time update them from the prebuilt assemblies directory before trying to load them again.  		// Shouldn't happen. The project manager loads the prebuilt API assemblies  		CRASH_COND_MSG(Main::is_project_manager(), "Failed to load one of the prebuilt API assemblies."); @@ -780,7 +866,7 @@ void GDMono::_load_api_assemblies() {  		CRASH_COND_MSG(domain_unload_err != OK, "Mono: Failed to unload scripts domain.");  		// 2. Update the API assemblies -		String update_error = update_api_assemblies_from_prebuilt(); +		String update_error = update_api_assemblies_from_prebuilt("Debug", &core_api_assembly.out_of_sync, &editor_api_assembly.out_of_sync);  		CRASH_COND_MSG(!update_error.empty(), update_error);  		// 3. Load the scripts domain again @@ -788,7 +874,7 @@ void GDMono::_load_api_assemblies() {  		CRASH_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain.");  		// 4. Try loading the updated assemblies -		api_assemblies_loaded = _try_load_api_assemblies(); +		api_assemblies_loaded = _try_load_api_assemblies_preset();  #endif  	} @@ -796,14 +882,14 @@ void GDMono::_load_api_assemblies() {  		// welp... too bad  		if (_are_api_assemblies_out_of_sync()) { -			if (core_api_assembly_out_of_sync) { +			if (core_api_assembly.out_of_sync) {  				ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync.");  			} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {  				ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed.");  			}  #ifdef TOOLS_ENABLED -			if (editor_api_assembly_out_of_sync) { +			if (editor_api_assembly.out_of_sync) {  				ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync.");  			}  #endif @@ -852,15 +938,14 @@ void GDMono::_install_trace_listener() {  #ifdef DEBUG_ENABLED  	// Install the trace listener now before the project assembly is loaded -	typedef void (*DebuggingUtils_InstallTraceListener)(MonoObject **); +	GDMonoClass *debug_utils = get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, "DebuggingUtils"); +	GDMonoMethod *install_func = debug_utils->get_method("InstallTraceListener"); +  	MonoException *exc = NULL; -	GDMonoClass *debug_utils = core_api_assembly->get_class(BINDINGS_NAMESPACE, "DebuggingUtils"); -	DebuggingUtils_InstallTraceListener install_func = -			(DebuggingUtils_InstallTraceListener)debug_utils->get_method_thunk("InstallTraceListener"); -	install_func((MonoObject **)&exc); +	install_func->invoke_raw(NULL, NULL, &exc);  	if (exc) { -		ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener.");  		GDMonoUtils::debug_print_unhandled_exception(exc); +		ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener.");  	}  #endif  } @@ -871,7 +956,7 @@ Error GDMono::_load_scripts_domain() {  	print_verbose("Mono: Loading scripts domain..."); -	scripts_domain = GDMonoUtils::create_domain("GodotEngine.ScriptsDomain"); +	scripts_domain = GDMonoUtils::create_domain("GodotEngine.Domain.Scripts");  	ERR_FAIL_NULL_V_MSG(scripts_domain, ERR_CANT_CREATE, "Mono: Could not create scripts app domain."); @@ -903,10 +988,8 @@ Error GDMono::_unload_scripts_domain() {  	_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain)); -	core_api_assembly = NULL;  	project_assembly = NULL;  #ifdef TOOLS_ENABLED -	editor_api_assembly = NULL;  	tools_assembly = NULL;  	tools_project_editor_assembly = NULL;  #endif @@ -1076,16 +1159,9 @@ GDMono::GDMono() {  	root_domain = NULL;  	scripts_domain = NULL; -	core_api_assembly_out_of_sync = false; -#ifdef TOOLS_ENABLED -	editor_api_assembly_out_of_sync = false; -#endif -  	corlib_assembly = NULL; -	core_api_assembly = NULL;  	project_assembly = NULL;  #ifdef TOOLS_ENABLED -	editor_api_assembly = NULL;  	tools_assembly = NULL;  	tools_project_editor_assembly = NULL;  #endif diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 343d68bc2d..e14a0d8409 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -41,7 +41,7 @@  #include "../utils/mono_reg_utils.h"  #endif -namespace APIAssembly { +namespace ApiAssemblyInfo {  enum Type {  	API_CORE,  	API_EDITOR @@ -76,7 +76,7 @@ struct Version {  };  String to_string(Type p_type); -} // namespace APIAssembly +} // namespace ApiAssemblyInfo  class GDMono { @@ -86,44 +86,58 @@ public:  		POLICY_LOG_ERROR  	}; +	struct LoadedApiAssembly { +		GDMonoAssembly *assembly; +		bool out_of_sync; + +		LoadedApiAssembly() : +				assembly(NULL), +				out_of_sync(false) { +		} +	}; +  private:  	bool runtime_initialized;  	bool finalizing_scripts_domain; +	UnhandledExceptionPolicy unhandled_exception_policy; +  	MonoDomain *root_domain;  	MonoDomain *scripts_domain; -	bool core_api_assembly_out_of_sync; -#ifdef TOOLS_ENABLED -	bool editor_api_assembly_out_of_sync; -#endif +	HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;  	GDMonoAssembly *corlib_assembly; -	GDMonoAssembly *core_api_assembly;  	GDMonoAssembly *project_assembly;  #ifdef TOOLS_ENABLED -	GDMonoAssembly *editor_api_assembly;  	GDMonoAssembly *tools_assembly;  	GDMonoAssembly *tools_project_editor_assembly;  #endif -	HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies; - -	UnhandledExceptionPolicy unhandled_exception_policy; +	LoadedApiAssembly core_api_assembly; +	LoadedApiAssembly editor_api_assembly; -	void _domain_assemblies_cleanup(uint32_t p_domain_id); +	typedef bool (*CoreApiAssemblyLoadedCallback)();  	bool _are_api_assemblies_out_of_sync(); +	bool _temp_domain_load_are_assemblies_out_of_sync(const String &p_config); + +	bool _load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly); +#ifdef TOOLS_ENABLED +	bool _load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly); +#endif + +	static bool _on_core_api_assembly_loaded();  	bool _load_corlib_assembly(); -	bool _load_core_api_assembly();  #ifdef TOOLS_ENABLED -	bool _load_editor_api_assembly();  	bool _load_tools_assemblies();  #endif  	bool _load_project_assembly(); -	bool _try_load_api_assemblies(); +	bool _try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly, +			const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback); +	bool _try_load_api_assemblies_preset();  	void _load_api_assemblies();  	void _install_trace_listener(); @@ -133,6 +147,8 @@ private:  	Error _load_scripts_domain();  	Error _unload_scripts_domain(); +	void _domain_assemblies_cleanup(uint32_t p_domain_id); +  	uint64_t api_core_hash;  #ifdef TOOLS_ENABLED  	uint64_t api_editor_hash; @@ -166,9 +182,21 @@ public:  #endif // TOOLS_ENABLED  #endif // DEBUG_METHODS_ENABLED +	_FORCE_INLINE_ static String get_expected_api_build_config() { +#ifdef TOOLS_ENABLED +		return "Debug"; +#else +#ifdef DEBUG_ENABLED +		return "Debug"; +#else +		return "Release"; +#endif +#endif +	} +  #ifdef TOOLS_ENABLED -	bool copy_prebuilt_api_assembly(APIAssembly::Type p_api_type, const String &p_config); -	String update_api_assemblies_from_prebuilt(); +	bool copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config); +	String update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync = NULL, const bool *p_editor_api_out_of_sync = NULL);  #endif  	static GDMono *get_singleton() { return singleton; } @@ -188,10 +216,10 @@ public:  	_FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }  	_FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; } -	_FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; } +	_FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly.assembly; }  	_FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }  #ifdef TOOLS_ENABLED -	_FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; } +	_FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly.assembly; }  	_FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; }  	_FORCE_INLINE_ GDMonoAssembly *get_tools_project_editor_assembly() const { return tools_project_editor_assembly; }  #endif diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index e385f4c601..6504fbe423 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -550,6 +550,8 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class)  }  MonoDomain *create_domain(const String &p_friendly_name) { +	print_verbose("Mono: Creating domain '" + p_friendly_name + "'..."); +  	MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL);  	if (domain) { diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index 7f36549ae4..4e90a08009 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -1039,7 +1039,7 @@ PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const  	}  	List<PropertyInfo> props; -	ClassDB::get_property_list(_get_base_type(), &props, true); +	ClassDB::get_property_list(_get_base_type(), &props, false);  	for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {  		if (E->get().name == property) {  			PropertyInfo pinfo = PropertyInfo(E->get().type, "value", PROPERTY_HINT_TYPE_STRING, E->get().hint_string); @@ -1808,7 +1808,7 @@ PropertyInfo VisualScriptPropertyGet::get_input_value_port_info(int p_idx) const  PropertyInfo VisualScriptPropertyGet::get_output_value_port_info(int p_idx) const {  	List<PropertyInfo> props; -	ClassDB::get_property_list(_get_base_type(), &props, true); +	ClassDB::get_property_list(_get_base_type(), &props, false);  	for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {  		if (E->get().name == property) {  			return PropertyInfo(E->get().type, "value." + String(index)); diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp index 42d4c5e209..62b818150c 100644 --- a/modules/visual_script/visual_script_property_selector.cpp +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -32,7 +32,7 @@  #include "core/os/keyboard.h"  #include "editor/editor_node.h" -#include "editor_scale.h" +#include "editor/editor_scale.h"  #include "modules/visual_script/visual_script.h"  #include "modules/visual_script/visual_script_builtin_funcs.h"  #include "modules/visual_script/visual_script_flow_control.h" diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h index 13ce9bdca2..405949e8c1 100644 --- a/modules/visual_script/visual_script_property_selector.h +++ b/modules/visual_script/visual_script_property_selector.h @@ -31,8 +31,8 @@  #ifndef VISUALSCRIPT_PROPERTYSELECTOR_H  #define VISUALSCRIPT_PROPERTYSELECTOR_H +#include "editor/editor_help.h"  #include "editor/property_editor.h" -#include "editor_help.h"  #include "scene/gui/rich_text_label.h"  class VisualScriptPropertySelector : public ConfirmationDialog { diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index 067fa6f4b9..67dce172dc 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -95,6 +95,11 @@ public class GodotLib {  	public static native void touch(int what, int pointer, int howmany, int[] arr);  	/** +	 * Forward hover events from the main thread to the GL thread. +	 */ +	public static native void hover(int type, int x, int y); + +	/**  	 * Forward accelerometer sensor events from the main thread to the GL thread.  	 * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)  	 */ diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 2beca67922..2756ca6c83 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -188,7 +188,18 @@ public class GodotInputHandler implements InputDeviceListener {  				}  				return true;  			} -		}; +		} else if ((event.getSource() & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS) { +			final int x = Math.round(event.getX()); +			final int y = Math.round(event.getY()); +			final int type = event.getAction(); +			queueEvent(new Runnable() { +				@Override +				public void run() { +					GodotLib.hover(type, x, y); +				} +			}); +			return true; +		}  		return false;  	} diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 30676783db..7daea19961 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -826,6 +826,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jo  	*/  } +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jobject obj, jint p_type, jint p_x, jint p_y) { +	if (step == 0) +		return; + +	os_android->process_hover(p_type, Point2(p_x, p_y)); +} +  /*   * Android Key codes.   */ diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 11bda94f8d..a564bbd4a1 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -45,6 +45,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj);  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj);  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jobject obj, jint p_type, jint p_x, jint p_y);  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed);  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed);  JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 49ab0ea84a..91bd6cbdd2 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -477,6 +477,23 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos>  	}  } +void OS_Android::process_hover(int p_type, Point2 p_pos) { +	// https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER +	switch (p_type) { +		case 7: // hover move +		case 9: // hover enter +		case 10: { // hover exit +			Ref<InputEventMouseMotion> ev; +			ev.instance(); +			ev->set_position(p_pos); +			ev->set_global_position(p_pos); +			ev->set_relative(p_pos - hover_prev_pos); +			input->parse_input_event(ev); +			hover_prev_pos = p_pos; +		} break; +	} +} +  void OS_Android::process_accelerometer(const Vector3 &p_accelerometer) {  	input->set_accelerometer(p_accelerometer); diff --git a/platform/android/os_android.h b/platform/android/os_android.h index a17941f7c0..9bad9b2e01 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -70,6 +70,7 @@ public:  private:  	Vector<TouchPos> touch; +	Point2 hover_prev_pos; // needed to calculate the relative position on hover events  	bool use_gl2;  	bool use_apk_expansion; @@ -186,6 +187,7 @@ public:  	void process_magnetometer(const Vector3 &p_magnetometer);  	void process_gyroscope(const Vector3 &p_gyroscope);  	void process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points); +	void process_hover(int p_type, Point2 p_pos);  	void process_joy_event(JoypadEvent p_event);  	void process_event(Ref<InputEvent> p_event);  	void init_video_mode(int p_video_width, int p_video_height); diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 69dd038709..4de98f7039 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -29,8 +29,8 @@  /*************************************************************************/  #include "core/io/zip_io.h" +#include "editor/editor_export.h"  #include "editor/editor_node.h" -#include "editor_export.h"  #include "main/splash.gen.h"  #include "platform/javascript/logo.gen.h"  #include "platform/javascript/run_icon.gen.h" diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index c8ecbd5a2d..9226aea369 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -132,10 +132,12 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)  	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false));  #ifdef OSX_ENABLED -	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity"), "")); +	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false)); +	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));  	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));  	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));  	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), "")); +	r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray()));  #endif  	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); @@ -375,16 +377,27 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese  		args.push_back("--entitlements");  		args.push_back(p_preset->get("codesign/entitlements"));  	} + +	PoolStringArray user_args = p_preset->get("codesign/custom_options"); +	for (int i = 0; i < user_args.size(); i++) { +		String user_arg = user_args[i].strip_edges(); +		if (!user_arg.empty()) { +			args.push_back(user_arg); +		} +	} +  	args.push_back("-s");  	args.push_back(p_preset->get("codesign/identity")); +  	args.push_back("-v"); /* provide some more feedback */ +  	args.push_back(p_path);  	String str;  	Error err = OS::get_singleton()->execute("codesign", args, true, NULL, &str, NULL, true);  	ERR_FAIL_COND_V(err != OK, err); -	print_line("codesign: " + str); +	print_line("codesign (" + p_path + "): " + str);  	if (str.find("no identity found") != -1) {  		EditorNode::add_io_error("codesign: no identity found");  		return FAILED; @@ -663,20 +676,20 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p  			err = save_pack(p_preset, pack_path, &shared_objects);  			// see if we can code sign our new package -			String identity = p_preset->get("codesign/identity"); +			bool sign_enabled = p_preset->get("codesign/enable");  			if (err == OK) {  				DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);  				for (int i = 0; i < shared_objects.size(); i++) {  					err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); -					if (err == OK && identity != "") { +					if (err == OK && sign_enabled) {  						err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());  					}  				}  				memdelete(da);  			} -			if (err == OK && identity != "") { +			if (err == OK && sign_enabled) {  				if (ep.step("Code signing bundle", 2)) {  					return ERR_SKIP;  				} diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 827daa2d58..3abb05b494 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -31,6 +31,7 @@  #include "core/os/file_access.h"  #include "core/os/os.h"  #include "editor/editor_export.h" +#include "editor/editor_node.h"  #include "editor/editor_settings.h"  #include "platform/windows/logo.gen.h" @@ -38,11 +39,22 @@ static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start,  class EditorExportPlatformWindows : public EditorExportPlatformPC { +	Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); +  public:  	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); +	virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);  	virtual void get_export_options(List<ExportOption> *r_options);  }; +Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { +	if (p_preset->get("codesign/enable")) { +		return _code_sign(p_preset, p_path); +	} else { +		return OK; +	} +} +  Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {  	Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags); @@ -133,12 +145,28 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>  	OS::get_singleton()->execute(wine_path, args, true);  #endif -	return OK; +	if (p_preset->get("codesign/enable") && err == OK) { +		err = _code_sign(p_preset, p_path); +	} + +	return err;  }  void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) {  	EditorExportPlatformPC::get_export_options(r_options); +	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false)); +#ifdef WINDOWS_ENABLED +	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0)); +#endif +	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), "")); +	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), "")); +	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); +	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), "")); +	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1)); +	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), "")); +	r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray())); +  	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), ""));  	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));  	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); @@ -149,11 +177,164 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio  	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), ""));  } +Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) { +	List<String> args; + +#ifdef WINDOWS_ENABLED +	String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool"); +	if (signtool_path != String() && !FileAccess::exists(signtool_path)) { +		ERR_PRINTS("Could not find signtool executable at " + signtool_path + ", aborting."); +		return ERR_FILE_NOT_FOUND; +	} +	if (signtool_path == String()) { +		signtool_path = "signtool"; // try to run signtool from PATH +	} +#else +	String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode"); +	if (signtool_path != String() && !FileAccess::exists(signtool_path)) { +		ERR_PRINTS("Could not find osslsigncode executable at " + signtool_path + ", aborting."); +		return ERR_FILE_NOT_FOUND; +	} +	if (signtool_path == String()) { +		signtool_path = "osslsigncode"; // try to run signtool from PATH +	} +#endif + +	args.push_back("sign"); + +	//identity +#ifdef WINDOWS_ENABLED +	int id_type = p_preset->get("codesign/identity_type"); +	if (id_type == 0) { //auto select +		args.push_back("/a"); +	} else if (id_type == 1) { //pkcs12 +		if (p_preset->get("codesign/identity") != "") { +			args.push_back("/f"); +			args.push_back(p_preset->get("codesign/identity")); +		} else { +			EditorNode::add_io_error("codesign: no identity found"); +			return FAILED; +		} +	} else if (id_type == 2) { //Windows certificate store +		if (p_preset->get("codesign/identity") != "") { +			args.push_back("/sha1"); +			args.push_back(p_preset->get("codesign/identity")); +		} else { +			EditorNode::add_io_error("codesign: no identity found"); +			return FAILED; +		} +	} else { +		EditorNode::add_io_error("codesign: invalid identity type"); +		return FAILED; +	} +#else +	if (p_preset->get("codesign/identity") != "") { +		args.push_back("-pkcs12"); +		args.push_back(p_preset->get("codesign/identity")); +	} else { +		EditorNode::add_io_error("codesign: no identity found"); +		return FAILED; +	} +#endif + +	//password +	if (p_preset->get("codesign/password") != "") { +#ifdef WINDOWS_ENABLED +		args.push_back("/p"); +#else +		args.push_back("-pass"); +#endif +		args.push_back(p_preset->get("codesign/password")); +	} + +	//timestamp +	if (p_preset->get("codesign/timestamp")) { +		if (p_preset->get("codesign/timestamp_server") != "") { +#ifdef WINDOWS_ENABLED +			args.push_back("/tr"); +			args.push_back(p_preset->get("codesign/timestamp_server_url")); +			args.push_back("/td"); +			if ((int)p_preset->get("codesign/digest_algorithm") == 0) { +				args.push_back("sha1"); +			} else { +				args.push_back("sha256"); +			} +#else +			args.push_back("-ts"); +			args.push_back(p_preset->get("codesign/timestamp_server_url")); +#endif +		} else { +			EditorNode::add_io_error("codesign: invalid timestamp server"); +			return FAILED; +		} +	} + +	//digest +#ifdef WINDOWS_ENABLED +	args.push_back("/fd"); +#else +	args.push_back("-h"); +#endif +	if ((int)p_preset->get("codesign/digest_algorithm") == 0) { +		args.push_back("sha1"); +	} else { +		args.push_back("sha256"); +	} + +	//description +	if (p_preset->get("codesign/description") != "") { +#ifdef WINDOWS_ENABLED +		args.push_back("/d"); +#else +		args.push_back("-n"); +#endif +		args.push_back(p_preset->get("codesign/description")); +	} + +	//user options +	PoolStringArray user_args = p_preset->get("codesign/custom_options"); +	for (int i = 0; i < user_args.size(); i++) { +		String user_arg = user_args[i].strip_edges(); +		if (!user_arg.empty()) { +			args.push_back(user_arg); +		} +	} + +#ifndef WINDOWS_ENABLED +	args.push_back("-in"); +#endif +	args.push_back(p_path); +#ifndef WINDOWS_ENABLED +	args.push_back("-out"); +	args.push_back(p_path); +#endif + +	String str; +	Error err = OS::get_singleton()->execute(signtool_path, args, true, NULL, &str, NULL, true); +	ERR_FAIL_COND_V(err != OK, err); + +	print_line("codesign (" + p_path + "): " + str); +#ifndef WINDOWS_ENABLED +	if (str.find("SignTool Error") != -1) { +#else +	if (str.find("Failed") != -1) { +#endif +		return FAILED; +	} + +	return OK; +} +  void register_windows_exporter() {  	EDITOR_DEF("export/windows/rcedit", "");  	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); -#ifndef WINDOWS_ENABLED +#ifdef WINDOWS_ENABLED +	EDITOR_DEF("export/windows/signtool", ""); +	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/signtool", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); +#else +	EDITOR_DEF("export/windows/osslsigncode", ""); +	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/osslsigncode", PROPERTY_HINT_GLOBAL_FILE));  	// On non-Windows we need WINE to run rcedit  	EDITOR_DEF("export/windows/wine", "");  	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/wine", PROPERTY_HINT_GLOBAL_FILE)); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index c9ba51bafb..173214dfe4 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -955,6 +955,7 @@ void TileMap::update_bitmask_region(const Vector2 &p_start, const Vector2 &p_end  void TileMap::update_cell_bitmask(int p_x, int p_y) { +	ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot update cell bitmask if Tileset is not open.");  	PosKey p(p_x, p_y);  	Map<PosKey, Cell>::Element *E = tile_map.find(p);  	if (E != NULL) { diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index ffe011e5f7..96b62b97f9 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -35,8 +35,8 @@  #include "core/os/os.h"  #ifdef TOOLS_ENABLED -#include "editor_scale.h" -#include "editor_settings.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h"  #endif  #include "scene/main/viewport.h" diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index d304a37f82..154e67b6f3 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -44,7 +44,7 @@ void GridContainer::_notification(int p_what) {  			int hsep = get_constant("hseparation");  			int vsep = get_constant("vseparation");  			int max_col = MIN(get_child_count(), columns); -			int max_row = get_child_count() / columns; +			int max_row = ceil((float)get_child_count() / (float)columns);  			// Compute the per-column/per-row data.  			int valid_controls_index = 0; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 4d06bee0d4..1a0539effa 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -926,7 +926,7 @@ void ItemList::_notification(int p_what) {  				current_columns = max_columns;  			while (true) { -				//repeat util all fits +				//repeat until all fits  				bool all_fit = true;  				Vector2 ofs;  				int col = 0; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index a7c6c5ccab..08faaf7d45 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -79,7 +79,7 @@ Size2 PopupMenu::get_minimum_size() const {  		if (items[i].checkable_type)  			has_check = true; -		String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text; +		String text = items[i].xl_text;  		size.width += font->get_string_size(text).width;  		if (i > 0)  			size.height += vseparation; @@ -519,7 +519,7 @@ void PopupMenu::_notification(int p_what) {  					hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation)));  				} -				String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text; +				String text = items[i].xl_text;  				item_ofs.x += items[i].h_ofs;  				if (items[i].separator) { @@ -627,63 +627,50 @@ void PopupMenu::_notification(int p_what) {  	}  } -void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) { +/* Methods to add items with or without icon, checkbox, shortcut. + * Be sure to keep them in sync when adding new properties in the Item struct. + */ -	Item item; -	item.icon = p_icon; -	item.text = p_label; -	item.xl_text = tr(p_label); +#define ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel) \ +	item.text = p_label;                              \ +	item.xl_text = tr(p_label);                       \ +	item.id = p_id == -1 ? items.size() : p_id;       \  	item.accel = p_accel; -	item.id = p_id; -	items.push_back(item); -	update(); -	minimum_size_changed(); -} +  void PopupMenu::add_item(const String &p_label, int p_id, uint32_t p_accel) {  	Item item; -	item.text = p_label; -	item.xl_text = tr(p_label); -	item.accel = p_accel; -	item.id = p_id == -1 ? items.size() : p_id; +	ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);  	items.push_back(item);  	update();  	minimum_size_changed();  } -void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) { +void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {  	Item item; -	item.text = p_label; -	item.xl_text = tr(p_label); -	item.id = p_id; -	item.submenu = p_submenu; +	ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); +	item.icon = p_icon;  	items.push_back(item);  	update();  	minimum_size_changed();  } -void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) { +void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel) {  	Item item; -	item.icon = p_icon; -	item.text = p_label; -	item.xl_text = tr(p_label); -	item.accel = p_accel; -	item.id = p_id; +	ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel);  	item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;  	items.push_back(item);  	update();  	minimum_size_changed();  } -void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel) { +void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {  	Item item; -	item.text = p_label; -	item.xl_text = tr(p_label); -	item.accel = p_accel; -	item.id = p_id == -1 ? items.size() : p_id; +	ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); +	item.icon = p_icon;  	item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;  	items.push_back(item);  	update(); @@ -692,63 +679,59 @@ void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel  void PopupMenu::add_radio_check_item(const String &p_label, int p_id, uint32_t p_accel) { -	add_check_item(p_label, p_id, p_accel); -	items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; +	Item item; +	ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); +	item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; +	items.push_back(item);  	update();  	minimum_size_changed();  }  void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) { -	add_icon_check_item(p_icon, p_label, p_id, p_accel); -	items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; +	Item item; +	ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); +	item.icon = p_icon; +	item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; +	items.push_back(item);  	update();  	minimum_size_changed();  } -void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { - -	ERR_FAIL_COND(p_shortcut.is_null()); - -	_ref_shortcut(p_shortcut); +void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id, uint32_t p_accel) {  	Item item; -	item.id = p_id; -	item.icon = p_icon; -	item.shortcut = p_shortcut; -	item.shortcut_is_global = p_global; +	ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); +	item.max_states = p_max_states; +	item.state = p_default_state;  	items.push_back(item);  	update();  	minimum_size_changed();  } -void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { - -	ERR_FAIL_COND(p_shortcut.is_null()); +#define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global)                           \ +	ERR_FAIL_COND_MSG(p_shortcut.is_null(), "Cannot add item with invalid ShortCut."); \ +	_ref_shortcut(p_shortcut);                                                         \ +	item.text = p_shortcut->get_name();                                                \ +	item.xl_text = tr(item.text);                                                      \ +	item.id = p_id == -1 ? items.size() : p_id;                                        \ +	item.shortcut = p_shortcut;                                                        \ +	item.shortcut_is_global = p_global; -	_ref_shortcut(p_shortcut); +void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {  	Item item; -	item.id = p_id; -	item.shortcut = p_shortcut; -	item.shortcut_is_global = p_global; +	ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);  	items.push_back(item);  	update();  	minimum_size_changed();  } -void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { - -	ERR_FAIL_COND(p_shortcut.is_null()); - -	_ref_shortcut(p_shortcut); +void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {  	Item item; -	item.id = p_id; -	item.shortcut = p_shortcut; -	item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; +	ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);  	item.icon = p_icon; -	item.shortcut_is_global = p_global;  	items.push_back(item);  	update();  	minimum_size_changed(); @@ -756,14 +739,19 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<Sh  void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { -	ERR_FAIL_COND(p_shortcut.is_null()); +	Item item; +	ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); +	item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; +	items.push_back(item); +	update(); +	minimum_size_changed(); +} -	_ref_shortcut(p_shortcut); +void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {  	Item item; -	item.id = p_id; -	item.shortcut = p_shortcut; -	item.shortcut_is_global = p_global; +	ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); +	item.icon = p_icon;  	item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;  	items.push_back(item);  	update(); @@ -772,26 +760,42 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bo  void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { -	add_check_shortcut(p_shortcut, p_id, p_global); -	items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; +	Item item; +	ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); +	item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; +	items.push_back(item);  	update();  	minimum_size_changed();  } -void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id, uint32_t p_accel) { +void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { + +	Item item; +	ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); +	item.icon = p_icon; +	item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; +	items.push_back(item); +	update(); +	minimum_size_changed(); +} + +void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) {  	Item item;  	item.text = p_label;  	item.xl_text = tr(p_label); -	item.accel = p_accel; -	item.id = p_id; -	item.max_states = p_max_states; -	item.state = p_default_state; +	item.id = p_id == -1 ? items.size() : p_id; +	item.submenu = p_submenu;  	items.push_back(item);  	update();  	minimum_size_changed();  } +#undef ITEM_SETUP_WITH_ACCEL +#undef ITEM_SETUP_WITH_SHORTCUT + +/* Methods to modify existing items. */ +  void PopupMenu::set_item_text(int p_idx, const String &p_text) {  	ERR_FAIL_INDEX(p_idx, items.size()); @@ -1380,18 +1384,24 @@ void PopupMenu::clear_autohide_areas() {  void PopupMenu::_bind_methods() {  	ClassDB::bind_method(D_METHOD("_gui_input"), &PopupMenu::_gui_input); -	ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_item, DEFVAL(-1), DEFVAL(0)); +  	ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0)); -	ClassDB::bind_method(D_METHOD("add_icon_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_check_item, DEFVAL(-1), DEFVAL(0)); +	ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_item, DEFVAL(-1), DEFVAL(0));  	ClassDB::bind_method(D_METHOD("add_check_item", "label", "id", "accel"), &PopupMenu::add_check_item, DEFVAL(-1), DEFVAL(0)); +	ClassDB::bind_method(D_METHOD("add_icon_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_check_item, DEFVAL(-1), DEFVAL(0));  	ClassDB::bind_method(D_METHOD("add_radio_check_item", "label", "id", "accel"), &PopupMenu::add_radio_check_item, DEFVAL(-1), DEFVAL(0)); -	ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1)); +	ClassDB::bind_method(D_METHOD("add_icon_radio_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_radio_check_item, DEFVAL(-1), DEFVAL(0)); + +	ClassDB::bind_method(D_METHOD("add_multistate_item", "label", "max_states", "default_state", "id", "accel"), &PopupMenu::add_multistate_item, DEFVAL(0), DEFVAL(-1), DEFVAL(0)); -	ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false));  	ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false)); -	ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false)); +	ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false));  	ClassDB::bind_method(D_METHOD("add_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_check_shortcut, DEFVAL(-1), DEFVAL(false)); +	ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false));  	ClassDB::bind_method(D_METHOD("add_radio_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_radio_check_shortcut, DEFVAL(-1), DEFVAL(false)); +	ClassDB::bind_method(D_METHOD("add_icon_radio_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_radio_check_shortcut, DEFVAL(-1), DEFVAL(false)); + +	ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1));  	ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &PopupMenu::set_item_text);  	ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &PopupMenu::set_item_icon); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 8bfe8fc607..8c33178b09 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -120,21 +120,23 @@ protected:  	static void _bind_methods();  public: -	void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);  	void add_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0); -	void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0); +	void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);  	void add_check_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0); +	void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);  	void add_radio_check_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0);  	void add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0); -	void add_submenu_item(const String &p_label, const String &p_submenu, int p_id = -1); -	void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); +	void add_multistate_item(const String &p_label, int p_max_states, int p_default_state = 0, int p_id = -1, uint32_t p_accel = 0); +  	void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); -	void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); +	void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);  	void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); +	void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);  	void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); +	void add_icon_radio_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); -	void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id = -1, uint32_t p_accel = 0); +	void add_submenu_item(const String &p_label, const String &p_submenu, int p_id = -1);  	void set_item_text(int p_idx, const String &p_text);  	void set_item_icon(int p_idx, const Ref<Texture> &p_icon); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 39c5759871..52ef225364 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1465,18 +1465,19 @@ void Viewport::_gui_show_tooltip() {  	rp->add_child(gui.tooltip_popup);  	gui.tooltip_popup->force_parent_owned();  	gui.tooltip_popup->set_as_toplevel(true); -	//gui.tooltip_popup->hide(); +	if (gui.tooltip) // Avoids crash when rapidly switching controls. +		gui.tooltip_popup->set_scale(gui.tooltip->get_global_transform().get_scale());  	Point2 tooltip_offset = ProjectSettings::get_singleton()->get("display/mouse_cursor/tooltip_position_offset");  	Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_minimum_size());  	Rect2 vr = gui.tooltip_popup->get_viewport_rect(); -	if (r.size.x + r.position.x > vr.size.x) -		r.position.x = vr.size.x - r.size.x; +	if (r.size.x * gui.tooltip_popup->get_scale().x + r.position.x > vr.size.x) +		r.position.x = vr.size.x - r.size.x * gui.tooltip_popup->get_scale().x;  	else if (r.position.x < 0)  		r.position.x = 0; -	if (r.size.y + r.position.y > vr.size.y) -		r.position.y = vr.size.y - r.size.y; +	if (r.size.y * gui.tooltip_popup->get_scale().y + r.position.y > vr.size.y) +		r.position.y = vr.size.y - r.size.y * gui.tooltip_popup->get_scale().y;  	else if (r.position.y < 0)  		r.position.y = 0; diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index e4a64a1de1..b5354bc3e2 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -197,16 +197,14 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start)  			| 4 | 8 | <- current pixel (curx,cury)  			+---+---+  			*/ -			//NOTE: due to the way we pick points from texture, rect needs to be smaller, otherwise it goes outside 1 pixel -			Rect2i fixed_rect = Rect2i(rect.position, rect.size - Size2i(2, 2));  			Point2i tl = Point2i(curx - 1, cury - 1); -			sv += (fixed_rect.has_point(tl) && get_bit(tl)) ? 1 : 0; +			sv += (rect.has_point(tl) && get_bit(tl)) ? 1 : 0;  			Point2i tr = Point2i(curx, cury - 1); -			sv += (fixed_rect.has_point(tr) && get_bit(tr)) ? 2 : 0; +			sv += (rect.has_point(tr) && get_bit(tr)) ? 2 : 0;  			Point2i bl = Point2i(curx - 1, cury); -			sv += (fixed_rect.has_point(bl) && get_bit(bl)) ? 4 : 0; +			sv += (rect.has_point(bl) && get_bit(bl)) ? 4 : 0;  			Point2i br = Point2i(curx, cury); -			sv += (fixed_rect.has_point(br) && get_bit(br)) ? 8 : 0; +			sv += (rect.has_point(br) && get_bit(br)) ? 8 : 0;  			ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>());  		} @@ -300,15 +298,15 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start)  				+---+---+  				| 4 |   |  				+---+---+ -				this normally go RIGHT, but if its coming from UP, it should go LEFT +				this normally go RIGHT, but if its coming from RIGHT, it should go LEFT  				*/  				if (case6s.has(Point2i(curx, cury))) { -					//found, so we go down, and delete from case6s; +					//found, so we go left, and delete from case6s;  					stepx = -1;  					stepy = 0;  					case6s.erase(Point2i(curx, cury));  				} else { -					//not found, we go up, and add to case6s; +					//not found, we go right, and add to case6s;  					stepx = 1;  					stepy = 0;  					case6s.insert(Point2i(curx, cury)); @@ -510,12 +508,19 @@ Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, fl  		for (int j = r.position.x; j < r.position.x + r.size.width; j++) {  			if (!fill->get_bit(Point2(j, i)) && get_bit(Point2(j, i))) { +				fill_bits(this, fill, Point2i(j, i), r); +  				Vector<Vector2> polygon = _march_square(r, Point2i(j, i));  				print_verbose("BitMap: Pre reduce: " + itos(polygon.size()));  				polygon = reduce(polygon, r, p_epsilon);  				print_verbose("BitMap: Post reduce: " + itos(polygon.size())); + +				if (polygon.size() < 3) { +					print_verbose("Invalid polygon, skipped"); +					continue; +				} +  				polygons.push_back(polygon); -				fill_bits(this, fill, Point2i(j, i), r);  			}  		}  	} @@ -525,6 +530,13 @@ Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, fl  void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { +	if (p_pixels == 0) { +		return; +	} + +	bool bit_value = (p_pixels > 0) ? true : false; +	p_pixels = Math::abs(p_pixels); +  	Rect2i r = Rect2i(0, 0, width, height).clip(p_rect);  	Ref<BitMap> copy; @@ -534,7 +546,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) {  	for (int i = r.position.y; i < r.position.y + r.size.height; i++) {  		for (int j = r.position.x; j < r.position.x + r.size.width; j++) { -			if (copy->get_bit(Point2(j, i))) +			if (bit_value == get_bit(Point2(j, i)))  				continue;  			bool found = false; @@ -542,16 +554,21 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) {  			for (int y = i - p_pixels; y <= i + p_pixels; y++) {  				for (int x = j - p_pixels; x <= j + p_pixels; x++) { -					if (x < p_rect.position.x || x >= p_rect.position.x + p_rect.size.x) -						continue; -					if (y < p_rect.position.y || y >= p_rect.position.y + p_rect.size.y) -						continue; +					bool outside = false; + +					if ((x < p_rect.position.x) || (x >= p_rect.position.x + p_rect.size.x) || (y < p_rect.position.y) || (y >= p_rect.position.y + p_rect.size.y)) { +						// outside of rectangle counts as bit not set +						if (!bit_value) +							outside = true; +						else +							continue; +					}  					float d = Point2(j, i).distance_to(Point2(x, y)) - CMP_EPSILON;  					if (d > p_pixels)  						continue; -					if (copy->get_bit(Point2(x, y))) { +					if (outside || (bit_value == copy->get_bit(Point2(x, y)))) {  						found = true;  						break;  					} @@ -561,12 +578,17 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) {  			}  			if (found) { -				set_bit(Point2(j, i), true); +				set_bit(Point2(j, i), bit_value);  			}  		}  	}  } +void BitMap::shrink_mask(int p_pixels, const Rect2 &p_rect) { + +	grow_mask(-p_pixels, p_rect); +} +  Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const {  	Vector<Vector<Vector2> > result = clip_opaque_to_polygons(p_rect, p_epsilon); diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index daf24affb1..b062dd7376 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -67,6 +67,7 @@ public:  	void resize(const Size2 &p_new_size);  	void grow_mask(int p_pixels, const Rect2 &p_rect); +	void shrink_mask(int p_pixels, const Rect2 &p_rect);  	void blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap);  	Ref<Image> convert_to_image() const; diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 4453032f67..9ee7c2936e 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -40,16 +40,22 @@ bool StyleBox::test_mask(const Point2 &p_point, const Rect2 &p_rect) const {  void StyleBox::set_default_margin(Margin p_margin, float p_value) { +	ERR_FAIL_INDEX((int)p_margin, 4); +  	margin[p_margin] = p_value;  	emit_changed();  }  float StyleBox::get_default_margin(Margin p_margin) const { +	ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); +  	return margin[p_margin];  }  float StyleBox::get_margin(Margin p_margin) const { +	ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); +  	if (margin[p_margin] < 0)  		return get_style_margin(p_margin);  	else @@ -157,11 +163,15 @@ void StyleBoxTexture::set_margin_size(Margin p_margin, float p_size) {  }  float StyleBoxTexture::get_margin_size(Margin p_margin) const { +	ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); +  	return margin[p_margin];  }  float StyleBoxTexture::get_style_margin(Margin p_margin) const { +	ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); +  	return margin[p_margin];  } @@ -250,6 +260,7 @@ Rect2 StyleBoxTexture::get_region_rect() const {  void StyleBoxTexture::set_h_axis_stretch_mode(AxisStretchMode p_mode) { +	ERR_FAIL_INDEX((int)p_mode, 3);  	axis_h = p_mode;  	emit_changed();  } @@ -261,6 +272,7 @@ StyleBoxTexture::AxisStretchMode StyleBoxTexture::get_h_axis_stretch_mode() cons  void StyleBoxTexture::set_v_axis_stretch_mode(AxisStretchMode p_mode) { +	ERR_FAIL_INDEX((int)p_mode, 3);  	axis_v = p_mode;  	emit_changed();  } @@ -391,11 +403,13 @@ int StyleBoxFlat::get_border_width_min() const {  }  void StyleBoxFlat::set_border_width(Margin p_margin, int p_width) { +	ERR_FAIL_INDEX((int)p_margin, 4);  	border_width[p_margin] = p_width;  	emit_changed();  }  int StyleBoxFlat::get_border_width(Margin p_margin) const { +	ERR_FAIL_INDEX_V((int)p_margin, 4, 0);  	return border_width[p_margin];  } @@ -437,15 +451,19 @@ int StyleBoxFlat::get_corner_radius_min() const {  void StyleBoxFlat::set_corner_radius(const Corner p_corner, const int radius) { +	ERR_FAIL_INDEX((int)p_corner, 4);  	corner_radius[p_corner] = radius;  	emit_changed();  }  int StyleBoxFlat::get_corner_radius(const Corner p_corner) const { + +	ERR_FAIL_INDEX_V((int)p_corner, 4, 0);  	return corner_radius[p_corner];  }  void StyleBoxFlat::set_expand_margin_size(Margin p_expand_margin, float p_size) { +	ERR_FAIL_INDEX((int)p_expand_margin, 4);  	expand_margin[p_expand_margin] = p_size;  	emit_changed();  } @@ -468,6 +486,7 @@ void StyleBoxFlat::set_expand_margin_size_all(float p_expand_margin_size) {  float StyleBoxFlat::get_expand_margin_size(Margin p_expand_margin) const { +	ERR_FAIL_INDEX_V((int)p_expand_margin, 4, 0.0);  	return expand_margin[p_expand_margin];  }  void StyleBoxFlat::set_draw_center(bool p_enabled) { @@ -814,6 +833,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {  }  float StyleBoxFlat::get_style_margin(Margin p_margin) const { +	ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0);  	return border_width[p_margin];  }  void StyleBoxFlat::_bind_methods() { @@ -997,6 +1017,7 @@ void StyleBoxLine::_bind_methods() {  	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");  }  float StyleBoxLine::get_style_margin(Margin p_margin) const { +	ERR_FAIL_INDEX_V((int)p_margin, 4, thickness);  	return thickness;  }  Size2 StyleBoxLine::get_center_size() const { diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 58bbf86241..cd77c8a850 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1067,7 +1067,16 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui  				VisualShaderNodeUniform *uniform = (VisualShaderNodeUniform *)graph[type].nodes[from_node].node.ptr();  				if (uniform) { -					inputs[i] = uniform->get_uniform_name(); +					inputs[i] = ""; +					switch (uniform->get_uniform_type()) { +						case VisualShaderNodeUniform::UTYPE_CUBEMAP: +							inputs[i] += "cube_"; +							break; +						case VisualShaderNodeUniform::UTYPE_SAMPLER2D: +							inputs[i] += "s2d_"; +							break; +					} +					inputs[i] += uniform->get_uniform_name();  				} else {  					inputs[i] = "";  				} @@ -1349,7 +1358,6 @@ void VisualShader::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_valid_node_id", "type"), &VisualShader::get_valid_node_id);  	ClassDB::bind_method(D_METHOD("remove_node", "type", "id"), &VisualShader::remove_node); -	ClassDB::bind_method(D_METHOD("rebuild"), &VisualShader::rebuild);  	ClassDB::bind_method(D_METHOD("is_node_connection", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection);  	ClassDB::bind_method(D_METHOD("can_connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection); @@ -1973,7 +1981,16 @@ void VisualShaderNodeUniform::_bind_methods() {  	ADD_PROPERTY(PropertyInfo(Variant::STRING, "uniform_name"), "set_uniform_name", "get_uniform_name");  } +int VisualShaderNodeUniform::get_uniform_type() const { +	return (int)uniform_type; +} + +void VisualShaderNodeUniform::set_uniform_type(int p_type) { +	uniform_type = (UniformType)p_type; +} +  VisualShaderNodeUniform::VisualShaderNodeUniform() { +	uniform_type = UTYPE_NONE;  }  ////////////// GroupBase diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 8b6b659836..016213c399 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -353,7 +353,16 @@ public:  class VisualShaderNodeUniform : public VisualShaderNode {  	GDCLASS(VisualShaderNodeUniform, VisualShaderNode); +public: +	enum UniformType { +		UTYPE_NONE, +		UTYPE_CUBEMAP, +		UTYPE_SAMPLER2D, +	}; + +private:  	String uniform_name; +	UniformType uniform_type;  protected:  	static void _bind_methods(); @@ -362,6 +371,9 @@ public:  	void set_uniform_name(const String &p_name);  	String get_uniform_name() const; +	int get_uniform_type() const; +	void set_uniform_type(int p_type); +  	VisualShaderNodeUniform();  }; diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 2e58c512b8..c56d5d7213 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -453,7 +453,12 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:  		String code;  		if (p_input_vars[0] == String()) { // Use UV by default. -			code += "\tvec4 " + id + "_read = texture( " + id + " , UV.xy );\n"; +			if (p_input_vars[1] == String()) { +				code += "\tvec4 " + id + "_read = texture( " + id + " , UV.xy );\n"; +			} else { +				code += "\tvec4 " + id + "_read = textureLod( " + id + " , UV.xy , " + p_input_vars[1] + " );\n"; +			} +  		} else if (p_input_vars[1] == String()) {  			//no lod  			code += "\tvec4 " + id + "_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n"; @@ -468,19 +473,63 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:  	if (source == SOURCE_PORT) {  		String id = p_input_vars[2]; + +		VisualShaderNodeUniform::UniformType utype = VisualShaderNodeUniform::UTYPE_NONE; +		if (id.begins_with("cube_")) { +			utype = VisualShaderNodeUniform::UTYPE_CUBEMAP; +			id = id.substr(5); +		} else if (id.begins_with("s2d_")) { +			utype = VisualShaderNodeUniform::UTYPE_SAMPLER2D; +			id = id.substr(4); +		} +  		String code;  		if (id == String()) {  			code += "\tvec4 " + id + "_tex_read = vec4(0.0);\n";  		} else {  			if (p_input_vars[0] == String()) { // Use UV by default. - -				code += "\tvec4 " + id + "_tex_read = texture( " + id + " , UV.xy );\n"; - +				switch (utype) { +					case VisualShaderNodeUniform::UTYPE_CUBEMAP: +						if (p_input_vars[1] == String()) { +							code += "\tvec4 " + id + "_tex_read = texture( " + id + " , vec3( UV, 0.0 ) );\n"; +						} else { +							code += "\tvec4 " + id + "_tex_read = textureLod( " + id + " , vec3( UV, 0.0 ) , " + p_input_vars[1] + " );\n"; +						} +						break; +					case VisualShaderNodeUniform::UTYPE_SAMPLER2D: +						if (p_input_vars[1] == String()) { +							code += "\tvec4 " + id + "_tex_read = texture( " + id + " , UV.xy );\n"; +						} else { +							code += "\tvec4 " + id + "_tex_read = textureLod( " + id + " , UV.xy , " + p_input_vars[1] + " );\n"; +						} +						break; +					default: +						break; +				}  			} else if (p_input_vars[1] == String()) {  				//no lod -				code += "\tvec4 " + id + "_tex_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n"; + +				switch (utype) { +					case VisualShaderNodeUniform::UTYPE_CUBEMAP: +						code += "\tvec4 " + id + "_tex_read = texture( " + id + " , " + p_input_vars[0] + " );\n"; +						break; +					case VisualShaderNodeUniform::UTYPE_SAMPLER2D: +						code += "\tvec4 " + id + "_tex_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n"; +						break; +					default: +						break; +				}  			} else { -				code += "\tvec4 " + id + "_tex_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; +				switch (utype) { +					case VisualShaderNodeUniform::UTYPE_CUBEMAP: +						code += "\tvec4 " + id + "_tex_read = textureLod( " + id + " , " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; +						break; +					case VisualShaderNodeUniform::UTYPE_SAMPLER2D: +						code += "\tvec4 " + id + "_tex_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; +						break; +					default: +						break; +				}  			}  			code += "\t" + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n"; @@ -494,11 +543,15 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:  		String code = "\t{\n";  		if (p_input_vars[0] == String() || p_for_preview) { // Use UV by default. -			code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , UV.xy, 0.0 );\n"; +			if (p_input_vars[1] == String()) { +				code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , UV.xy , 0.0 );\n"; +			} else { +				code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , UV.xy , " + p_input_vars[1] + ");\n"; +			}  		} else if (p_input_vars[1] == String()) {  			//no lod -			code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy, 0.0 );\n"; +			code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy , 0.0 );\n";  		} else {  			code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n";  		} @@ -514,7 +567,11 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:  		String code = "\t{\n";  		if (p_input_vars[0] == String()) { // Use UV by default. -			code += "\t\tvec4 _tex_read = texture( TEXTURE , UV.xy );\n"; +			if (p_input_vars[1] == String()) { +				code += "\t\tvec4 _tex_read = texture( TEXTURE , UV.xy );\n"; +			} else { +				code += "\t\tvec4 _tex_read = textureLod( TEXTURE , UV.xy , " + p_input_vars[1] + " );\n"; +			}  		} else if (p_input_vars[1] == String()) {  			//no lod @@ -534,7 +591,11 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:  		String code = "\t{\n";  		if (p_input_vars[0] == String()) { // Use UV by default. -			code += "\t\tvec4 _tex_read = texture( NORMAL_TEXTURE , UV.xy );\n"; +			if (p_input_vars[1] == String()) { +				code += "\t\tvec4 _tex_read = texture( NORMAL_TEXTURE , UV.xy );\n"; +			} else { +				code += "\t\tvec4 _tex_read = textureLod( NORMAL_TEXTURE , UV.xy , " + p_input_vars[1] + " );\n"; +			}  		} else if (p_input_vars[1] == String()) {  			//no lod @@ -564,7 +625,11 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:  		String code = "\t{\n";  		if (p_input_vars[0] == String()) { // Use UV by default. -			code += "\t\tfloat _depth = texture( DEPTH_TEXTURE , UV.xy ).r;\n"; +			if (p_input_vars[1] == String()) { +				code += "\t\tfloat _depth = texture( DEPTH_TEXTURE , UV.xy ).r;\n"; +			} else { +				code += "\t\tfloat _depth = textureLod( DEPTH_TEXTURE , UV.xy , " + p_input_vars[1] + " ).r;\n"; +			}  		} else if (p_input_vars[1] == String()) {  			//no lod @@ -738,7 +803,7 @@ Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCubeMap::get_default_t  String VisualShaderNodeCubeMap::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { -	String u = "uniform sampler2DCube " + make_unique_id(p_type, p_id, "cube"); +	String u = "uniform samplerCube " + make_unique_id(p_type, p_id, "cube");  	switch (texture_type) {  		case TYPE_DATA: break;  		case TYPE_COLOR: u += " : hint_albedo"; break; @@ -751,9 +816,13 @@ String VisualShaderNodeCubeMap::generate_code(Shader::Mode p_mode, VisualShader:  	String id = make_unique_id(p_type, p_id, "cube");  	String code; -	if (p_input_vars[0] == String()) { //none bound, do nothing +	if (p_input_vars[0] == String()) { // Use UV by default. -		code += "\tvec4 " + id + "_read = vec4(0.0);\n"; +		if (p_input_vars[1] == String()) { +			code += "\tvec4 " + id + "_read = texture( " + id + " , vec3( UV, 0.0 ) );\n"; +		} else { +			code += "\tvec4 " + id + "_read = textureLod( " + id + " , vec3( UV, 0.0 )" + " , " + p_input_vars[1] + " );\n"; +		}  	} else if (p_input_vars[1] == String()) {  		//no lod @@ -767,6 +836,13 @@ String VisualShaderNodeCubeMap::generate_code(Shader::Mode p_mode, VisualShader:  	return code;  } +String VisualShaderNodeCubeMap::get_input_port_default_hint(int p_port) const { +	if (p_port == 0) { +		return "vec3(UV, 0.0)"; +	} +	return ""; +} +  void VisualShaderNodeCubeMap::set_cube_map(Ref<CubeMap> p_value) {  	cube_map = p_value; @@ -3135,8 +3211,11 @@ String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, Visual  	String id = get_uniform_name();  	String code = "\t{\n";  	if (p_input_vars[0] == String()) { // Use UV by default. - -		code += "\t\tvec4 n_tex_read = texture( " + id + " , UV.xy );\n"; +		if (p_input_vars[1] == String()) { +			code += "\t\tvec4 n_tex_read = texture( " + id + " , UV.xy );\n"; +		} else { +			code += "\t\tvec4 n_tex_read = textureLod( " + id + " , UV.xy , " + p_input_vars[1] + " );\n"; +		}  	} else if (p_input_vars[1] == String()) {  		//no lod  		code += "\t\tvec4 n_tex_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n"; @@ -3205,6 +3284,7 @@ String VisualShaderNodeTextureUniform::get_input_port_default_hint(int p_port) c  VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() {  	texture_type = TYPE_DATA;  	color_default = COLOR_DEFAULT_WHITE; +	set_uniform_type(VisualShaderNodeUniform::UTYPE_SAMPLER2D);  }  ////////////// Texture Uniform (Triplanar) @@ -3314,28 +3394,55 @@ String VisualShaderNodeCubeMapUniform::get_caption() const {  	return "CubeMapUniform";  } +int VisualShaderNodeCubeMapUniform::get_output_port_count() const { +	return 1; +} + +VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_output_port_type(int p_port) const { +	return PORT_TYPE_SAMPLER; +} + +String VisualShaderNodeCubeMapUniform::get_output_port_name(int p_port) const { +	return "sampler"; +} +  int VisualShaderNodeCubeMapUniform::get_input_port_count() const { -	return 2; +	return 0;  }  VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_input_port_type(int p_port) const { -	return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +	return PORT_TYPE_SCALAR;  }  String VisualShaderNodeCubeMapUniform::get_input_port_name(int p_port) const { -	return p_port == 0 ? "normal" : "lod"; +	return "";  } -int VisualShaderNodeCubeMapUniform::get_output_port_count() const { -	return 2; +String VisualShaderNodeCubeMapUniform::get_input_port_default_hint(int p_port) const { +	return "";  } -VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_output_port_type(int p_port) const { -	return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; -} +String VisualShaderNodeCubeMapUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { +	String code = "uniform samplerCube " + get_uniform_name(); -String VisualShaderNodeCubeMapUniform::get_output_port_name(int p_port) const { -	return p_port == 0 ? "rgb" : "alpha"; +	switch (texture_type) { +		case TYPE_DATA: +			if (color_default == COLOR_DEFAULT_BLACK) +				code += " : hint_black;\n"; +			else +				code += ";\n"; +			break; +		case TYPE_COLOR: +			if (color_default == COLOR_DEFAULT_BLACK) +				code += " : hint_black_albedo;\n"; +			else +				code += " : hint_albedo;\n"; +			break; +		case TYPE_NORMALMAP: code += " : hint_normal;\n"; break; +		case TYPE_ANISO: code += " : hint_aniso;\n"; break; +	} + +	return code;  }  String VisualShaderNodeCubeMapUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { @@ -3343,6 +3450,7 @@ String VisualShaderNodeCubeMapUniform::generate_code(Shader::Mode p_mode, Visual  }  VisualShaderNodeCubeMapUniform::VisualShaderNodeCubeMapUniform() { +	set_uniform_type(VisualShaderNodeUniform::UTYPE_CUBEMAP);  }  ////////////// If diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index d5ee990191..350e73408c 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -277,6 +277,7 @@ public:  	virtual int get_input_port_count() const;  	virtual PortType get_input_port_type(int p_port) const;  	virtual String get_input_port_name(int p_port) const; +	virtual String get_input_port_default_hint(int p_port) const;  	virtual int get_output_port_count() const;  	virtual PortType get_output_port_type(int p_port) const; @@ -1412,7 +1413,7 @@ public:  		COLOR_DEFAULT_BLACK  	}; -private: +protected:  	TextureType texture_type;  	ColorDefault color_default; @@ -1471,8 +1472,8 @@ public:  /////////////////////////////////////// -class VisualShaderNodeCubeMapUniform : public VisualShaderNode { -	GDCLASS(VisualShaderNodeCubeMapUniform, VisualShaderNode); +class VisualShaderNodeCubeMapUniform : public VisualShaderNodeTextureUniform { +	GDCLASS(VisualShaderNodeCubeMapUniform, VisualShaderNodeTextureUniform);  public:  	virtual String get_caption() const; @@ -1485,6 +1486,8 @@ public:  	virtual PortType get_output_port_type(int p_port) const;  	virtual String get_output_port_name(int p_port) const; +	virtual String get_input_port_default_hint(int p_port) const; +	virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;  	virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty  	VisualShaderNodeCubeMapUniform(); diff --git a/scene/resources/world.cpp b/scene/resources/world.cpp index 0ca5d7eb36..2c22f45f9d 100644 --- a/scene/resources/world.cpp +++ b/scene/resources/world.cpp @@ -332,7 +332,9 @@ World::World() {  	PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/3d/default_gravity", 9.8));  	PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/3d/default_gravity_vector", Vector3(0, -1, 0)));  	PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/3d/default_linear_damp", 0.1)); +	ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_linear_damp", PropertyInfo(Variant::REAL, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));  	PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1)); +	ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::REAL, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));  #ifdef _3D_DISABLED  	indexer = NULL; diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 13b45f58dc..b5743ad416 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -394,7 +394,9 @@ World2D::World2D() {  	Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/2d/default_gravity", 98));  	Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/2d/default_gravity_vector", Vector2(0, 1)));  	Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/2d/default_linear_damp", 0.1)); -	Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/2d/default_angular_damp", 1)); +	ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/default_linear_damp", PropertyInfo(Variant::REAL, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); +	Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/2d/default_angular_damp", 1.0)); +	ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/default_angular_damp", PropertyInfo(Variant::REAL, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));  	indexer = memnew(SpatialIndexer2D);  } diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index ae99d64eee..5b5ba56ebe 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -5419,7 +5419,7 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct  					if (block->parent_function) {  						if (comp_ident) {  							for (int i = 0; i < block->parent_function->arguments.size(); i++) { -								matches.insert(block->parent_function->arguments[i].name, ScriptCodeCompletionOption::KIND_FUNCTION); +								matches.insert(block->parent_function->arguments[i].name, ScriptCodeCompletionOption::KIND_VARIABLE);  							}  						}  						skip_function = block->parent_function->name; diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index f5a1276c27..ed06a67e4c 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -680,11 +680,22 @@ void VisualServerCanvas::canvas_item_add_texture_rect_region(RID p_item, const R  		rect->flags |= RasterizerCanvas::CANVAS_RECT_FLIP_H;  		rect->rect.size.x = -rect->rect.size.x;  	} +	if (p_src_rect.size.x < 0) { + +		rect->flags ^= RasterizerCanvas::CANVAS_RECT_FLIP_H; +		rect->source.size.x = -rect->source.size.x; +	}  	if (p_rect.size.y < 0) {  		rect->flags |= RasterizerCanvas::CANVAS_RECT_FLIP_V;  		rect->rect.size.y = -rect->rect.size.y;  	} +	if (p_src_rect.size.y < 0) { + +		rect->flags ^= RasterizerCanvas::CANVAS_RECT_FLIP_V; +		rect->source.size.y = -rect->source.size.y; +	} +  	if (p_transpose) {  		rect->flags |= RasterizerCanvas::CANVAS_RECT_TRANSPOSE;  		SWAP(rect->rect.size.x, rect->rect.size.y);  |