Merge pull request #60557 from akien-mga/3.x-cherrypicks

This commit is contained in:
Rémi Verschelde 2022-04-27 17:18:57 +02:00 committed by GitHub
commit b974c9816c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 215 additions and 188 deletions

View File

@ -1102,6 +1102,10 @@ void _OS::dump_resources_to_file(const String &p_file) {
OS::get_singleton()->dump_resources_to_file(p_file.utf8().get_data());
}
Error _OS::move_to_trash(const String &p_path) const {
return OS::get_singleton()->move_to_trash(p_path);
}
String _OS::get_user_data_dir() const {
return OS::get_singleton()->get_user_data_dir();
};
@ -1399,6 +1403,7 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_static_memory_peak_usage"), &_OS::get_static_memory_peak_usage);
ClassDB::bind_method(D_METHOD("get_dynamic_memory_usage"), &_OS::get_dynamic_memory_usage);
ClassDB::bind_method(D_METHOD("move_to_trash", "path"), &_OS::move_to_trash);
ClassDB::bind_method(D_METHOD("get_user_data_dir"), &_OS::get_user_data_dir);
ClassDB::bind_method(D_METHOD("get_system_dir", "dir", "shared_storage"), &_OS::get_system_dir, DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_config_dir"), &_OS::get_config_dir);

View File

@ -354,6 +354,7 @@ public:
String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const;
Error move_to_trash(const String &p_path) const;
String get_user_data_dir() const;
String get_config_dir() const;
String get_data_dir() const;

View File

@ -2224,6 +2224,39 @@ Ref<Image> Image::get_rect(const Rect2 &p_area) const {
return img;
}
void Image::_get_clipped_src_and_dest_rects(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest, Rect2i &r_clipped_src_rect, Rect2i &r_clipped_dest_rect) const {
r_clipped_dest_rect.position = p_dest;
r_clipped_src_rect = p_src_rect;
if (r_clipped_src_rect.position.x < 0) {
r_clipped_dest_rect.position.x -= r_clipped_src_rect.position.x;
r_clipped_src_rect.size.x += r_clipped_src_rect.position.x;
r_clipped_src_rect.position.x = 0;
}
if (r_clipped_src_rect.position.y < 0) {
r_clipped_dest_rect.position.y -= r_clipped_src_rect.position.y;
r_clipped_src_rect.size.y += r_clipped_src_rect.position.y;
r_clipped_src_rect.position.y = 0;
}
if (r_clipped_dest_rect.position.x < 0) {
r_clipped_src_rect.position.x -= r_clipped_dest_rect.position.x;
r_clipped_src_rect.size.x += r_clipped_dest_rect.position.x;
r_clipped_dest_rect.position.x = 0;
}
if (r_clipped_dest_rect.position.y < 0) {
r_clipped_src_rect.position.y -= r_clipped_dest_rect.position.y;
r_clipped_src_rect.size.y += r_clipped_dest_rect.position.y;
r_clipped_dest_rect.position.y = 0;
}
r_clipped_src_rect.size.x = MAX(0, MIN(r_clipped_src_rect.size.x, MIN(p_src->width - r_clipped_src_rect.position.x, width - r_clipped_dest_rect.position.x)));
r_clipped_src_rect.size.y = MAX(0, MIN(r_clipped_src_rect.size.y, MIN(p_src->height - r_clipped_src_rect.position.y, height - r_clipped_dest_rect.position.y)));
r_clipped_dest_rect.size.x = r_clipped_src_rect.size.x;
r_clipped_dest_rect.size.y = r_clipped_src_rect.size.y;
}
void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest) {
ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object.");
int dsize = data.size();
@ -2233,22 +2266,13 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po
ERR_FAIL_COND(format != p_src->format);
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot blit_rect in compressed or custom image formats.");
Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect);
if (p_dest.x < 0) {
clipped_src_rect.position.x = ABS(p_dest.x);
}
if (p_dest.y < 0) {
clipped_src_rect.position.y = ABS(p_dest.y);
}
if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
Rect2i src_rect;
Rect2i dest_rect;
_get_clipped_src_and_dest_rects(p_src, p_src_rect, p_dest, src_rect, dest_rect);
if (src_rect.has_no_area() || dest_rect.has_no_area()) {
return;
}
Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y));
Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size));
PoolVector<uint8_t>::Write wp = data.write();
uint8_t *dst_data_ptr = wp.ptr();
@ -2259,8 +2283,8 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po
for (int i = 0; i < dest_rect.size.y; i++) {
for (int j = 0; j < dest_rect.size.x; j++) {
int src_x = clipped_src_rect.position.x + j;
int src_y = clipped_src_rect.position.y + i;
int src_x = src_rect.position.x + j;
int src_y = src_rect.position.y + i;
int dst_x = dest_rect.position.x + j;
int dst_y = dest_rect.position.y + i;
@ -2288,22 +2312,13 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co
ERR_FAIL_COND_MSG(p_src->height != p_mask->height, "Source image height is different from mask height.");
ERR_FAIL_COND(format != p_src->format);
Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect);
if (p_dest.x < 0) {
clipped_src_rect.position.x = ABS(p_dest.x);
}
if (p_dest.y < 0) {
clipped_src_rect.position.y = ABS(p_dest.y);
}
if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
Rect2i src_rect;
Rect2i dest_rect;
_get_clipped_src_and_dest_rects(p_src, p_src_rect, p_dest, src_rect, dest_rect);
if (src_rect.has_no_area() || dest_rect.has_no_area()) {
return;
}
Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y));
Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size));
PoolVector<uint8_t>::Write wp = data.write();
uint8_t *dst_data_ptr = wp.ptr();
@ -2317,8 +2332,8 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co
for (int i = 0; i < dest_rect.size.y; i++) {
for (int j = 0; j < dest_rect.size.x; j++) {
int src_x = clipped_src_rect.position.x + j;
int src_y = clipped_src_rect.position.y + i;
int src_x = src_rect.position.x + j;
int src_y = src_rect.position.y + i;
if (msk->get_pixel(src_x, src_y).a != 0) {
int dst_x = dest_rect.position.x + j;
@ -2345,30 +2360,21 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P
ERR_FAIL_COND(srcdsize == 0);
ERR_FAIL_COND(format != p_src->format);
Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect);
if (p_dest.x < 0) {
clipped_src_rect.position.x = ABS(p_dest.x);
}
if (p_dest.y < 0) {
clipped_src_rect.position.y = ABS(p_dest.y);
}
if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
Rect2i src_rect;
Rect2i dest_rect;
_get_clipped_src_and_dest_rects(p_src, p_src_rect, p_dest, src_rect, dest_rect);
if (src_rect.has_no_area() || dest_rect.has_no_area()) {
return;
}
Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y));
Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size));
lock();
Ref<Image> img = p_src;
img->lock();
for (int i = 0; i < dest_rect.size.y; i++) {
for (int j = 0; j < dest_rect.size.x; j++) {
int src_x = clipped_src_rect.position.x + j;
int src_y = clipped_src_rect.position.y + i;
int src_x = src_rect.position.x + j;
int src_y = src_rect.position.y + i;
int dst_x = dest_rect.position.x + j;
int dst_y = dest_rect.position.y + i;
@ -2399,22 +2405,13 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c
ERR_FAIL_COND_MSG(p_src->height != p_mask->height, "Source image height is different from mask height.");
ERR_FAIL_COND(format != p_src->format);
Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect);
if (p_dest.x < 0) {
clipped_src_rect.position.x = ABS(p_dest.x);
}
if (p_dest.y < 0) {
clipped_src_rect.position.y = ABS(p_dest.y);
}
if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
Rect2i src_rect;
Rect2i dest_rect;
_get_clipped_src_and_dest_rects(p_src, p_src_rect, p_dest, src_rect, dest_rect);
if (src_rect.has_no_area() || dest_rect.has_no_area()) {
return;
}
Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y));
Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size));
lock();
Ref<Image> img = p_src;
Ref<Image> msk = p_mask;
@ -2423,8 +2420,8 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c
for (int i = 0; i < dest_rect.size.y; i++) {
for (int j = 0; j < dest_rect.size.x; j++) {
int src_x = clipped_src_rect.position.x + j;
int src_y = clipped_src_rect.position.y + i;
int src_x = src_rect.position.x + j;
int src_y = src_rect.position.y + i;
// If the mask's pixel is transparent then we skip it
//Color c = msk->get_pixel(src_x, src_y);

View File

@ -186,6 +186,8 @@ private:
static int _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1);
bool _can_modify(Format p_format) const;
_FORCE_INLINE_ void _get_clipped_src_and_dest_rects(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest, Rect2i &r_clipped_src_rect, Rect2i &r_clipped_dest_rect) const;
_FORCE_INLINE_ void _put_pixelb(int p_x, int p_y, uint32_t p_pixel_size, uint8_t *p_data, const uint8_t *p_pixel);
_FORCE_INLINE_ void _get_pixelb(int p_x, int p_y, uint32_t p_pixel_size, const uint8_t *p_data, uint8_t *p_pixel);

View File

@ -97,12 +97,17 @@ VARIANT_ENUM_CAST(Time::Weekday);
#define VALIDATE_YMDHMS(ret) \
ERR_FAIL_COND_V_MSG(month == 0, ret, "Invalid month value of: " + itos(month) + ", months are 1-indexed and cannot be 0. See the Time.Month enum for valid values."); \
ERR_FAIL_COND_V_MSG(month < 0, ret, "Invalid month value of: " + itos(month) + "."); \
ERR_FAIL_COND_V_MSG(month > 12, ret, "Invalid month value of: " + itos(month) + ". See the Time.Month enum for valid values."); \
ERR_FAIL_COND_V_MSG(hour > 23, ret, "Invalid hour value of: " + itos(hour) + "."); \
ERR_FAIL_COND_V_MSG(hour < 0, ret, "Invalid hour value of: " + itos(hour) + "."); \
ERR_FAIL_COND_V_MSG(minute > 59, ret, "Invalid minute value of: " + itos(minute) + "."); \
ERR_FAIL_COND_V_MSG(minute < 0, ret, "Invalid minute value of: " + itos(minute) + "."); \
ERR_FAIL_COND_V_MSG(second > 59, ret, "Invalid second value of: " + itos(second) + " (leap seconds are not supported)."); \
ERR_FAIL_COND_V_MSG(second < 0, ret, "Invalid second value of: " + itos(second) + "."); \
ERR_FAIL_COND_V_MSG(day == 0, ret, "Invalid day value of: " + itos(day) + ", days are 1-indexed and cannot be 0."); \
ERR_FAIL_COND_V_MSG(day < 0, ret, "Invalid day value of: " + itos(day) + "."); \
/* Do this check after month is tested as valid. */ \
ERR_FAIL_COND_V_MSG(day == 0, ret, "Invalid day value of: " + itos(month) + ", days are 1-indexed and cannot be 0."); \
uint8_t days_in_this_month = MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month - 1]; \
ERR_FAIL_COND_V_MSG(day > days_in_this_month, ret, "Invalid day value of: " + itos(day) + " which is larger than the maximum for this month, " + itos(days_in_this_month) + ".");
@ -124,61 +129,65 @@ VARIANT_ENUM_CAST(Time::Weekday);
} \
}
#define PARSE_ISO8601_STRING \
int64_t year = UNIX_EPOCH_YEAR_AD; \
Month month = MONTH_JANUARY; \
uint8_t day = 1; \
uint8_t hour = 0; \
uint8_t minute = 0; \
uint8_t second = 0; \
{ \
bool has_date = false, has_time = false; \
String date, time; \
if (p_datetime.find_char('T') > 0) { \
has_date = has_time = true; \
Vector<String> array = p_datetime.split("T"); \
date = array[0]; \
time = array[1]; \
} else if (p_datetime.find_char(' ') > 0) { \
has_date = has_time = true; \
Vector<String> array = p_datetime.split(" "); \
date = array[0]; \
time = array[1]; \
} else if (p_datetime.find_char('-', 1) > 0) { \
has_date = true; \
date = p_datetime; \
} else if (p_datetime.find_char(':') > 0) { \
has_time = true; \
time = p_datetime; \
} \
/* Set the variables from the contents of the string. */ \
if (has_date) { \
Vector<int> array = date.split_ints("-", false); \
year = array[0]; \
month = (Month)array[1]; \
day = array[2]; \
/* Handle negative years. */ \
if (p_datetime.find_char('-') == 0) { \
year *= -1; \
} \
} \
if (has_time) { \
Vector<int> array = time.split_ints(":", false); \
hour = array[0]; \
minute = array[1]; \
second = array[2]; \
} \
#define PARSE_ISO8601_STRING(ret) \
int64_t year = UNIX_EPOCH_YEAR_AD; \
Month month = MONTH_JANUARY; \
int day = 1; \
int hour = 0; \
int minute = 0; \
int second = 0; \
{ \
bool has_date = false, has_time = false; \
String date, time; \
if (p_datetime.find_char('T') > 0) { \
has_date = has_time = true; \
Vector<String> array = p_datetime.split("T"); \
ERR_FAIL_COND_V_MSG(array.size() < 2, ret, "Invalid ISO 8601 date/time string."); \
date = array[0]; \
time = array[1]; \
} else if (p_datetime.find_char(' ') > 0) { \
has_date = has_time = true; \
Vector<String> array = p_datetime.split(" "); \
ERR_FAIL_COND_V_MSG(array.size() < 2, ret, "Invalid ISO 8601 date/time string."); \
date = array[0]; \
time = array[1]; \
} else if (p_datetime.find_char('-', 1) > 0) { \
has_date = true; \
date = p_datetime; \
} else if (p_datetime.find_char(':') > 0) { \
has_time = true; \
time = p_datetime; \
} \
/* Set the variables from the contents of the string. */ \
if (has_date) { \
Vector<int> array = date.split_ints("-", false); \
ERR_FAIL_COND_V_MSG(array.size() < 3, ret, "Invalid ISO 8601 date string."); \
year = array[0]; \
month = (Month)array[1]; \
day = array[2]; \
/* Handle negative years. */ \
if (p_datetime.find_char('-') == 0) { \
year *= -1; \
} \
} \
if (has_time) { \
Vector<int> array = time.split_ints(":", false); \
ERR_FAIL_COND_V_MSG(array.size() < 3, ret, "Invalid ISO 8601 time string."); \
hour = array[0]; \
minute = array[1]; \
second = array[2]; \
} \
}
#define EXTRACT_FROM_DICTIONARY \
/* Get all time values from the dictionary. If it doesn't exist, set the */ \
/* values to the default values for Unix epoch (1970-01-01 00:00:00). */ \
int64_t year = p_datetime.has(YEAR_KEY) ? int64_t(p_datetime[YEAR_KEY]) : UNIX_EPOCH_YEAR_AD; \
Month month = Month((p_datetime.has(MONTH_KEY)) ? uint8_t(p_datetime[MONTH_KEY]) : 1); \
uint8_t day = p_datetime.has(DAY_KEY) ? uint8_t(p_datetime[DAY_KEY]) : 1; \
uint8_t hour = p_datetime.has(HOUR_KEY) ? uint8_t(p_datetime[HOUR_KEY]) : 0; \
uint8_t minute = p_datetime.has(MINUTE_KEY) ? uint8_t(p_datetime[MINUTE_KEY]) : 0; \
uint8_t second = p_datetime.has(SECOND_KEY) ? uint8_t(p_datetime[SECOND_KEY]) : 0;
Month month = Month((p_datetime.has(MONTH_KEY)) ? int(p_datetime[MONTH_KEY]) : 1); \
int day = p_datetime.has(DAY_KEY) ? int(p_datetime[DAY_KEY]) : 1; \
int hour = p_datetime.has(HOUR_KEY) ? int(p_datetime[HOUR_KEY]) : 0; \
int minute = p_datetime.has(MINUTE_KEY) ? int(p_datetime[MINUTE_KEY]) : 0; \
int second = p_datetime.has(SECOND_KEY) ? int(p_datetime[SECOND_KEY]) : 0;
Time *Time::singleton = nullptr;
@ -253,7 +262,7 @@ String Time::get_time_string_from_unix_time(int64_t p_unix_time_val) const {
}
Dictionary Time::get_datetime_dict_from_string(String p_datetime, bool p_weekday) const {
PARSE_ISO8601_STRING
PARSE_ISO8601_STRING(Dictionary())
Dictionary dict;
dict[YEAR_KEY] = year;
dict[MONTH_KEY] = (uint8_t)month;
@ -293,7 +302,7 @@ int64_t Time::get_unix_time_from_datetime_dict(const Dictionary p_datetime) cons
}
int64_t Time::get_unix_time_from_datetime_string(String p_datetime) const {
PARSE_ISO8601_STRING
PARSE_ISO8601_STRING(-1)
VALIDATE_YMDHMS(0)
YMD_TO_DAY_NUMBER
return day_number * SECONDS_PER_DAY + hour * 3600 + minute * 60 + second;

View File

@ -51,7 +51,7 @@ class Time : public Object {
public:
static Time *get_singleton();
enum Month : uint8_t {
enum Month {
/// Start at 1 to follow Windows SYSTEMTIME structure
/// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx
MONTH_JANUARY = 1,

View File

@ -508,7 +508,8 @@
<method name="is_drag_successful" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if drag operation is successful.
Returns [code]true[/code] if a drag operation is successful. Alternative to [method Viewport.gui_is_drag_successful].
Best used with [constant Node.NOTIFICATION_DRAG_END].
</description>
</method>
<method name="minimum_size_changed">

View File

@ -153,7 +153,8 @@
<return type="int" enum="Error" />
<argument index="0" name="path" type="String" />
<description>
Deletes the target file or an empty directory. The argument can be relative to the current directory, or an absolute path. If the target directory is not empty, the operation will fail.
Permanently deletes the target file or an empty directory. The argument can be relative to the current directory, or an absolute path. If the target directory is not empty, the operation will fail.
If you don't want to delete the file/directory permanently, use [method OS.move_to_trash] instead.
Returns one of the [enum Error] code constants ([code]OK[/code] on success).
</description>
</method>

View File

@ -18,7 +18,7 @@
<argument index="1" name="src_rect" type="Rect2" />
<argument index="2" name="dst" type="Vector2" />
<description>
Alpha-blends [code]src_rect[/code] from [code]src[/code] image to this image at coordinates [code]dest[/code].
Alpha-blends [code]src_rect[/code] from [code]src[/code] image to this image at coordinates [code]dest[/code], clipped accordingly to both image bounds. This image and [code]src[/code] image [b]must[/b] have the same format. [code]src_rect[/code] with not positive size is treated as empty.
</description>
</method>
<method name="blend_rect_mask">
@ -28,7 +28,7 @@
<argument index="2" name="src_rect" type="Rect2" />
<argument index="3" name="dst" type="Vector2" />
<description>
Alpha-blends [code]src_rect[/code] from [code]src[/code] image to this image using [code]mask[/code] image at coordinates [code]dst[/code]. Alpha channels are required for both [code]src[/code] and [code]mask[/code]. [code]dst[/code] pixels and [code]src[/code] pixels will blend if the corresponding mask pixel's alpha value is not 0. [code]src[/code] image and [code]mask[/code] image [b]must[/b] have the same size (width and height) but they can have different formats.
Alpha-blends [code]src_rect[/code] from [code]src[/code] image to this image using [code]mask[/code] image at coordinates [code]dst[/code], clipped accordingly to both image bounds. Alpha channels are required for both [code]src[/code] and [code]mask[/code]. [code]dst[/code] pixels and [code]src[/code] pixels will blend if the corresponding mask pixel's alpha value is not 0. This image and [code]src[/code] image [b]must[/b] have the same format. [code]src[/code] image and [code]mask[/code] image [b]must[/b] have the same size (width and height) but they can have different formats. [code]src_rect[/code] with not positive size is treated as empty.
</description>
</method>
<method name="blit_rect">
@ -37,7 +37,7 @@
<argument index="1" name="src_rect" type="Rect2" />
<argument index="2" name="dst" type="Vector2" />
<description>
Copies [code]src_rect[/code] from [code]src[/code] image to this image at coordinates [code]dst[/code].
Copies [code]src_rect[/code] from [code]src[/code] image to this image at coordinates [code]dst[/code], clipped accordingly to both image bounds. This image and [code]src[/code] image [b]must[/b] have the same format. [code]src_rect[/code] with not positive size is treated as empty.
</description>
</method>
<method name="blit_rect_mask">
@ -47,7 +47,7 @@
<argument index="2" name="src_rect" type="Rect2" />
<argument index="3" name="dst" type="Vector2" />
<description>
Blits [code]src_rect[/code] area from [code]src[/code] image to this image at the coordinates given by [code]dst[/code]. [code]src[/code] pixel is copied onto [code]dst[/code] if the corresponding [code]mask[/code] pixel's alpha value is not 0. [code]src[/code] image and [code]mask[/code] image [b]must[/b] have the same size (width and height) but they can have different formats.
Blits [code]src_rect[/code] area from [code]src[/code] image to this image at the coordinates given by [code]dst[/code], clipped accordingly to both image bounds. [code]src[/code] pixel is copied onto [code]dst[/code] if the corresponding [code]mask[/code] pixel's alpha value is not 0. This image and [code]src[/code] image [b]must[/b] have the same format. [code]src[/code] image and [code]mask[/code] image [b]must[/b] have the same size (width and height) but they can have different formats. [code]src_rect[/code] with not positive size is treated as empty.
</description>
</method>
<method name="bumpmap_to_normalmap">

View File

@ -825,10 +825,13 @@
Notification received when the node is instanced.
</constant>
<constant name="NOTIFICATION_DRAG_BEGIN" value="21">
Notification received when a drag begins.
Notification received when a drag operation begins. All nodes receive this notification, not only the dragged one.
Can be triggered either by dragging a [Control] that provides drag data (see [method Control.get_drag_data]) or using [method Control.force_drag].
Use [method Viewport.gui_get_drag_data] to get the dragged data.
</constant>
<constant name="NOTIFICATION_DRAG_END" value="22">
Notification received when a drag ends.
Notification received when a drag operation ends.
Use [method Viewport.gui_is_drag_successful] to check if the drag succeeded.
</constant>
<constant name="NOTIFICATION_PATH_CHANGED" value="23">
Notification received when the node's [NodePath] changed.

View File

@ -766,6 +766,14 @@
[b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
</description>
</method>
<method name="move_to_trash" qualifiers="const">
<return type="int" enum="Error" />
<argument index="0" name="path" type="String" />
<description>
Moves the file or directory to the system's recycle bin. See also [method Directory.remove].
[b]Note:[/b] If the user has disabled the recycle bin on their system, the file will be permanently deleted instead.
</description>
</method>
<method name="move_window_to_foreground">
<return type="void" />
<description>

View File

@ -50,6 +50,7 @@
<description>
Converts the given ISO 8601 date and time string (YYYY-MM-DDTHH:MM:SS) to a dictionary of keys: [code]year[/code], [code]month[/code], [code]day[/code], [code]weekday[/code], [code]hour[/code], [code]minute[/code], and [code]second[/code].
If [code]weekday[/code] is false, then the [code]weekday[/code] entry is excluded (the calculation is relatively expensive).
[b]Note:[/b] Any decimal fraction in the time string will be ignored silently.
</description>
</method>
<method name="get_datetime_dict_from_system" qualifiers="const">
@ -171,12 +172,14 @@
<description>
Converts the given ISO 8601 date and/or time string to a Unix timestamp. The string can contain a date only, a time only, or both.
[b]Note:[/b] Unix timestamps are often in UTC. This method does not do any timezone conversion, so the timestamp will be in the same timezone as the given datetime string.
[b]Note:[/b] Any decimal fraction in the time string will be ignored silently.
</description>
</method>
<method name="get_unix_time_from_system" qualifiers="const">
<return type="float" />
<description>
Returns the current Unix timestamp in seconds based on the system time in UTC. This method is implemented by the operating system and always returns the time in UTC.
[b]Note:[/b] Unlike other methods that use integer timestamps, this method returns the timestamp as a [float] for sub-second precision.
</description>
</method>
</methods>

View File

@ -4,7 +4,7 @@
Vector used for 3D math.
</brief_description>
<description>
3-element structure that can be used to represent positions in 3D space or any other pair of numeric values.
3-element structure that can be used to represent positions in 3D space or any other triplet of numeric values.
[b]Note:[/b] In a boolean context, a Vector3 will evaluate to [code]false[/code] if it's equal to [code]Vector3(0, 0, 0)[/code]. Otherwise, a Vector3 will always evaluate to [code]true[/code].
</description>
<tutorials>

View File

@ -124,6 +124,7 @@
<return type="bool" />
<description>
Returns [code]true[/code] if the viewport is currently performing a drag operation.
Alternative to [constant Node.NOTIFICATION_DRAG_BEGIN] and [constant Node.NOTIFICATION_DRAG_END] when you prefer polling the value.
</description>
</method>
<method name="input">

View File

@ -619,15 +619,14 @@ void EditorNode::_notification(int p_what) {
PopupMenu *p = help_menu->get_popup();
p->set_item_icon(p->get_item_index(HELP_SEARCH), gui_base->get_icon("HelpSearch", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_REPORT_A_BUG), gui_base->get_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_icon("ExternalLink", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_icon("ExternalLink", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_REPORT_A_BUG), gui_base->get_icon("ExternalLink", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_icon("ExternalLink", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_icon("ExternalLink", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_icon("ExternalLink", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_icon("Godot", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), gui_base->get_icon("Heart", "EditorIcons"));
_update_update_spinner();
} break;
@ -6560,12 +6559,12 @@ EditorNode::EditorNode() {
p->add_icon_shortcut(gui_base->get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_F1), HELP_SEARCH);
#endif
p->add_separator();
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/online_docs", TTR("Online Documentation")), HELP_DOCS);
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/q&a", TTR("Questions & Answers")), HELP_QA);
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG);
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE);
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK);
p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/community", TTR("Community")), HELP_COMMUNITY);
p->add_icon_shortcut(gui_base->get_icon("ExternalLink", "EditorIcons"), ED_SHORTCUT("editor/online_docs", TTR("Online Documentation")), HELP_DOCS);
p->add_icon_shortcut(gui_base->get_icon("ExternalLink", "EditorIcons"), ED_SHORTCUT("editor/q&a", TTR("Questions & Answers")), HELP_QA);
p->add_icon_shortcut(gui_base->get_icon("ExternalLink", "EditorIcons"), ED_SHORTCUT("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG);
p->add_icon_shortcut(gui_base->get_icon("ExternalLink", "EditorIcons"), ED_SHORTCUT("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE);
p->add_icon_shortcut(gui_base->get_icon("ExternalLink", "EditorIcons"), ED_SHORTCUT("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK);
p->add_icon_shortcut(gui_base->get_icon("ExternalLink", "EditorIcons"), ED_SHORTCUT("editor/community", TTR("Community")), HELP_COMMUNITY);
p->add_separator();
p->add_icon_shortcut(gui_base->get_icon("Godot", "EditorIcons"), ED_SHORTCUT("editor/about", TTR("About Godot")), HELP_ABOUT);
p->add_icon_shortcut(gui_base->get_icon("Heart", "EditorIcons"), ED_SHORTCUT("editor/support_development", TTR("Support Godot Development")), HELP_SUPPORT_GODOT_DEVELOPMENT);

View File

@ -110,6 +110,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["arm64-v8a"] = "arm64-v8a";
capitalize_string_remaps["armeabi-v7a"] = "armeabi-v7a";
capitalize_string_remaps["arvr"] = "ARVR";
capitalize_string_remaps["bidi"] = "BiDi";
capitalize_string_remaps["bg"] = "BG";
capitalize_string_remaps["bp"] = "BP";
capitalize_string_remaps["bpc"] = "BPC";
@ -129,6 +130,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["erp"] = "ERP";
capitalize_string_remaps["etc"] = "ETC";
capitalize_string_remaps["etc2"] = "ETC2";
capitalize_string_remaps["filesystem"] = "FileSystem";
capitalize_string_remaps["fbx"] = "FBX";
capitalize_string_remaps["fft"] = "FFT";
capitalize_string_remaps["fg"] = "FG";
@ -181,6 +183,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
//capitalize_string_remaps["msec"] = "(msec)"; // Unit.
capitalize_string_remaps["msaa"] = "MSAA";
capitalize_string_remaps["nfc"] = "NFC";
capitalize_string_remaps["navmesh"] = "NavMesh";
capitalize_string_remaps["normalmap"] = "Normal Map";
capitalize_string_remaps["ofs"] = "Offset";
capitalize_string_remaps["ok"] = "OK";

View File

@ -223,10 +223,11 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
bool fav_changed = false;
for (int i = favorite_paths.size() - 1; i >= 0; i--) {
if (!da->dir_exists(favorite_paths[i])) {
favorite_paths.remove(i);
fav_changed = true;
if (da->dir_exists(favorite_paths[i]) || da->file_exists(favorite_paths[i])) {
continue;
}
favorite_paths.remove(i);
fav_changed = true;
}
if (fav_changed) {
EditorSettings::get_singleton()->set_favorites(favorite_paths);

View File

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path d="m0 0h16v16h-16z"/></clipPath><g clip-path="url(#a)" fill="#e0e0e0"><path d="m-1940-64.061 5.5-5.5-2.44-2.439h7v7l-2.439-2.439-5.5 5.5z" transform="translate(1944.939 73)"/><path d="m12 15h-8a3.079 3.079 0 0 1 -3-3v-8a3.04 3.04 0 0 1 3-3h2a1 1 0 0 1 0 2h-2a1.04 1.04 0 0 0 -1 1v8a1.083 1.083 0 0 0 1 1h8a1.068 1.068 0 0 0 1-1v-2a1 1 0 0 1 2 0v2a3.063 3.063 0 0 1 -3 3z"/></g></svg>

After

Width:  |  Height:  |  Size: 533 B

View File

@ -51,7 +51,7 @@ void GradientEditor::_gradient_changed() {
void GradientEditor::_ramp_changed() {
editing = true;
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
undo_redo->create_action(TTR("Gradient Edited"));
undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS);
undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets());
undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors());
undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets());

View File

@ -1429,7 +1429,7 @@ void ScriptEditor::_notification(int p_what) {
}
case NOTIFICATION_THEME_CHANGED: {
help_search->set_icon(get_icon("HelpSearch", "EditorIcons"));
site_search->set_icon(get_icon("Instance", "EditorIcons"));
site_search->set_icon(get_icon("ExternalLink", "EditorIcons"));
script_forward->set_icon(get_icon("Forward", "EditorIcons"));
script_back->set_icon(get_icon("Back", "EditorIcons"));

View File

@ -353,7 +353,7 @@ void ShaderEditor::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
PopupMenu *popup = help_menu->get_popup();
popup->set_item_icon(popup->get_item_index(HELP_DOCS), get_icon("Instance", "EditorIcons"));
popup->set_item_icon(popup->get_item_index(HELP_DOCS), get_icon("ExternalLink", "EditorIcons"));
} break;
case MainLoop::NOTIFICATION_WM_FOCUS_IN: {

View File

@ -2210,7 +2210,7 @@ void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos) {
if (error_tree->is_anything_selected()) {
item_menu->add_icon_item(get_icon("ActionCopy", "EditorIcons"), TTR("Copy Error"), ITEM_MENU_COPY_ERROR);
item_menu->add_icon_item(get_icon("Instance", "EditorIcons"), TTR("Open C++ Source on GitHub"), ITEM_MENU_OPEN_SOURCE);
item_menu->add_icon_item(get_icon("ExternalLink", "EditorIcons"), TTR("Open C++ Source on GitHub"), ITEM_MENU_OPEN_SOURCE);
}
if (item_menu->get_item_count() > 0) {

View File

@ -377,14 +377,12 @@ void CSGShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
Array csg_meshes = cs->get_meshes();
if (csg_meshes.size() != 2) {
return;
}
Ref<Mesh> csg_mesh = csg_meshes[1];
if (csg_mesh.is_valid()) {
p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh());
if (cs->is_root_shape()) {
Array csg_meshes = cs->get_meshes();
Ref<Mesh> csg_mesh = csg_meshes[1];
if (csg_mesh.is_valid()) {
p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh());
}
}
if (p_gizmo->is_selected()) {

View File

@ -65,10 +65,6 @@ public abstract class FullScreenGodotApp extends FragmentActivity implements God
} else {
Log.v(TAG, "Creating new Godot fragment instance.");
godotFragment = initGodotInstance();
if (godotFragment == null) {
throw new IllegalStateException("Godot instance must be non-null.");
}
getSupportFragmentManager().beginTransaction().replace(R.id.godot_fragment_container, godotFragment).setPrimaryNavigationFragment(godotFragment).commitNowAllowingStateLoss();
}
}

View File

@ -629,17 +629,14 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
translucent = true;
} else if (command_line[i].equals("--use_immersive")) {
use_immersive = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // check if the application runs on an android 4.4+
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar
View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
UiChangeListener();
}
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar
View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
UiChangeListener();
} else if (command_line[i].equals("--use_apk_expansion")) {
use_apk_expansion = true;
} else if (has_extra && command_line[i].equals("--apk_expansion_md5")) {
@ -825,7 +822,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
if (use_immersive && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // check if the application runs on an android 4.4+
if (use_immersive) {
Window window = getActivity().getWindow();
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
@ -848,15 +845,13 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
final View decorView = getActivity().getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(visibility -> {
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
});
}
@ -1024,9 +1019,8 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
// Create Hex String
StringBuilder hexString = new StringBuilder();
for (int i = 0; i < messageDigest.length; i++) {
String s = Integer.toHexString(0xFF & messageDigest[i]);
for (byte b : messageDigest) {
String s = Integer.toHexString(0xFF & b);
if (s.length() == 1) {
s = "0" + s;
}

View File

@ -43,8 +43,8 @@ public class Crypt {
// Create Hex String
StringBuilder hexString = new StringBuilder();
for (int i = 0; i < messageDigest.length; i++)
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
for (byte b : messageDigest)
hexString.append(Integer.toHexString(0xFF & b));
return hexString.toString();
} catch (Exception e) {

View File

@ -114,7 +114,7 @@ def configure(env):
## Architecture
is64 = sys.maxsize > 2 ** 32
is64 = sys.maxsize > 2**32
if env["bits"] == "default":
env["bits"] = "64" if is64 else "32"
@ -336,13 +336,14 @@ def configure(env):
if os.system("pkg-config --exists alsa") == 0: # 0 means found
env["alsa"] = True
env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"])
env.ParseConfig("pkg-config alsa --cflags") # Only cflags, we dlopen the library.
else:
print("Warning: ALSA libraries not found. Disabling the ALSA audio driver.")
if env["pulseaudio"]:
if os.system("pkg-config --exists libpulse") == 0: # 0 means found
env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED"])
env.ParseConfig("pkg-config --cflags libpulse")
env.ParseConfig("pkg-config libpulse --cflags") # Only cflags, we dlopen the library.
else:
print("Warning: PulseAudio development libraries not found. Disabling the PulseAudio audio driver.")
@ -351,6 +352,7 @@ def configure(env):
if env["udev"]:
if os.system("pkg-config --exists libudev") == 0: # 0 means found
env.Append(CPPDEFINES=["UDEV_ENABLED"])
env.ParseConfig("pkg-config libudev --cflags") # Only cflags, we dlopen the library.
else:
print("Warning: libudev development libraries not found. Disabling controller hotplugging support.")
else:
@ -362,7 +364,10 @@ def configure(env):
env.Prepend(CPPPATH=["#platform/x11"])
env.Append(CPPDEFINES=["X11_ENABLED", "UNIX_ENABLED", "OPENGL_ENABLED", "GLES_ENABLED", ("_FILE_OFFSET_BITS", 64)])
env.Append(LIBS=["GL", "pthread"])
env.ParseConfig("pkg-config gl --cflags --libs")
env.Append(LIBS=["pthread"])
if platform.system() == "Linux":
env.Append(LIBS=["dl"])

View File

@ -110,9 +110,8 @@ void RootMotionView::_notification(int p_what) {
first = false;
transform.orthonormalize(); //don't want scale, too imprecise
transform.affine_invert();
accumulated = transform * accumulated;
accumulated = accumulated * transform;
accumulated.origin.x = Math::fposmod(accumulated.origin.x, cell_size);
if (zero_y) {
accumulated.origin.y = 0;
@ -129,9 +128,9 @@ void RootMotionView::_notification(int p_what) {
Vector3 from(i * cell_size, 0, j * cell_size);
Vector3 from_i((i + 1) * cell_size, 0, j * cell_size);
Vector3 from_j(i * cell_size, 0, (j + 1) * cell_size);
from = accumulated.xform(from);
from_i = accumulated.xform(from_i);
from_j = accumulated.xform(from_j);
from = accumulated.xform_inv(from);
from_i = accumulated.xform_inv(from_i);
from_j = accumulated.xform_inv(from_j);
Color c = color, c_i = color, c_j = color;
c.a *= MAX(0, 1.0 - from.length() / radius);