diff options
40 files changed, 620 insertions, 196 deletions
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/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/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/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/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/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_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/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 0d388512f4..f54411c72b 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -5359,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/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/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/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_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/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/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/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); |