diff options
Diffstat (limited to 'scene')
450 files changed, 14220 insertions, 8672 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index 0f98fad824..d9d551074d 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -654,7 +654,7 @@ String AnimatedSprite2D::get_configuration_warning() const { String warning = Node2D::get_configuration_warning(); if (frames.is_null()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite to display frames."); diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h index fddbf39be2..a558a3e657 100644 --- a/scene/2d/animated_sprite_2d.h +++ b/scene/2d/animated_sprite_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index d51ee3f9a8..a4e15008a0 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h index 01426db999..0b45675555 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 9bd716aeaa..f1eb7d017d 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index 4e236a367e..4eb709facb 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp index a36e0a86e1..91c887cb84 100644 --- a/scene/2d/back_buffer_copy.cpp +++ b/scene/2d/back_buffer_copy.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h index b58034de19..0916d344f9 100644 --- a/scene/2d/back_buffer_copy.h +++ b/scene/2d/back_buffer_copy.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 79b0b64efb..4f45325d3a 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -77,6 +77,9 @@ void Camera2D::_update_process_mode() { } void Camera2D::set_zoom(const Vector2 &p_zoom) { + // Setting zoom to zero causes 'affine_invert' issues + ERR_FAIL_COND_MSG(Math::is_zero_approx(p_zoom.x) || Math::is_zero_approx(p_zoom.y), "Zoom level must be different from 0 (can be negative)."); + zoom = p_zoom; Point2 old_smoothed_camera_pos = smoothed_camera_pos; _update_scroll(); @@ -101,31 +104,31 @@ Transform2D Camera2D::get_camera_transform() { if (!first) { if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) { - if (h_drag_enabled && !Engine::get_singleton()->is_editor_hint() && !h_offset_changed) { - camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom.x * drag_margin[MARGIN_LEFT])); - camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom.x * drag_margin[MARGIN_RIGHT])); + if (drag_horizontal_enabled && !Engine::get_singleton()->is_editor_hint() && !drag_horizontal_offset_changed) { + camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom.x * drag_margin[SIDE_LEFT])); + camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom.x * drag_margin[SIDE_RIGHT])); } else { - if (h_ofs < 0) { - camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT] * h_ofs; + if (drag_horizontal_offset < 0) { + camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_RIGHT] * drag_horizontal_offset; } else { - camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT] * h_ofs; + camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_LEFT] * drag_horizontal_offset; } - h_offset_changed = false; + drag_horizontal_offset_changed = false; } - if (v_drag_enabled && !Engine::get_singleton()->is_editor_hint() && !v_offset_changed) { - camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom.y * drag_margin[MARGIN_TOP])); - camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom.y * drag_margin[MARGIN_BOTTOM])); + if (drag_vertical_enabled && !Engine::get_singleton()->is_editor_hint() && !drag_vertical_offset_changed) { + camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom.y * drag_margin[SIDE_TOP])); + camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom.y * drag_margin[SIDE_BOTTOM])); } else { - if (v_ofs < 0) { - camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM] * v_ofs; + if (drag_vertical_offset < 0) { + camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_BOTTOM] * drag_vertical_offset; } else { - camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP] * v_ofs; + camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_TOP] * drag_vertical_offset; } - v_offset_changed = false; + drag_vertical_offset_changed = false; } } else if (anchor_mode == ANCHOR_MODE_FIXED_TOP_LEFT) { @@ -136,20 +139,20 @@ Transform2D Camera2D::get_camera_transform() { Rect2 screen_rect(-screen_offset + camera_pos, screen_size * zoom); if (limit_smoothing_enabled) { - if (screen_rect.position.x < limit[MARGIN_LEFT]) { - camera_pos.x -= screen_rect.position.x - limit[MARGIN_LEFT]; + if (screen_rect.position.x < limit[SIDE_LEFT]) { + camera_pos.x -= screen_rect.position.x - limit[SIDE_LEFT]; } - if (screen_rect.position.x + screen_rect.size.x > limit[MARGIN_RIGHT]) { - camera_pos.x -= screen_rect.position.x + screen_rect.size.x - limit[MARGIN_RIGHT]; + if (screen_rect.position.x + screen_rect.size.x > limit[SIDE_RIGHT]) { + camera_pos.x -= screen_rect.position.x + screen_rect.size.x - limit[SIDE_RIGHT]; } - if (screen_rect.position.y + screen_rect.size.y > limit[MARGIN_BOTTOM]) { - camera_pos.y -= screen_rect.position.y + screen_rect.size.y - limit[MARGIN_BOTTOM]; + if (screen_rect.position.y + screen_rect.size.y > limit[SIDE_BOTTOM]) { + camera_pos.y -= screen_rect.position.y + screen_rect.size.y - limit[SIDE_BOTTOM]; } - if (screen_rect.position.y < limit[MARGIN_TOP]) { - camera_pos.y -= screen_rect.position.y - limit[MARGIN_TOP]; + if (screen_rect.position.y < limit[SIDE_TOP]) { + camera_pos.y -= screen_rect.position.y - limit[SIDE_TOP]; } } @@ -175,20 +178,20 @@ Transform2D Camera2D::get_camera_transform() { } Rect2 screen_rect(-screen_offset + ret_camera_pos, screen_size * zoom); - if (screen_rect.position.x < limit[MARGIN_LEFT]) { - screen_rect.position.x = limit[MARGIN_LEFT]; + if (screen_rect.position.x < limit[SIDE_LEFT]) { + screen_rect.position.x = limit[SIDE_LEFT]; } - if (screen_rect.position.x + screen_rect.size.x > limit[MARGIN_RIGHT]) { - screen_rect.position.x = limit[MARGIN_RIGHT] - screen_rect.size.x; + if (screen_rect.position.x + screen_rect.size.x > limit[SIDE_RIGHT]) { + screen_rect.position.x = limit[SIDE_RIGHT] - screen_rect.size.x; } - if (screen_rect.position.y + screen_rect.size.y > limit[MARGIN_BOTTOM]) { - screen_rect.position.y = limit[MARGIN_BOTTOM] - screen_rect.size.y; + if (screen_rect.position.y + screen_rect.size.y > limit[SIDE_BOTTOM]) { + screen_rect.position.y = limit[SIDE_BOTTOM] - screen_rect.size.y; } - if (screen_rect.position.y < limit[MARGIN_TOP]) { - screen_rect.position.y = limit[MARGIN_TOP]; + if (screen_rect.position.y < limit[SIDE_TOP]) { + screen_rect.position.y = limit[SIDE_TOP]; } if (offset != Vector2()) { @@ -301,10 +304,10 @@ void Camera2D::_notification(int p_what) { Vector2 camera_origin = get_global_transform().get_origin(); Vector2 camera_scale = get_global_transform().get_scale().abs(); Vector2 limit_points[4] = { - (Vector2(limit[MARGIN_LEFT], limit[MARGIN_TOP]) - camera_origin) / camera_scale, - (Vector2(limit[MARGIN_RIGHT], limit[MARGIN_TOP]) - camera_origin) / camera_scale, - (Vector2(limit[MARGIN_RIGHT], limit[MARGIN_BOTTOM]) - camera_origin) / camera_scale, - (Vector2(limit[MARGIN_LEFT], limit[MARGIN_BOTTOM]) - camera_origin) / camera_scale + (Vector2(limit[SIDE_LEFT], limit[SIDE_TOP]) - camera_origin) / camera_scale, + (Vector2(limit[SIDE_RIGHT], limit[SIDE_TOP]) - camera_origin) / camera_scale, + (Vector2(limit[SIDE_RIGHT], limit[SIDE_BOTTOM]) - camera_origin) / camera_scale, + (Vector2(limit[SIDE_LEFT], limit[SIDE_BOTTOM]) - camera_origin) / camera_scale }; for (int i = 0; i < 4; i++) { @@ -324,10 +327,10 @@ void Camera2D::_notification(int p_what) { Size2 screen_size = _get_camera_screen_size(); Vector2 margin_endpoints[4] = { - inv_camera_transform.xform(Vector2((screen_size.width / 2) - ((screen_size.width / 2) * drag_margin[MARGIN_LEFT]), (screen_size.height / 2) - ((screen_size.height / 2) * drag_margin[MARGIN_TOP]))), - inv_camera_transform.xform(Vector2((screen_size.width / 2) + ((screen_size.width / 2) * drag_margin[MARGIN_RIGHT]), (screen_size.height / 2) - ((screen_size.height / 2) * drag_margin[MARGIN_TOP]))), - inv_camera_transform.xform(Vector2((screen_size.width / 2) + ((screen_size.width / 2) * drag_margin[MARGIN_RIGHT]), (screen_size.height / 2) + ((screen_size.height / 2) * drag_margin[MARGIN_BOTTOM]))), - inv_camera_transform.xform(Vector2((screen_size.width / 2) - ((screen_size.width / 2) * drag_margin[MARGIN_LEFT]), (screen_size.height / 2) + ((screen_size.height / 2) * drag_margin[MARGIN_BOTTOM]))) + inv_camera_transform.xform(Vector2((screen_size.width / 2) - ((screen_size.width / 2) * drag_margin[SIDE_LEFT]), (screen_size.height / 2) - ((screen_size.height / 2) * drag_margin[SIDE_TOP]))), + inv_camera_transform.xform(Vector2((screen_size.width / 2) + ((screen_size.width / 2) * drag_margin[SIDE_RIGHT]), (screen_size.height / 2) - ((screen_size.height / 2) * drag_margin[SIDE_TOP]))), + inv_camera_transform.xform(Vector2((screen_size.width / 2) + ((screen_size.width / 2) * drag_margin[SIDE_RIGHT]), (screen_size.height / 2) + ((screen_size.height / 2) * drag_margin[SIDE_BOTTOM]))), + inv_camera_transform.xform(Vector2((screen_size.width / 2) - ((screen_size.width / 2) * drag_margin[SIDE_LEFT]), (screen_size.height / 2) + ((screen_size.height / 2) * drag_margin[SIDE_BOTTOM]))) }; Transform2D inv_transform = get_global_transform().affine_inverse(); // undo global space @@ -422,15 +425,15 @@ void Camera2D::clear_current() { } } -void Camera2D::set_limit(Margin p_margin, int p_limit) { - ERR_FAIL_INDEX((int)p_margin, 4); - limit[p_margin] = p_limit; +void Camera2D::set_limit(Side p_side, int p_limit) { + ERR_FAIL_INDEX((int)p_side, 4); + limit[p_side] = p_limit; update(); } -int Camera2D::get_limit(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, 0); - return limit[p_margin]; +int Camera2D::get_limit(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); + return limit[p_side]; } void Camera2D::set_limit_smoothing_enabled(bool enable) { @@ -442,15 +445,15 @@ bool Camera2D::is_limit_smoothing_enabled() const { return limit_smoothing_enabled; } -void Camera2D::set_drag_margin(Margin p_margin, float p_drag_margin) { - ERR_FAIL_INDEX((int)p_margin, 4); - drag_margin[p_margin] = p_drag_margin; +void Camera2D::set_drag_margin(Side p_side, float p_drag_margin) { + ERR_FAIL_INDEX((int)p_side, 4); + drag_margin[p_side] = p_drag_margin; update(); } -float Camera2D::get_drag_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, 0); - return drag_margin[p_margin]; +float Camera2D::get_drag_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); + return drag_margin[p_side]; } Vector2 Camera2D::get_camera_position() const { @@ -473,15 +476,15 @@ void Camera2D::align() { Point2 current_camera_pos = get_global_transform().get_origin(); if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) { - if (h_ofs < 0) { - camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT] * h_ofs; + if (drag_horizontal_offset < 0) { + camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_RIGHT] * drag_horizontal_offset; } else { - camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT] * h_ofs; + camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_LEFT] * drag_horizontal_offset; } - if (v_ofs < 0) { - camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP] * v_ofs; + if (drag_vertical_offset < 0) { + camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_TOP] * drag_vertical_offset; } else { - camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM] * v_ofs; + camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_BOTTOM] * drag_vertical_offset; } } else if (anchor_mode == ANCHOR_MODE_FIXED_TOP_LEFT) { camera_pos = current_camera_pos; @@ -515,44 +518,44 @@ Size2 Camera2D::_get_camera_screen_size() const { return get_viewport_rect().size; } -void Camera2D::set_h_drag_enabled(bool p_enabled) { - h_drag_enabled = p_enabled; +void Camera2D::set_drag_horizontal_enabled(bool p_enabled) { + drag_horizontal_enabled = p_enabled; } -bool Camera2D::is_h_drag_enabled() const { - return h_drag_enabled; +bool Camera2D::is_drag_horizontal_enabled() const { + return drag_horizontal_enabled; } -void Camera2D::set_v_drag_enabled(bool p_enabled) { - v_drag_enabled = p_enabled; +void Camera2D::set_drag_vertical_enabled(bool p_enabled) { + drag_vertical_enabled = p_enabled; } -bool Camera2D::is_v_drag_enabled() const { - return v_drag_enabled; +bool Camera2D::is_drag_vertical_enabled() const { + return drag_vertical_enabled; } -void Camera2D::set_v_offset(float p_offset) { - v_ofs = p_offset; - v_offset_changed = true; +void Camera2D::set_drag_vertical_offset(float p_offset) { + drag_vertical_offset = p_offset; + drag_vertical_offset_changed = true; Point2 old_smoothed_camera_pos = smoothed_camera_pos; _update_scroll(); smoothed_camera_pos = old_smoothed_camera_pos; } -float Camera2D::get_v_offset() const { - return v_ofs; +float Camera2D::get_drag_vertical_offset() const { + return drag_vertical_offset; } -void Camera2D::set_h_offset(float p_offset) { - h_ofs = p_offset; - h_offset_changed = true; +void Camera2D::set_drag_horizontal_offset(float p_offset) { + drag_horizontal_offset = p_offset; + drag_horizontal_offset_changed = true; Point2 old_smoothed_camera_pos = smoothed_camera_pos; _update_scroll(); smoothed_camera_pos = old_smoothed_camera_pos; } -float Camera2D::get_h_offset() const { - return h_ofs; +float Camera2D::get_drag_horizontal_offset() const { + return drag_horizontal_offset; } void Camera2D::_set_old_smoothing(float p_enable) { @@ -660,17 +663,17 @@ void Camera2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_limit_smoothing_enabled", "limit_smoothing_enabled"), &Camera2D::set_limit_smoothing_enabled); ClassDB::bind_method(D_METHOD("is_limit_smoothing_enabled"), &Camera2D::is_limit_smoothing_enabled); - ClassDB::bind_method(D_METHOD("set_v_drag_enabled", "enabled"), &Camera2D::set_v_drag_enabled); - ClassDB::bind_method(D_METHOD("is_v_drag_enabled"), &Camera2D::is_v_drag_enabled); + ClassDB::bind_method(D_METHOD("set_drag_vertical_enabled", "enabled"), &Camera2D::set_drag_vertical_enabled); + ClassDB::bind_method(D_METHOD("is_drag_vertical_enabled"), &Camera2D::is_drag_vertical_enabled); - ClassDB::bind_method(D_METHOD("set_h_drag_enabled", "enabled"), &Camera2D::set_h_drag_enabled); - ClassDB::bind_method(D_METHOD("is_h_drag_enabled"), &Camera2D::is_h_drag_enabled); + ClassDB::bind_method(D_METHOD("set_drag_horizontal_enabled", "enabled"), &Camera2D::set_drag_horizontal_enabled); + ClassDB::bind_method(D_METHOD("is_drag_horizontal_enabled"), &Camera2D::is_drag_horizontal_enabled); - ClassDB::bind_method(D_METHOD("set_v_offset", "ofs"), &Camera2D::set_v_offset); - ClassDB::bind_method(D_METHOD("get_v_offset"), &Camera2D::get_v_offset); + ClassDB::bind_method(D_METHOD("set_drag_vertical_offset", "offset"), &Camera2D::set_drag_vertical_offset); + ClassDB::bind_method(D_METHOD("get_drag_vertical_offset"), &Camera2D::get_drag_vertical_offset); - ClassDB::bind_method(D_METHOD("set_h_offset", "ofs"), &Camera2D::set_h_offset); - ClassDB::bind_method(D_METHOD("get_h_offset"), &Camera2D::get_h_offset); + ClassDB::bind_method(D_METHOD("set_drag_horizontal_offset", "offset"), &Camera2D::set_drag_horizontal_offset); + ClassDB::bind_method(D_METHOD("get_drag_horizontal_offset"), &Camera2D::get_drag_horizontal_offset); ClassDB::bind_method(D_METHOD("set_drag_margin", "margin", "drag_margin"), &Camera2D::set_drag_margin); ClassDB::bind_method(D_METHOD("get_drag_margin", "margin"), &Camera2D::get_drag_margin); @@ -714,29 +717,25 @@ void Camera2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode"); ADD_GROUP("Limit", "limit_"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_left"), "set_limit", "get_limit", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_top"), "set_limit", "get_limit", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_right"), "set_limit", "get_limit", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_bottom"), "set_limit", "get_limit", MARGIN_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_left"), "set_limit", "get_limit", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_top"), "set_limit", "get_limit", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_right"), "set_limit", "get_limit", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_bottom"), "set_limit", "get_limit", SIDE_BOTTOM); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "limit_smoothed"), "set_limit_smoothing_enabled", "is_limit_smoothing_enabled"); - ADD_GROUP("Draw Margin", "draw_margin_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_margin_h_enabled"), "set_h_drag_enabled", "is_h_drag_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_margin_v_enabled"), "set_v_drag_enabled", "is_v_drag_enabled"); - ADD_GROUP("Smoothing", "smoothing_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smoothing_enabled"), "set_enable_follow_smoothing", "is_follow_smoothing_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "smoothing_speed"), "set_follow_smoothing", "get_follow_smoothing"); - ADD_GROUP("Offset", "offset_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "offset_h", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_h_offset", "get_h_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "offset_v", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_v_offset", "get_v_offset"); - - ADD_GROUP("Drag Margin", "drag_margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_margin_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_margin_top", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_margin_right", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_margin_bottom", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_BOTTOM); + ADD_GROUP("Drag", "drag_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_horizontal_enabled"), "set_drag_horizontal_enabled", "is_drag_horizontal_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_vertical_enabled"), "set_drag_vertical_enabled", "is_drag_vertical_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_horizontal_offset", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_drag_horizontal_offset", "get_drag_horizontal_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_vertical_offset", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_drag_vertical_offset", "get_drag_vertical_offset"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_left_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_top_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_right_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_bottom_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_BOTTOM); ADD_GROUP("Editor", "editor_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_screen"), "set_screen_drawing_enabled", "is_screen_drawing_enabled"); @@ -753,15 +752,15 @@ Camera2D::Camera2D() { anchor_mode = ANCHOR_MODE_DRAG_CENTER; rotating = false; current = false; - limit[MARGIN_LEFT] = -10000000; - limit[MARGIN_TOP] = -10000000; - limit[MARGIN_RIGHT] = 10000000; - limit[MARGIN_BOTTOM] = 10000000; - - drag_margin[MARGIN_LEFT] = 0.2; - drag_margin[MARGIN_TOP] = 0.2; - drag_margin[MARGIN_RIGHT] = 0.2; - drag_margin[MARGIN_BOTTOM] = 0.2; + limit[SIDE_LEFT] = -10000000; + limit[SIDE_TOP] = -10000000; + limit[SIDE_RIGHT] = 10000000; + limit[SIDE_BOTTOM] = 10000000; + + drag_margin[SIDE_LEFT] = 0.2; + drag_margin[SIDE_TOP] = 0.2; + drag_margin[SIDE_RIGHT] = 0.2; + drag_margin[SIDE_BOTTOM] = 0.2; camera_pos = Vector2(); first = true; smoothing_enabled = false; @@ -777,12 +776,12 @@ Camera2D::Camera2D() { limit_drawing_enabled = false; margin_drawing_enabled = false; - h_drag_enabled = false; - v_drag_enabled = false; - h_ofs = 0; - v_ofs = 0; - h_offset_changed = false; - v_offset_changed = false; + drag_horizontal_enabled = false; + drag_vertical_enabled = false; + drag_horizontal_offset = 0; + drag_vertical_offset = 0; + drag_horizontal_offset_changed = false; + drag_vertical_offset_changed = false; set_notify_transform(true); } diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h index 867a5562b2..8977d872f0 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -69,15 +69,14 @@ protected: bool smoothing_enabled; int limit[4]; bool limit_smoothing_enabled; - float drag_margin[4]; - - bool h_drag_enabled; - bool v_drag_enabled; - float h_ofs; - float v_ofs; - bool h_offset_changed; - bool v_offset_changed; + float drag_margin[4]; + bool drag_horizontal_enabled; + bool drag_vertical_enabled; + float drag_horizontal_offset; + float drag_vertical_offset; + bool drag_horizontal_offset_changed; + bool drag_vertical_offset_changed; Point2 camera_screen_center; void _update_process_mode(); @@ -111,26 +110,26 @@ public: void set_rotating(bool p_rotating); bool is_rotating() const; - void set_limit(Margin p_margin, int p_limit); - int get_limit(Margin p_margin) const; + void set_limit(Side p_side, int p_limit); + int get_limit(Side p_side) const; void set_limit_smoothing_enabled(bool enable); bool is_limit_smoothing_enabled() const; - void set_h_drag_enabled(bool p_enabled); - bool is_h_drag_enabled() const; + void set_drag_horizontal_enabled(bool p_enabled); + bool is_drag_horizontal_enabled() const; - void set_v_drag_enabled(bool p_enabled); - bool is_v_drag_enabled() const; + void set_drag_vertical_enabled(bool p_enabled); + bool is_drag_vertical_enabled() const; - void set_drag_margin(Margin p_margin, float p_drag_margin); - float get_drag_margin(Margin p_margin) const; + void set_drag_margin(Side p_side, float p_drag_margin); + float get_drag_margin(Side p_side) const; - void set_v_offset(float p_offset); - float get_v_offset() const; + void set_drag_horizontal_offset(float p_offset); + float get_drag_horizontal_offset() const; - void set_h_offset(float p_offset); - float get_h_offset() const; + void set_drag_vertical_offset(float p_offset); + float get_drag_vertical_offset() const; void set_enable_follow_smoothing(bool p_enabled); bool is_follow_smoothing_enabled() const; diff --git a/scene/2d/canvas_group.cpp b/scene/2d/canvas_group.cpp index 39cae8e0c6..0f0e583ea7 100644 --- a/scene/2d/canvas_group.cpp +++ b/scene/2d/canvas_group.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -84,4 +84,5 @@ CanvasGroup::CanvasGroup() { set_fit_margin(10.0); //sets things } CanvasGroup::~CanvasGroup() { + RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CANVAS_GROUP_MODE_DISABLED); } diff --git a/scene/2d/canvas_group.h b/scene/2d/canvas_group.h index 19630befc7..cecf7c24f4 100644 --- a/scene/2d/canvas_group.h +++ b/scene/2d/canvas_group.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp index 8fb16534e8..6e90afde21 100644 --- a/scene/2d/canvas_modulate.cpp +++ b/scene/2d/canvas_modulate.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -84,7 +84,7 @@ String CanvasModulate::get_configuration_warning() const { get_tree()->get_nodes_in_group("_canvas_modulate_" + itos(get_canvas().get_id()), &nodes); if (nodes.size() > 1) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("Only one visible CanvasModulate is allowed per scene (or set of instanced scenes). The first created one will work, while the rest will be ignored."); diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h index eac3cf9e54..6efc0cab9b 100644 --- a/scene/2d/canvas_modulate.h +++ b/scene/2d/canvas_modulate.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index fe16d4089a..f0b007f843 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -366,8 +366,8 @@ void CollisionObject2D::_update_pickable() { String CollisionObject2D::get_configuration_warning() const { String warning = Node2D::get_configuration_warning(); - if (shapes.empty()) { - if (!warning.empty()) { + if (shapes.is_empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape."); diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index 8eff1b3aec..a2112c27f4 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 64d82d715c..851e40cda6 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,7 +36,7 @@ #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" -#include "thirdparty/misc/triangulator.h" +#include "thirdparty/misc/polypartition.h" void CollisionPolygon2D::_build_polygon() { parent->shape_owner_clear_shapes(owner_id); @@ -194,6 +194,7 @@ void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) { if (parent) { _build_polygon(); + _update_in_shape_owner(); } update(); update_configuration_warning(); @@ -208,6 +209,7 @@ void CollisionPolygon2D::set_build_mode(BuildMode p_mode) { build_mode = p_mode; if (parent) { _build_polygon(); + _update_in_shape_owner(); } } @@ -233,14 +235,14 @@ String CollisionPolygon2D::get_configuration_warning() const { String warning = Node2D::get_configuration_warning(); if (!Object::cast_to<CollisionObject2D>(get_parent())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."); } - if (polygon.empty()) { - if (!warning.empty()) { + if (polygon.is_empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("An empty CollisionPolygon2D has no effect on collision."); diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h index 0f6b654149..caa5b2c3ec 100644 --- a/scene/2d/collision_polygon_2d.h +++ b/scene/2d/collision_polygon_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index a5cd624235..37bed577ac 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -141,6 +141,9 @@ void CollisionShape2D::_notification(int p_what) { } void CollisionShape2D::set_shape(const Ref<Shape2D> &p_shape) { + if (p_shape == shape) { + return; + } if (shape.is_valid()) { shape->disconnect("changed", callable_mp(this, &CollisionShape2D::_shape_changed)); } @@ -151,6 +154,7 @@ void CollisionShape2D::set_shape(const Ref<Shape2D> &p_shape) { if (shape.is_valid()) { parent->shape_owner_add_shape(owner_id, shape); } + _update_in_shape_owner(); } if (shape.is_valid()) { diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h index ced90d46f0..8a4d885393 100644 --- a/scene/2d/collision_shape_2d.h +++ b/scene/2d/collision_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index ef2a9a4911..7c9bd118d8 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -397,14 +397,14 @@ Ref<Gradient> CPUParticles2D::get_color_ramp() const { return color_ramp; } -void CPUParticles2D::set_particle_flag(Flags p_flag, bool p_enable) { - ERR_FAIL_INDEX(p_flag, FLAG_MAX); - flags[p_flag] = p_enable; +void CPUParticles2D::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) { + ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX); + particle_flags[p_particle_flag] = p_enable; } -bool CPUParticles2D::get_particle_flag(Flags p_flag) const { - ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); - return flags[p_flag]; +bool CPUParticles2D::get_particle_flag(ParticleFlags p_particle_flag) const { + ERR_FAIL_INDEX_V(p_particle_flag, PARTICLE_FLAG_MAX, false); + return particle_flags[p_particle_flag]; } void CPUParticles2D::set_emission_shape(EmissionShape p_shape) { @@ -743,7 +743,7 @@ void CPUParticles2D::_particles_process(float p_delta) { Vector2 normal = emission_normals.get(random_idx); Transform2D m2; m2.set_axis(0, normal); - m2.set_axis(1, normal.tangent()); + m2.set_axis(1, normal.orthogonal()); p.velocity = m2.basis_xform(p.velocity); } @@ -905,10 +905,10 @@ void CPUParticles2D::_particles_process(float p_delta) { p.color *= p.base_color; - if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { if (p.velocity.length() > 0.0) { p.transform.elements[1] = p.velocity.normalized(); - p.transform.elements[0] = p.transform.elements[1].tangent(); + p.transform.elements[0] = p.transform.elements[1].orthogonal(); } } else { @@ -1130,7 +1130,7 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) { set_color_ramp(gt->get_gradient()); } - set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY)); + set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); set_emission_shape(EmissionShape(material->get_emission_shape())); set_emission_sphere_radius(material->get_emission_sphere_radius()); @@ -1246,8 +1246,8 @@ void CPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles2D::set_color_ramp); ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles2D::get_color_ramp); - ClassDB::bind_method(D_METHOD("set_particle_flag", "flag", "enable"), &CPUParticles2D::set_particle_flag); - ClassDB::bind_method(D_METHOD("get_particle_flag", "flag"), &CPUParticles2D::get_particle_flag); + ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &CPUParticles2D::set_particle_flag); + ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &CPUParticles2D::get_particle_flag); ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &CPUParticles2D::set_emission_shape); ClassDB::bind_method(D_METHOD("get_emission_shape"), &CPUParticles2D::get_emission_shape); @@ -1279,8 +1279,8 @@ void CPUParticles2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "emission_points"), "set_emission_points", "get_emission_points"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors"); - ADD_GROUP("Flags", "flag_"); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_particle_flag", "get_particle_flag", FLAG_ALIGN_Y_TO_VELOCITY); + ADD_GROUP("Particle Flags", "particle_flag_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); ADD_GROUP("Direction", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "direction"), "set_direction", "get_direction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread"); @@ -1351,10 +1351,10 @@ void CPUParticles2D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET); BIND_ENUM_CONSTANT(PARAM_MAX); - BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY); - BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); // Unused, but exposed for consistency with 3D. - BIND_ENUM_CONSTANT(FLAG_DISABLE_Z); // Unused, but exposed for consistency with 3D. - BIND_ENUM_CONSTANT(FLAG_MAX); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_ROTATE_Y); // Unused, but exposed for consistency with 3D. + BIND_ENUM_CONSTANT(PARTICLE_FLAG_DISABLE_Z); // Unused, but exposed for consistency with 3D. + BIND_ENUM_CONSTANT(PARTICLE_FLAG_MAX); BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE); @@ -1415,8 +1415,8 @@ CPUParticles2D::CPUParticles2D() { set_param_randomness(Parameter(i), 0); } - for (int i = 0; i < FLAG_MAX; i++) { - flags[i] = false; + for (int i = 0; i < PARTICLE_FLAG_MAX; i++) { + particle_flags[i] = false; } set_color(Color(1, 1, 1, 1)); diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index 0f2a3e4920..3793f6a418 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -61,11 +61,11 @@ public: PARAM_MAX }; - enum Flags { - FLAG_ALIGN_Y_TO_VELOCITY, - FLAG_ROTATE_Y, // Unused, but exposed for consistency with 3D. - FLAG_DISABLE_Z, // Unused, but exposed for consistency with 3D. - FLAG_MAX + enum ParticleFlags { + PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, + PARTICLE_FLAG_ROTATE_Y, // Unused, but exposed for consistency with 3D. + PARTICLE_FLAG_DISABLE_Z, // Unused, but exposed for consistency with 3D. + PARTICLE_FLAG_MAX }; enum EmissionShape { @@ -159,7 +159,7 @@ private: Color color; Ref<Gradient> color_ramp; - bool flags[FLAG_MAX]; + bool particle_flags[PARTICLE_FLAG_MAX]; EmissionShape emission_shape; float emission_sphere_radius; @@ -253,8 +253,8 @@ public: void set_color_ramp(const Ref<Gradient> &p_ramp); Ref<Gradient> get_color_ramp() const; - void set_particle_flag(Flags p_flag, bool p_enable); - bool get_particle_flag(Flags p_flag) const; + void set_particle_flag(ParticleFlags p_particle_flag, bool p_enable); + bool get_particle_flag(ParticleFlags p_particle_flag) const; void set_emission_shape(EmissionShape p_shape); void set_emission_sphere_radius(float p_radius); @@ -287,7 +287,7 @@ public: VARIANT_ENUM_CAST(CPUParticles2D::DrawOrder) VARIANT_ENUM_CAST(CPUParticles2D::Parameter) -VARIANT_ENUM_CAST(CPUParticles2D::Flags) +VARIANT_ENUM_CAST(CPUParticles2D::ParticleFlags) VARIANT_ENUM_CAST(CPUParticles2D::EmissionShape) #endif // CPU_PARTICLES_2D_H diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index a4a930455b..2e477a88a9 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -127,9 +127,9 @@ void GPUParticles2D::_update_particle_emission_transform() { void GPUParticles2D::set_process_material(const Ref<Material> &p_material) { process_material = p_material; Ref<ParticlesMaterial> pm = p_material; - if (pm.is_valid() && !pm->get_flag(ParticlesMaterial::FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) { + if (pm.is_valid() && !pm->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) { // Likely a new (3D) material, modify it to match 2D space - pm->set_flag(ParticlesMaterial::FLAG_DISABLE_Z, true); + pm->set_particle_flag(ParticlesMaterial::PARTICLE_FLAG_DISABLE_Z, true); pm->set_gravity(Vector3(0, 98, 0)); } RID material_rid; diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index 0d1b82d93e..774cef9cc9 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp index f5d13fd641..1a31f72a59 100644 --- a/scene/2d/joints_2d.cpp +++ b/scene/2d/joints_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,8 +32,38 @@ #include "core/config/engine.h" #include "physics_body_2d.h" +#include "scene/scene_string_names.h" #include "servers/physics_server_2d.h" +void Joint2D::_disconnect_signals() { + Node *node_a = get_node_or_null(a); + PhysicsBody2D *body_a = Object::cast_to<PhysicsBody2D>(node_a); + if (body_a) { + body_a->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint2D::_body_exit_tree)); + } + + Node *node_b = get_node_or_null(b); + PhysicsBody2D *body_b = Object::cast_to<PhysicsBody2D>(node_b); + if (body_b) { + body_b->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint2D::_body_exit_tree)); + } +} + +void Joint2D::_body_exit_tree(const ObjectID &p_body_id) { + _disconnect_signals(); + Object *object = ObjectDB::get_instance(p_body_id); + PhysicsBody2D *body = Object::cast_to<PhysicsBody2D>(object); + ERR_FAIL_NULL(body); + RID body_rid = body->get_rid(); + if (ba == body_rid) { + a = NodePath(); + } + if (bb == body_rid) { + b = NodePath(); + } + _update_joint(); +} + void Joint2D::_update_joint(bool p_only_free) { if (joint.is_valid()) { if (ba.is_valid() && bb.is_valid() && exclude_from_collision) { @@ -51,8 +81,8 @@ void Joint2D::_update_joint(bool p_only_free) { return; } - Node *node_a = has_node(get_node_a()) ? get_node(get_node_a()) : (Node *)nullptr; - Node *node_b = has_node(get_node_b()) ? get_node(get_node_b()) : (Node *)nullptr; + Node *node_a = get_node_or_null(a); + Node *node_b = get_node_or_null(b); PhysicsBody2D *body_a = Object::cast_to<PhysicsBody2D>(node_a); PhysicsBody2D *body_b = Object::cast_to<PhysicsBody2D>(node_b); @@ -90,6 +120,14 @@ void Joint2D::_update_joint(bool p_only_free) { warning = String(); update_configuration_warning(); + if (body_a) { + body_a->force_update_transform(); + } + + if (body_b) { + body_b->force_update_transform(); + } + joint = _configure_joint(body_a, body_b); ERR_FAIL_COND_MSG(!joint.is_valid(), "Failed to configure the joint."); @@ -99,6 +137,9 @@ void Joint2D::_update_joint(bool p_only_free) { ba = body_a->get_rid(); bb = body_b->get_rid(); + body_a->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint2D::_body_exit_tree), make_binds(body_a->get_instance_id())); + body_b->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint2D::_body_exit_tree), make_binds(body_b->get_instance_id())); + PhysicsServer2D::get_singleton()->joint_disable_collisions_between_bodies(joint, exclude_from_collision); } @@ -107,6 +148,10 @@ void Joint2D::set_node_a(const NodePath &p_node_a) { return; } + if (joint.is_valid()) { + _disconnect_signals(); + } + a = p_node_a; _update_joint(); } @@ -119,6 +164,11 @@ void Joint2D::set_node_b(const NodePath &p_node_b) { if (b == p_node_b) { return; } + + if (joint.is_valid()) { + _disconnect_signals(); + } + b = p_node_b; _update_joint(); } @@ -134,6 +184,7 @@ void Joint2D::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { if (joint.is_valid()) { + _disconnect_signals(); _update_joint(true); } } break; @@ -168,8 +219,8 @@ bool Joint2D::get_exclude_nodes_from_collision() const { String Joint2D::get_configuration_warning() const { String node_warning = Node2D::get_configuration_warning(); - if (!warning.empty()) { - if (!node_warning.empty()) { + if (!warning.is_empty()) { + if (!node_warning.is_empty()) { node_warning += "\n\n"; } node_warning += warning; diff --git a/scene/2d/joints_2d.h b/scene/2d/joints_2d.h index 759e7de8a0..d0fbb9fd3a 100644 --- a/scene/2d/joints_2d.h +++ b/scene/2d/joints_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -49,6 +49,8 @@ class Joint2D : public Node2D { String warning; protected: + void _disconnect_signals(); + void _body_exit_tree(const ObjectID &p_body_id); void _update_joint(bool p_only_free = false); void _notification(int p_what); diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 2b373a669b..ba4372f040 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -404,7 +404,7 @@ String PointLight2D::get_configuration_warning() const { String warning = Node2D::get_configuration_warning(); if (!texture.is_valid()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("A texture with the shape of the light must be supplied to the \"Texture\" property."); diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h index 7dfeddc8e5..0112ab1648 100644 --- a/scene/2d/light_2d.h +++ b/scene/2d/light_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 6908fbeada..3146e64c08 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -238,7 +238,7 @@ Ref<OccluderPolygon2D> LightOccluder2D::get_occluder_polygon() const { void LightOccluder2D::set_occluder_light_mask(int p_mask) { mask = p_mask; - RS::get_singleton()->canvas_light_occluder_set_light_mask(occluder, mask); + RS::get_singleton()->canvas_light_occluder_set_light_mask(occluder, p_mask); } int LightOccluder2D::get_occluder_light_mask() const { @@ -249,14 +249,14 @@ String LightOccluder2D::get_configuration_warning() const { String warning = Node2D::get_configuration_warning(); if (!occluder_polygon.is_valid()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("An occluder polygon must be set (or drawn) for this occluder to take effect."); } if (occluder_polygon.is_valid() && occluder_polygon->get_polygon().size() == 0) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("The occluder polygon for this occluder is empty. Please draw a polygon."); @@ -285,7 +285,7 @@ void LightOccluder2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D"), "set_occluder_polygon", "get_occluder_polygon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdf_collision"), "set_as_sdf_collision", "is_set_as_sdf_collision"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "occluder_light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask"); } LightOccluder2D::LightOccluder2D() { diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h index 97574af542..7944ccad3a 100644 --- a/scene/2d/light_occluder_2d.h +++ b/scene/2d/light_occluder_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index e990e9f53e..0a3f5673f8 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h index 43739ee638..e0b1cea60a 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index f1522dbaeb..2f4459785b 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -459,39 +459,6 @@ void LineBuilder::strip_begin(Vector2 up, Vector2 down, Color color, float uvx) _last_index[DOWN] = vi + 1; } -void LineBuilder::strip_new_quad(Vector2 up, Vector2 down, Color color, float uvx) { - int vi = vertices.size(); - - vertices.push_back(vertices[_last_index[UP]]); - vertices.push_back(vertices[_last_index[DOWN]]); - vertices.push_back(up); - vertices.push_back(down); - - if (_interpolate_color) { - colors.push_back(color); - colors.push_back(color); - colors.push_back(color); - colors.push_back(color); - } - - if (texture_mode != Line2D::LINE_TEXTURE_NONE) { - uvs.push_back(uvs[_last_index[UP]]); - uvs.push_back(uvs[_last_index[DOWN]]); - uvs.push_back(Vector2(uvx, UP)); - uvs.push_back(Vector2(uvx, DOWN)); - } - - indices.push_back(vi); - indices.push_back(vi + 3); - indices.push_back(vi + 1); - indices.push_back(vi); - indices.push_back(vi + 2); - indices.push_back(vi + 3); - - _last_index[UP] = vi + 2; - _last_index[DOWN] = vi + 3; -} - void LineBuilder::strip_add_quad(Vector2 up, Vector2 down, Color color, float uvx) { int vi = vertices.size(); diff --git a/scene/2d/line_builder.h b/scene/2d/line_builder.h index 0e033d9be1..bd419d330b 100644 --- a/scene/2d/line_builder.h +++ b/scene/2d/line_builder.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp index 037e423ce9..430e655fc6 100644 --- a/scene/2d/mesh_instance_2d.cpp +++ b/scene/2d/mesh_instance_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/mesh_instance_2d.h b/scene/2d/mesh_instance_2d.h index f10ab17a7c..adfda4cf7f 100644 --- a/scene/2d/mesh_instance_2d.h +++ b/scene/2d/mesh_instance_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp index c258e30eab..5164e5c7e9 100644 --- a/scene/2d/multimesh_instance_2d.cpp +++ b/scene/2d/multimesh_instance_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/multimesh_instance_2d.h b/scene/2d/multimesh_instance_2d.h index aadedac42a..213cbd19b0 100644 --- a/scene/2d/multimesh_instance_2d.h +++ b/scene/2d/multimesh_instance_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp index 039c6f2e53..bec5ee7984 100644 --- a/scene/2d/navigation_2d.cpp +++ b/scene/2d/navigation_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/navigation_2d.h b/scene/2d/navigation_2d.h index 6046bddb32..12847e52ac 100644 --- a/scene/2d/navigation_2d.h +++ b/scene/2d/navigation_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 1c7063d0d3..534e31b1f2 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -274,7 +274,7 @@ String NavigationAgent2D::get_configuration_warning() const { String warning = Node::get_configuration_warning(); if (!Object::cast_to<Node2D>(get_parent())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("The NavigationAgent2D can be used only under a Node2D node"); diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 1f2377837b..6b7da4a5f2 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index 252d7cbb96..7e1aefe5e2 100644 --- a/scene/2d/navigation_obstacle_2d.cpp +++ b/scene/2d/navigation_obstacle_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -109,7 +109,7 @@ String NavigationObstacle2D::get_configuration_warning() const { String warning = Node::get_configuration_warning(); if (!Object::cast_to<Node2D>(get_parent())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object."); diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h index d65f44bc0e..421f8ca7cd 100644 --- a/scene/2d/navigation_obstacle_2d.h +++ b/scene/2d/navigation_obstacle_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 98817ec03e..7360fce330 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,7 +37,7 @@ #include "navigation_2d.h" #include "servers/navigation_server_2d.h" -#include "thirdparty/misc/triangulator.h" +#include "thirdparty/misc/polypartition.h" #ifdef TOOLS_ENABLED Rect2 NavigationPolygon::_edit_get_rect() const { @@ -228,7 +228,7 @@ void NavigationPolygon::make_polygons_from_outlines() { MutexLock lock(navmesh_generation); navmesh.unref(); } - List<TriangulatorPoly> in_poly, out_poly; + List<TPPLPoly> in_poly, out_poly; Vector2 outside_point(-1e10, -1e10); @@ -278,23 +278,23 @@ void NavigationPolygon::make_polygons_from_outlines() { bool outer = (interscount % 2) == 0; - TriangulatorPoly tp; + TPPLPoly tp; tp.Init(olsize); for (int j = 0; j < olsize; j++) { tp[j] = r[j]; } if (outer) { - tp.SetOrientation(TRIANGULATOR_CCW); + tp.SetOrientation(TPPL_ORIENTATION_CCW); } else { - tp.SetOrientation(TRIANGULATOR_CW); + tp.SetOrientation(TPPL_ORIENTATION_CW); tp.SetHole(true); } in_poly.push_back(tp); } - TriangulatorPartition tpart; + TPPLPartition tpart; if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed! ERR_PRINT("NavigationPolygon: Convex partition failed!"); return; @@ -304,8 +304,8 @@ void NavigationPolygon::make_polygons_from_outlines() { vertices.resize(0); Map<Vector2, int> points; - for (List<TriangulatorPoly>::Element *I = out_poly.front(); I; I = I->next()) { - TriangulatorPoly &tp = I->get(); + for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { + TPPLPoly &tp = I->get(); struct Polygon p; @@ -503,7 +503,7 @@ String NavigationRegion2D::get_configuration_warning() const { String warning = Node2D::get_configuration_warning(); if (!navpoly.is_valid()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("A NavigationPolygon resource must be set or created for this node to work. Please set a property or draw a polygon."); @@ -516,7 +516,7 @@ String NavigationRegion2D::get_configuration_warning() const { c = Object::cast_to<Node2D>(c->get_parent()); } - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } return warning + TTR("NavigationRegion2D must be a child or grandchild to a Navigation2D node. It only provides navigation data."); diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h index ba92d27a95..0b9a258a25 100644 --- a/scene/2d/navigation_region_2d.h +++ b/scene/2d/navigation_region_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 42c2585487..70a4e3f0fb 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -475,12 +475,3 @@ void Node2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_index", "get_z_index"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "z_as_relative"), "set_z_as_relative", "is_z_relative"); } - -Node2D::Node2D() { - angle = 0; - _scale = Vector2(1, 1); - skew = 0; - _xform_dirty = false; - z_index = 0; - z_relative = true; -} diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index e20f746447..a5ae5dae0e 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,15 +37,15 @@ class Node2D : public CanvasItem { GDCLASS(Node2D, CanvasItem); Point2 pos; - float angle; - Size2 _scale; - float skew; - int z_index; - bool z_relative; + float angle = 0; + Size2 _scale = Vector2(1, 1); + float skew = 0; + int z_index = 0; + bool z_relative = true; Transform2D _mat; - bool _xform_dirty; + bool _xform_dirty = false; void _update_transform(); @@ -121,7 +121,7 @@ public: Transform2D get_transform() const override; - Node2D(); + Node2D() {} }; #endif // NODE2D_H diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp index 8c9432f2fa..a35ffaa668 100644 --- a/scene/2d/parallax_background.cpp +++ b/scene/2d/parallax_background.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/parallax_background.h b/scene/2d/parallax_background.h index 1667880ddb..578e615be9 100644 --- a/scene/2d/parallax_background.h +++ b/scene/2d/parallax_background.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index 01aa5838b4..129082cb91 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -139,7 +139,7 @@ String ParallaxLayer::get_configuration_warning() const { String warning = Node2D::get_configuration_warning(); if (!Object::cast_to<ParallaxBackground>(get_parent())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("ParallaxLayer node only works when set as child of a ParallaxBackground node."); diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h index 788df19a75..b6895b0aca 100644 --- a/scene/2d/parallax_layer.h +++ b/scene/2d/parallax_layer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index d305f2805e..41c91115ad 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -170,7 +170,7 @@ void PathFollow2D::_update_transform() { } Vector2 pos = c->interpolate_baked(offset, cubic); - if (rotate) { + if (rotates) { float ahead = offset + lookahead; if (loop && ahead >= path_length) { @@ -200,7 +200,7 @@ void PathFollow2D::_update_transform() { tangent_to_curve = (ahead_pos - pos).normalized(); } - Vector2 normal_of_curve = -tangent_to_curve.tangent(); + Vector2 normal_of_curve = -tangent_to_curve.orthogonal(); pos += tangent_to_curve * h_offset; pos += normal_of_curve * v_offset; @@ -257,7 +257,7 @@ String PathFollow2D::get_configuration_warning() const { String warning = Node2D::get_configuration_warning(); if (!Object::cast_to<Path2D>(get_parent())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("PathFollow2D only works when set as a child of a Path2D node."); @@ -279,7 +279,7 @@ void PathFollow2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_unit_offset", "unit_offset"), &PathFollow2D::set_unit_offset); ClassDB::bind_method(D_METHOD("get_unit_offset"), &PathFollow2D::get_unit_offset); - ClassDB::bind_method(D_METHOD("set_rotate", "enable"), &PathFollow2D::set_rotate); + ClassDB::bind_method(D_METHOD("set_rotates", "enable"), &PathFollow2D::set_rotates); ClassDB::bind_method(D_METHOD("is_rotating"), &PathFollow2D::is_rotating); ClassDB::bind_method(D_METHOD("set_cubic_interpolation", "enable"), &PathFollow2D::set_cubic_interpolation); @@ -295,7 +295,7 @@ void PathFollow2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset"), "set_h_offset", "get_h_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset"), "set_v_offset", "get_v_offset"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotate"), "set_rotate", "is_rotating"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotates"), "set_rotates", "is_rotating"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lookahead", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001"), "set_lookahead", "get_lookahead"); @@ -371,13 +371,13 @@ float PathFollow2D::get_lookahead() const { return lookahead; } -void PathFollow2D::set_rotate(bool p_rotate) { - rotate = p_rotate; +void PathFollow2D::set_rotates(bool p_rotates) { + rotates = p_rotates; _update_transform(); } bool PathFollow2D::is_rotating() const { - return rotate; + return rotates; } void PathFollow2D::set_loop(bool p_loop) { @@ -387,14 +387,3 @@ void PathFollow2D::set_loop(bool p_loop) { bool PathFollow2D::has_loop() const { return loop; } - -PathFollow2D::PathFollow2D() { - offset = 0; - h_offset = 0; - v_offset = 0; - path = nullptr; - rotate = true; - cubic = true; - loop = true; - lookahead = 4; -} diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h index 7fea75cd7c..3c5e0e4180 100644 --- a/scene/2d/path_2d.h +++ b/scene/2d/path_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -63,14 +63,14 @@ class PathFollow2D : public Node2D { public: private: - Path2D *path; - real_t offset; - real_t h_offset; - real_t v_offset; - real_t lookahead; - bool cubic; - bool loop; - bool rotate; + Path2D *path = nullptr; + real_t offset = 0; + real_t h_offset = 0; + real_t v_offset = 0; + real_t lookahead = 4; + bool cubic = true; + bool loop = true; + bool rotates = true; void _update_transform(); @@ -99,7 +99,7 @@ public: void set_loop(bool p_loop); bool has_loop() const; - void set_rotate(bool p_rotate); + void set_rotates(bool p_rotates); bool is_rotating() const; void set_cubic_interpolation(bool p_enable); @@ -107,7 +107,7 @@ public: String get_configuration_warning() const override; - PathFollow2D(); + PathFollow2D() {} }; #endif // PATH_2D_H diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 300799ff9d..a65009d072 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -301,7 +301,7 @@ void RigidBody2D::_body_inout(int p_status, ObjectID p_instance, int p_body_shap bool in_scene = E->get().in_scene; - if (E->get().shapes.empty()) { + if (E->get().shapes.is_empty()) { if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody2D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody2D::_body_exit_tree)); @@ -460,8 +460,6 @@ RigidBody2D::Mode RigidBody2D::get_mode() const { void RigidBody2D::set_mass(real_t p_mass) { ERR_FAIL_COND(p_mass <= 0); mass = p_mass; - _change_notify("mass"); - _change_notify("weight"); PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_MASS, mass); } @@ -478,14 +476,6 @@ real_t RigidBody2D::get_inertia() const { return PhysicsServer2D::get_singleton()->body_get_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA); } -void RigidBody2D::set_weight(real_t p_weight) { - set_mass(p_weight / (real_t(GLOBAL_DEF("physics/2d/default_gravity", 98)) / 10)); -} - -real_t RigidBody2D::get_weight() const { - return mass * (real_t(GLOBAL_DEF("physics/2d/default_gravity", 98)) / 10); -} - void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { if (physics_material_override.is_valid()) { if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics))) { @@ -734,7 +724,7 @@ String RigidBody2D::get_configuration_warning() const { String warning = CollisionObject2D::get_configuration_warning(); if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05)) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("Size changes to RigidBody2D (in character or rigid modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."); @@ -753,9 +743,6 @@ void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_inertia"), &RigidBody2D::get_inertia); ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidBody2D::set_inertia); - ClassDB::bind_method(D_METHOD("set_weight", "weight"), &RigidBody2D::set_weight); - ClassDB::bind_method(D_METHOD("get_weight"), &RigidBody2D::get_weight); - ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody2D::set_physics_material_override); ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody2D::get_physics_material_override); @@ -818,7 +805,6 @@ void RigidBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Rigid,Static,Character,Kinematic"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", 0), "set_inertia", "get_inertia"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 1db2d23bb8..159f73b269 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -192,9 +192,6 @@ public: void set_inertia(real_t p_inertia); real_t get_inertia() const; - void set_weight(real_t p_weight); - real_t get_weight() const; - void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); Ref<PhysicsMaterial> get_physics_material_override() const; diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index 26340bb861..82e1b7ac96 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h index e2a8db414a..cdb3052225 100644 --- a/scene/2d/polygon_2d.h +++ b/scene/2d/polygon_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp index 8e4165cf50..ff7a0dbac3 100644 --- a/scene/2d/position_2d.cpp +++ b/scene/2d/position_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h index 01b380bca8..fcaef0e6a3 100644 --- a/scene/2d/position_2d.h +++ b/scene/2d/position_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index e53f89c46d..2c4a549acf 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h index 14932f782b..ff7a970ebf 100644 --- a/scene/2d/ray_cast_2d.h +++ b/scene/2d/ray_cast_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp index 7655416ce2..4ed4c3a8ff 100644 --- a/scene/2d/remote_transform_2d.cpp +++ b/scene/2d/remote_transform_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -189,7 +189,7 @@ String RemoteTransform2D::get_configuration_warning() const { String warning = Node2D::get_configuration_warning(); if (!has_node(remote_node) || !Object::cast_to<Node2D>(get_node(remote_node))) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("Path property must point to a valid Node2D node to work."); diff --git a/scene/2d/remote_transform_2d.h b/scene/2d/remote_transform_2d.h index 8b6f8d9678..cb4bd37f1f 100644 --- a/scene/2d/remote_transform_2d.h +++ b/scene/2d/remote_transform_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index ea1d9f5930..bb5a309f65 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -136,7 +136,7 @@ int Bone2D::get_index_in_skeleton() const { String Bone2D::get_configuration_warning() const { String warning = Node2D::get_configuration_warning(); if (!skeleton) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } if (parent_bone) { @@ -147,7 +147,7 @@ String Bone2D::get_configuration_warning() const { } if (rest == Transform2D(0, 0, 0, 0, 0, 0)) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one."); diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index 7e9ffd98e6..36b9e1b339 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp index a065565a0f..773214deeb 100644 --- a/scene/2d/sprite_2d.cpp +++ b/scene/2d/sprite_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/sprite_2d.h b/scene/2d/sprite_2d.h index 2875d333bb..716882d485 100644 --- a/scene/2d/sprite_2d.h +++ b/scene/2d/sprite_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index bff191a2bf..01b7a9c260 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -1024,7 +1024,9 @@ void TileMap::update_dirty_bitmask() { void TileMap::fix_invalid_tiles() { ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open."); - for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) { + + Map<PosKey, Cell> temp_tile_map = tile_map; + for (Map<PosKey, Cell>::Element *E = temp_tile_map.front(); E; E = E->next()) { if (!tile_set->has_tile(get_cell(E->key().x, E->key().y))) { set_cell(E->key().x, E->key().y, INVALID_CELL); } @@ -1714,7 +1716,7 @@ String TileMap::get_configuration_warning() const { String warning = Node2D::get_configuration_warning(); if (use_parent && !collision_parent) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } return TTR("TileMap with Use Parent on needs a parent CollisionObject2D to give shapes to. Please use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 22b615a379..b1e54aafae 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index 4597300db8..04a1aedcd3 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -405,5 +405,5 @@ TouchScreenButton::TouchScreenButton() { shape_centered = true; shape_visible = true; unit_rect = Ref<RectangleShape2D>(memnew(RectangleShape2D)); - unit_rect->set_extents(Vector2(0.5, 0.5)); + unit_rect->set_size(Vector2(1, 1)); } diff --git a/scene/2d/touch_screen_button.h b/scene/2d/touch_screen_button.h index 287f886c2c..f4ae3ca26c 100644 --- a/scene/2d/touch_screen_button.h +++ b/scene/2d/touch_screen_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp index e217f2a394..f3dddd5af8 100644 --- a/scene/2d/visibility_notifier_2d.cpp +++ b/scene/2d/visibility_notifier_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -317,7 +317,7 @@ String VisibilityEnabler2D::get_configuration_warning() const { #ifdef TOOLS_ENABLED if (is_inside_tree() && get_parent() && (get_parent()->get_filename() == String() && get_parent() != get_tree()->get_edited_scene_root())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("VisibilityEnabler2D works best when used with the edited scene root directly as parent."); diff --git a/scene/2d/visibility_notifier_2d.h b/scene/2d/visibility_notifier_2d.h index 671378bd4e..ea3b5df37b 100644 --- a/scene/2d/visibility_notifier_2d.h +++ b/scene/2d/visibility_notifier_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/y_sort.cpp b/scene/2d/y_sort.cpp index 7c2b41db70..987e4581f4 100644 --- a/scene/2d/y_sort.cpp +++ b/scene/2d/y_sort.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/2d/y_sort.h b/scene/2d/y_sort.h index 62787d6744..0882a88e3a 100644 --- a/scene/2d/y_sort.h +++ b/scene/2d/y_sort.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index b1ffe76662..b69357387e 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -505,11 +505,11 @@ bool Area3D::is_overriding_audio_bus() const { return audio_bus_override; } -void Area3D::set_audio_bus(const StringName &p_audio_bus) { +void Area3D::set_audio_bus_name(const StringName &p_audio_bus) { audio_bus = p_audio_bus; } -StringName Area3D::get_audio_bus() const { +StringName Area3D::get_audio_bus_name() const { for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { if (AudioServer::get_singleton()->get_bus_name(i) == audio_bus) { return audio_bus; @@ -625,8 +625,8 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_audio_bus_override", "enable"), &Area3D::set_audio_bus_override); ClassDB::bind_method(D_METHOD("is_overriding_audio_bus"), &Area3D::is_overriding_audio_bus); - ClassDB::bind_method(D_METHOD("set_audio_bus", "name"), &Area3D::set_audio_bus); - ClassDB::bind_method(D_METHOD("get_audio_bus"), &Area3D::get_audio_bus); + ClassDB::bind_method(D_METHOD("set_audio_bus_name", "name"), &Area3D::set_audio_bus_name); + ClassDB::bind_method(D_METHOD("get_audio_bus_name"), &Area3D::get_audio_bus_name); ClassDB::bind_method(D_METHOD("set_use_reverb_bus", "enable"), &Area3D::set_use_reverb_bus); ClassDB::bind_method(D_METHOD("is_using_reverb_bus"), &Area3D::is_using_reverb_bus); @@ -665,7 +665,7 @@ void Area3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_GROUP("Audio Bus", "audio_bus_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_bus_override"), "set_audio_bus_override", "is_overriding_audio_bus"); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "audio_bus_name", PROPERTY_HINT_ENUM, ""), "set_audio_bus", "get_audio_bus"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "audio_bus_name", PROPERTY_HINT_ENUM, ""), "set_audio_bus_name", "get_audio_bus_name"); ADD_GROUP("Reverb Bus", "reverb_bus_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reverb_bus_enable"), "set_use_reverb_bus", "is_using_reverb_bus"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "reverb_bus_name", PROPERTY_HINT_ENUM, ""), "set_reverb_bus", "get_reverb_bus"); diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h index 7d1f030baf..5d8e1933ba 100644 --- a/scene/3d/area_3d.h +++ b/scene/3d/area_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -190,8 +190,8 @@ public: void set_audio_bus_override(bool p_override); bool is_overriding_audio_bus() const; - void set_audio_bus(const StringName &p_audio_bus); - StringName get_audio_bus() const; + void set_audio_bus_name(const StringName &p_audio_bus); + StringName get_audio_bus_name() const; void set_use_reverb_bus(bool p_enable); bool is_using_reverb_bus() const; diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index b093788d75..2bcf898ced 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -327,9 +327,6 @@ float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { return att; } -void _update_sound() { -} - void AudioStreamPlayer3D::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { velocity_tracker->reset(get_global_transform().origin); @@ -487,7 +484,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { if (area) { if (area->is_overriding_audio_bus()) { //override audio bus - StringName bus_name = area->get_audio_bus(); + StringName bus_name = area->get_audio_bus_name(); output.bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name); } diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 339475b469..bcfe2b2229 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp index 38c9e96fbc..14c8755d99 100644 --- a/scene/3d/baked_lightmap.cpp +++ b/scene/3d/baked_lightmap.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -678,7 +678,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d } TypedArray<Image> images = RS::get_singleton()->bake_render_uv2(mf.mesh->get_rid(), overrides, lightmap_size); - ERR_FAIL_COND_V(images.empty(), BAKE_ERROR_CANT_CREATE_IMAGE); + ERR_FAIL_COND_V(images.is_empty(), BAKE_ERROR_CANT_CREATE_IMAGE); Ref<Image> albedo = images[RS::BAKE_CHANNEL_ALBEDO_ALPHA]; Ref<Image> orm = images[RS::BAKE_CHANNEL_ORM]; diff --git a/scene/3d/baked_lightmap.h b/scene/3d/baked_lightmap.h index 8808569215..7fd1cf892e 100644 --- a/scene/3d/baked_lightmap.h +++ b/scene/3d/baked_lightmap.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index 68303bbfe5..30fe1ecea8 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index 70b871430f..0dddaa1028 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 191159448a..53c3204b3e 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -476,13 +476,13 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_fov"), &Camera3D::get_fov); ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera3D::get_frustum_offset); ClassDB::bind_method(D_METHOD("get_size"), &Camera3D::get_size); - ClassDB::bind_method(D_METHOD("get_zfar"), &Camera3D::get_zfar); - ClassDB::bind_method(D_METHOD("get_znear"), &Camera3D::get_znear); + ClassDB::bind_method(D_METHOD("get_far"), &Camera3D::get_far); + ClassDB::bind_method(D_METHOD("get_near"), &Camera3D::get_near); ClassDB::bind_method(D_METHOD("set_fov"), &Camera3D::set_fov); ClassDB::bind_method(D_METHOD("set_frustum_offset"), &Camera3D::set_frustum_offset); ClassDB::bind_method(D_METHOD("set_size"), &Camera3D::set_size); - ClassDB::bind_method(D_METHOD("set_zfar"), &Camera3D::set_zfar); - ClassDB::bind_method(D_METHOD("set_znear"), &Camera3D::set_znear); + ClassDB::bind_method(D_METHOD("set_far"), &Camera3D::set_far); + ClassDB::bind_method(D_METHOD("set_near"), &Camera3D::set_near); ClassDB::bind_method(D_METHOD("get_projection"), &Camera3D::get_projection); ClassDB::bind_method(D_METHOD("set_projection"), &Camera3D::set_projection); ClassDB::bind_method(D_METHOD("set_h_offset", "ofs"), &Camera3D::set_h_offset); @@ -519,8 +519,8 @@ void Camera3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_EXP_RANGE, "0.001,8192,0.001,or_greater"), "set_znear", "get_znear"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01,or_greater"), "set_zfar", "get_zfar"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_EXP_RANGE, "0.001,10,0.001,or_greater"), "set_near", "get_near"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_EXP_RANGE, "0.01,4000,0.01,or_greater"), "set_far", "get_far"); BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE); BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL); @@ -542,7 +542,7 @@ float Camera3D::get_size() const { return size; } -float Camera3D::get_znear() const { +float Camera3D::get_near() const { return near; } @@ -550,7 +550,7 @@ Vector2 Camera3D::get_frustum_offset() const { return frustum_offset; } -float Camera3D::get_zfar() const { +float Camera3D::get_far() const { return far; } @@ -572,8 +572,8 @@ void Camera3D::set_size(float p_size) { _change_notify("size"); } -void Camera3D::set_znear(float p_znear) { - near = p_znear; +void Camera3D::set_near(float p_near) { + near = p_near; _update_camera_mode(); } @@ -582,8 +582,8 @@ void Camera3D::set_frustum_offset(Vector2 p_offset) { _update_camera_mode(); } -void Camera3D::set_zfar(float p_zfar) { - far = p_zfar; +void Camera3D::set_far(float p_far) { + far = p_far; _update_camera_mode(); } @@ -662,7 +662,7 @@ Camera3D::Camera3D() { viewport = nullptr; force_change = false; mode = PROJECTION_PERSPECTIVE; - set_perspective(75.0, 0.05, 100.0); + set_perspective(75.0, 0.05, 4000.0); keep_aspect = KEEP_HEIGHT; layers = 0xfffff; v_offset = 0; diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index 04cec92b14..aa36c8800d 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -121,16 +121,16 @@ public: float get_fov() const; float get_size() const; - float get_zfar() const; - float get_znear() const; + float get_far() const; + float get_near() const; Vector2 get_frustum_offset() const; Projection get_projection() const; void set_fov(float p_fov); void set_size(float p_size); - void set_zfar(float p_zfar); - void set_znear(float p_znear); + void set_far(float p_far); + void set_near(float p_near); void set_frustum_offset(Vector2 p_offset); virtual Transform get_camera_transform() const; diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 356992e922..f1d7ac64b2 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -349,8 +349,8 @@ bool CollisionObject3D::get_capture_input_on_drag() const { String CollisionObject3D::get_configuration_warning() const { String warning = Node3D::get_configuration_warning(); - if (shapes.empty()) { - if (!warning.empty()) { + if (shapes.is_empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape."); diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index 39e7df40a8..da65791b9c 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index b8a4ab74ee..5e77937533 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -159,14 +159,14 @@ String CollisionPolygon3D::get_configuration_warning() const { String warning = Node3D::get_configuration_warning(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, KinematicBody3D, etc. to give them a shape."); } - if (polygon.empty()) { - if (!warning.empty()) { + if (polygon.is_empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("An empty CollisionPolygon3D has no effect on collision."); diff --git a/scene/3d/collision_polygon_3d.h b/scene/3d/collision_polygon_3d.h index bab85b6011..ec13b9af6d 100644 --- a/scene/3d/collision_polygon_3d.h +++ b/scene/3d/collision_polygon_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index e1c691b89a..503d1be104 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -60,7 +60,7 @@ void CollisionShape3D::make_convex_from_siblings() { if (m.is_valid()) { for (int j = 0; j < m->get_surface_count(); j++) { Array a = m->surface_get_arrays(j); - if (!a.empty()) { + if (!a.is_empty()) { Vector<Vector3> v = a[RenderingServer::ARRAY_VERTEX]; for (int k = 0; k < v.size(); k++) { vertices.append(mi->get_transform().xform(v[k])); @@ -93,7 +93,6 @@ void CollisionShape3D::_notification(int p_what) { if (shape.is_valid()) { parent->shape_owner_add_shape(owner_id, shape); } - _update_in_shape_owner(); } } break; case NOTIFICATION_ENTER_TREE: { @@ -127,14 +126,14 @@ String CollisionShape3D::get_configuration_warning() const { String warning = Node3D::get_configuration_warning(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, KinematicBody3D, etc. to give them a shape."); } if (!shape.is_valid()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it."); @@ -144,7 +143,7 @@ String CollisionShape3D::get_configuration_warning() const { Object::cast_to<RigidBody3D>(get_parent()) && Object::cast_to<ConcavePolygonShape3D>(*shape) && Object::cast_to<RigidBody3D>(get_parent())->get_mode() != RigidBody3D::MODE_STATIC) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("ConcavePolygonShape3D doesn't support RigidBody3D in another mode than static."); @@ -170,6 +169,9 @@ void CollisionShape3D::_bind_methods() { } void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) { + if (p_shape == shape) { + return; + } if (!shape.is_null()) { shape->unregister_owner(this); shape->disconnect("changed", callable_mp(this, &CollisionShape3D::_shape_changed)); diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h index 35f40d27b1..5f556b01a9 100644 --- a/scene/3d/collision_shape_3d.h +++ b/scene/3d/collision_shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index c977e0d4aa..3562f7b778 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,7 +39,7 @@ AABB CPUParticles3D::get_aabb() const { return AABB(); } -Vector<Face3> CPUParticles3D::get_faces(uint32_t p_usage_flags) const { +Vector<Face3> CPUParticles3D::get_faces(uint32_t p_usage_particle_flags) const { return Vector<Face3>(); } @@ -368,17 +368,17 @@ Ref<Gradient> CPUParticles3D::get_color_ramp() const { return color_ramp; } -void CPUParticles3D::set_particle_flag(Flags p_flag, bool p_enable) { - ERR_FAIL_INDEX(p_flag, FLAG_MAX); - flags[p_flag] = p_enable; - if (p_flag == FLAG_DISABLE_Z) { +void CPUParticles3D::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) { + ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX); + particle_flags[p_particle_flag] = p_enable; + if (p_particle_flag == PARTICLE_FLAG_DISABLE_Z) { _change_notify(); } } -bool CPUParticles3D::get_particle_flag(Flags p_flag) const { - ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); - return flags[p_flag]; +bool CPUParticles3D::get_particle_flag(ParticleFlags p_particle_flag) const { + ERR_FAIL_INDEX_V(p_particle_flag, PARTICLE_FLAG_MAX, false); + return particle_flags[p_particle_flag]; } void CPUParticles3D::set_emission_shape(EmissionShape p_shape) { @@ -459,7 +459,7 @@ void CPUParticles3D::_validate_property(PropertyInfo &property) const { property.usage = 0; } - if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) { + if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { property.usage = 0; } } @@ -675,7 +675,7 @@ void CPUParticles3D::_particles_process(float p_delta) { p.hue_rot_rand = Math::randf(); p.anim_offset_rand = Math::randf(); - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { float angle1_rad = Math::atan2(direction.y, direction.x) + (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0; Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0); p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); @@ -725,12 +725,12 @@ void CPUParticles3D::_particles_process(float p_delta) { p.transform.origin = emission_points.get(random_idx); if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS && emission_normals.size() == pc) { - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { Vector3 normal = emission_normals.get(random_idx); Vector2 normal_2d(normal.x, normal.y); Transform2D m2; m2.set_axis(0, normal_2d); - m2.set_axis(1, normal_2d.tangent()); + m2.set_axis(1, normal_2d.orthogonal()); Vector2 velocity_2d(p.velocity.x, p.velocity.y); velocity_2d = m2.basis_xform(velocity_2d); p.velocity.x = velocity_2d.x; @@ -762,7 +762,7 @@ void CPUParticles3D::_particles_process(float p_delta) { p.transform = emission_xform * p.transform; } - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { p.velocity.z = 0.0; p.transform.origin.z = 0.0; } @@ -783,7 +783,7 @@ void CPUParticles3D::_particles_process(float p_delta) { } float tex_orbit_velocity = 0.0; - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(p.custom[1]); } @@ -830,7 +830,7 @@ void CPUParticles3D::_particles_process(float p_delta) { Vector3 force = gravity; Vector3 position = p.transform.origin; - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { position.z = 0.0; } //apply linear acceleration @@ -840,7 +840,7 @@ void CPUParticles3D::_particles_process(float p_delta) { Vector3 diff = position - org; force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector3(); //apply tangential acceleration; - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { Vector2 yx = Vector2(diff.y, diff.x); Vector2 yx2 = (yx * Vector2(-1.0, 1.0)).normalized(); force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); @@ -852,7 +852,7 @@ void CPUParticles3D::_particles_process(float p_delta) { //apply attractor forces p.velocity += force * local_delta; //orbit velocity - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]); if (orbit_amount != 0.0) { float ang = orbit_amount * local_delta * Math_PI * 2.0; @@ -923,8 +923,8 @@ void CPUParticles3D::_particles_process(float p_delta) { p.color *= p.base_color; - if (flags[FLAG_DISABLE_Z]) { - if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { if (p.velocity.length() > 0.0) { p.transform.basis.set_axis(1, p.velocity.normalized()); } else { @@ -941,7 +941,7 @@ void CPUParticles3D::_particles_process(float p_delta) { } else { //orient particle Y towards velocity - if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { if (p.velocity.length() > 0.0) { p.transform.basis.set_axis(1, p.velocity.normalized()); } else { @@ -959,7 +959,7 @@ void CPUParticles3D::_particles_process(float p_delta) { } //turn particle by rotation in Y - if (flags[FLAG_ROTATE_Y]) { + if (particle_flags[PARTICLE_FLAG_ROTATE_Y]) { Basis rot_y(Vector3(0, 1, 0), p.custom[0]); p.transform.basis = p.transform.basis * rot_y; } @@ -973,7 +973,7 @@ void CPUParticles3D::_particles_process(float p_delta) { p.transform.basis.scale(Vector3(1, 1, 1) * base_scale); - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { p.velocity.z = 0.0; p.transform.origin.z = 0.0; } @@ -1199,9 +1199,9 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) { set_color_ramp(gt->get_gradient()); } - set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY)); - set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y)); - set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z)); + set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); + set_particle_flag(PARTICLE_FLAG_ROTATE_Y, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ROTATE_Y)); + set_particle_flag(PARTICLE_FLAG_DISABLE_Z, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_DISABLE_Z)); set_emission_shape(EmissionShape(material->get_emission_shape())); set_emission_sphere_radius(material->get_emission_sphere_radius()); @@ -1318,8 +1318,8 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles3D::set_color_ramp); ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles3D::get_color_ramp); - ClassDB::bind_method(D_METHOD("set_particle_flag", "flag", "enable"), &CPUParticles3D::set_particle_flag); - ClassDB::bind_method(D_METHOD("get_particle_flag", "flag"), &CPUParticles3D::get_particle_flag); + ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &CPUParticles3D::set_particle_flag); + ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &CPUParticles3D::get_particle_flag); ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &CPUParticles3D::set_emission_shape); ClassDB::bind_method(D_METHOD("get_emission_shape"), &CPUParticles3D::get_emission_shape); @@ -1351,10 +1351,10 @@ void CPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "emission_points"), "set_emission_points", "get_emission_points"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors"); - ADD_GROUP("Flags", "flag_"); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_particle_flag", "get_particle_flag", FLAG_ALIGN_Y_TO_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_particle_flag", "get_particle_flag", FLAG_ROTATE_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_particle_flag", "get_particle_flag", FLAG_DISABLE_Z); + ADD_GROUP("Particle Flags", "particle_flag_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_rotate_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ROTATE_Y); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_disable_z"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_DISABLE_Z); ADD_GROUP("Direction", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "direction"), "set_direction", "get_direction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread"); @@ -1426,10 +1426,10 @@ void CPUParticles3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET); BIND_ENUM_CONSTANT(PARAM_MAX); - BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY); - BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); - BIND_ENUM_CONSTANT(FLAG_DISABLE_Z); - BIND_ENUM_CONSTANT(FLAG_MAX); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_ROTATE_Y); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_DISABLE_Z); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_MAX); BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE); @@ -1493,8 +1493,8 @@ CPUParticles3D::CPUParticles3D() { set_param_randomness(Parameter(i), 0); } - for (int i = 0; i < FLAG_MAX; i++) { - flags[i] = false; + for (int i = 0; i < PARTICLE_FLAG_MAX; i++) { + particle_flags[i] = false; } can_update = false; diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index da4811b60e..3ee03ff3d8 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -61,11 +61,11 @@ public: PARAM_MAX }; - enum Flags { - FLAG_ALIGN_Y_TO_VELOCITY, - FLAG_ROTATE_Y, - FLAG_DISABLE_Z, - FLAG_MAX + enum ParticleFlags { + PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, + PARTICLE_FLAG_ROTATE_Y, + PARTICLE_FLAG_DISABLE_Z, + PARTICLE_FLAG_MAX }; enum EmissionShape { @@ -160,7 +160,7 @@ private: Color color; Ref<Gradient> color_ramp; - bool flags[FLAG_MAX]; + bool particle_flags[PARTICLE_FLAG_MAX]; EmissionShape emission_shape; float emission_sphere_radius; @@ -256,8 +256,8 @@ public: void set_color_ramp(const Ref<Gradient> &p_ramp); Ref<Gradient> get_color_ramp() const; - void set_particle_flag(Flags p_flag, bool p_enable); - bool get_particle_flag(Flags p_flag) const; + void set_particle_flag(ParticleFlags p_particle_flag, bool p_enable); + bool get_particle_flag(ParticleFlags p_particle_flag) const; void set_emission_shape(EmissionShape p_shape); void set_emission_sphere_radius(float p_radius); @@ -290,7 +290,7 @@ public: VARIANT_ENUM_CAST(CPUParticles3D::DrawOrder) VARIANT_ENUM_CAST(CPUParticles3D::Parameter) -VARIANT_ENUM_CAST(CPUParticles3D::Flags) +VARIANT_ENUM_CAST(CPUParticles3D::ParticleFlags) VARIANT_ENUM_CAST(CPUParticles3D::EmissionShape) #endif // CPU_PARTICLES_H diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index fb72e10171..e67802cfc4 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/decal.h b/scene/3d/decal.h index e821461772..500a1425f2 100644 --- a/scene/3d/decal.h +++ b/scene/3d/decal.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp index fd592012f8..b00a0ec30b 100644 --- a/scene/3d/gi_probe.cpp +++ b/scene/3d/gi_probe.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -516,7 +516,7 @@ String GIProbe::get_configuration_warning() const { String warning = VisualInstance3D::get_configuration_warning(); if (RenderingServer::get_singleton()->is_low_end()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("GIProbes are not supported by the GLES2 video driver.\nUse a BakedLightmap instead."); diff --git a/scene/3d/gi_probe.h b/scene/3d/gi_probe.h index 2dadf48a06..afea94fe12 100644 --- a/scene/3d/gi_probe.h +++ b/scene/3d/gi_probe.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index ec33d7bcab..7d77578b01 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index f0e5f05e5b..0c1a1a510c 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 1f0d5d587d..97241be60f 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -293,11 +293,11 @@ void GPUParticlesCollisionSDF::_find_closest_distance(const Vector3 &p_pos, cons SGN(cb.cross(nor).dot(pb)) + SGN(ac.cross(nor).dot(pc)) < 2.0) ? - MIN(MIN( + MIN(MIN( Vector3_dot2(ba * CLAMP(ba.dot(pa) / Vector3_dot2(ba), 0.0, 1.0) - pa), Vector3_dot2(cb * CLAMP(cb.dot(pb) / Vector3_dot2(cb), 0.0, 1.0) - pb)), Vector3_dot2(ac * CLAMP(ac.dot(pc) / Vector3_dot2(ac), 0.0, 1.0) - pc)) : - nor.dot(pa) * nor.dot(pa) / Vector3_dot2(nor)); + nor.dot(pa) * nor.dot(pa) / Vector3_dot2(nor)); closest_distance = MIN(closest_distance, inside_d); } diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h index 9b644ade6b..7d6301ee50 100644 --- a/scene/3d/gpu_particles_collision_3d.h +++ b/scene/3d/gpu_particles_collision_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/immediate_geometry_3d.cpp b/scene/3d/immediate_geometry_3d.cpp index 7ccfd527a1..17410d5870 100644 --- a/scene/3d/immediate_geometry_3d.cpp +++ b/scene/3d/immediate_geometry_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/immediate_geometry_3d.h b/scene/3d/immediate_geometry_3d.h index 1403936e22..8e546e27e7 100644 --- a/scene/3d/immediate_geometry_3d.h +++ b/scene/3d/immediate_geometry_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 2dc3cd3230..b9293b364d 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -394,6 +394,15 @@ bool DirectionalLight3D::is_blend_splits_enabled() const { return blend_splits; } +void DirectionalLight3D::set_sky_only(bool p_sky_only) { + sky_only = p_sky_only; + RS::get_singleton()->light_directional_set_sky_only(light, p_sky_only); +} + +bool DirectionalLight3D::is_sky_only() const { + return sky_only; +} + void DirectionalLight3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shadow_mode", "mode"), &DirectionalLight3D::set_shadow_mode); ClassDB::bind_method(D_METHOD("get_shadow_mode"), &DirectionalLight3D::get_shadow_mode); @@ -404,6 +413,9 @@ void DirectionalLight3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_blend_splits", "enabled"), &DirectionalLight3D::set_blend_splits); ClassDB::bind_method(D_METHOD("is_blend_splits_enabled"), &DirectionalLight3D::is_blend_splits_enabled); + ClassDB::bind_method(D_METHOD("set_sky_only", "priority"), &DirectionalLight3D::set_sky_only); + ClassDB::bind_method(D_METHOD("is_sky_only"), &DirectionalLight3D::is_sky_only); + ADD_GROUP("Directional Shadow", "directional_shadow_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_mode", PROPERTY_HINT_ENUM, "Orthogonal (Fast),PSSM 2 Splits (Average),PSSM 4 Splits (Slow)"), "set_shadow_mode", "get_shadow_mode"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_1", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_1_OFFSET); @@ -415,6 +427,8 @@ void DirectionalLight3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_max_distance", PROPERTY_HINT_EXP_RANGE, "0,8192,0.1,or_greater"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_pancake_size", PROPERTY_HINT_EXP_RANGE, "0,1024,0.1,or_greater"), "set_param", "get_param", PARAM_SHADOW_PANCAKE_SIZE); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_in_sky_only"), "set_sky_only", "is_sky_only"); + BIND_ENUM_CONSTANT(SHADOW_ORTHOGONAL); BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS); BIND_ENUM_CONSTANT(SHADOW_PARALLEL_4_SPLITS); @@ -448,7 +462,7 @@ String OmniLight3D::get_configuration_warning() const { String warning = Light3D::get_configuration_warning(); if (!has_shadow() && get_projector().is_valid()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("Projector texture only works with shadows active."); @@ -482,14 +496,14 @@ String SpotLight3D::get_configuration_warning() const { String warning = Light3D::get_configuration_warning(); if (has_shadow() && get_param(PARAM_SPOT_ANGLE) >= 90.0) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows."); } if (!has_shadow() && get_projector().is_valid()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("Projector texture only works with shadows active."); diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index 69c0478b86..833598c4a1 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -158,6 +158,7 @@ private: bool blend_splits; ShadowMode shadow_mode; ShadowDepthRange shadow_depth_range; + bool sky_only = false; protected: static void _bind_methods(); @@ -172,6 +173,9 @@ public: void set_blend_splits(bool p_enable); bool is_blend_splits_enabled() const; + void set_sky_only(bool p_sky_only); + bool is_sky_only() const; + DirectionalLight3D(); }; diff --git a/scene/3d/lightmap_probe.cpp b/scene/3d/lightmap_probe.cpp index ee21934b80..830b97ffab 100644 --- a/scene/3d/lightmap_probe.cpp +++ b/scene/3d/lightmap_probe.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/lightmap_probe.h b/scene/3d/lightmap_probe.h index c4bd33556f..df87ed49dd 100644 --- a/scene/3d/lightmap_probe.h +++ b/scene/3d/lightmap_probe.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/lightmapper.cpp b/scene/3d/lightmapper.cpp index 26faf5154c..c17ac52aa2 100644 --- a/scene/3d/lightmapper.cpp +++ b/scene/3d/lightmapper.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index ccf9bde279..a07a964c01 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/listener_3d.cpp b/scene/3d/listener_3d.cpp index 0a5b9ad09f..23adb0aa19 100644 --- a/scene/3d/listener_3d.cpp +++ b/scene/3d/listener_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/listener_3d.h b/scene/3d/listener_3d.h index 4b6923d6e8..2a78efb2c7 100644 --- a/scene/3d/listener_3d.h +++ b/scene/3d/listener_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 13f40aed4f..865510731e 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h index 4434e305e8..822e04e656 100644 --- a/scene/3d/mesh_instance_3d.h +++ b/scene/3d/mesh_instance_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/multimesh_instance_3d.cpp b/scene/3d/multimesh_instance_3d.cpp index 88dff111f7..2adef115cf 100644 --- a/scene/3d/multimesh_instance_3d.cpp +++ b/scene/3d/multimesh_instance_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/multimesh_instance_3d.h b/scene/3d/multimesh_instance_3d.h index 6e075b7f7f..63735fd3a6 100644 --- a/scene/3d/multimesh_instance_3d.h +++ b/scene/3d/multimesh_instance_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/navigation_3d.cpp b/scene/3d/navigation_3d.cpp index 851966db2b..2a7195be1a 100644 --- a/scene/3d/navigation_3d.cpp +++ b/scene/3d/navigation_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/navigation_3d.h b/scene/3d/navigation_3d.h index 890caed171..3e8adaf5ff 100644 --- a/scene/3d/navigation_3d.h +++ b/scene/3d/navigation_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index f9f8f276a3..8917cc4664 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -290,7 +290,7 @@ String NavigationAgent3D::get_configuration_warning() const { String warning = Node::get_configuration_warning(); if (!Object::cast_to<Node3D>(get_parent())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("The NavigationAgent3D can be used only under a spatial node."); diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index dcfd302561..bd890a051b 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index adbff06ed6..01bf7de913 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -116,7 +116,7 @@ String NavigationObstacle3D::get_configuration_warning() const { String warning = Node::get_configuration_warning(); if (!Object::cast_to<Node3D>(get_parent())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("The NavigationObstacle3D only serves to provide collision avoidance to a spatial object."); diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h index f2dc9182ca..b8d05b8a87 100644 --- a/scene/3d/navigation_obstacle_3d.h +++ b/scene/3d/navigation_obstacle_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 3d3467583d..b7b1415091 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -182,6 +182,7 @@ void NavigationRegion3D::bake_navigation_mesh() { void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) { set_navigation_mesh(p_nav_mesh); bake_thread = nullptr; + emit_signal("bake_finished"); } String NavigationRegion3D::get_configuration_warning() const { @@ -192,7 +193,7 @@ String NavigationRegion3D::get_configuration_warning() const { String warning = Node3D::get_configuration_warning(); if (!navmesh.is_valid()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("A NavigationMesh resource must be set or created for this node to work."); @@ -207,7 +208,7 @@ String NavigationRegion3D::get_configuration_warning() const { c = Object::cast_to<Node3D>(c->get_parent()); } - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } return warning + TTR("NavigationRegion3D must be a child or grandchild to a Navigation3D node. It only provides navigation data."); diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index 4b1d59206b..a5b8c2cd5e 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index e8005f38ed..2a49e60669 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -239,8 +239,8 @@ void Node3D::set_transform(const Transform &p_transform) { void Node3D::set_global_transform(const Transform &p_transform) { Transform xform = (data.parent && !data.top_level_active) ? - data.parent->get_global_transform().affine_inverse() * p_transform : - p_transform; + data.parent->get_global_transform().affine_inverse() * p_transform : + p_transform; set_transform(xform); } @@ -784,28 +784,4 @@ void Node3D::_bind_methods() { } Node3D::Node3D() : - xform_change(this) { - data.dirty = DIRTY_NONE; - data.children_lock = 0; - - data.ignore_notification = false; - data.top_level = false; - data.top_level_active = false; - data.scale = Vector3(1, 1, 1); - data.viewport = nullptr; - data.inside_world = false; - data.visible = true; - data.disable_scale = false; - -#ifdef TOOLS_ENABLED - data.gizmo_disabled = false; - data.gizmo_dirty = false; -#endif - data.notify_local_transform = false; - data.notify_transform = false; - data.parent = nullptr; - data.C = nullptr; -} - -Node3D::~Node3D() { -} + xform_change(this) {} diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 5fb421c930..8610e2c0bd 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -65,32 +65,32 @@ class Node3D : public Node { mutable Transform global_transform; mutable Transform local_transform; mutable Vector3 rotation; - mutable Vector3 scale; + mutable Vector3 scale = Vector3(1, 1, 1); - mutable int dirty; + mutable int dirty = DIRTY_NONE; - Viewport *viewport; + Viewport *viewport = nullptr; - bool top_level_active; - bool top_level; - bool inside_world; + bool top_level_active = false; + bool top_level = false; + bool inside_world = false; - int children_lock; - Node3D *parent; + int children_lock = 0; + Node3D *parent = nullptr; List<Node3D *> children; - List<Node3D *>::Element *C; + List<Node3D *>::Element *C = nullptr; - bool ignore_notification; - bool notify_local_transform; - bool notify_transform; + bool ignore_notification = false; + bool notify_local_transform = false; + bool notify_transform = false; - bool visible; - bool disable_scale; + bool visible = true; + bool disable_scale = false; #ifdef TOOLS_ENABLED Ref<Node3DGizmo> gizmo; - bool gizmo_disabled; - bool gizmo_dirty; + bool gizmo_disabled = false; + bool gizmo_dirty = false; #endif } data; @@ -197,7 +197,6 @@ public: void force_update_transform(); Node3D(); - ~Node3D(); }; #endif // NODE_3D_H diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index f25a64c567..3f048beb71 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -94,10 +94,6 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { return; } - if (delta_offset == 0) { - return; - } - float bl = c->get_baked_length(); if (bl == 0.0) { return; @@ -156,7 +152,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { t.origin = pos; - if (p_update_xyz_rot) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree + if (p_update_xyz_rot && delta_offset != 0) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree. Vector3 t_prev = (pos - c->interpolate_baked(offset - delta_offset, cubic)).normalized(); Vector3 t_cur = (c->interpolate_baked(offset + delta_offset, cubic) - pos).normalized(); @@ -253,14 +249,14 @@ String PathFollow3D::get_configuration_warning() const { String warning = Node3D::get_configuration_warning(); if (!Object::cast_to<Path3D>(get_parent())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("PathFollow3D only works when set as a child of a Path3D node."); } else { Path3D *path = Object::cast_to<Path3D>(get_parent()); if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled() && rotation_mode == ROTATION_ORIENTED) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource."); @@ -389,14 +385,3 @@ void PathFollow3D::set_loop(bool p_loop) { bool PathFollow3D::has_loop() const { return loop; } - -PathFollow3D::PathFollow3D() { - offset = 0; - delta_offset = 0; - h_offset = 0; - v_offset = 0; - path = nullptr; - rotation_mode = ROTATION_XYZ; - cubic = true; - loop = true; -} diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 1b0f5fa4e0..15e38c95e7 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -65,14 +65,14 @@ public: }; private: - Path3D *path; - real_t delta_offset; // change in offset since last _update_transform - real_t offset; - real_t h_offset; - real_t v_offset; - bool cubic; - bool loop; - RotationMode rotation_mode; + Path3D *path = nullptr; + real_t delta_offset = 0; // Change in offset since last _update_transform. + real_t offset = 0; + real_t h_offset = 0; + real_t v_offset = 0; + bool cubic = true; + bool loop = true; + RotationMode rotation_mode = ROTATION_XYZ; void _update_transform(bool p_update_xyz_rot = true); @@ -106,7 +106,7 @@ public: String get_configuration_warning() const override; - PathFollow3D(); + PathFollow3D() {} }; VARIANT_ENUM_CAST(PathFollow3D::RotationMode); diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index a3e00d2cc3..5645923f22 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -311,7 +311,7 @@ void RigidBody3D::_body_inout(int p_status, ObjectID p_instance, int p_body_shap bool in_tree = E->get().in_tree; - if (E->get().shapes.empty()) { + if (E->get().shapes.is_empty()) { if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody3D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody3D::_body_exit_tree)); @@ -478,8 +478,6 @@ RigidBody3D::Mode RigidBody3D::get_mode() const { void RigidBody3D::set_mass(real_t p_mass) { ERR_FAIL_COND(p_mass <= 0); mass = p_mass; - _change_notify("mass"); - _change_notify("weight"); PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_MASS, mass); } @@ -487,14 +485,6 @@ real_t RigidBody3D::get_mass() const { return mass; } -void RigidBody3D::set_weight(real_t p_weight) { - set_mass(p_weight / real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8))); -} - -real_t RigidBody3D::get_weight() const { - return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8)); -} - void RigidBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { if (physics_material_override.is_valid()) { if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics))) { @@ -724,7 +714,7 @@ String RigidBody3D::get_configuration_warning() const { String warning = CollisionObject3D::get_configuration_warning(); if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("Size changes to RigidBody3D (in character or rigid modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."); @@ -740,9 +730,6 @@ void RigidBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidBody3D::set_mass); ClassDB::bind_method(D_METHOD("get_mass"), &RigidBody3D::get_mass); - ClassDB::bind_method(D_METHOD("set_weight", "weight"), &RigidBody3D::set_weight); - ClassDB::bind_method(D_METHOD("get_weight"), &RigidBody3D::get_weight); - ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody3D::set_physics_material_override); ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody3D::get_physics_material_override); @@ -802,7 +789,6 @@ void RigidBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Rigid,Static,Character,Kinematic"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); @@ -1985,7 +1971,7 @@ bool PhysicalBone3D::_set(const StringName &p_name, const Variant &p_value) { } if (joint_data) { - if (joint_data->_set(p_name, p_value)) { + if (joint_data->_set(p_name, p_value, joint)) { #ifdef TOOLS_ENABLED if (get_gizmo().is_valid()) { get_gizmo()->redraw(); @@ -2121,9 +2107,6 @@ void PhysicalBone3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mass", "mass"), &PhysicalBone3D::set_mass); ClassDB::bind_method(D_METHOD("get_mass"), &PhysicalBone3D::get_mass); - ClassDB::bind_method(D_METHOD("set_weight", "weight"), &PhysicalBone3D::set_weight); - ClassDB::bind_method(D_METHOD("get_weight"), &PhysicalBone3D::get_weight); - ClassDB::bind_method(D_METHOD("set_friction", "friction"), &PhysicalBone3D::set_friction); ClassDB::bind_method(D_METHOD("get_friction"), &PhysicalBone3D::get_friction); @@ -2154,7 +2137,6 @@ void PhysicalBone3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "body_offset"), "set_body_offset", "get_body_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_weight", "get_weight"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale"); @@ -2456,14 +2438,6 @@ real_t PhysicalBone3D::get_mass() const { return mass; } -void PhysicalBone3D::set_weight(real_t p_weight) { - set_mass(p_weight / real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8))); -} - -real_t PhysicalBone3D::get_weight() const { - return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8)); -} - void PhysicalBone3D::set_friction(real_t p_friction) { ERR_FAIL_COND(p_friction < 0 || p_friction > 1); diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index da947ba02e..d9b95e6551 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -185,9 +185,6 @@ public: virtual float get_inverse_mass() const override { return 1.0 / mass; } - void set_weight(real_t p_weight); - real_t get_weight() const; - void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); Ref<PhysicsMaterial> get_physics_material_override() const; @@ -373,7 +370,7 @@ public: virtual JointType get_joint_type() { return JOINT_TYPE_NONE; } /// "j" is used to set the parameter inside the PhysicsServer3D - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; @@ -383,7 +380,7 @@ public: struct PinJointData : public JointData { virtual JointType get_joint_type() { return JOINT_TYPE_PIN; } - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; @@ -397,7 +394,7 @@ public: struct ConeJointData : public JointData { virtual JointType get_joint_type() { return JOINT_TYPE_CONE; } - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; @@ -414,7 +411,7 @@ public: struct HingeJointData : public JointData { virtual JointType get_joint_type() { return JOINT_TYPE_HINGE; } - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; @@ -434,7 +431,7 @@ public: struct SliderJointData : public JointData { virtual JointType get_joint_type() { return JOINT_TYPE_SLIDER; } - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; @@ -481,7 +478,7 @@ public: virtual JointType get_joint_type() { return JOINT_TYPE_6DOF; } - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; @@ -572,9 +569,6 @@ public: void set_mass(real_t p_mass); real_t get_mass() const; - void set_weight(real_t p_weight); - real_t get_weight() const; - void set_friction(real_t p_friction); real_t get_friction() const; diff --git a/scene/3d/physics_joint_3d.cpp b/scene/3d/physics_joint_3d.cpp index ab9cdb9fd8..326b91b6ed 100644 --- a/scene/3d/physics_joint_3d.cpp +++ b/scene/3d/physics_joint_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,6 +30,37 @@ #include "physics_joint_3d.h" +#include "scene/scene_string_names.h" + +void Joint3D::_disconnect_signals() { + Node *node_a = get_node_or_null(a); + PhysicsBody3D *body_a = Object::cast_to<PhysicsBody3D>(node_a); + if (body_a) { + body_a->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree)); + } + + Node *node_b = get_node_or_null(b); + PhysicsBody3D *body_b = Object::cast_to<PhysicsBody3D>(node_b); + if (body_b) { + body_b->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree)); + } +} + +void Joint3D::_body_exit_tree(const ObjectID &p_body_id) { + _disconnect_signals(); + Object *object = ObjectDB::get_instance(p_body_id); + PhysicsBody3D *body = Object::cast_to<PhysicsBody3D>(object); + ERR_FAIL_NULL(body); + RID body_rid = body->get_rid(); + if (ba == body_rid) { + a = NodePath(); + } + if (bb == body_rid) { + b = NodePath(); + } + _update_joint(); +} + void Joint3D::_update_joint(bool p_only_free) { if (joint.is_valid()) { if (ba.is_valid() && bb.is_valid()) { @@ -47,8 +78,8 @@ void Joint3D::_update_joint(bool p_only_free) { return; } - Node *node_a = has_node(get_node_a()) ? get_node(get_node_a()) : (Node *)nullptr; - Node *node_b = has_node(get_node_b()) ? get_node(get_node_b()) : (Node *)nullptr; + Node *node_a = get_node_or_null(a); + Node *node_b = get_node_or_null(b); PhysicsBody3D *body_a = Object::cast_to<PhysicsBody3D>(node_a); PhysicsBody3D *body_b = Object::cast_to<PhysicsBody3D>(node_b); @@ -83,22 +114,27 @@ void Joint3D::_update_joint(bool p_only_free) { return; } - if (!body_a) { - SWAP(body_a, body_b); - } - warning = String(); update_configuration_warning(); - joint = _configure_joint(body_a, body_b); + if (body_a) { + joint = _configure_joint(body_a, body_b); + } else if (body_b) { + joint = _configure_joint(body_b, nullptr); + } ERR_FAIL_COND_MSG(!joint.is_valid(), "Failed to configure the joint."); PhysicsServer3D::get_singleton()->joint_set_solver_priority(joint, solver_priority); - ba = body_a->get_rid(); + if (body_a) { + ba = body_a->get_rid(); + body_a->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree), make_binds(body_a->get_instance_id())); + } + if (body_b) { bb = body_b->get_rid(); + body_b->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree), make_binds(body_b->get_instance_id())); } PhysicsServer3D::get_singleton()->joint_disable_collisions_between_bodies(joint, exclude_from_collision); @@ -109,6 +145,10 @@ void Joint3D::set_node_a(const NodePath &p_node_a) { return; } + if (joint.is_valid()) { + _disconnect_signals(); + } + a = p_node_a; _update_joint(); } @@ -121,6 +161,11 @@ void Joint3D::set_node_b(const NodePath &p_node_b) { if (b == p_node_b) { return; } + + if (joint.is_valid()) { + _disconnect_signals(); + } + b = p_node_b; _update_joint(); } @@ -147,6 +192,7 @@ void Joint3D::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { if (joint.is_valid()) { + _disconnect_signals(); _update_joint(true); } } break; @@ -168,8 +214,8 @@ bool Joint3D::get_exclude_nodes_from_collision() const { String Joint3D::get_configuration_warning() const { String node_warning = Node3D::get_configuration_warning(); - if (!warning.empty()) { - if (!node_warning.empty()) { + if (!warning.is_empty()) { + if (!node_warning.is_empty()) { node_warning += "\n\n"; } node_warning += warning; @@ -710,9 +756,6 @@ void Generic6DOFJoint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flag_z", "flag", "value"), &Generic6DOFJoint3D::set_flag_z); ClassDB::bind_method(D_METHOD("get_flag_z", "flag"), &Generic6DOFJoint3D::get_flag_z); - ClassDB::bind_method(D_METHOD("set_precision", "precision"), &Generic6DOFJoint3D::set_precision); - ClassDB::bind_method(D_METHOD("get_precision"), &Generic6DOFJoint3D::get_precision); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/upper_distance"), "set_param_x", "get_param_x", PARAM_LINEAR_UPPER_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/lower_distance"), "set_param_x", "get_param_x", PARAM_LINEAR_LOWER_LIMIT); @@ -801,8 +844,6 @@ void Generic6DOFJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/damping"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); - ADD_PROPERTY(PropertyInfo(Variant::INT, "precision", PROPERTY_HINT_RANGE, "1,99999,1"), "set_precision", "get_precision"); - BIND_ENUM_CONSTANT(PARAM_LINEAR_LOWER_LIMIT); BIND_ENUM_CONSTANT(PARAM_LINEAR_UPPER_LIMIT); BIND_ENUM_CONSTANT(PARAM_LINEAR_LIMIT_SOFTNESS); @@ -921,14 +962,6 @@ bool Generic6DOFJoint3D::get_flag_z(Flag p_flag) const { return flags_z[p_flag]; } -void Generic6DOFJoint3D::set_precision(int p_precision) { - precision = p_precision; - - PhysicsServer3D::get_singleton()->generic_6dof_joint_set_precision( - get_joint(), - precision); -} - RID Generic6DOFJoint3D::_configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) { Transform gt = get_global_transform(); //Vector3 cone_twistpos = gt.origin; diff --git a/scene/3d/physics_joint_3d.h b/scene/3d/physics_joint_3d.h index a65f6db3bf..9702076318 100644 --- a/scene/3d/physics_joint_3d.h +++ b/scene/3d/physics_joint_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -49,6 +49,8 @@ class Joint3D : public Node3D { String warning; protected: + void _disconnect_signals(); + void _body_exit_tree(const ObjectID &p_body_id); void _update_joint(bool p_only_free = false); void _notification(int p_what); @@ -300,8 +302,6 @@ protected: float params_z[PARAM_MAX]; bool flags_z[FLAG_MAX]; - int precision = 1; - virtual RID _configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; static void _bind_methods(); @@ -324,11 +324,6 @@ public: void set_flag_z(Flag p_flag, bool p_enabled); bool get_flag_z(Flag p_flag) const; - void set_precision(int p_precision); - int get_precision() const { - return precision; - } - Generic6DOFJoint3D(); }; diff --git a/scene/3d/position_3d.cpp b/scene/3d/position_3d.cpp index e63c588e28..b231ba0df7 100644 --- a/scene/3d/position_3d.cpp +++ b/scene/3d/position_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/position_3d.h b/scene/3d/position_3d.h index 1c5f05ef95..065b14c3bd 100644 --- a/scene/3d/position_3d.h +++ b/scene/3d/position_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/proximity_group_3d.cpp b/scene/3d/proximity_group_3d.cpp index 1a0677c603..9d9fea68b0 100644 --- a/scene/3d/proximity_group_3d.cpp +++ b/scene/3d/proximity_group_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,7 @@ #include "core/math/math_funcs.h" -void ProximityGroup3D::clear_groups() { +void ProximityGroup3D::_clear_groups() { Map<StringName, uint32_t>::Element *E; { @@ -43,21 +43,21 @@ void ProximityGroup3D::clear_groups() { while (E && num < size) { if (E->get() != group_version) { remove_list[num++] = E->key(); - }; + } E = E->next(); - }; + } for (int i = 0; i < num; i++) { groups.erase(remove_list[i]); - }; - }; + } + } if (E) { - clear_groups(); // call until we go through the whole list - }; -}; + _clear_groups(); // call until we go through the whole list + } +} -void ProximityGroup3D::update_groups() { +void ProximityGroup3D::_update_groups() { if (grid_radius == Vector3(0, 0, 0)) { return; } @@ -68,20 +68,20 @@ void ProximityGroup3D::update_groups() { Vector3 vcell = pos / cell_size; int cell[3] = { Math::fast_ftoi(vcell.x), Math::fast_ftoi(vcell.y), Math::fast_ftoi(vcell.z) }; - add_groups(cell, group_name, 0); + _add_groups(cell, group_name, 0); - clear_groups(); -}; + _clear_groups(); +} -void ProximityGroup3D::add_groups(int *p_cell, String p_base, int p_depth) { +void ProximityGroup3D::_add_groups(int *p_cell, String p_base, int p_depth) { p_base = p_base + "|"; if (grid_radius[p_depth] == 0) { if (p_depth == 2) { _new_group(p_base); } else { - add_groups(p_cell, p_base, p_depth + 1); - }; - }; + _add_groups(p_cell, p_base, p_depth + 1); + } + } int start = p_cell[p_depth] - grid_radius[p_depth]; int end = p_cell[p_depth] + grid_radius[p_depth]; @@ -91,72 +91,72 @@ void ProximityGroup3D::add_groups(int *p_cell, String p_base, int p_depth) { if (p_depth == 2) { _new_group(gname); } else { - add_groups(p_cell, gname, p_depth + 1); - }; - }; -}; + _add_groups(p_cell, gname, p_depth + 1); + } + } +} void ProximityGroup3D::_new_group(StringName p_name) { const Map<StringName, uint32_t>::Element *E = groups.find(p_name); if (!E) { add_to_group(p_name); - }; + } groups[p_name] = group_version; -}; +} void ProximityGroup3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_EXIT_TREE: ++group_version; - clear_groups(); + _clear_groups(); break; case NOTIFICATION_TRANSFORM_CHANGED: - update_groups(); + _update_groups(); break; - }; -}; + } +} -void ProximityGroup3D::broadcast(String p_name, Variant p_params) { +void ProximityGroup3D::broadcast(String p_method, Variant p_parameters) { Map<StringName, uint32_t>::Element *E; E = groups.front(); while (E) { - get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFAULT, E->key(), "_proximity_group_broadcast", p_name, p_params); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFAULT, E->key(), "_proximity_group_broadcast", p_method, p_parameters); E = E->next(); - }; -}; + } +} -void ProximityGroup3D::_proximity_group_broadcast(String p_name, Variant p_params) { +void ProximityGroup3D::_proximity_group_broadcast(String p_method, Variant p_parameters) { if (dispatch_mode == MODE_PROXY) { - get_parent()->call(p_name, p_params); + get_parent()->call(p_method, p_parameters); } else { - emit_signal("broadcast", p_name, p_params); - }; -}; + emit_signal("broadcast", p_method, p_parameters); + } +} void ProximityGroup3D::set_group_name(const String &p_group_name) { group_name = p_group_name; -}; +} String ProximityGroup3D::get_group_name() const { return group_name; -}; +} void ProximityGroup3D::set_dispatch_mode(DispatchMode p_mode) { dispatch_mode = p_mode; -}; +} ProximityGroup3D::DispatchMode ProximityGroup3D::get_dispatch_mode() const { return dispatch_mode; -}; +} void ProximityGroup3D::set_grid_radius(const Vector3 &p_radius) { grid_radius = p_radius; -}; +} Vector3 ProximityGroup3D::get_grid_radius() const { return grid_radius; -}; +} void ProximityGroup3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_group_name", "name"), &ProximityGroup3D::set_group_name); @@ -165,19 +165,21 @@ void ProximityGroup3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_dispatch_mode"), &ProximityGroup3D::get_dispatch_mode); ClassDB::bind_method(D_METHOD("set_grid_radius", "radius"), &ProximityGroup3D::set_grid_radius); ClassDB::bind_method(D_METHOD("get_grid_radius"), &ProximityGroup3D::get_grid_radius); - ClassDB::bind_method(D_METHOD("broadcast", "name", "parameters"), &ProximityGroup3D::broadcast); - ClassDB::bind_method(D_METHOD("_proximity_group_broadcast", "name", "params"), &ProximityGroup3D::_proximity_group_broadcast); + + ClassDB::bind_method(D_METHOD("broadcast", "method", "parameters"), &ProximityGroup3D::broadcast); + + ClassDB::bind_method(D_METHOD("_proximity_group_broadcast", "method", "parameters"), &ProximityGroup3D::_proximity_group_broadcast); ADD_PROPERTY(PropertyInfo(Variant::STRING, "group_name"), "set_group_name", "get_group_name"); ADD_PROPERTY(PropertyInfo(Variant::INT, "dispatch_mode", PROPERTY_HINT_ENUM, "Proxy,Signal"), "set_dispatch_mode", "get_dispatch_mode"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "grid_radius"), "set_grid_radius", "get_grid_radius"); - ADD_SIGNAL(MethodInfo("broadcast", PropertyInfo(Variant::STRING, "group_name"), PropertyInfo(Variant::ARRAY, "parameters"))); + ADD_SIGNAL(MethodInfo("broadcast", PropertyInfo(Variant::STRING, "method"), PropertyInfo(Variant::ARRAY, "parameters"))); BIND_ENUM_CONSTANT(MODE_PROXY); BIND_ENUM_CONSTANT(MODE_SIGNAL); -}; +} ProximityGroup3D::ProximityGroup3D() { set_notify_transform(true); -}; +} diff --git a/scene/3d/proximity_group_3d.h b/scene/3d/proximity_group_3d.h index dd3a2f0a87..05aa00b228 100644 --- a/scene/3d/proximity_group_3d.h +++ b/scene/3d/proximity_group_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,7 +35,6 @@ class ProximityGroup3D : public Node3D { GDCLASS(ProximityGroup3D, Node3D); - OBJ_CATEGORY("3D"); public: enum DispatchMode { @@ -43,25 +42,25 @@ public: MODE_SIGNAL, }; -public: - void clear_groups(); - void update_groups(); - - void _notification(int p_what); - - DispatchMode dispatch_mode = MODE_PROXY; - +private: Map<StringName, uint32_t> groups; + String group_name; + DispatchMode dispatch_mode = MODE_PROXY; + Vector3 grid_radius = Vector3(1, 1, 1); float cell_size = 1.0; - Vector3 grid_radius = Vector3(1, 1, 1); uint32_t group_version = 0; - void add_groups(int *p_cell, String p_base, int p_depth); + void _clear_groups(); + void _update_groups(); + void _add_groups(int *p_cell, String p_base, int p_depth); void _new_group(StringName p_name); - void _proximity_group_broadcast(String p_name, Variant p_params); + void _proximity_group_broadcast(String p_method, Variant p_parameters); + +protected: + void _notification(int p_what); static void _bind_methods(); @@ -75,7 +74,7 @@ public: void set_grid_radius(const Vector3 &p_radius); Vector3 get_grid_radius() const; - void broadcast(String p_name, Variant p_params); + void broadcast(String p_method, Variant p_parameters); ProximityGroup3D(); ~ProximityGroup3D() {} @@ -83,4 +82,4 @@ public: VARIANT_ENUM_CAST(ProximityGroup3D::DispatchMode); -#endif +#endif // PROXIMITY_GROUP_H diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index 811e8a331b..59b59c7163 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h index f4fe7ba621..4109853280 100644 --- a/scene/3d/ray_cast_3d.h +++ b/scene/3d/ray_cast_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index c7948395d3..63fa0c06c3 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -76,6 +76,15 @@ float ReflectionProbe::get_max_distance() const { return max_distance; } +void ReflectionProbe::set_lod_threshold(float p_pixels) { + lod_threshold = p_pixels; + RS::get_singleton()->reflection_probe_set_lod_threshold(probe, p_pixels); +} + +float ReflectionProbe::get_lod_threshold() const { + return lod_threshold; +} + void ReflectionProbe::set_extents(const Vector3 &p_extents) { extents = p_extents; @@ -199,6 +208,9 @@ void ReflectionProbe::_bind_methods() { ClassDB::bind_method(D_METHOD("set_max_distance", "max_distance"), &ReflectionProbe::set_max_distance); ClassDB::bind_method(D_METHOD("get_max_distance"), &ReflectionProbe::get_max_distance); + ClassDB::bind_method(D_METHOD("set_lod_threshold", "ratio"), &ReflectionProbe::set_lod_threshold); + ClassDB::bind_method(D_METHOD("get_lod_threshold"), &ReflectionProbe::get_lod_threshold); + ClassDB::bind_method(D_METHOD("set_extents", "extents"), &ReflectionProbe::set_extents); ClassDB::bind_method(D_METHOD("get_extents"), &ReflectionProbe::get_extents); @@ -229,6 +241,7 @@ void ReflectionProbe::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_as_interior", "is_set_as_interior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_shadows"), "set_enable_shadows", "are_shadows_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold"); ADD_GROUP("Ambient", "ambient_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_mode", PROPERTY_HINT_ENUM, "Disabled,Environment,ConstantColor"), "set_ambient_mode", "get_ambient_mode"); @@ -256,6 +269,7 @@ ReflectionProbe::ReflectionProbe() { enable_shadows = false; cull_mask = (1 << 20) - 1; update_mode = UPDATE_ONCE; + lod_threshold = 1.0; probe = RenderingServer::get_singleton()->reflection_probe_create(); RS::get_singleton()->instance_set_base(get_instance(), probe); diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 56177d0f95..a43d4e2422 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -63,6 +63,7 @@ private: AmbientMode ambient_mode; Color ambient_color; float ambient_color_energy; + float lod_threshold; uint32_t cull_mask; UpdateMode update_mode; @@ -90,6 +91,9 @@ public: void set_max_distance(float p_distance); float get_max_distance() const; + void set_lod_threshold(float p_pixels); + float get_lod_threshold() const; + void set_extents(const Vector3 &p_extents); Vector3 get_extents() const; diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp index 358f9346f8..4afbb80779 100644 --- a/scene/3d/remote_transform_3d.cpp +++ b/scene/3d/remote_transform_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -183,7 +183,7 @@ String RemoteTransform3D::get_configuration_warning() const { String warning = Node3D::get_configuration_warning(); if (!has_node(remote_node) || !Object::cast_to<Node3D>(get_node(remote_node))) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work."); diff --git a/scene/3d/remote_transform_3d.h b/scene/3d/remote_transform_3d.h index 0ce30f7fce..3b30c835be 100644 --- a/scene/3d/remote_transform_3d.h +++ b/scene/3d/remote_transform_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 0ae1c0e3b6..5e09a15b80 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -848,7 +848,7 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { skin_bindings.insert(skin_ref.operator->()); - skin->connect_compat("changed", skin_ref.operator->(), "_skin_changed"); + skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed")); _make_dirty(); //skin needs to be updated, so update skeleton diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index c54f89d3ce..73417dce21 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 32d7afd5df..cb486a12ae 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -204,7 +204,7 @@ void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_ while (sub_chain_root) { // Reach the tip sub_chain_root->current_pos = origin; - if (!sub_chain_root->children.empty()) { + if (!sub_chain_root->children.is_empty()) { ChainItem &child(sub_chain_root->children.write[0]); // Is not tip @@ -301,7 +301,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove Transform new_bone_pose(ci->initial_transform); new_bone_pose.origin = ci->current_pos; - if (!ci->children.empty()) { + if (!ci->children.is_empty()) { /// Rotate basis const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized()); const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); @@ -321,7 +321,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove p_task->skeleton->set_bone_global_pose_override(ci->bone, new_bone_pose, 1.0, true); - if (!ci->children.empty()) { + if (!ci->children.is_empty()) { ci = &ci->children.write[0]; } else { ci = nullptr; diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h index 80acc3e937..557647aa62 100644 --- a/scene/3d/skeleton_ik_3d.h +++ b/scene/3d/skeleton_ik_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -76,10 +76,6 @@ class FabrikInverseKinematic { ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) : chain_item(p_chain_item), end_effector(p_end_effector) {} - - ChainTip(const ChainTip &p_other_ct) : - chain_item(p_other_ct.chain_item), - end_effector(p_other_ct.end_effector) {} }; struct Chain { diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index e633b1eb4a..785465f28e 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -380,7 +380,7 @@ String SoftBody3D::get_configuration_warning() const { String warning = MeshInstance3D::get_configuration_warning(); if (get_mesh().is_null()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } @@ -389,7 +389,7 @@ String SoftBody3D::get_configuration_warning() const { Transform t = get_transform(); if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } @@ -480,7 +480,6 @@ void SoftBody3D::become_mesh_owner() { Dictionary surface_lods = mesh->surface_get_lods(0); uint32_t surface_format = mesh->surface_get_format(0); - surface_format &= ~(Mesh::ARRAY_COMPRESS_NORMAL); surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE; Ref<ArrayMesh> soft_mesh; diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_body_3d.h index c59a0b3aa3..d3c5dc022f 100644 --- a/scene/3d/soft_body_3d.h +++ b/scene/3d/soft_body_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp index 287d760db0..6812282844 100644 --- a/scene/3d/spring_arm_3d.cpp +++ b/scene/3d/spring_arm_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/spring_arm_3d.h b/scene/3d/spring_arm_3d.h index 7f6fe2f1a2..4c2d2a54ff 100644 --- a/scene/3d/spring_arm_3d.h +++ b/scene/3d/spring_arm_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index b6999beff4..3e49750366 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -486,7 +486,7 @@ void Sprite3D::_draw() { RS::get_singleton()->immediate_normal(immediate, normal); RS::get_singleton()->immediate_tangent(immediate, tangent); RS::get_singleton()->immediate_color(immediate, color); - RS::get_singleton()->immediate_uv(immediate, uvs[i]); + RS::get_singleton()->immediate_uv(immediate, uvs[index[i]]); Vector3 vtx; vtx[x_axis] = vertices[index[i]][0]; @@ -815,7 +815,7 @@ void AnimatedSprite3D::_draw() { RS::get_singleton()->immediate_normal(immediate, normal); RS::get_singleton()->immediate_tangent(immediate, tangent); RS::get_singleton()->immediate_color(immediate, color); - RS::get_singleton()->immediate_uv(immediate, uvs[i]); + RS::get_singleton()->immediate_uv(immediate, uvs[indices[i]]); Vector3 vtx; vtx[x_axis] = vertices[indices[i]][0]; @@ -1074,7 +1074,7 @@ String AnimatedSprite3D::get_configuration_warning() const { String warning = SpriteBase3D::get_configuration_warning(); if (frames.is_null()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames."); diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index cb8467aac6..3e38d4581e 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index e27307e75f..120bbbae43 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -106,7 +106,7 @@ String VehicleWheel3D::get_configuration_warning() const { String warning = Node3D::get_configuration_warning(); if (!Object::cast_to<VehicleBody3D>(get_parent())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D."); diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h index 0b4b3a4440..ca7ea6574d 100644 --- a/scene/3d/vehicle_body_3d.h +++ b/scene/3d/vehicle_body_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/velocity_tracker_3d.cpp b/scene/3d/velocity_tracker_3d.cpp index eba7d44c16..35d00f3639 100644 --- a/scene/3d/velocity_tracker_3d.cpp +++ b/scene/3d/velocity_tracker_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -45,7 +45,7 @@ void VelocityTracker3D::update_position(const Vector3 &p_position) { if (physics_step) { ph.frame = Engine::get_singleton()->get_physics_frames(); } else { - ph.frame = Engine::get_singleton()->get_idle_frame_ticks(); + ph.frame = Engine::get_singleton()->get_frame_ticks(); } if (position_history_len == 0 || position_history[0].frame != ph.frame) { //in same frame, use latest @@ -72,7 +72,7 @@ Vector3 VelocityTracker3D::get_tracked_linear_velocity() const { uint64_t base = Engine::get_singleton()->get_physics_frames(); base_time = float(base - position_history[0].frame) / Engine::get_singleton()->get_iterations_per_second(); } else { - uint64_t base = Engine::get_singleton()->get_idle_frame_ticks(); + uint64_t base = Engine::get_singleton()->get_frame_ticks(); base_time = double(base - position_history[0].frame) / 1000000.0; } } @@ -109,7 +109,7 @@ void VelocityTracker3D::reset(const Vector3 &p_new_pos) { if (physics_step) { ph.frame = Engine::get_singleton()->get_physics_frames(); } else { - ph.frame = Engine::get_singleton()->get_idle_frame_ticks(); + ph.frame = Engine::get_singleton()->get_frame_ticks(); } position_history.write[0] = ph; diff --git a/scene/3d/velocity_tracker_3d.h b/scene/3d/velocity_tracker_3d.h index a7ab3dce4d..3fc89cc7fb 100644 --- a/scene/3d/velocity_tracker_3d.h +++ b/scene/3d/velocity_tracker_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/visibility_notifier_3d.cpp b/scene/3d/visibility_notifier_3d.cpp index 9f5c40caf4..494709fe84 100644 --- a/scene/3d/visibility_notifier_3d.cpp +++ b/scene/3d/visibility_notifier_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/visibility_notifier_3d.h b/scene/3d/visibility_notifier_3d.h index 35f6c02e83..29552510b7 100644 --- a/scene/3d/visibility_notifier_3d.h +++ b/scene/3d/visibility_notifier_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index a1c498e8ab..1d0a830383 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -278,6 +278,16 @@ float GeometryInstance3D::get_extra_cull_margin() const { return extra_cull_margin; } +void GeometryInstance3D::set_lod_bias(float p_bias) { + ERR_FAIL_COND(p_bias < 0.0); + lod_bias = p_bias; + RS::get_singleton()->instance_geometry_set_lod_bias(get_instance(), lod_bias); +} + +float GeometryInstance3D::get_lod_bias() const { + return lod_bias; +} + void GeometryInstance3D::set_shader_instance_uniform(const StringName &p_uniform, const Variant &p_value) { if (p_value.get_type() == Variant::NIL) { Variant def_value = RS::get_singleton()->instance_geometry_get_shader_parameter_default_value(get_instance(), p_uniform); @@ -361,6 +371,9 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_gi_mode", "mode"), &GeometryInstance3D::set_gi_mode); ClassDB::bind_method(D_METHOD("get_gi_mode"), &GeometryInstance3D::get_gi_mode); + ClassDB::bind_method(D_METHOD("set_lod_bias", "p_bias"), &GeometryInstance3D::set_lod_bias); + ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias); + ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &GeometryInstance3D::set_custom_aabb); ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance3D::get_aabb); @@ -369,6 +382,7 @@ void GeometryInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_override", "get_material_override"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias"); ADD_GROUP("Global Illumination", "gi_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Baked,Dynamic"), "set_gi_mode", "get_gi_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, "1x,2x,4x,8x"), "set_lightmap_scale", "get_lightmap_scale"); @@ -403,6 +417,8 @@ GeometryInstance3D::GeometryInstance3D() { lod_min_hysteresis = 0; lod_max_hysteresis = 0; + lod_bias = 1.0; + gi_mode = GI_MODE_DISABLED; lightmap_scale = LIGHTMAP_SCALE_1X; diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 51bcb411da..f370f29cb0 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -112,6 +112,8 @@ private: float lod_min_hysteresis; float lod_max_hysteresis; + float lod_bias; + mutable HashMap<StringName, Variant> instance_uniforms; mutable HashMap<StringName, StringName> instance_uniform_property_remap; @@ -151,6 +153,9 @@ public: void set_extra_cull_margin(float p_margin); float get_extra_cull_margin() const; + void set_lod_bias(float p_bias); + float get_lod_bias() const; + void set_gi_mode(GIMode p_mode); GIMode get_gi_mode() const; diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index c570fc7b7c..b99d753dde 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -294,7 +294,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co Vector<Color> Voxelizer::_get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add) { Vector<Color> ret; - if (p_image.is_null() || p_image->empty()) { + if (p_image.is_null() || p_image->is_empty()) { ret.resize(bake_texture_size * bake_texture_size); for (int i = 0; i < bake_texture_size * bake_texture_size; i++) { ret.write[i] = p_color_add; diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h index 3546fd7729..a0b581d625 100644 --- a/scene/3d/voxelizer.h +++ b/scene/3d/voxelizer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp index 3c12d4991e..cf1c319acc 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -113,7 +113,7 @@ String WorldEnvironment::get_configuration_warning() const { String warning = Node::get_configuration_warning(); if (!environment.is_valid()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("WorldEnvironment requires its \"Environment\" property to contain an Environment to have a visible effect."); @@ -127,7 +127,7 @@ String WorldEnvironment::get_configuration_warning() const { get_tree()->get_nodes_in_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), &nodes); if (nodes.size() > 1) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."); diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h index 3fd3dc0b50..3dfba20bf0 100644 --- a/scene/3d/world_environment.h +++ b/scene/3d/world_environment.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index c0015aa338..7fed34c7c6 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -65,7 +65,7 @@ String XRCamera3D::get_configuration_warning() const { // must be child node of XROrigin3D! XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); if (origin == nullptr) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("XRCamera3D must have an XROrigin3D node as its parent."); @@ -91,9 +91,9 @@ Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const { Vector2 cpos = get_viewport()->get_camera_coords(p_pos); Vector3 ray; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); Vector2 screen_he = cm.get_viewport_half_extents(); - ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_znear()).normalized(); + ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized(); return ray; }; @@ -113,7 +113,7 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const { Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); Plane p(get_camera_transform().xform_inv(p_pos), 1.0); @@ -142,7 +142,7 @@ Vector3 XRCamera3D::project_position(const Point2 &p_point, float p_z_depth) con Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); Vector2 vp_he = cm.get_viewport_half_extents(); @@ -170,7 +170,7 @@ Vector<Plane> XRCamera3D::get_frustum() const { ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>()); Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); return cm.get_projection_planes(get_camera_transform()); }; @@ -211,7 +211,7 @@ void XRController3D::_notification(int p_what) { emit_signal("button_pressed", i); button_states += mask; } else if (was_pressed && !is_pressed) { - emit_signal("button_release", i); + emit_signal("button_released", i); button_states -= mask; }; @@ -247,7 +247,7 @@ void XRController3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joystick_axis", "axis"), &XRController3D::get_joystick_axis); ClassDB::bind_method(D_METHOD("get_is_active"), &XRController3D::get_is_active); - ClassDB::bind_method(D_METHOD("get_hand"), &XRController3D::get_hand); + ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand); ClassDB::bind_method(D_METHOD("get_rumble"), &XRController3D::get_rumble); ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &XRController3D::set_rumble); @@ -257,7 +257,7 @@ void XRController3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mesh"), &XRController3D::get_mesh); ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::INT, "button"))); - ADD_SIGNAL(MethodInfo("button_release", PropertyInfo(Variant::INT, "button"))); + ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::INT, "button"))); ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"))); }; @@ -282,7 +282,7 @@ String XRController3D::get_controller_name() const { return String("Not connected"); }; - return tracker->get_name(); + return tracker->get_tracker_name(); }; int XRController3D::get_joystick_id() const { @@ -349,7 +349,7 @@ bool XRController3D::get_is_active() const { return is_active; }; -XRPositionalTracker::TrackerHand XRController3D::get_hand() const { +XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const { // get our XRServer XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, XRPositionalTracker::TRACKER_HAND_UNKNOWN); @@ -359,7 +359,7 @@ XRPositionalTracker::TrackerHand XRController3D::get_hand() const { return XRPositionalTracker::TRACKER_HAND_UNKNOWN; }; - return tracker->get_hand(); + return tracker->get_tracker_hand(); }; String XRController3D::get_configuration_warning() const { @@ -372,14 +372,14 @@ String XRController3D::get_configuration_warning() const { // must be child node of XROrigin! XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); if (origin == nullptr) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("XRController3D must have an XROrigin3D node as its parent."); }; if (controller_id == 0) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("The controller ID must not be 0 or this controller won't be bound to an actual controller."); @@ -480,7 +480,7 @@ String XRAnchor3D::get_anchor_name() const { return String("Not connected"); }; - return tracker->get_name(); + return tracker->get_tracker_name(); }; bool XRAnchor3D::get_is_active() const { @@ -497,14 +497,14 @@ String XRAnchor3D::get_configuration_warning() const { // must be child node of XROrigin3D! XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); if (origin == nullptr) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("XRAnchor3D must have an XROrigin3D node as its parent."); }; if (anchor_id == 0) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("The anchor ID must not be 0 or this anchor won't be bound to an actual anchor."); @@ -536,7 +536,7 @@ String XROrigin3D::get_configuration_warning() const { String warning = Node3D::get_configuration_warning(); if (tracked_camera == nullptr) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("XROrigin3D requires an XRCamera3D child node."); diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index 751b2b68cb..7cd6e2ac57 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -93,7 +93,7 @@ public: void set_rumble(real_t p_rumble); bool get_is_active() const; - XRPositionalTracker::TrackerHand get_hand() const; + XRPositionalTracker::TrackerHand get_tracker_hand() const; Ref<Mesh> get_mesh() const; diff --git a/scene/SCsub b/scene/SCsub index f9fc00f3f2..ccd2bab8ff 100644 --- a/scene/SCsub +++ b/scene/SCsub @@ -4,24 +4,9 @@ Import("env") env.scene_sources = [] -# Thirdparty code -thirdparty_dir = "#thirdparty/misc/" -thirdparty_sources = [ - # C++ sources - "easing_equations.cpp", - # C sources - "mikktspace.c", -] -thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - -env_thirdparty = env.Clone() -env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(env.scene_sources, thirdparty_sources) - -# Godot's own sources +# Godot source files env.add_source_files(env.scene_sources, "*.cpp") - # Chain load SCsubs SConscript("main/SCsub") SConscript("gui/SCsub") @@ -32,7 +17,6 @@ SConscript("audio/SCsub") SConscript("resources/SCsub") SConscript("debugger/SCsub") - # Build it all as a library lib = env.add_library("scene", env.scene_sources) env.Prepend(LIBS=[lib]) diff --git a/scene/animation/SCsub b/scene/animation/SCsub index fc61250247..cc33a5af84 100644 --- a/scene/animation/SCsub +++ b/scene/animation/SCsub @@ -2,4 +2,23 @@ Import("env") -env.add_source_files(env.scene_sources, "*.cpp") +# Thirdparty code + +thirdparty_obj = [] + +thirdparty_sources = "#thirdparty/misc/easing_equations.cpp" + +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.scene_sources += thirdparty_obj + +# Godot source files + +scene_obj = [] + +env.add_source_files(scene_obj, "*.cpp") +env.scene_sources += scene_obj + +# Needed to force rebuilding the scene files when the thirdparty code is updated. +env.Depends(scene_obj, thirdparty_obj) diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index e426e98def..41e1318b1f 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index 816d3c9d4e..6edbc4e319 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 5a42e2af7a..c32cc82bd8 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h index 2aff678aad..c74afcb8ce 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 56995c0c13..24ae01e9d8 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 7241a6bc13..45a9f2e7e1 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp index abb2cf1b65..6ef2da6977 100644 --- a/scene/animation/animation_cache.cpp +++ b/scene/animation/animation_cache.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_cache.h b/scene/animation/animation_cache.h index feff1d364a..484b710b06 100644 --- a/scene/animation/animation_cache.h +++ b/scene/animation/animation_cache.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 17ce05f130..ef9f531f04 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -495,6 +495,8 @@ void AnimationNodeStateMachinePlayback::_bind_methods() { ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachinePlayback::stop); ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachinePlayback::is_playing); ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachinePlayback::get_current_node); + ClassDB::bind_method(D_METHOD("get_current_play_position"), &AnimationNodeStateMachinePlayback::get_current_play_pos); + ClassDB::bind_method(D_METHOD("get_current_length"), &AnimationNodeStateMachinePlayback::get_current_length); ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachinePlayback::get_travel_path); } @@ -565,7 +567,7 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima { Ref<AnimationNode> node = states[p_name].node; if (node.is_valid()) { - node->disconnect_compat("tree_changed", this, "_tree_changed"); + node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed)); } } @@ -574,7 +576,7 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima emit_changed(); emit_signal("tree_changed"); - p_node->connect_compat("tree_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); + p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED); } Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const { diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index ae8975e940..daeb2fabaa 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index e9e17148d6..8e50c733ae 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,6 +36,7 @@ #include "servers/audio/audio_stream.h" #ifdef TOOLS_ENABLED +#include "editor/editor_node.h" #include "editor/editor_settings.h" #include "scene/2d/skeleton_2d.h" @@ -53,6 +54,21 @@ void AnimatedValuesBackup::update_skeletons() { } } } + +void AnimatedValuesBackup::restore() const { + for (int i = 0; i < entries.size(); i++) { + const AnimatedValuesBackup::Entry *entry = &entries[i]; + if (entry->bone_idx == -1) { + entry->object->set_indexed(entry->subpath, entry->value); + } else { + Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value); + } + } +} + +void AnimatedValuesBackup::_bind_methods() { + ClassDB::bind_method(D_METHOD("restore"), &AnimatedValuesBackup::restore); +} #endif bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) { @@ -766,13 +782,13 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, f delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here) if (&cd == &playback.current) { - if (!backwards && cd.pos <= len && next_pos == len /*&& playback.blend.empty()*/) { + if (!backwards && cd.pos <= len && next_pos == len) { //playback finished end_reached = true; end_notify = cd.pos < len; // Notify only if not already at the end } - if (backwards && cd.pos >= 0 && next_pos == 0 /*&& playback.blend.empty()*/) { + if (backwards && cd.pos >= 0 && next_pos == 0) { //playback finished end_reached = true; end_notify = cd.pos > 0; // Notify only if not already at the beginning @@ -1379,6 +1395,14 @@ String AnimationPlayer::get_autoplay() const { return autoplay; } +void AnimationPlayer::set_reset_on_save_enabled(bool p_enabled) { + reset_on_save = p_enabled; +} + +bool AnimationPlayer::is_reset_on_save_enabled() const { + return reset_on_save; +} + void AnimationPlayer::set_animation_process_mode(AnimationProcessMode p_mode) { if (animation_process_mode == p_mode) { return; @@ -1473,15 +1497,15 @@ void AnimationPlayer::get_argument_options(const StringName &p_function, int p_i } #ifdef TOOLS_ENABLED -AnimatedValuesBackup AnimationPlayer::backup_animated_values() { +Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values() { + Ref<AnimatedValuesBackup> backup; if (!playback.current.from) { - return AnimatedValuesBackup(); + return backup; } _ensure_node_caches(playback.current.from); - AnimatedValuesBackup backup; - + backup.instance(); for (int i = 0; i < playback.current.from->node_cache.size(); i++) { TrackNodeCache *nc = playback.current.from->node_cache[i]; if (!nc) { @@ -1497,7 +1521,7 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() { entry.object = nc->skeleton; entry.bone_idx = nc->bone_idx; entry.value = nc->skeleton->get_bone_pose(nc->bone_idx); - backup.entries.push_back(entry); + backup->entries.push_back(entry); } else { if (nc->spatial) { AnimatedValuesBackup::Entry entry; @@ -1505,7 +1529,7 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() { entry.subpath.push_back("transform"); entry.value = nc->spatial->get_transform(); entry.bone_idx = -1; - backup.entries.push_back(entry); + backup->entries.push_back(entry); } else { for (Map<StringName, TrackNodeCache::PropertyAnim>::Element *E = nc->property_anim.front(); E; E = E->next()) { AnimatedValuesBackup::Entry entry; @@ -1515,7 +1539,7 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() { entry.value = E->value().object->get_indexed(E->value().subpath, &valid); entry.bone_idx = -1; if (valid) { - backup.entries.push_back(entry); + backup->entries.push_back(entry); } } } @@ -1525,15 +1549,40 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() { return backup; } -void AnimationPlayer::restore_animated_values(const AnimatedValuesBackup &p_backup) { - for (int i = 0; i < p_backup.entries.size(); i++) { - const AnimatedValuesBackup::Entry *entry = &p_backup.entries[i]; - if (entry->bone_idx == -1) { - entry->object->set_indexed(entry->subpath, entry->value); - } else { - Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value); - } +Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) { + ERR_FAIL_COND_V(!can_apply_reset(), Ref<AnimatedValuesBackup>()); + + Ref<Animation> reset_anim = animation_set["RESET"].animation; + ERR_FAIL_COND_V(reset_anim.is_null(), Ref<AnimatedValuesBackup>()); + + Node *root_node = get_node_or_null(root); + ERR_FAIL_COND_V(!root_node, Ref<AnimatedValuesBackup>()); + + AnimationPlayer *aux_player = memnew(AnimationPlayer); + EditorNode::get_singleton()->add_child(aux_player); + aux_player->set_root(aux_player->get_path_to(root_node)); + aux_player->add_animation("RESET", reset_anim); + aux_player->set_assigned_animation("RESET"); + Ref<AnimatedValuesBackup> old_values = aux_player->backup_animated_values(); + aux_player->seek(0.0f, true); + aux_player->queue_delete(); + + if (p_user_initiated) { + Ref<AnimatedValuesBackup> new_values = aux_player->backup_animated_values(); + old_values->restore(); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Anim Apply Reset")); + ur->add_do_method(new_values.ptr(), "restore"); + ur->add_undo_method(old_values.ptr(), "restore"); + ur->commit_action(); } + + return old_values; +} + +bool AnimationPlayer::can_apply_reset() const { + return has_animation("RESET") && playback.assigned != StringName("RESET"); } #endif @@ -1577,6 +1626,9 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_autoplay", "name"), &AnimationPlayer::set_autoplay); ClassDB::bind_method(D_METHOD("get_autoplay"), &AnimationPlayer::get_autoplay); + ClassDB::bind_method(D_METHOD("set_reset_on_save_enabled", "enabled"), &AnimationPlayer::set_reset_on_save_enabled); + ClassDB::bind_method(D_METHOD("is_reset_on_save_enabled"), &AnimationPlayer::is_reset_on_save_enabled); + ClassDB::bind_method(D_METHOD("set_root", "path"), &AnimationPlayer::set_root); ClassDB::bind_method(D_METHOD("get_root"), &AnimationPlayer::get_root); @@ -1600,6 +1652,7 @@ void AnimationPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ANIMATE_AS_TRIGGER), "set_current_animation", "get_current_animation"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "assigned_animation", PROPERTY_HINT_NONE, "", 0), "set_assigned_animation", "get_assigned_animation"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_autoplay", "get_autoplay"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_length", PROPERTY_HINT_NONE, "", 0), "", "get_current_animation_length"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_position", PROPERTY_HINT_NONE, "", 0), "", "get_current_animation_position"); @@ -1631,6 +1684,7 @@ AnimationPlayer::AnimationPlayer() { speed_scale = 1; end_reached = false; end_notify = false; + reset_on_save = true; animation_process_mode = ANIMATION_PROCESS_IDLE; method_call_mode = ANIMATION_METHOD_CALL_DEFERRED; processing = false; diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index dbce5643c7..4532825f5a 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,8 +37,9 @@ #include "scene/resources/animation.h" #ifdef TOOLS_ENABLED -// To save/restore animated values -class AnimatedValuesBackup { +class AnimatedValuesBackup : public Reference { + GDCLASS(AnimatedValuesBackup, Reference); + struct Entry { Object *object; Vector<StringName> subpath; // Unused if bone @@ -49,8 +50,12 @@ class AnimatedValuesBackup { friend class AnimationPlayer; +protected: + static void _bind_methods(); + public: void update_skeletons(); + void restore() const; }; #endif @@ -215,6 +220,7 @@ private: bool end_notify; String autoplay; + bool reset_on_save; AnimationProcessMode animation_process_mode; AnimationMethodCallMode method_call_mode; bool processing; @@ -304,6 +310,9 @@ public: void set_autoplay(const String &p_name); String get_autoplay() const; + void set_reset_on_save_enabled(bool p_enabled); + bool is_reset_on_save_enabled() const; + void set_animation_process_mode(AnimationProcessMode p_mode); AnimationProcessMode get_animation_process_mode() const; @@ -325,9 +334,9 @@ public: void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; #ifdef TOOLS_ENABLED - // These may be interesting for games, but are too dangerous for general use - AnimatedValuesBackup backup_animated_values(); - void restore_animated_values(const AnimatedValuesBackup &p_backup); + Ref<AnimatedValuesBackup> backup_animated_values(); + Ref<AnimatedValuesBackup> apply_reset(bool p_user_initiated = false); + bool can_apply_reset() const; #endif AnimationPlayer(); diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 2ef3ba87b2..e6abbc0c7a 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,7 +40,7 @@ void AnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const { Array parameters = get_script_instance()->call("get_parameter_list"); for (int i = 0; i < parameters.size(); i++) { Dictionary d = parameters[i]; - ERR_CONTINUE(d.empty()); + ERR_CONTINUE(d.is_empty()); r_list->push_back(PropertyInfo::from_dict(d)); } } @@ -1287,14 +1287,14 @@ String AnimationTree::get_configuration_warning() const { String warning = Node::get_configuration_warning(); if (!root.is_valid()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("No root AnimationNode for the graph is set."); } if (!has_node(animation_player)) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("Path to an AnimationPlayer node containing animations is not set."); @@ -1302,12 +1302,12 @@ String AnimationTree::get_configuration_warning() const { AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player)); if (!player) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node."); } else if (!player->has_node(player->get_root())) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("The AnimationPlayer root node is not a valid node."); diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 166ca04f40..a0171e617b 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp index cbf2e4a6ff..5fd936df8e 100644 --- a/scene/animation/root_motion_view.cpp +++ b/scene/animation/root_motion_view.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/root_motion_view.h b/scene/animation/root_motion_view.h index 77c51fe47a..063fb8feda 100644 --- a/scene/animation/root_motion_view.h +++ b/scene/animation/root_motion_view.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 1a2a97ada8..8bdb5c7447 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 822fcf0b6f..c65a6f850f 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index 14aae9c7bf..4a9e146696 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h index 2d9c4cb481..fa45a39682 100644 --- a/scene/audio/audio_stream_player.h +++ b/scene/audio/audio_stream_player.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index f848fc3e68..4dbe3cc1c4 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h index af2d8904b5..51632446f8 100644 --- a/scene/debugger/scene_debugger.h +++ b/scene/debugger/scene_debugger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp index 9f60131186..c7f6c0e2da 100644 --- a/scene/gui/aspect_ratio_container.cpp +++ b/scene/gui/aspect_ratio_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -73,6 +73,7 @@ void AspectRatioContainer::set_alignment_vertical(AlignMode p_alignment_vertical void AspectRatioContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { + bool rtl = is_layout_rtl(); Size2 size = get_size(); for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -130,7 +131,11 @@ void AspectRatioContainer::_notification(int p_what) { } Vector2 offset = (size - child_size) * Vector2(align_x, align_y); - fit_child_in_rect(c, Rect2(offset, child_size)); + if (rtl) { + fit_child_in_rect(c, Rect2(Vector2(size.x - offset.x - child_size.x, offset.y), child_size)); + } else { + fit_child_in_rect(c, Rect2(offset, child_size)); + } } } break; } diff --git a/scene/gui/aspect_ratio_container.h b/scene/gui/aspect_ratio_container.h index 8ffc4363c3..c95c6a7274 100644 --- a/scene/gui/aspect_ratio_container.h +++ b/scene/gui/aspect_ratio_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 1a19c75d27..27e33be9d7 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -317,16 +317,21 @@ bool BaseButton::is_keep_pressed_outside() const { void BaseButton::set_shortcut(const Ref<Shortcut> &p_shortcut) { shortcut = p_shortcut; - set_process_unhandled_input(shortcut.is_valid()); + set_process_unhandled_key_input(shortcut.is_valid()); } Ref<Shortcut> BaseButton::get_shortcut() const { return shortcut; } -void BaseButton::_unhandled_input(Ref<InputEvent> p_event) { +void BaseButton::_unhandled_key_input(Ref<InputEvent> p_event) { + if (!_is_focus_owner_in_shorcut_context()) { + return; + } + if (!is_disabled() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->is_shortcut(p_event)) { on_action_event(p_event); + accept_event(); } } @@ -360,9 +365,34 @@ Ref<ButtonGroup> BaseButton::get_button_group() const { return button_group; } +void BaseButton::set_shortcut_context(Node *p_node) { + ERR_FAIL_NULL_MSG(p_node, "Shortcut context node can't be null."); + shortcut_context = p_node->get_instance_id(); +} + +Node *BaseButton::get_shortcut_context() const { + Object *ctx_obj = ObjectDB::get_instance(shortcut_context); + Node *ctx_node = Object::cast_to<Node>(ctx_obj); + + return ctx_node; +} + +bool BaseButton::_is_focus_owner_in_shorcut_context() const { + if (shortcut_context == ObjectID()) { + // No context, therefore global - always "in" context. + return true; + } + + Node *ctx_node = get_shortcut_context(); + Control *vp_focus = get_focus_owner(); + + // If the context is valid and the viewport focus is valid, check if the context is the focus or is a parent of it. + return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_a_parent_of(vp_focus)); +} + void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &BaseButton::_gui_input); - ClassDB::bind_method(D_METHOD("_unhandled_input"), &BaseButton::_unhandled_input); + ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &BaseButton::_unhandled_key_input); ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &BaseButton::set_pressed); ClassDB::bind_method(D_METHOD("is_pressed"), &BaseButton::is_pressed); ClassDB::bind_method(D_METHOD("is_hovered"), &BaseButton::is_hovered); @@ -386,6 +416,9 @@ void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_button_group", "button_group"), &BaseButton::set_button_group); ClassDB::bind_method(D_METHOD("get_button_group"), &BaseButton::get_button_group); + ClassDB::bind_method(D_METHOD("set_shortcut_context", "node"), &BaseButton::set_shortcut_context); + ClassDB::bind_method(D_METHOD("get_shortcut_context"), &BaseButton::get_shortcut_context); + BIND_VMETHOD(MethodInfo("_pressed")); BIND_VMETHOD(MethodInfo("_toggled", PropertyInfo(Variant::BOOL, "button_pressed"))); @@ -402,6 +435,7 @@ void BaseButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "button_group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut_context", PROPERTY_HINT_RESOURCE_TYPE, "Node"), "set_shortcut_context", "get_shortcut_context"); BIND_ENUM_CONSTANT(DRAW_NORMAL); BIND_ENUM_CONSTANT(DRAW_PRESSED); @@ -425,6 +459,7 @@ BaseButton::BaseButton() { set_focus_mode(FOCUS_ALL); action_mode = ACTION_MODE_BUTTON_RELEASE; button_mask = BUTTON_MASK_LEFT; + shortcut_context = ObjectID(); } BaseButton::~BaseButton() { diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index 33f19949cd..b349b75761 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -50,6 +50,7 @@ private: bool shortcut_in_tooltip; bool keep_pressed_outside; Ref<Shortcut> shortcut; + ObjectID shortcut_context; ActionMode action_mode; struct Status { @@ -75,9 +76,11 @@ protected: virtual void toggled(bool p_pressed); static void _bind_methods(); virtual void _gui_input(Ref<InputEvent> p_event); - virtual void _unhandled_input(Ref<InputEvent> p_event); + virtual void _unhandled_key_input(Ref<InputEvent> p_event); void _notification(int p_what); + bool _is_focus_owner_in_shorcut_context() const; + public: enum DrawMode { DRAW_NORMAL, @@ -122,6 +125,9 @@ public: void set_button_group(const Ref<ButtonGroup> &p_group); Ref<ButtonGroup> get_button_group() const; + void set_shortcut_context(Node *p_node); + Node *get_shortcut_context() const; + BaseButton(); ~BaseButton(); }; diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index 33e030a573..a00d755c9c 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -44,6 +44,7 @@ void BoxContainer::_resort() { Size2i new_size = get_size(); int sep = get_theme_constant("separation"); //,vertical?"VBoxContainer":"HBoxContainer"); + bool rtl = is_layout_rtl(); bool first = true; int children_count = 0; @@ -152,22 +153,53 @@ void BoxContainer::_resort() { int ofs = 0; if (!has_stretched) { - switch (align) { - case ALIGN_BEGIN: - break; - case ALIGN_CENTER: - ofs = stretch_diff / 2; - break; - case ALIGN_END: - ofs = stretch_diff; - break; + if (!vertical) { + switch (align) { + case ALIGN_BEGIN: + if (rtl) { + ofs = stretch_diff; + } + break; + case ALIGN_CENTER: + ofs = stretch_diff / 2; + break; + case ALIGN_END: + if (!rtl) { + ofs = stretch_diff; + } + break; + } + } else { + switch (align) { + case ALIGN_BEGIN: + break; + case ALIGN_CENTER: + ofs = stretch_diff / 2; + break; + case ALIGN_END: + ofs = stretch_diff; + break; + } } } first = true; int idx = 0; - for (int i = 0; i < get_child_count(); i++) { + int start; + int end; + int delta; + if (!rtl || vertical) { + start = 0; + end = get_child_count(); + delta = +1; + } else { + start = get_child_count() - 1; + end = -1; + delta = -1; + } + + for (int i = start; i != end; i += delta) { Control *c = Object::cast_to<Control>(get_child(i)); if (!c || !c->is_visible_in_tree()) { continue; @@ -265,6 +297,10 @@ void BoxContainer::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { minimum_size_changed(); } break; + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + queue_sort(); + } break; } } diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h index c4d75c3cf1..8285c2b9a2 100644 --- a/scene/gui/box_container.h +++ b/scene/gui/box_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index e86ad09aa6..119f3539e1 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,7 +34,7 @@ #include "servers/rendering_server.h" Size2 Button::get_minimum_size() const { - Size2 minsize = get_theme_font("font")->get_string_size(xl_text); + Size2 minsize = text_buf->get_size(); if (clip_text) { minsize.width = 0; } @@ -59,14 +59,25 @@ Size2 Button::get_minimum_size() const { return get_theme_stylebox("normal")->get_minimum_size() + minsize; } -void Button::_set_internal_margin(Margin p_margin, float p_value) { - _internal_margin[p_margin] = p_value; +void Button::_set_internal_margin(Side p_side, float p_value) { + _internal_margin[p_side] = p_value; } void Button::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + update(); + } break; case NOTIFICATION_TRANSLATION_CHANGED: { xl_text = tr(text); + _shape(); + + minimum_size_changed(); + update(); + } break; + case NOTIFICATION_THEME_CHANGED: { + _shape(); + minimum_size_changed(); update(); } break; @@ -77,10 +88,16 @@ void Button::_notification(int p_what) { Color color_icon(1, 1, 1, 1); Ref<StyleBox> style = get_theme_stylebox("normal"); + bool rtl = is_layout_rtl(); switch (get_draw_mode()) { case DRAW_NORMAL: { - style = get_theme_stylebox("normal"); + if (rtl && has_theme_stylebox("normal_mirrored")) { + style = get_theme_stylebox("normal_mirrored"); + } else { + style = get_theme_stylebox("normal"); + } + if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } @@ -91,7 +108,12 @@ void Button::_notification(int p_what) { } break; case DRAW_HOVER_PRESSED: { if (has_theme_stylebox("hover_pressed") && has_theme_stylebox_override("hover_pressed")) { - style = get_theme_stylebox("hover_pressed"); + if (rtl && has_theme_stylebox("hover_pressed_mirrored")) { + style = get_theme_stylebox("hover_pressed_mirrored"); + } else { + style = get_theme_stylebox("hover_pressed"); + } + if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } @@ -109,7 +131,12 @@ void Button::_notification(int p_what) { [[fallthrough]]; } case DRAW_PRESSED: { - style = get_theme_stylebox("pressed"); + if (rtl && has_theme_stylebox("pressed_mirrored")) { + style = get_theme_stylebox("pressed_mirrored"); + } else { + style = get_theme_stylebox("pressed"); + } + if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } @@ -124,7 +151,12 @@ void Button::_notification(int p_what) { } break; case DRAW_HOVER: { - style = get_theme_stylebox("hover"); + if (rtl && has_theme_stylebox("hover_mirrored")) { + style = get_theme_stylebox("hover_mirrored"); + } else { + style = get_theme_stylebox("hover"); + } + if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } @@ -135,7 +167,12 @@ void Button::_notification(int p_what) { } break; case DRAW_DISABLED: { - style = get_theme_stylebox("disabled"); + if (rtl && has_theme_stylebox("disabled_mirrored")) { + style = get_theme_stylebox("disabled_mirrored"); + } else { + style = get_theme_stylebox("disabled"); + } + if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } @@ -152,7 +189,6 @@ void Button::_notification(int p_what) { style2->draw(ci, Rect2(Point2(), size)); } - Ref<Font> font = get_theme_font("font"); Ref<Texture2D> _icon; if (icon.is_null() && has_theme_icon("icon")) { _icon = Control::get_theme_icon("icon"); @@ -168,15 +204,21 @@ void Button::_notification(int p_what) { } float icon_ofs_region = 0; - if (_internal_margin[MARGIN_LEFT] > 0) { - icon_ofs_region = _internal_margin[MARGIN_LEFT] + get_theme_constant("hseparation"); + if (rtl) { + if (_internal_margin[SIDE_RIGHT] > 0) { + icon_ofs_region = _internal_margin[SIDE_RIGHT] + get_theme_constant("hseparation"); + } + } else { + if (_internal_margin[SIDE_LEFT] > 0) { + icon_ofs_region = _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); + } } if (expand_icon) { Size2 _size = get_size() - style->get_offset() * 2; _size.width -= get_theme_constant("hseparation") + icon_ofs_region; if (!clip_text) { - _size.width -= get_theme_font("font")->get_string_size(xl_text).width; + _size.width -= text_buf->get_size().width; } float icon_width = _icon->get_width() * _size.height / _icon->get_height(); float icon_height = _size.height; @@ -186,29 +228,49 @@ void Button::_notification(int p_what) { icon_height = _icon->get_height() * icon_width / _icon->get_width(); } - icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, (_size.height - icon_height) / 2), Size2(icon_width, icon_height)); + if (rtl) { + icon_region = Rect2(Point2(size.width - (icon_ofs_region + icon_width + style->get_margin(SIDE_RIGHT)), style->get_margin(SIDE_TOP) + (_size.height - icon_height) / 2), Size2(icon_width, icon_height)); + } else { + icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, (_size.height - icon_height) / 2), Size2(icon_width, icon_height)); + } } else { - icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, Math::floor((valign - _icon->get_height()) / 2.0)), _icon->get_size()); + if (rtl) { + icon_region = Rect2(Point2(size.width - (icon_ofs_region + _icon->get_size().width + style->get_margin(SIDE_RIGHT)), style->get_margin(SIDE_TOP) + Math::floor((valign - _icon->get_height()) / 2.0)), _icon->get_size()); + } else { + icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, Math::floor((valign - _icon->get_height()) / 2.0)), _icon->get_size()); + } } } Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_theme_constant("hseparation"), 0) : Point2(); int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width; - if (_internal_margin[MARGIN_LEFT] > 0) { - text_clip -= _internal_margin[MARGIN_LEFT] + get_theme_constant("hseparation"); + text_buf->set_width(clip_text ? text_clip : -1); + + int text_width = clip_text ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x; + + if (_internal_margin[SIDE_LEFT] > 0) { + text_clip -= _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); } - if (_internal_margin[MARGIN_RIGHT] > 0) { - text_clip -= _internal_margin[MARGIN_RIGHT] + get_theme_constant("hseparation"); + if (_internal_margin[SIDE_RIGHT] > 0) { + text_clip -= _internal_margin[SIDE_RIGHT] + get_theme_constant("hseparation"); } - Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0; + Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0; switch (align) { case ALIGN_LEFT: { - if (_internal_margin[MARGIN_LEFT] > 0) { - text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_theme_constant("hseparation"); + if (rtl) { + if (_internal_margin[SIDE_RIGHT] > 0) { + text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant("hseparation"); + } else { + text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width; + } } else { - text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x; + if (_internal_margin[SIDE_LEFT] > 0) { + text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); + } else { + text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x; + } } text_ofs.y += style->get_offset().y; } break; @@ -220,17 +282,34 @@ void Button::_notification(int p_what) { text_ofs += style->get_offset(); } break; case ALIGN_RIGHT: { - if (_internal_margin[MARGIN_RIGHT] > 0) { - text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_theme_constant("hseparation"); + if (rtl) { + if (_internal_margin[SIDE_LEFT] > 0) { + text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); + } else { + text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x; + } } else { - text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x; + if (_internal_margin[SIDE_RIGHT] > 0) { + text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant("hseparation"); + } else { + text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width; + } } text_ofs.y += style->get_offset().y; } break; } - text_ofs.y += font->get_ascent(); - font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1); + if (rtl) { + text_ofs.x -= icon_ofs.x; + } + + Color font_outline_modulate = get_theme_color("font_outline_modulate"); + int outline_size = get_theme_constant("outline_size"); + if (outline_size > 0 && font_outline_modulate.a > 0) { + text_buf->draw_outline(ci, text_ofs.floor(), outline_size, font_outline_modulate); + } + + text_buf->draw(ci, text_ofs.floor(), color); if (!_icon.is_null() && icon_region.size.width > 0) { draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), _icon->get_size()), color_icon); @@ -239,29 +318,90 @@ void Button::_notification(int p_what) { } } +void Button::_shape() { + Ref<Font> font = get_theme_font("font"); + int font_size = get_theme_font_size("font_size"); + + text_buf->clear(); + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { + text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + text_buf->set_direction((TextServer::Direction)text_direction); + } + text_buf->add_string(xl_text, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); +} + void Button::set_text(const String &p_text) { - if (text == p_text) { - return; + if (text != p_text) { + text = p_text; + xl_text = tr(text); + _shape(); + + update(); + _change_notify("text"); + minimum_size_changed(); } - text = p_text; - xl_text = tr(p_text); - update(); - _change_notify("text"); - minimum_size_changed(); } String Button::get_text() const { return text; } -void Button::set_icon(const Ref<Texture2D> &p_icon) { - if (icon == p_icon) { - return; +void Button::set_text_direction(Control::TextDirection p_text_direction) { + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (text_direction != p_text_direction) { + text_direction = p_text_direction; + _shape(); + update(); } - icon = p_icon; +} + +Control::TextDirection Button::get_text_direction() const { + return text_direction; +} + +void Button::clear_opentype_features() { + opentype_features.clear(); + _shape(); update(); - _change_notify("icon"); - minimum_size_changed(); +} + +void Button::set_opentype_feature(const String &p_name, int p_value) { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { + opentype_features[tag] = p_value; + _shape(); + update(); + } +} + +int Button::get_opentype_feature(const String &p_name) const { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag)) { + return -1; + } + return opentype_features[tag]; +} + +void Button::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + _shape(); + update(); + } +} + +String Button::get_language() const { + return language; +} + +void Button::set_icon(const Ref<Texture2D> &p_icon) { + if (icon != p_icon) { + icon = p_icon; + update(); + _change_notify("icon"); + minimum_size_changed(); + } } Ref<Texture2D> Button::get_icon() const { @@ -269,9 +409,11 @@ Ref<Texture2D> Button::get_icon() const { } void Button::set_expand_icon(bool p_expand_icon) { - expand_icon = p_expand_icon; - update(); - minimum_size_changed(); + if (expand_icon != p_expand_icon) { + expand_icon = p_expand_icon; + update(); + minimum_size_changed(); + } } bool Button::is_expand_icon() const { @@ -279,9 +421,11 @@ bool Button::is_expand_icon() const { } void Button::set_flat(bool p_flat) { - flat = p_flat; - update(); - _change_notify("flat"); + if (flat != p_flat) { + flat = p_flat; + update(); + _change_notify("flat"); + } } bool Button::is_flat() const { @@ -289,9 +433,11 @@ bool Button::is_flat() const { } void Button::set_clip_text(bool p_clip_text) { - clip_text = p_clip_text; - update(); - minimum_size_changed(); + if (clip_text != p_clip_text) { + clip_text = p_clip_text; + update(); + minimum_size_changed(); + } } bool Button::get_clip_text() const { @@ -299,17 +445,76 @@ bool Button::get_clip_text() const { } void Button::set_text_align(TextAlign p_align) { - align = p_align; - update(); + if (align != p_align) { + align = p_align; + update(); + } } Button::TextAlign Button::get_text_align() const { return align; } +bool Button::_set(const StringName &p_name, const Variant &p_value) { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + double value = p_value; + if (value == -1) { + if (opentype_features.has(tag)) { + opentype_features.erase(tag); + _shape(); + update(); + } + } else { + if ((double)opentype_features[tag] != value) { + opentype_features[tag] = value; + _shape(); + update(); + } + } + _change_notify(); + return true; + } + + return false; +} + +bool Button::_get(const StringName &p_name, Variant &r_ret) const { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + if (opentype_features.has(tag)) { + r_ret = opentype_features[tag]; + return true; + } else { + r_ret = -1; + return true; + } + } + return false; +} + +void Button::_get_property_list(List<PropertyInfo> *p_list) const { + for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { + String name = TS->tag_to_name(*ftr); + p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name)); + } + p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); +} + void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text", "text"), &Button::set_text); ClassDB::bind_method(D_METHOD("get_text"), &Button::get_text); + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Button::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &Button::get_text_direction); + ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &Button::set_opentype_feature); + ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &Button::get_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_opentype_features"), &Button::clear_opentype_features); + ClassDB::bind_method(D_METHOD("set_language", "language"), &Button::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &Button::get_language); ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon); ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon); ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon); @@ -325,7 +530,9 @@ void Button::_bind_methods() { BIND_ENUM_CONSTANT(ALIGN_CENTER); BIND_ENUM_CONSTANT(ALIGN_RIGHT); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); @@ -334,6 +541,9 @@ void Button::_bind_methods() { } Button::Button(const String &p_text) { + text_buf.instance(); + text_buf->set_flags(TextServer::BREAK_MANDATORY); + flat = false; clip_text = false; expand_icon = false; diff --git a/scene/gui/button.h b/scene/gui/button.h index 5b44b1322e..d633fddc8a 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,6 +32,7 @@ #define BUTTON_H #include "scene/gui/base_button.h" +#include "scene/resources/text_paragraph.h" class Button : public BaseButton { GDCLASS(Button, BaseButton); @@ -47,23 +48,45 @@ private: bool flat; String text; String xl_text; + Ref<TextParagraph> text_buf; + + Dictionary opentype_features; + String language; + TextDirection text_direction = TEXT_DIRECTION_AUTO; + Ref<Texture2D> icon; bool expand_icon; bool clip_text; TextAlign align; float _internal_margin[4]; + void _shape(); + protected: - void _set_internal_margin(Margin p_margin, float p_value); + void _set_internal_margin(Side p_side, float p_value); void _notification(int p_what); static void _bind_methods(); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + public: virtual Size2 get_minimum_size() const override; void set_text(const String &p_text); String get_text() const; + void set_text_direction(TextDirection p_text_direction); + TextDirection get_text_direction() const; + + void set_opentype_feature(const String &p_name, int p_value); + int get_opentype_feature(const String &p_name) const; + void clear_opentype_features(); + + void set_language(const String &p_language); + String get_language() const; + void set_icon(const Ref<Texture2D> &p_icon); Ref<Texture2D> get_icon() const; diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp index 1a72f3ca4d..f2d1dee0fc 100644 --- a/scene/gui/center_container.cpp +++ b/scene/gui/center_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/center_container.h b/scene/gui/center_container.h index 638843c389..ee8b2e0e48 100644 --- a/scene/gui/center_container.h +++ b/scene/gui/center_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp index df6f38f65d..9df328dd11 100644 --- a/scene/gui/check_box.cpp +++ b/scene/gui/check_box.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -62,14 +62,20 @@ Size2 CheckBox::get_minimum_size() const { minsize.width += get_theme_constant("hseparation"); } Ref<StyleBox> sb = get_theme_stylebox("normal"); - minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(MARGIN_TOP) + sb->get_margin(MARGIN_BOTTOM)); + minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM)); return minsize; } void CheckBox::_notification(int p_what) { - if (p_what == NOTIFICATION_THEME_CHANGED) { - _set_internal_margin(MARGIN_LEFT, get_icon_size().width); + if ((p_what == NOTIFICATION_THEME_CHANGED) || (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || (p_what == NOTIFICATION_TRANSLATION_CHANGED))) { + if (is_layout_rtl()) { + _set_internal_margin(SIDE_LEFT, 0.f); + _set_internal_margin(SIDE_RIGHT, get_icon_size().width); + } else { + _set_internal_margin(SIDE_LEFT, get_icon_size().width); + _set_internal_margin(SIDE_RIGHT, 0.f); + } } else if (p_what == NOTIFICATION_DRAW) { RID ci = get_canvas_item(); @@ -78,7 +84,11 @@ void CheckBox::_notification(int p_what) { Ref<StyleBox> sb = get_theme_stylebox("normal"); Vector2 ofs; - ofs.x = sb->get_margin(MARGIN_LEFT); + if (is_layout_rtl()) { + ofs.x = get_size().x - sb->get_margin(SIDE_RIGHT) - get_icon_size().width; + } else { + ofs.x = sb->get_margin(SIDE_LEFT); + } ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant("check_vadjust"); if (is_pressed()) { @@ -96,8 +106,14 @@ bool CheckBox::is_radio() { CheckBox::CheckBox(const String &p_text) : Button(p_text) { set_toggle_mode(true); + set_text_align(ALIGN_LEFT); - _set_internal_margin(MARGIN_LEFT, get_icon_size().width); + + if (is_layout_rtl()) { + _set_internal_margin(SIDE_RIGHT, get_icon_size().width); + } else { + _set_internal_margin(SIDE_LEFT, get_icon_size().width); + } } CheckBox::~CheckBox() { diff --git a/scene/gui/check_box.h b/scene/gui/check_box.h index cc00524698..9fb0aea218 100644 --- a/scene/gui/check_box.h +++ b/scene/gui/check_box.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index 790faeb4fd..a8bf449355 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -55,25 +55,46 @@ Size2 CheckButton::get_minimum_size() const { minsize.width += get_theme_constant("hseparation"); } Ref<StyleBox> sb = get_theme_stylebox("normal"); - minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(MARGIN_TOP) + sb->get_margin(MARGIN_BOTTOM)); + minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM)); return minsize; } void CheckButton::_notification(int p_what) { - if (p_what == NOTIFICATION_THEME_CHANGED) { - _set_internal_margin(MARGIN_RIGHT, get_icon_size().width); + if ((p_what == NOTIFICATION_THEME_CHANGED) || (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) || (p_what == NOTIFICATION_TRANSLATION_CHANGED)) { + if (is_layout_rtl()) { + _set_internal_margin(SIDE_LEFT, get_icon_size().width); + _set_internal_margin(SIDE_RIGHT, 0.f); + } else { + _set_internal_margin(SIDE_LEFT, 0.f); + _set_internal_margin(SIDE_RIGHT, get_icon_size().width); + } } else if (p_what == NOTIFICATION_DRAW) { RID ci = get_canvas_item(); + bool rtl = is_layout_rtl(); - Ref<Texture2D> on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on"); - Ref<Texture2D> off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off"); + Ref<Texture2D> on; + if (rtl) { + on = Control::get_theme_icon(is_disabled() ? "on_disabled_mirrored" : "on_mirrored"); + } else { + on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on"); + } + Ref<Texture2D> off; + if (rtl) { + off = Control::get_theme_icon(is_disabled() ? "off_disabled_mirrored" : "off_mirrored"); + } else { + off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off"); + } Ref<StyleBox> sb = get_theme_stylebox("normal"); Vector2 ofs; Size2 tex_size = get_icon_size(); - ofs.x = get_size().width - (tex_size.width + sb->get_margin(MARGIN_RIGHT)); + if (rtl) { + ofs.x = sb->get_margin(SIDE_LEFT); + } else { + ofs.x = get_size().width - (tex_size.width + sb->get_margin(SIDE_RIGHT)); + } ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant("check_vadjust"); if (is_pressed()) { @@ -87,8 +108,11 @@ void CheckButton::_notification(int p_what) { CheckButton::CheckButton() { set_toggle_mode(true); set_text_align(ALIGN_LEFT); - - _set_internal_margin(MARGIN_RIGHT, get_icon_size().width); + if (is_layout_rtl()) { + _set_internal_margin(SIDE_LEFT, get_icon_size().width); + } else { + _set_internal_margin(SIDE_RIGHT, get_icon_size().width); + } } CheckButton::~CheckButton() { diff --git a/scene/gui/check_button.h b/scene/gui/check_button.h index 99a12a3270..29c557ce89 100644 --- a/scene/gui/check_button.h +++ b/scene/gui/check_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index f6f52fbf55..28a0ea0100 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,9 +34,9 @@ void CodeEdit::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_ENTER_TREE: { - set_gutter_width(main_gutter, cache.row_height); - set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0').width); - set_gutter_width(fold_gutter, cache.row_height / 1.2); + set_gutter_width(main_gutter, get_row_height()); + set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0', 0, cache.font_size).width); + set_gutter_width(fold_gutter, get_row_height() / 1.2); breakpoint_color = get_theme_color("breakpoint_color"); breakpoint_icon = get_theme_icon("breakpoint"); @@ -234,14 +234,16 @@ bool CodeEdit::is_line_numbers_zero_padded() const { } void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { - String fc = String::num(p_line + 1).lpad(line_number_digits, line_number_padding); - - int yofs = p_region.position.y + (cache.row_height - cache.font->get_height()) / 2; + String fc = TS->format_number(String::num(p_line + 1).lpad(line_number_digits, line_number_padding)); + Ref<TextLine> tl; + tl.instance(); + tl->add_string(fc, cache.font, cache.font_size); + int yofs = p_region.position.y + (get_row_height() - tl->get_size().y) / 2; Color number_color = get_line_gutter_item_color(p_line, line_number_gutter); if (number_color == Color(1, 1, 1)) { number_color = line_number_color; } - cache.font->draw(get_canvas_item(), Point2(p_region.position.x, yofs + cache.font->get_ascent()), fc, number_color); + tl->draw(get_canvas_item(), Point2(p_region.position.x, yofs), number_color); } /* Fold Gutter */ @@ -368,7 +370,7 @@ void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) { while (lc /= 10) { line_number_digits++; } - set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0').width); + set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0', 0, cache.font_size).width); int from_line = MIN(p_from_line, p_to_line); int line_count = (p_to_line - p_from_line); @@ -410,6 +412,10 @@ void CodeEdit::_update_gutter_indexes() { } CodeEdit::CodeEdit() { + /* Text Direction */ + set_layout_direction(LAYOUT_DIRECTION_LTR); + set_text_direction(TEXT_DIRECTION_LTR); + /* Gutters */ int gutter_idx = 0; diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index c989e5ed79..d0c39ec0f1 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index f8a67d154b..6c81ef0998 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -66,7 +66,7 @@ void ColorPicker::_notification(int p_what) { } break; case NOTIFICATION_PARENTED: { for (int i = 0; i < 4; i++) { - set_margin((Margin)i, get_margin((Margin)i) + get_theme_constant("margin")); + set_offset((Side)i, get_offset((Side)i) + get_theme_constant("margin")); } } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -591,7 +591,7 @@ void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) { } Ref<Image> img = r->get_texture()->get_data(); - if (img.is_valid() && !img->empty()) { + if (img.is_valid() && !img->is_empty()) { Vector2 ofs = mev->get_global_position() - r->get_visible_rect().get_position(); Color c = img->get_pixel(ofs.x, r->get_visible_rect().size.height - ofs.y); @@ -611,11 +611,11 @@ void ColorPicker::_screen_pick_pressed() { screen = memnew(Control); r->add_child(screen); screen->set_as_top_level(true); - screen->set_anchors_and_margins_preset(Control::PRESET_WIDE); + screen->set_anchors_and_offsets_preset(Control::PRESET_WIDE); screen->set_default_cursor_shape(CURSOR_POINTING_HAND); screen->connect("gui_input", callable_mp(this, &ColorPicker::_screen_input)); // It immediately toggles off in the first press otherwise. - screen->call_deferred("connect", "hide", Callable(btn_pick, "set_pressed"), varray(false)); + screen->call_deferred("connect", "hidden", Callable(btn_pick, "set_pressed"), varray(false)); } screen->raise(); #ifndef _MSC_VER @@ -973,7 +973,7 @@ void ColorPickerButton::_update_picker() { popup = memnew(PopupPanel); popup->set_wrap_controls(true); picker = memnew(ColorPicker); - picker->set_anchors_and_margins_preset(PRESET_WIDE); + picker->set_anchors_and_offsets_preset(PRESET_WIDE); popup->add_child(picker); add_child(popup); picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed)); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 128664b49d..3695820a79 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/color_rect.cpp b/scene/gui/color_rect.cpp index 0c38b93c60..e35d37d520 100644 --- a/scene/gui/color_rect.cpp +++ b/scene/gui/color_rect.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/color_rect.h b/scene/gui/color_rect.h index 61d57f7cca..5c650f9f01 100644 --- a/scene/gui/color_rect.h +++ b/scene/gui/color_rect.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index 5643110b89..03bade6702 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -121,12 +121,7 @@ void Container::fit_child_in_rect(Control *p_child, const Rect2 &p_rect) { } } - for (int i = 0; i < 4; i++) { - p_child->set_anchor(Margin(i), ANCHOR_BEGIN); - } - - p_child->set_position(r.position); - p_child->set_size(r.size); + p_child->set_rect(r); p_child->set_rotation(0); p_child->set_scale(Vector2(1, 1)); } @@ -168,7 +163,7 @@ String Container::get_configuration_warning() const { String warning = Control::get_configuration_warning(); if (get_class() == "Container" && get_script().is_null()) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead."); diff --git a/scene/gui/container.h b/scene/gui/container.h index b789bcf3b0..5e60ca04dc 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 0381f69bcb..ad21c351d0 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,12 +36,15 @@ #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/string/print_string.h" +#include "core/string/translation.h" + #include "scene/gui/label.h" #include "scene/gui/panel.h" #include "scene/main/canvas_layer.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" #include "servers/rendering_server.h" +#include "servers/text_server.h" #ifdef TOOLS_ENABLED #include "editor/editor_settings.h" @@ -55,17 +58,17 @@ Dictionary Control::_edit_get_state() const { s["scale"] = get_scale(); s["pivot"] = get_pivot_offset(); Array anchors; - anchors.push_back(get_anchor(MARGIN_LEFT)); - anchors.push_back(get_anchor(MARGIN_TOP)); - anchors.push_back(get_anchor(MARGIN_RIGHT)); - anchors.push_back(get_anchor(MARGIN_BOTTOM)); + anchors.push_back(get_anchor(SIDE_LEFT)); + anchors.push_back(get_anchor(SIDE_TOP)); + anchors.push_back(get_anchor(SIDE_RIGHT)); + anchors.push_back(get_anchor(SIDE_BOTTOM)); s["anchors"] = anchors; - Array margins; - margins.push_back(get_margin(MARGIN_LEFT)); - margins.push_back(get_margin(MARGIN_TOP)); - margins.push_back(get_margin(MARGIN_RIGHT)); - margins.push_back(get_margin(MARGIN_BOTTOM)); - s["margins"] = margins; + Array offsets; + offsets.push_back(get_offset(SIDE_LEFT)); + offsets.push_back(get_offset(SIDE_TOP)); + offsets.push_back(get_offset(SIDE_RIGHT)); + offsets.push_back(get_offset(SIDE_BOTTOM)); + s["offsets"] = offsets; return s; } @@ -76,15 +79,15 @@ void Control::_edit_set_state(const Dictionary &p_state) { set_scale(state["scale"]); set_pivot_offset(state["pivot"]); Array anchors = state["anchors"]; - data.anchor[MARGIN_LEFT] = anchors[0]; - data.anchor[MARGIN_TOP] = anchors[1]; - data.anchor[MARGIN_RIGHT] = anchors[2]; - data.anchor[MARGIN_BOTTOM] = anchors[3]; - Array margins = state["margins"]; - data.margin[MARGIN_LEFT] = margins[0]; - data.margin[MARGIN_TOP] = margins[1]; - data.margin[MARGIN_RIGHT] = margins[2]; - data.margin[MARGIN_BOTTOM] = margins[3]; + data.anchor[SIDE_LEFT] = anchors[0]; + data.anchor[SIDE_TOP] = anchors[1]; + data.anchor[SIDE_RIGHT] = anchors[2]; + data.anchor[SIDE_BOTTOM] = anchors[3]; + Array offsets = state["offsets"]; + data.offset[SIDE_LEFT] = offsets[0]; + data.offset[SIDE_TOP] = offsets[1]; + data.offset[SIDE_RIGHT] = offsets[2]; + data.offset[SIDE_BOTTOM] = offsets[3]; _size_changed(); } @@ -220,13 +223,6 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) { } data.icon_override.erase(dname); notification(NOTIFICATION_THEME_CHANGED); - } else if (name.begins_with("custom_shaders/")) { - String dname = name.get_slicec('/', 1); - if (data.shader_override.has(dname)) { - data.shader_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed)); - } - data.shader_override.erase(dname); - notification(NOTIFICATION_THEME_CHANGED); } else if (name.begins_with("custom_styles/")) { String dname = name.get_slicec('/', 1); if (data.style_override.has(dname)) { @@ -241,6 +237,10 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) { } data.font_override.erase(dname); notification(NOTIFICATION_THEME_CHANGED); + } else if (name.begins_with("custom_font_sizes/")) { + String dname = name.get_slicec('/', 1); + data.font_size_override.erase(dname); + notification(NOTIFICATION_THEME_CHANGED); } else if (name.begins_with("custom_colors/")) { String dname = name.get_slicec('/', 1); data.color_override.erase(dname); @@ -257,15 +257,15 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) { if (name.begins_with("custom_icons/")) { String dname = name.get_slicec('/', 1); add_theme_icon_override(dname, p_value); - } else if (name.begins_with("custom_shaders/")) { - String dname = name.get_slicec('/', 1); - add_theme_shader_override(dname, p_value); } else if (name.begins_with("custom_styles/")) { String dname = name.get_slicec('/', 1); add_theme_style_override(dname, p_value); } else if (name.begins_with("custom_fonts/")) { String dname = name.get_slicec('/', 1); add_theme_font_override(dname, p_value); + } else if (name.begins_with("custom_font_sizes/")) { + String dname = name.get_slicec('/', 1); + add_theme_font_size_override(dname, p_value); } else if (name.begins_with("custom_colors/")) { String dname = name.get_slicec('/', 1); add_theme_color_override(dname, p_value); @@ -307,20 +307,16 @@ bool Control::_get(const StringName &p_name, Variant &r_ret) const { if (sname.begins_with("custom_icons/")) { String name = sname.get_slicec('/', 1); - r_ret = data.icon_override.has(name) ? Variant(data.icon_override[name]) : Variant(); - } else if (sname.begins_with("custom_shaders/")) { - String name = sname.get_slicec('/', 1); - - r_ret = data.shader_override.has(name) ? Variant(data.shader_override[name]) : Variant(); } else if (sname.begins_with("custom_styles/")) { String name = sname.get_slicec('/', 1); - r_ret = data.style_override.has(name) ? Variant(data.style_override[name]) : Variant(); } else if (sname.begins_with("custom_fonts/")) { String name = sname.get_slicec('/', 1); - r_ret = data.font_override.has(name) ? Variant(data.font_override[name]) : Variant(); + } else if (sname.begins_with("custom_font_sizes/")) { + String name = sname.get_slicec('/', 1); + r_ret = data.font_size_override.has(name) ? Variant(data.font_size_override[name]) : Variant(); } else if (sname.begins_with("custom_colors/")) { String name = sname.get_slicec('/', 1); r_ret = data.color_override.has(name) ? Variant(data.color_override[name]) : Variant(); @@ -359,38 +355,38 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const { } { List<StringName> names; - theme->get_shader_list(get_class_name(), &names); + theme->get_stylebox_list(get_class_name(), &names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { uint32_t hint = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; - if (data.shader_override.has(E->get())) { + if (data.style_override.has(E->get())) { hint |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_shaders/" + E->get(), PROPERTY_HINT_RESOURCE_TYPE, "Shader,VisualShader", hint)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_styles/" + E->get(), PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", hint)); } } { List<StringName> names; - theme->get_stylebox_list(get_class_name(), &names); + theme->get_font_list(get_class_name(), &names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { uint32_t hint = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; - if (data.style_override.has(E->get())) { + if (data.font_override.has(E->get())) { hint |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_styles/" + E->get(), PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", hint)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_fonts/" + E->get(), PROPERTY_HINT_RESOURCE_TYPE, "Font", hint)); } } { List<StringName> names; - theme->get_font_list(get_class_name(), &names); + theme->get_font_size_list(get_class_name(), &names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { uint32_t hint = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; - if (data.font_override.has(E->get())) { + if (data.font_size_override.has(E->get())) { hint |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_fonts/" + E->get(), PROPERTY_HINT_RESOURCE_TYPE, "Font", hint)); + p_list->push_back(PropertyInfo(Variant::INT, "custom_font_sizes/" + E->get(), PROPERTY_HINT_NONE, "", hint)); } } { @@ -423,8 +419,41 @@ Control *Control::get_parent_control() const { return data.parent; } -void Control::_resize(const Size2 &p_size) { - _size_changed(); +void Control::set_layout_direction(Control::LayoutDirection p_direction) { + ERR_FAIL_INDEX((int)p_direction, 4); + + data.layout_dir = p_direction; + propagate_notification(NOTIFICATION_LAYOUT_DIRECTION_CHANGED); +} + +Control::LayoutDirection Control::get_layout_direction() const { + return data.layout_dir; +} + +bool Control::is_layout_rtl() const { + if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) { + Window *parent_window = Object::cast_to<Window>(get_parent()); + Control *parent_control = get_parent_control(); + if (parent_control) { + return parent_control->is_layout_rtl(); + } else if (parent_window) { + return parent_window->is_layout_rtl(); + } else { + if (GLOBAL_GET("display/window/force_right_to_left_layout_direction")) { + return true; + } + String locale = TranslationServer::get_singleton()->get_tool_locale(); + return TS->is_locale_right_to_left(locale); + } + } else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) { + if (GLOBAL_GET("display/window/force_right_to_left_layout_direction")) { + return true; + } + String locale = TranslationServer::get_singleton()->get_tool_locale(); + return TS->is_locale_right_to_left(locale); + } else { + return (data.layout_dir == LAYOUT_DIRECTION_RTL); + } } //moved theme configuration here, so controls can set up even if still not inside active scene @@ -581,7 +610,6 @@ void Control::_notification(int p_notification) { case NOTIFICATION_FOCUS_EXIT: { emit_signal(SceneStringNames::get_singleton()->focus_exited); update(); - } break; case NOTIFICATION_THEME_CHANGED: { minimum_size_changed(); @@ -601,6 +629,10 @@ void Control::_notification(int p_notification) { } } break; + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + _size_changed(); + } break; } } @@ -840,35 +872,6 @@ Ref<Texture2D> Control::get_icons(Control *p_theme_owner, Window *p_theme_owner_ return Theme::get_default()->get_icon(p_name, p_node_type); } -Ref<Shader> Control::get_theme_shader(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { - const Ref<Shader> *sdr = data.shader_override.getptr(p_name); - if (sdr) { - return *sdr; - } - } - - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_shaders(data.theme_owner, data.theme_owner_window, p_name, type); -} - -Ref<Shader> Control::get_shaders(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - Ref<Shader> shader; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, shader, &Theme::get_shader, &Theme::has_shader, p_name, p_node_type)) { - return shader; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_shader(p_name, p_node_type)) { - return Theme::get_project_default()->get_shader(p_name, p_node_type); - } - } - - return Theme::get_default()->get_shader(p_name, p_node_type); -} - Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_node_type) const { if (p_node_type == StringName() || p_node_type == get_class_name()) { const Ref<StyleBox> *style = data.style_override.getptr(p_name); @@ -911,6 +914,19 @@ Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_ return get_fonts(data.theme_owner, data.theme_owner_window, p_name, type); } +int Control::get_theme_font_size(const StringName &p_name, const StringName &p_node_type) const { + if (p_node_type == StringName() || p_node_type == get_class_name()) { + const int *font_size = data.font_size_override.getptr(p_name); + if (font_size) { + return *font_size; + } + } + + StringName type = p_node_type ? p_node_type : get_class_name(); + + return get_font_sizes(data.theme_owner, data.theme_owner_window, p_name, type); +} + Ref<Font> Control::get_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { Ref<Font> font; @@ -927,6 +943,22 @@ Ref<Font> Control::get_fonts(Control *p_theme_owner, Window *p_theme_owner_windo return Theme::get_default()->get_font(p_name, p_node_type); } +int Control::get_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { + int font_size; + + if (_find_theme_item(p_theme_owner, p_theme_owner_window, font_size, &Theme::get_font_size, &Theme::has_font_size, p_name, p_node_type)) { + return font_size; + } + + if (Theme::get_project_default().is_valid()) { + if (Theme::get_project_default()->has_font_size(p_name, p_node_type)) { + return Theme::get_project_default()->get_font_size(p_name, p_node_type); + } + } + + return Theme::get_default()->get_font_size(p_name, p_node_type); +} + Color Control::get_theme_color(const StringName &p_name, const StringName &p_node_type) const { if (p_node_type == StringName() || p_node_type == get_class_name()) { const Color *color = data.color_override.getptr(p_name); @@ -988,11 +1020,6 @@ bool Control::has_theme_icon_override(const StringName &p_name) const { return tex != nullptr; } -bool Control::has_theme_shader_override(const StringName &p_name) const { - const Ref<Shader> *sdr = data.shader_override.getptr(p_name); - return sdr != nullptr; -} - bool Control::has_theme_stylebox_override(const StringName &p_name) const { const Ref<StyleBox> *style = data.style_override.getptr(p_name); return style != nullptr; @@ -1003,6 +1030,11 @@ bool Control::has_theme_font_override(const StringName &p_name) const { return font != nullptr; } +bool Control::has_theme_font_size_override(const StringName &p_name) const { + const int *font_size = data.font_size_override.getptr(p_name); + return font_size != nullptr; +} + bool Control::has_theme_color_override(const StringName &p_name) const { const Color *color = data.color_override.getptr(p_name); return color != nullptr; @@ -1038,79 +1070,79 @@ bool Control::has_icons(Control *p_theme_owner, Window *p_theme_owner_window, co return Theme::get_default()->has_icon(p_name, p_node_type); } -bool Control::has_theme_shader(const StringName &p_name, const StringName &p_node_type) const { +bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_node_type) const { if (p_node_type == StringName() || p_node_type == get_class_name()) { - if (has_theme_shader_override(p_name)) { + if (has_theme_stylebox_override(p_name)) { return true; } } StringName type = p_node_type ? p_node_type : get_class_name(); - return has_shaders(data.theme_owner, data.theme_owner_window, p_name, type); + return has_styleboxs(data.theme_owner, data.theme_owner_window, p_name, type); } -bool Control::has_shaders(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_shader, p_name, p_node_type)) { +bool Control::has_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { + if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_stylebox, p_name, p_node_type)) { return true; } if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_shader(p_name, p_node_type)) { + if (Theme::get_project_default()->has_stylebox(p_name, p_node_type)) { return true; } } - return Theme::get_default()->has_shader(p_name, p_node_type); + return Theme::get_default()->has_stylebox(p_name, p_node_type); } -bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_node_type) const { +bool Control::has_theme_font(const StringName &p_name, const StringName &p_node_type) const { if (p_node_type == StringName() || p_node_type == get_class_name()) { - if (has_theme_stylebox_override(p_name)) { + if (has_theme_font_override(p_name)) { return true; } } StringName type = p_node_type ? p_node_type : get_class_name(); - return has_styleboxs(data.theme_owner, data.theme_owner_window, p_name, type); + return has_fonts(data.theme_owner, data.theme_owner_window, p_name, type); } -bool Control::has_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_stylebox, p_name, p_node_type)) { +bool Control::has_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { + if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_font, p_name, p_node_type)) { return true; } if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_stylebox(p_name, p_node_type)) { + if (Theme::get_project_default()->has_font(p_name, p_node_type)) { return true; } } - return Theme::get_default()->has_stylebox(p_name, p_node_type); + return Theme::get_default()->has_font(p_name, p_node_type); } -bool Control::has_theme_font(const StringName &p_name, const StringName &p_node_type) const { +bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_node_type) const { if (p_node_type == StringName() || p_node_type == get_class_name()) { - if (has_theme_font_override(p_name)) { + if (has_theme_font_size_override(p_name)) { return true; } } StringName type = p_node_type ? p_node_type : get_class_name(); - return has_fonts(data.theme_owner, data.theme_owner_window, p_name, type); + return has_font_sizes(data.theme_owner, data.theme_owner_window, p_name, type); } -bool Control::has_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_font, p_name, p_node_type)) { +bool Control::has_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { + if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_font_size, p_name, p_node_type)) { return true; } if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_font(p_name, p_node_type)) { + if (Theme::get_project_default()->has_font_size(p_name, p_node_type)) { return true; } } - return Theme::get_default()->has_font(p_name, p_node_type); + return Theme::get_default()->has_font_size(p_name, p_node_type); } bool Control::has_theme_color(const StringName &p_name, const StringName &p_node_type) const { @@ -1195,15 +1227,15 @@ Size2 Control::get_parent_area_size() const { void Control::_size_changed() { Rect2 parent_rect = get_parent_anchorable_rect(); - float margin_pos[4]; + float edge_pos[4]; for (int i = 0; i < 4; i++) { float area = parent_rect.size[i & 1]; - margin_pos[i] = data.margin[i] + (data.anchor[i] * area); + edge_pos[i] = data.offset[i] + (data.anchor[i] * area); } - Point2 new_pos_cache = Point2(margin_pos[0], margin_pos[1]); - Size2 new_size_cache = Point2(margin_pos[2], margin_pos[3]) - new_pos_cache; + Point2 new_pos_cache = Point2(edge_pos[0], edge_pos[1]); + Size2 new_size_cache = Point2(edge_pos[2], edge_pos[3]) - new_pos_cache; Size2 minimum_size = get_combined_minimum_size(); @@ -1217,6 +1249,10 @@ void Control::_size_changed() { new_size_cache.width = minimum_size.width; } + if (is_layout_rtl()) { + new_pos_cache.x = parent_rect.size.x - new_pos_cache.x - new_size_cache.x; + } + if (minimum_size.height > new_size_cache.height) { if (data.v_grow == GROW_DIRECTION_BEGIN) { new_pos_cache.y += new_size_cache.height - minimum_size.height; @@ -1239,7 +1275,7 @@ void Control::_size_changed() { } if (pos_changed || size_changed) { item_rect_changed(size_changed); - _change_notify_margins(); + _change_notify_offsets(); _notify_transform(); } @@ -1249,29 +1285,29 @@ void Control::_size_changed() { } } -void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bool p_push_opposite_anchor) { - ERR_FAIL_INDEX((int)p_margin, 4); +void Control::set_anchor(Side p_side, float p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) { + ERR_FAIL_INDEX((int)p_side, 4); Rect2 parent_rect = get_parent_anchorable_rect(); - float parent_range = (p_margin == MARGIN_LEFT || p_margin == MARGIN_RIGHT) ? parent_rect.size.x : parent_rect.size.y; - float previous_margin_pos = data.margin[p_margin] + data.anchor[p_margin] * parent_range; - float previous_opposite_margin_pos = data.margin[(p_margin + 2) % 4] + data.anchor[(p_margin + 2) % 4] * parent_range; + float parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y; + float previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range; + float previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range; - data.anchor[p_margin] = p_anchor; + data.anchor[p_side] = p_anchor; - if (((p_margin == MARGIN_LEFT || p_margin == MARGIN_TOP) && data.anchor[p_margin] > data.anchor[(p_margin + 2) % 4]) || - ((p_margin == MARGIN_RIGHT || p_margin == MARGIN_BOTTOM) && data.anchor[p_margin] < data.anchor[(p_margin + 2) % 4])) { + if (((p_side == SIDE_LEFT || p_side == SIDE_TOP) && data.anchor[p_side] > data.anchor[(p_side + 2) % 4]) || + ((p_side == SIDE_RIGHT || p_side == SIDE_BOTTOM) && data.anchor[p_side] < data.anchor[(p_side + 2) % 4])) { if (p_push_opposite_anchor) { - data.anchor[(p_margin + 2) % 4] = data.anchor[p_margin]; + data.anchor[(p_side + 2) % 4] = data.anchor[p_side]; } else { - data.anchor[p_margin] = data.anchor[(p_margin + 2) % 4]; + data.anchor[p_side] = data.anchor[(p_side + 2) % 4]; } } - if (!p_keep_margin) { - data.margin[p_margin] = previous_margin_pos - data.anchor[p_margin] * parent_range; + if (!p_keep_offset) { + data.offset[p_side] = previous_pos - data.anchor[p_side] * parent_range; if (p_push_opposite_anchor) { - data.margin[(p_margin + 2) % 4] = previous_opposite_margin_pos - data.anchor[(p_margin + 2) % 4] * parent_range; + data.offset[(p_side + 2) % 4] = previous_opposite_pos - data.anchor[(p_side + 2) % 4] * parent_range; } } if (is_inside_tree()) { @@ -1285,16 +1321,16 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo _change_notify("anchor_bottom"); } -void Control::_set_anchor(Margin p_margin, float p_anchor) { - set_anchor(p_margin, p_anchor); +void Control::_set_anchor(Side p_side, float p_anchor) { + set_anchor(p_side, p_anchor); } -void Control::set_anchor_and_margin(Margin p_margin, float p_anchor, float p_pos, bool p_push_opposite_anchor) { - set_anchor(p_margin, p_anchor, false, p_push_opposite_anchor); - set_margin(p_margin, p_pos); +void Control::set_anchor_and_offset(Side p_side, float p_anchor, float p_pos, bool p_push_opposite_anchor) { + set_anchor(p_side, p_anchor, false, p_push_opposite_anchor); + set_offset(p_side, p_pos); } -void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins) { +void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) { ERR_FAIL_INDEX((int)p_preset, 16); //Left @@ -1307,21 +1343,21 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins) { case PRESET_LEFT_WIDE: case PRESET_HCENTER_WIDE: case PRESET_WIDE: - set_anchor(MARGIN_LEFT, ANCHOR_BEGIN, p_keep_margins); + set_anchor(SIDE_LEFT, ANCHOR_BEGIN, p_keep_offsets); break; case PRESET_CENTER_TOP: case PRESET_CENTER_BOTTOM: case PRESET_CENTER: case PRESET_VCENTER_WIDE: - set_anchor(MARGIN_LEFT, 0.5, p_keep_margins); + set_anchor(SIDE_LEFT, 0.5, p_keep_offsets); break; case PRESET_TOP_RIGHT: case PRESET_BOTTOM_RIGHT: case PRESET_CENTER_RIGHT: case PRESET_RIGHT_WIDE: - set_anchor(MARGIN_LEFT, ANCHOR_END, p_keep_margins); + set_anchor(SIDE_LEFT, ANCHOR_END, p_keep_offsets); break; } @@ -1335,21 +1371,21 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins) { case PRESET_TOP_WIDE: case PRESET_VCENTER_WIDE: case PRESET_WIDE: - set_anchor(MARGIN_TOP, ANCHOR_BEGIN, p_keep_margins); + set_anchor(SIDE_TOP, ANCHOR_BEGIN, p_keep_offsets); break; case PRESET_CENTER_LEFT: case PRESET_CENTER_RIGHT: case PRESET_CENTER: case PRESET_HCENTER_WIDE: - set_anchor(MARGIN_TOP, 0.5, p_keep_margins); + set_anchor(SIDE_TOP, 0.5, p_keep_offsets); break; case PRESET_BOTTOM_LEFT: case PRESET_BOTTOM_RIGHT: case PRESET_CENTER_BOTTOM: case PRESET_BOTTOM_WIDE: - set_anchor(MARGIN_TOP, ANCHOR_END, p_keep_margins); + set_anchor(SIDE_TOP, ANCHOR_END, p_keep_offsets); break; } @@ -1359,14 +1395,14 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins) { case PRESET_BOTTOM_LEFT: case PRESET_CENTER_LEFT: case PRESET_LEFT_WIDE: - set_anchor(MARGIN_RIGHT, ANCHOR_BEGIN, p_keep_margins); + set_anchor(SIDE_RIGHT, ANCHOR_BEGIN, p_keep_offsets); break; case PRESET_CENTER_TOP: case PRESET_CENTER_BOTTOM: case PRESET_CENTER: case PRESET_VCENTER_WIDE: - set_anchor(MARGIN_RIGHT, 0.5, p_keep_margins); + set_anchor(SIDE_RIGHT, 0.5, p_keep_offsets); break; case PRESET_TOP_RIGHT: @@ -1377,7 +1413,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins) { case PRESET_BOTTOM_WIDE: case PRESET_HCENTER_WIDE: case PRESET_WIDE: - set_anchor(MARGIN_RIGHT, ANCHOR_END, p_keep_margins); + set_anchor(SIDE_RIGHT, ANCHOR_END, p_keep_offsets); break; } @@ -1387,14 +1423,14 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins) { case PRESET_TOP_RIGHT: case PRESET_CENTER_TOP: case PRESET_TOP_WIDE: - set_anchor(MARGIN_BOTTOM, ANCHOR_BEGIN, p_keep_margins); + set_anchor(SIDE_BOTTOM, ANCHOR_BEGIN, p_keep_offsets); break; case PRESET_CENTER_LEFT: case PRESET_CENTER_RIGHT: case PRESET_CENTER: case PRESET_HCENTER_WIDE: - set_anchor(MARGIN_BOTTOM, 0.5, p_keep_margins); + set_anchor(SIDE_BOTTOM, 0.5, p_keep_offsets); break; case PRESET_BOTTOM_LEFT: @@ -1405,12 +1441,12 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins) { case PRESET_BOTTOM_WIDE: case PRESET_VCENTER_WIDE: case PRESET_WIDE: - set_anchor(MARGIN_BOTTOM, ANCHOR_END, p_keep_margins); + set_anchor(SIDE_BOTTOM, ANCHOR_END, p_keep_offsets); break; } } -void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode, int p_margin) { +void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode, int p_margin) { ERR_FAIL_INDEX((int)p_preset, 16); ERR_FAIL_INDEX((int)p_resize_mode, 4); @@ -1426,6 +1462,10 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz Rect2 parent_rect = get_parent_anchorable_rect(); + float x = parent_rect.size.x; + if (is_layout_rtl()) { + x = parent_rect.size.x - x - new_size.x; + } //Left switch (p_preset) { case PRESET_TOP_LEFT: @@ -1436,21 +1476,21 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz case PRESET_LEFT_WIDE: case PRESET_HCENTER_WIDE: case PRESET_WIDE: - data.margin[0] = parent_rect.size.x * (0.0 - data.anchor[0]) + p_margin + parent_rect.position.x; + data.offset[0] = x * (0.0 - data.anchor[0]) + p_margin + parent_rect.position.x; break; case PRESET_CENTER_TOP: case PRESET_CENTER_BOTTOM: case PRESET_CENTER: case PRESET_VCENTER_WIDE: - data.margin[0] = parent_rect.size.x * (0.5 - data.anchor[0]) - new_size.x / 2 + parent_rect.position.x; + data.offset[0] = x * (0.5 - data.anchor[0]) - new_size.x / 2 + parent_rect.position.x; break; case PRESET_TOP_RIGHT: case PRESET_BOTTOM_RIGHT: case PRESET_CENTER_RIGHT: case PRESET_RIGHT_WIDE: - data.margin[0] = parent_rect.size.x * (1.0 - data.anchor[0]) - new_size.x - p_margin + parent_rect.position.x; + data.offset[0] = x * (1.0 - data.anchor[0]) - new_size.x - p_margin + parent_rect.position.x; break; } @@ -1464,21 +1504,21 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz case PRESET_TOP_WIDE: case PRESET_VCENTER_WIDE: case PRESET_WIDE: - data.margin[1] = parent_rect.size.y * (0.0 - data.anchor[1]) + p_margin + parent_rect.position.y; + data.offset[1] = parent_rect.size.y * (0.0 - data.anchor[1]) + p_margin + parent_rect.position.y; break; case PRESET_CENTER_LEFT: case PRESET_CENTER_RIGHT: case PRESET_CENTER: case PRESET_HCENTER_WIDE: - data.margin[1] = parent_rect.size.y * (0.5 - data.anchor[1]) - new_size.y / 2 + parent_rect.position.y; + data.offset[1] = parent_rect.size.y * (0.5 - data.anchor[1]) - new_size.y / 2 + parent_rect.position.y; break; case PRESET_BOTTOM_LEFT: case PRESET_BOTTOM_RIGHT: case PRESET_CENTER_BOTTOM: case PRESET_BOTTOM_WIDE: - data.margin[1] = parent_rect.size.y * (1.0 - data.anchor[1]) - new_size.y - p_margin + parent_rect.position.y; + data.offset[1] = parent_rect.size.y * (1.0 - data.anchor[1]) - new_size.y - p_margin + parent_rect.position.y; break; } @@ -1488,14 +1528,14 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz case PRESET_BOTTOM_LEFT: case PRESET_CENTER_LEFT: case PRESET_LEFT_WIDE: - data.margin[2] = parent_rect.size.x * (0.0 - data.anchor[2]) + new_size.x + p_margin + parent_rect.position.x; + data.offset[2] = x * (0.0 - data.anchor[2]) + new_size.x + p_margin + parent_rect.position.x; break; case PRESET_CENTER_TOP: case PRESET_CENTER_BOTTOM: case PRESET_CENTER: case PRESET_VCENTER_WIDE: - data.margin[2] = parent_rect.size.x * (0.5 - data.anchor[2]) + new_size.x / 2 + parent_rect.position.x; + data.offset[2] = x * (0.5 - data.anchor[2]) + new_size.x / 2 + parent_rect.position.x; break; case PRESET_TOP_RIGHT: @@ -1506,7 +1546,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz case PRESET_BOTTOM_WIDE: case PRESET_HCENTER_WIDE: case PRESET_WIDE: - data.margin[2] = parent_rect.size.x * (1.0 - data.anchor[2]) - p_margin + parent_rect.position.x; + data.offset[2] = x * (1.0 - data.anchor[2]) - p_margin + parent_rect.position.x; break; } @@ -1516,14 +1556,14 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz case PRESET_TOP_RIGHT: case PRESET_CENTER_TOP: case PRESET_TOP_WIDE: - data.margin[3] = parent_rect.size.y * (0.0 - data.anchor[3]) + new_size.y + p_margin + parent_rect.position.y; + data.offset[3] = parent_rect.size.y * (0.0 - data.anchor[3]) + new_size.y + p_margin + parent_rect.position.y; break; case PRESET_CENTER_LEFT: case PRESET_CENTER_RIGHT: case PRESET_CENTER: case PRESET_HCENTER_WIDE: - data.margin[3] = parent_rect.size.y * (0.5 - data.anchor[3]) + new_size.y / 2 + parent_rect.position.y; + data.offset[3] = parent_rect.size.y * (0.5 - data.anchor[3]) + new_size.y / 2 + parent_rect.position.y; break; case PRESET_BOTTOM_LEFT: @@ -1534,65 +1574,65 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz case PRESET_BOTTOM_WIDE: case PRESET_VCENTER_WIDE: case PRESET_WIDE: - data.margin[3] = parent_rect.size.y * (1.0 - data.anchor[3]) - p_margin + parent_rect.position.y; + data.offset[3] = parent_rect.size.y * (1.0 - data.anchor[3]) - p_margin + parent_rect.position.y; break; } _size_changed(); } -void Control::set_anchors_and_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode, int p_margin) { +void Control::set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode, int p_margin) { set_anchors_preset(p_preset); - set_margins_preset(p_preset, p_resize_mode, p_margin); + set_offsets_preset(p_preset, p_resize_mode, p_margin); } -float Control::get_anchor(Margin p_margin) const { - ERR_FAIL_INDEX_V(int(p_margin), 4, 0.0); +float Control::get_anchor(Side p_side) const { + ERR_FAIL_INDEX_V(int(p_side), 4, 0.0); - return data.anchor[p_margin]; + return data.anchor[p_side]; } -void Control::_change_notify_margins() { +void Control::_change_notify_offsets() { // this avoids sending the whole object data again on a change - _change_notify("margin_left"); - _change_notify("margin_top"); - _change_notify("margin_right"); - _change_notify("margin_bottom"); + _change_notify("offset_left"); + _change_notify("offset_top"); + _change_notify("offset_right"); + _change_notify("offset_bottom"); _change_notify("rect_position"); _change_notify("rect_size"); } -void Control::set_margin(Margin p_margin, float p_value) { - ERR_FAIL_INDEX((int)p_margin, 4); +void Control::set_offset(Side p_side, float p_value) { + ERR_FAIL_INDEX((int)p_side, 4); - data.margin[p_margin] = p_value; + data.offset[p_side] = p_value; _size_changed(); } void Control::set_begin(const Size2 &p_point) { - data.margin[0] = p_point.x; - data.margin[1] = p_point.y; + data.offset[0] = p_point.x; + data.offset[1] = p_point.y; _size_changed(); } void Control::set_end(const Size2 &p_point) { - data.margin[2] = p_point.x; - data.margin[3] = p_point.y; + data.offset[2] = p_point.x; + data.offset[3] = p_point.y; _size_changed(); } -float Control::get_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, 0); +float Control::get_offset(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); - return data.margin[p_margin]; + return data.offset[p_side]; } Size2 Control::get_begin() const { - return Size2(data.margin[0], data.margin[1]); + return Size2(data.offset[0], data.offset[1]); } Size2 Control::get_end() const { - return Size2(data.margin[2], data.margin[3]); + return Size2(data.offset[2], data.offset[3]); } Point2 Control::get_global_position() const { @@ -1614,57 +1654,77 @@ void Control::_set_global_position(const Point2 &p_point) { set_global_position(p_point); } -void Control::set_global_position(const Point2 &p_point, bool p_keep_margins) { +void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) { Transform2D inv; if (data.parent_canvas_item) { inv = data.parent_canvas_item->get_global_transform().affine_inverse(); } - set_position(inv.xform(p_point), p_keep_margins); + set_position(inv.xform(p_point), p_keep_offsets); } -void Control::_compute_anchors(Rect2 p_rect, const float p_margins[4], float (&r_anchors)[4]) { +void Control::_compute_anchors(Rect2 p_rect, const float p_offsets[4], float (&r_anchors)[4]) { Size2 parent_rect_size = get_parent_anchorable_rect().size; ERR_FAIL_COND(parent_rect_size.x == 0.0); ERR_FAIL_COND(parent_rect_size.y == 0.0); - r_anchors[0] = (p_rect.position.x - p_margins[0]) / parent_rect_size.x; - r_anchors[1] = (p_rect.position.y - p_margins[1]) / parent_rect_size.y; - r_anchors[2] = (p_rect.position.x + p_rect.size.x - p_margins[2]) / parent_rect_size.x; - r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_margins[3]) / parent_rect_size.y; + float x = p_rect.position.x; + if (is_layout_rtl()) { + x = parent_rect_size.x - x - p_rect.size.x; + } + r_anchors[0] = (x - p_offsets[0]) / parent_rect_size.x; + r_anchors[1] = (p_rect.position.y - p_offsets[1]) / parent_rect_size.y; + r_anchors[2] = (x + p_rect.size.x - p_offsets[2]) / parent_rect_size.x; + r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_offsets[3]) / parent_rect_size.y; } -void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]) { +void Control::_compute_offsets(Rect2 p_rect, const float p_anchors[4], float (&r_offsets)[4]) { Size2 parent_rect_size = get_parent_anchorable_rect().size; - r_margins[0] = p_rect.position.x - (p_anchors[0] * parent_rect_size.x); - r_margins[1] = p_rect.position.y - (p_anchors[1] * parent_rect_size.y); - r_margins[2] = p_rect.position.x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x); - r_margins[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y); + + float x = p_rect.position.x; + if (is_layout_rtl()) { + x = parent_rect_size.x - x - p_rect.size.x; + } + r_offsets[0] = x - (p_anchors[0] * parent_rect_size.x); + r_offsets[1] = p_rect.position.y - (p_anchors[1] * parent_rect_size.y); + r_offsets[2] = x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x); + r_offsets[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y); } void Control::_set_position(const Size2 &p_point) { set_position(p_point); } -void Control::set_position(const Size2 &p_point, bool p_keep_margins) { - if (p_keep_margins) { - _compute_anchors(Rect2(p_point, data.size_cache), data.margin, data.anchor); +void Control::set_position(const Size2 &p_point, bool p_keep_offsets) { + if (p_keep_offsets) { + _compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor); _change_notify("anchor_left"); _change_notify("anchor_right"); _change_notify("anchor_top"); _change_notify("anchor_bottom"); } else { - _compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin); + _compute_offsets(Rect2(p_point, data.size_cache), data.anchor, data.offset); } _size_changed(); } +void Control::set_rect(const Rect2 &p_rect) { + for (int i = 0; i < 4; i++) { + data.anchor[i] = ANCHOR_BEGIN; + } + + _compute_offsets(p_rect, data.anchor, data.offset); + if (is_inside_tree()) { + _size_changed(); + } +} + void Control::_set_size(const Size2 &p_size) { set_size(p_size); } -void Control::set_size(const Size2 &p_size, bool p_keep_margins) { +void Control::set_size(const Size2 &p_size, bool p_keep_offsets) { Size2 new_size = p_size; Size2 min = get_combined_minimum_size(); if (new_size.x < min.x) { @@ -1674,14 +1734,14 @@ void Control::set_size(const Size2 &p_size, bool p_keep_margins) { new_size.y = min.y; } - if (p_keep_margins) { - _compute_anchors(Rect2(data.pos_cache, new_size), data.margin, data.anchor); + if (p_keep_offsets) { + _compute_anchors(Rect2(data.pos_cache, new_size), data.offset, data.anchor); _change_notify("anchor_left"); _change_notify("anchor_right"); _change_notify("anchor_top"); _change_notify("anchor_bottom"); } else { - _compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin); + _compute_offsets(Rect2(data.pos_cache, new_size), data.anchor, data.offset); } _size_changed(); } @@ -1743,23 +1803,6 @@ void Control::add_theme_icon_override(const StringName &p_name, const Ref<Textur notification(NOTIFICATION_THEME_CHANGED); } -void Control::add_theme_shader_override(const StringName &p_name, const Ref<Shader> &p_shader) { - if (data.shader_override.has(p_name)) { - data.shader_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); - } - - // clear if "null" is passed instead of a shader - if (p_shader.is_null()) { - data.shader_override.erase(p_name); - } else { - data.shader_override[p_name] = p_shader; - if (data.shader_override[p_name].is_valid()) { - data.shader_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED); - } - } - notification(NOTIFICATION_THEME_CHANGED); -} - void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) { if (data.style_override.has(p_name)) { data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed)); @@ -1794,6 +1837,11 @@ void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> notification(NOTIFICATION_THEME_CHANGED); } +void Control::add_theme_font_size_override(const StringName &p_name, int p_font_size) { + data.font_size_override[p_name] = p_font_size; + notification(NOTIFICATION_THEME_CHANGED); +} + void Control::add_theme_color_override(const StringName &p_name, const Color &p_color) { data.color_override[p_name] = p_color; notification(NOTIFICATION_THEME_CHANGED); @@ -2158,14 +2206,14 @@ String Control::_get_tooltip() const { return data.tooltip; } -void Control::set_focus_neighbour(Margin p_margin, const NodePath &p_neighbour) { - ERR_FAIL_INDEX((int)p_margin, 4); - data.focus_neighbour[p_margin] = p_neighbour; +void Control::set_focus_neighbor(Side p_side, const NodePath &p_neighbor) { + ERR_FAIL_INDEX((int)p_side, 4); + data.focus_neighbor[p_side] = p_neighbor; } -NodePath Control::get_focus_neighbour(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, NodePath()); - return data.focus_neighbour[p_margin]; +NodePath Control::get_focus_neighbor(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, NodePath()); + return data.focus_neighbor[p_side]; } void Control::set_focus_next(const NodePath &p_next) { @@ -2184,17 +2232,17 @@ NodePath Control::get_focus_previous() const { return data.focus_prev; } -#define MAX_NEIGHBOUR_SEARCH_COUNT 512 +#define MAX_NEIGHBOR_SEARCH_COUNT 512 -Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) { - ERR_FAIL_INDEX_V((int)p_margin, 4, nullptr); +Control *Control::_get_focus_neighbor(Side p_side, int p_count) { + ERR_FAIL_INDEX_V((int)p_side, 4, nullptr); - if (p_count >= MAX_NEIGHBOUR_SEARCH_COUNT) { + if (p_count >= MAX_NEIGHBOR_SEARCH_COUNT) { return nullptr; } - if (!data.focus_neighbour[p_margin].is_empty()) { + if (!data.focus_neighbor[p_side].is_empty()) { Control *c = nullptr; - Node *n = get_node(data.focus_neighbour[p_margin]); + Node *n = get_node(data.focus_neighbor[p_side]); if (n) { c = Object::cast_to<Control>(n); ERR_FAIL_COND_V_MSG(!c, nullptr, "Neighbor focus node is not a control: " + n->get_name() + "."); @@ -2212,7 +2260,7 @@ Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) { return c; } - c = c->_get_focus_neighbour(p_margin, p_count + 1); + c = c->_get_focus_neighbor(p_side, p_count + 1); return c; } @@ -2235,7 +2283,7 @@ Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) { Vector2(0, 1) }; - Vector2 vdir = dir[p_margin]; + Vector2 vdir = dir[p_side]; float maxd = -1e7; @@ -2262,12 +2310,12 @@ Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) { return nullptr; } - _window_find_focus_neighbour(vdir, base, points, maxd, dist, &result); + _window_find_focus_neighbor(vdir, base, points, maxd, dist, &result); return result; } -void Control::_window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest) { +void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest) { if (Object::cast_to<Viewport>(p_at)) { return; //bye } @@ -2320,7 +2368,7 @@ void Control::_window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, con if (childc && childc->data.RI) { continue; //subwindow, ignore } - _window_find_focus_neighbour(p_dir, p_at->get_child(i), p_points, p_min, r_closest_dist, r_closest); + _window_find_focus_neighbor(p_dir, p_at->get_child(i), p_points, p_min, r_closest_dist, r_closest); } } @@ -2436,6 +2484,95 @@ bool Control::is_text_field() const { return false; } +Vector<Vector2i> Control::structured_text_parser(StructuredTextParser p_node_type, const Array &p_args, const String p_text) const { + Vector<Vector2i> ret; + switch (p_node_type) { + case STRUCTURED_TEXT_URI: { + int prev = 0; + for (int i = 0; i < p_text.length(); i++) { + if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == '.') || (p_text[i] == ':') || (p_text[i] == '&') || (p_text[i] == '=') || (p_text[i] == '@') || (p_text[i] == '?') || (p_text[i] == '#')) { + if (prev != i) { + ret.push_back(Vector2i(prev, i)); + } + ret.push_back(Vector2i(i, i + 1)); + prev = i + 1; + } + } + if (prev != p_text.length()) { + ret.push_back(Vector2i(prev, p_text.length())); + } + } break; + case STRUCTURED_TEXT_FILE: { + int prev = 0; + for (int i = 0; i < p_text.length(); i++) { + if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == ':')) { + if (prev != i) { + ret.push_back(Vector2i(prev, i)); + } + ret.push_back(Vector2i(i, i + 1)); + prev = i + 1; + } + } + if (prev != p_text.length()) { + ret.push_back(Vector2i(prev, p_text.length())); + } + } break; + case STRUCTURED_TEXT_EMAIL: { + bool local = true; + int prev = 0; + for (int i = 0; i < p_text.length(); i++) { + if ((p_text[i] == '@') && local) { // Add full "local" as single context. + local = false; + ret.push_back(Vector2i(prev, i)); + ret.push_back(Vector2i(i, i + 1)); + prev = i + 1; + } else if (!local & (p_text[i] == '.')) { // Add each dot separated "domain" part as context. + if (prev != i) { + ret.push_back(Vector2i(prev, i)); + } + ret.push_back(Vector2i(i, i + 1)); + prev = i + 1; + } + } + if (prev != p_text.length()) { + ret.push_back(Vector2i(prev, p_text.length())); + } + } break; + case STRUCTURED_TEXT_LIST: { + if (p_args.size() == 1 && p_args[0].get_type() == Variant::STRING) { + Vector<String> tags = p_text.split(String(p_args[0])); + int prev = 0; + for (int i = 0; i < tags.size(); i++) { + if (prev != i) { + ret.push_back(Vector2i(prev, prev + tags[i].length())); + } + ret.push_back(Vector2i(prev + tags[i].length(), prev + tags[i].length() + 1)); + prev = prev + tags[i].length() + 1; + } + } + } break; + case STRUCTURED_TEXT_CUSTOM: { + if (get_script_instance()) { + Variant data = get_script_instance()->call(SceneStringNames::get_singleton()->_structured_text_parser, p_args, p_text); + if (data.get_type() == Variant::ARRAY) { + Array _data = data; + for (int i = 0; i < _data.size(); i++) { + if (_data[i].get_type() == Variant::VECTOR2I) { + ret.push_back(Vector2i(_data[i])); + } + } + } + } + } break; + case STRUCTURED_TEXT_NONE: + case STRUCTURED_TEXT_DEFAULT: + default: { + ret.push_back(Vector2i(0, p_text.length())); + } + } + return ret; +} + void Control::set_rotation(float p_radians) { data.rotation = p_radians; update(); @@ -2544,6 +2681,8 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List Theme::get_default()->get_stylebox_list(get_class(), &sn); } else if (pf == "add_font_override" || pf == "has_font" || pf == "has_font_override" || pf == "get_font") { Theme::get_default()->get_font_list(get_class(), &sn); + } else if (pf == "add_font_size_override" || pf == "has_font_size" || pf == "has_font_size_override" || pf == "get_font_size") { + Theme::get_default()->get_font_size_list(get_class(), &sn); } else if (pf == "add_constant_override" || pf == "has_constant" || pf == "has_constant_override" || pf == "get_constant") { Theme::get_default()->get_constant_list(get_class(), &sn); } @@ -2559,7 +2698,7 @@ String Control::get_configuration_warning() const { String warning = CanvasItem::get_configuration_warning(); if (data.mouse_filter == MOUSE_FILTER_IGNORE && data.tooltip != "") { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."); @@ -2606,28 +2745,28 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event); ClassDB::bind_method(D_METHOD("get_minimum_size"), &Control::get_minimum_size); ClassDB::bind_method(D_METHOD("get_combined_minimum_size"), &Control::get_combined_minimum_size); - ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_margins"), &Control::set_anchors_preset, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("set_margins_preset", "preset", "resize_mode", "margin"), &Control::set_margins_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("set_anchors_and_margins_preset", "preset", "resize_mode", "margin"), &Control::set_anchors_and_margins_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("_set_anchor", "margin", "anchor"), &Control::_set_anchor); - ClassDB::bind_method(D_METHOD("set_anchor", "margin", "anchor", "keep_margin", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("get_anchor", "margin"), &Control::get_anchor); - ClassDB::bind_method(D_METHOD("set_margin", "margin", "offset"), &Control::set_margin); - ClassDB::bind_method(D_METHOD("set_anchor_and_margin", "margin", "anchor", "offset", "push_opposite_anchor"), &Control::set_anchor_and_margin, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_offsets"), &Control::set_anchors_preset, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("set_offsets_preset", "preset", "resize_mode", "margin"), &Control::set_offsets_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("set_anchors_and_offsets_preset", "preset", "resize_mode", "margin"), &Control::set_anchors_and_offsets_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("_set_anchor", "side", "anchor"), &Control::_set_anchor); + ClassDB::bind_method(D_METHOD("set_anchor", "side", "anchor", "keep_offset", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_anchor", "side"), &Control::get_anchor); + ClassDB::bind_method(D_METHOD("set_offset", "side", "offset"), &Control::set_offset); + ClassDB::bind_method(D_METHOD("set_anchor_and_offset", "side", "anchor", "offset", "push_opposite_anchor"), &Control::set_anchor_and_offset, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_begin", "position"), &Control::set_begin); ClassDB::bind_method(D_METHOD("set_end", "position"), &Control::set_end); - ClassDB::bind_method(D_METHOD("set_position", "position", "keep_margins"), &Control::set_position, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("_set_position", "margin"), &Control::_set_position); - ClassDB::bind_method(D_METHOD("set_size", "size", "keep_margins"), &Control::set_size, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("set_position", "position", "keep_offsets"), &Control::set_position, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("_set_position", "position"), &Control::_set_position); + ClassDB::bind_method(D_METHOD("set_size", "size", "keep_offsets"), &Control::set_size, DEFVAL(false)); ClassDB::bind_method(D_METHOD("_set_size", "size"), &Control::_set_size); ClassDB::bind_method(D_METHOD("set_custom_minimum_size", "size"), &Control::set_custom_minimum_size); - ClassDB::bind_method(D_METHOD("set_global_position", "position", "keep_margins"), &Control::set_global_position, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("set_global_position", "position", "keep_offsets"), &Control::set_global_position, DEFVAL(false)); ClassDB::bind_method(D_METHOD("_set_global_position", "position"), &Control::_set_global_position); ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation); ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Control::set_rotation_degrees); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale); ClassDB::bind_method(D_METHOD("set_pivot_offset", "pivot_offset"), &Control::set_pivot_offset); - ClassDB::bind_method(D_METHOD("get_margin", "margin"), &Control::get_margin); + ClassDB::bind_method(D_METHOD("get_offset", "offset"), &Control::get_offset); ClassDB::bind_method(D_METHOD("get_begin"), &Control::get_begin); ClassDB::bind_method(D_METHOD("get_end"), &Control::get_end); ClassDB::bind_method(D_METHOD("get_position"), &Control::get_position); @@ -2661,30 +2800,32 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("get_theme"), &Control::get_theme); ClassDB::bind_method(D_METHOD("add_theme_icon_override", "name", "texture"), &Control::add_theme_icon_override); - ClassDB::bind_method(D_METHOD("add_theme_shader_override", "name", "shader"), &Control::add_theme_shader_override); ClassDB::bind_method(D_METHOD("add_theme_stylebox_override", "name", "stylebox"), &Control::add_theme_style_override); ClassDB::bind_method(D_METHOD("add_theme_font_override", "name", "font"), &Control::add_theme_font_override); + ClassDB::bind_method(D_METHOD("add_theme_font_size_override", "name", "font_size"), &Control::add_theme_font_size_override); ClassDB::bind_method(D_METHOD("add_theme_color_override", "name", "color"), &Control::add_theme_color_override); ClassDB::bind_method(D_METHOD("add_theme_constant_override", "name", "constant"), &Control::add_theme_constant_override); - ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "type"), &Control::get_theme_icon, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "type"), &Control::get_theme_stylebox, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_font", "name", "type"), &Control::get_theme_font, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_color", "name", "type"), &Control::get_theme_color, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "type"), &Control::get_theme_constant, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "node_type"), &Control::get_theme_icon, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "node_type"), &Control::get_theme_stylebox, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_font", "name", "node_type"), &Control::get_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "node_type"), &Control::get_theme_font_size, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_color", "name", "node_type"), &Control::get_theme_color, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "node_type"), &Control::get_theme_constant, DEFVAL("")); ClassDB::bind_method(D_METHOD("has_theme_icon_override", "name"), &Control::has_theme_icon_override); - ClassDB::bind_method(D_METHOD("has_theme_shader_override", "name"), &Control::has_theme_shader_override); ClassDB::bind_method(D_METHOD("has_theme_stylebox_override", "name"), &Control::has_theme_stylebox_override); ClassDB::bind_method(D_METHOD("has_theme_font_override", "name"), &Control::has_theme_font_override); + ClassDB::bind_method(D_METHOD("has_theme_font_size_override", "name"), &Control::has_theme_font_size_override); ClassDB::bind_method(D_METHOD("has_theme_color_override", "name"), &Control::has_theme_color_override); ClassDB::bind_method(D_METHOD("has_theme_constant_override", "name"), &Control::has_theme_constant_override); - ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "type"), &Control::has_theme_icon, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "type"), &Control::has_theme_stylebox, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_font", "name", "type"), &Control::has_theme_font, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_color", "name", "type"), &Control::has_theme_color, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "type"), &Control::has_theme_constant, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "node_type"), &Control::has_theme_icon, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "node_type"), &Control::has_theme_stylebox, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_font", "name", "node_type"), &Control::has_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "node_type"), &Control::has_theme_font_size, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_color", "name", "node_type"), &Control::has_theme_color, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "node_type"), &Control::has_theme_constant, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_parent_control"), &Control::get_parent_control); @@ -2702,8 +2843,8 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("get_default_cursor_shape"), &Control::get_default_cursor_shape); ClassDB::bind_method(D_METHOD("get_cursor_shape", "position"), &Control::get_cursor_shape, DEFVAL(Point2())); - ClassDB::bind_method(D_METHOD("set_focus_neighbour", "margin", "neighbour"), &Control::set_focus_neighbour); - ClassDB::bind_method(D_METHOD("get_focus_neighbour", "margin"), &Control::get_focus_neighbour); + ClassDB::bind_method(D_METHOD("set_focus_neighbor", "side", "neighbor"), &Control::set_focus_neighbor); + ClassDB::bind_method(D_METHOD("get_focus_neighbor", "side"), &Control::get_focus_neighbor); ClassDB::bind_method(D_METHOD("set_focus_next", "next"), &Control::set_focus_next); ClassDB::bind_method(D_METHOD("get_focus_next"), &Control::get_focus_next); @@ -2728,6 +2869,12 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("minimum_size_changed"), &Control::minimum_size_changed); + ClassDB::bind_method(D_METHOD("set_layout_direction", "direction"), &Control::set_layout_direction); + ClassDB::bind_method(D_METHOD("get_layout_direction"), &Control::get_layout_direction); + ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Control::is_layout_rtl); + + BIND_VMETHOD(MethodInfo("_structured_text_parser", PropertyInfo(Variant::ARRAY, "args"), PropertyInfo(Variant::STRING, "text"))); + BIND_VMETHOD(MethodInfo("_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_get_minimum_size")); @@ -2743,27 +2890,31 @@ void Control::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::BOOL, "_clips_input")); ADD_GROUP("Anchor", "anchor_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_BOTTOM); - ADD_GROUP("Margin", "margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_top", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_right", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_BOTTOM); + ADD_GROUP("Offset", "offset_"); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_top", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_right", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_BOTTOM); ADD_GROUP("Grow Direction", "grow_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_h_grow_direction", "get_h_grow_direction"); ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction"); + ADD_GROUP("Layout Direction", "layout_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-right,Right-to-left"), "set_layout_direction", "get_layout_direction"); + ADD_GROUP("Rect", "rect_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "_set_global_position", "get_global_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rect_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater"), "set_rotation_degrees", "get_rotation_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rect_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rect_rotation_degrees", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater"), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_pivot_offset"), "set_pivot_offset", "get_pivot_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents"); @@ -2772,10 +2923,10 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip"); ADD_GROUP("Focus", "focus_"); - ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_top", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_right", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_bottom", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_top", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_right", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_bottom", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", SIDE_BOTTOM); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "focus_next", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_next", "get_focus_next"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "focus_previous", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_previous", "get_focus_previous"); ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode"); @@ -2804,6 +2955,7 @@ void Control::_bind_methods() { BIND_CONSTANT(NOTIFICATION_THEME_CHANGED); BIND_CONSTANT(NOTIFICATION_SCROLL_BEGIN); BIND_CONSTANT(NOTIFICATION_SCROLL_END); + BIND_CONSTANT(NOTIFICATION_LAYOUT_DIRECTION_CHANGED); BIND_ENUM_CONSTANT(CURSOR_ARROW); BIND_ENUM_CONSTANT(CURSOR_IBEAM); @@ -2862,6 +3014,24 @@ void Control::_bind_methods() { BIND_ENUM_CONSTANT(ANCHOR_BEGIN); BIND_ENUM_CONSTANT(ANCHOR_END); + BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_INHERITED); + BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE); + BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LTR); + BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_RTL); + + BIND_ENUM_CONSTANT(TEXT_DIRECTION_INHERITED); + BIND_ENUM_CONSTANT(TEXT_DIRECTION_AUTO); + BIND_ENUM_CONSTANT(TEXT_DIRECTION_LTR); + BIND_ENUM_CONSTANT(TEXT_DIRECTION_RTL); + + BIND_ENUM_CONSTANT(STRUCTURED_TEXT_DEFAULT); + BIND_ENUM_CONSTANT(STRUCTURED_TEXT_URI); + BIND_ENUM_CONSTANT(STRUCTURED_TEXT_FILE); + BIND_ENUM_CONSTANT(STRUCTURED_TEXT_EMAIL); + BIND_ENUM_CONSTANT(STRUCTURED_TEXT_LIST); + BIND_ENUM_CONSTANT(STRUCTURED_TEXT_NONE); + BIND_ENUM_CONSTANT(STRUCTURED_TEXT_CUSTOM); + ADD_SIGNAL(MethodInfo("resized")); ADD_SIGNAL(MethodInfo("gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); ADD_SIGNAL(MethodInfo("mouse_entered")); @@ -2874,37 +3044,3 @@ void Control::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::BOOL, "has_point", PropertyInfo(Variant::VECTOR2, "point"))); } - -Control::Control() { - data.parent = nullptr; - - data.mouse_filter = MOUSE_FILTER_STOP; - - data.RI = nullptr; - data.theme_owner = nullptr; - data.theme_owner_window = nullptr; - data.default_cursor = CURSOR_ARROW; - data.h_size_flags = SIZE_FILL; - data.v_size_flags = SIZE_FILL; - data.expand = 1; - data.rotation = 0; - data.parent_canvas_item = nullptr; - data.scale = Vector2(1, 1); - - data.block_minimum_size_adjust = false; - data.disable_visibility_clip = false; - data.h_grow = GROW_DIRECTION_END; - data.v_grow = GROW_DIRECTION_END; - data.minimum_size_valid = false; - data.updating_last_minimum_size = false; - - data.clip_contents = false; - for (int i = 0; i < 4; i++) { - data.anchor[i] = ANCHOR_BEGIN; - data.margin[i] = 0; - } - data.focus_mode = FOCUS_NONE; -} - -Control::~Control() { -} diff --git a/scene/gui/control.h b/scene/gui/control.h index e4fe0bb25d..ac2a1b35de 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -127,6 +127,30 @@ public: PRESET_MODE_KEEP_SIZE }; + enum LayoutDirection { + LAYOUT_DIRECTION_INHERITED, + LAYOUT_DIRECTION_LOCALE, + LAYOUT_DIRECTION_LTR, + LAYOUT_DIRECTION_RTL + }; + + enum TextDirection { + TEXT_DIRECTION_AUTO = TextServer::DIRECTION_AUTO, + TEXT_DIRECTION_LTR = TextServer::DIRECTION_LTR, + TEXT_DIRECTION_RTL = TextServer::DIRECTION_RTL, + TEXT_DIRECTION_INHERITED, + }; + + enum StructuredTextParser { + STRUCTURED_TEXT_DEFAULT, + STRUCTURED_TEXT_URI, + STRUCTURED_TEXT_FILE, + STRUCTURED_TEXT_EMAIL, + STRUCTURED_TEXT_LIST, + STRUCTURED_TEXT_NONE, + STRUCTURED_TEXT_CUSTOM + }; + private: struct CComparator { bool operator()(const Control *p_a, const Control *p_b) const { @@ -142,53 +166,55 @@ private: Point2 pos_cache; Size2 size_cache; Size2 minimum_size_cache; - bool minimum_size_valid; + bool minimum_size_valid = false; Size2 last_minimum_size; - bool updating_last_minimum_size; + bool updating_last_minimum_size = false; + + float offset[4] = { 0.0, 0.0, 0.0, 0.0 }; + float anchor[4] = { ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN }; + FocusMode focus_mode = FOCUS_NONE; + GrowDirection h_grow = GROW_DIRECTION_END; + GrowDirection v_grow = GROW_DIRECTION_END; - float margin[4]; - float anchor[4]; - FocusMode focus_mode; - GrowDirection h_grow; - GrowDirection v_grow; + LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED; - float rotation; - Vector2 scale; + float rotation = 0; + Vector2 scale = Vector2(1, 1); Vector2 pivot_offset; - int h_size_flags; - int v_size_flags; - float expand; + int h_size_flags = SIZE_FILL; + int v_size_flags = SIZE_FILL; + float expand = 1; Point2 custom_minimum_size; - MouseFilter mouse_filter; + MouseFilter mouse_filter = MOUSE_FILTER_STOP; - bool clip_contents; + bool clip_contents = false; - bool block_minimum_size_adjust; - bool disable_visibility_clip; + bool block_minimum_size_adjust = false; + bool disable_visibility_clip = false; - Control *parent; + Control *parent = nullptr; ObjectID drag_owner; Ref<Theme> theme; - Control *theme_owner; - Window *theme_owner_window; + Control *theme_owner = nullptr; + Window *theme_owner_window = nullptr; String tooltip; - CursorShape default_cursor; + CursorShape default_cursor = CURSOR_ARROW; - List<Control *>::Element *RI; + List<Control *>::Element *RI = nullptr; - CanvasItem *parent_canvas_item; + CanvasItem *parent_canvas_item = nullptr; - NodePath focus_neighbour[4]; + NodePath focus_neighbor[4]; NodePath focus_next; NodePath focus_prev; HashMap<StringName, Ref<Texture2D>> icon_override; - HashMap<StringName, Ref<Shader>> shader_override; HashMap<StringName, Ref<StyleBox>> style_override; HashMap<StringName, Ref<Font>> font_override; + HashMap<StringName, int> font_size_override; HashMap<StringName, Color> color_override; HashMap<StringName, int> constant_override; @@ -197,24 +223,23 @@ private: // used internally Control *_find_control_at_pos(CanvasItem *p_node, const Point2 &p_pos, const Transform2D &p_xform, Transform2D &r_inv_xform); - void _window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest); - Control *_get_focus_neighbour(Margin p_margin, int p_count = 0); + void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest); + Control *_get_focus_neighbor(Side p_side, int p_count = 0); - void _set_anchor(Margin p_margin, float p_anchor); + void _set_anchor(Side p_side, float p_anchor); void _set_position(const Point2 &p_point); void _set_global_position(const Point2 &p_point); void _set_size(const Size2 &p_size); void _theme_changed(); - void _change_notify_margins(); + void _change_notify_offsets(); void _update_minimum_size(); void _update_scroll(); - void _resize(const Size2 &p_size); - void _compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]); - void _compute_anchors(Rect2 p_rect, const float p_margins[4], float (&r_anchors)[4]); + void _compute_offsets(Rect2 p_rect, const float p_anchors[4], float (&r_offsets)[4]); + void _compute_anchors(Rect2 p_rect, const float p_offsets[4], float (&r_anchors)[4]); void _size_changed(); String _get_tooltip() const; @@ -237,16 +262,16 @@ private: _FORCE_INLINE_ static bool _has_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_node_type); static Ref<Texture2D> get_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static Ref<Shader> get_shaders(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); static Ref<StyleBox> get_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); static Ref<Font> get_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); + static int get_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); static Color get_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); static int get_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); static bool has_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static bool has_shaders(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); static bool has_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); static bool has_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); + static bool has_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); static bool has_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); static bool has_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); @@ -256,6 +281,8 @@ protected: //virtual void _window_gui_input(InputEvent p_event); + virtual Vector<Vector2i> structured_text_parser(StructuredTextParser p_node_type, const Array &p_args, const String p_text) const; + bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; @@ -278,6 +305,7 @@ public: NOTIFICATION_THEME_CHANGED = 45, NOTIFICATION_SCROLL_BEGIN = 47, NOTIFICATION_SCROLL_END = 48, + NOTIFICATION_LAYOUT_DIRECTION_CHANGED = 49, }; @@ -325,19 +353,23 @@ public: Control *get_parent_control() const; + void set_layout_direction(LayoutDirection p_direction); + LayoutDirection get_layout_direction() const; + virtual bool is_layout_rtl() const; + /* POSITIONING */ - void set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins = true); - void set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); - void set_anchors_and_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); + void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true); + void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); + void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); - void set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin = true, bool p_push_opposite_anchor = true); - float get_anchor(Margin p_margin) const; + void set_anchor(Side p_side, float p_anchor, bool p_keep_offset = true, bool p_push_opposite_anchor = true); + float get_anchor(Side p_side) const; - void set_margin(Margin p_margin, float p_value); - float get_margin(Margin p_margin) const; + void set_offset(Side p_side, float p_value); + float get_offset(Side p_side) const; - void set_anchor_and_margin(Margin p_margin, float p_anchor, float p_pos, bool p_push_opposite_anchor = true); + void set_anchor_and_offset(Side p_side, float p_anchor, float p_pos, bool p_push_opposite_anchor = true); void set_begin(const Point2 &p_point); // helper void set_end(const Point2 &p_point); // helper @@ -345,13 +377,13 @@ public: Point2 get_begin() const; Point2 get_end() const; - void set_position(const Point2 &p_point, bool p_keep_margins = false); - void set_global_position(const Point2 &p_point, bool p_keep_margins = false); + void set_position(const Point2 &p_point, bool p_keep_offsets = false); + void set_global_position(const Point2 &p_point, bool p_keep_offsets = false); Point2 get_position() const; Point2 get_global_position() const; Point2 get_screen_position() const; - void set_size(const Size2 &p_size, bool p_keep_margins = false); + void set_size(const Size2 &p_size, bool p_keep_offsets = false); Size2 get_size() const; Rect2 get_rect() const; @@ -360,6 +392,8 @@ public: Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the visual server Rect2 get_anchorable_rect() const override; + void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting. + void set_rotation(float p_radians); void set_rotation_degrees(float p_degrees); float get_rotation() const; @@ -402,8 +436,8 @@ public: Control *find_next_valid_focus() const; Control *find_prev_valid_focus() const; - void set_focus_neighbour(Margin p_margin, const NodePath &p_neighbour); - NodePath get_focus_neighbour(Margin p_margin) const; + void set_focus_neighbor(Side p_side, const NodePath &p_neighbor); + NodePath get_focus_neighbor(Side p_side) const; void set_focus_next(const NodePath &p_next); NodePath get_focus_next() const; @@ -418,30 +452,30 @@ public: /* SKINNING */ void add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon); - void add_theme_shader_override(const StringName &p_name, const Ref<Shader> &p_shader); void add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style); void add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font); + void add_theme_font_size_override(const StringName &p_name, int p_font_size); void add_theme_color_override(const StringName &p_name, const Color &p_color); void add_theme_constant_override(const StringName &p_name, int p_constant); Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_node_type = StringName()) const; - Ref<Shader> get_theme_shader(const StringName &p_name, const StringName &p_node_type = StringName()) const; Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_node_type = StringName()) const; Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_node_type = StringName()) const; + int get_theme_font_size(const StringName &p_name, const StringName &p_node_type = StringName()) const; Color get_theme_color(const StringName &p_name, const StringName &p_node_type = StringName()) const; int get_theme_constant(const StringName &p_name, const StringName &p_node_type = StringName()) const; bool has_theme_icon_override(const StringName &p_name) const; - bool has_theme_shader_override(const StringName &p_name) const; bool has_theme_stylebox_override(const StringName &p_name) const; bool has_theme_font_override(const StringName &p_name) const; + bool has_theme_font_size_override(const StringName &p_name) const; bool has_theme_color_override(const StringName &p_name) const; bool has_theme_constant_override(const StringName &p_name) const; bool has_theme_icon(const StringName &p_name, const StringName &p_node_type = StringName()) const; - bool has_theme_shader(const StringName &p_name, const StringName &p_node_type = StringName()) const; bool has_theme_stylebox(const StringName &p_name, const StringName &p_node_type = StringName()) const; bool has_theme_font(const StringName &p_name, const StringName &p_node_type = StringName()) const; + bool has_theme_font_size(const StringName &p_name, const StringName &p_node_type = StringName()) const; bool has_theme_color(const StringName &p_name, const StringName &p_node_type = StringName()) const; bool has_theme_constant(const StringName &p_name, const StringName &p_node_type = StringName()) const; @@ -484,8 +518,7 @@ public: virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; virtual String get_configuration_warning() const override; - Control(); - ~Control(); + Control() {} }; VARIANT_ENUM_CAST(Control::FocusMode); @@ -496,5 +529,8 @@ VARIANT_ENUM_CAST(Control::LayoutPresetMode); VARIANT_ENUM_CAST(Control::MouseFilter); VARIANT_ENUM_CAST(Control::GrowDirection); VARIANT_ENUM_CAST(Control::Anchor); +VARIANT_ENUM_CAST(Control::LayoutDirection); +VARIANT_ENUM_CAST(Control::TextDirection); +VARIANT_ENUM_CAST(Control::StructuredTextParser); #endif diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 4f59f4a36a..773c609a70 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -60,7 +60,7 @@ void AcceptDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { if (is_visible()) { - get_ok()->grab_focus(); + get_ok_button()->grab_focus(); _update_child_rects(); parent_visible = get_parent_visible_window(); if (parent_visible) { @@ -165,7 +165,7 @@ void AcceptDialog::register_text_enter(Node *p_line_edit) { void AcceptDialog::_update_child_rects() { Size2 label_size = label->get_minimum_size(); - if (label->get_text().empty()) { + if (label->get_text().is_empty()) { label_size.height = 0; } int margin = hbc->get_theme_constant("margin", "Dialogs"); @@ -253,7 +253,7 @@ Button *AcceptDialog::add_button(const String &p_text, bool p_right, const Strin return button; } -Button *AcceptDialog::add_cancel(const String &p_cancel) { +Button *AcceptDialog::add_cancel_button(const String &p_cancel) { String c = p_cancel; if (p_cancel == "") { c = RTR("Cancel"); @@ -264,12 +264,12 @@ Button *AcceptDialog::add_cancel(const String &p_cancel) { } void AcceptDialog::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_ok"), &AcceptDialog::get_ok); + ClassDB::bind_method(D_METHOD("get_ok_button"), &AcceptDialog::get_ok_button); ClassDB::bind_method(D_METHOD("get_label"), &AcceptDialog::get_label); ClassDB::bind_method(D_METHOD("set_hide_on_ok", "enabled"), &AcceptDialog::set_hide_on_ok); ClassDB::bind_method(D_METHOD("get_hide_on_ok"), &AcceptDialog::get_hide_on_ok); ClassDB::bind_method(D_METHOD("add_button", "text", "right", "action"), &AcceptDialog::add_button, DEFVAL(false), DEFVAL("")); - ClassDB::bind_method(D_METHOD("add_cancel", "name"), &AcceptDialog::add_cancel); + ClassDB::bind_method(D_METHOD("add_cancel_button", "name"), &AcceptDialog::add_cancel_button); ClassDB::bind_method(D_METHOD("register_text_enter", "line_edit"), &AcceptDialog::register_text_enter); ClassDB::bind_method(D_METHOD("set_text", "text"), &AcceptDialog::set_text); ClassDB::bind_method(D_METHOD("get_text"), &AcceptDialog::get_text); @@ -309,8 +309,8 @@ AcceptDialog::AcceptDialog() { int button_margin = hbc->get_theme_constant("button_margin", "Dialogs"); label = memnew(Label); - label->set_anchor(MARGIN_RIGHT, Control::ANCHOR_END); - label->set_anchor(MARGIN_BOTTOM, Control::ANCHOR_END); + label->set_anchor(SIDE_RIGHT, Control::ANCHOR_END); + label->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END); label->set_begin(Point2(margin, margin)); label->set_end(Point2(-margin, -button_margin - 10)); add_child(label); @@ -337,10 +337,10 @@ AcceptDialog::~AcceptDialog() { // ConfirmationDialog void ConfirmationDialog::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_cancel"), &ConfirmationDialog::get_cancel); + ClassDB::bind_method(D_METHOD("get_cancel_button"), &ConfirmationDialog::get_cancel_button); } -Button *ConfirmationDialog::get_cancel() { +Button *ConfirmationDialog::get_cancel_button() { return cancel; } @@ -349,5 +349,5 @@ ConfirmationDialog::ConfirmationDialog() { #ifdef TOOLS_ENABLED set_min_size(Size2(200, 70) * EDSCALE); #endif - cancel = add_cancel(); + cancel = add_cancel_button(); } diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index de08685ce2..e450a3c30a 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -79,9 +79,9 @@ public: void register_text_enter(Node *p_line_edit); - Button *get_ok() { return ok; } + Button *get_ok_button() { return ok; } Button *add_button(const String &p_text, bool p_right = false, const String &p_action = ""); - Button *add_cancel(const String &p_cancel = ""); + Button *add_cancel_button(const String &p_cancel = ""); void set_hide_on_ok(bool p_hide); bool get_hide_on_ok() const; @@ -104,7 +104,7 @@ protected: static void _bind_methods(); public: - Button *get_cancel(); + Button *get_cancel_button(); ConfirmationDialog(); }; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 7ce4e90f28..5765d6b932 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -136,7 +136,7 @@ void FileDialog::update_dir() { } // Deselect any item, to make "Select Current Folder" button text by default. - deselect_items(); + deselect_all(); } void FileDialog::_dir_entered(String p_dir) { @@ -172,7 +172,7 @@ void FileDialog::_post_popup() { // For open dir mode, deselect all items on file dialog open. if (mode == FILE_MODE_OPEN_DIR) { - deselect_items(); + deselect_all(); file_box->set_visible(false); } else { file_box->set_visible(true); @@ -318,21 +318,21 @@ void FileDialog::_go_up() { update_dir(); } -void FileDialog::deselect_items() { +void FileDialog::deselect_all() { // Clear currently selected items in file manager. tree->deselect_all(); // And change get_ok title. if (!tree->is_anything_selected()) { - get_ok()->set_disabled(_is_open_should_be_disabled()); + get_ok_button()->set_disabled(_is_open_should_be_disabled()); switch (mode) { case FILE_MODE_OPEN_FILE: case FILE_MODE_OPEN_FILES: - get_ok()->set_text(RTR("Open")); + get_ok_button()->set_text(RTR("Open")); break; case FILE_MODE_OPEN_DIR: - get_ok()->set_text(RTR("Select Current Folder")); + get_ok_button()->set_text(RTR("Select Current Folder")); break; case FILE_MODE_OPEN_ANY: case FILE_MODE_SAVE_FILE: @@ -356,10 +356,10 @@ void FileDialog::_tree_selected() { if (!d["dir"]) { file->set_text(d["name"]); } else if (mode == FILE_MODE_OPEN_DIR) { - get_ok()->set_text(RTR("Select This Folder")); + get_ok_button()->set_text(RTR("Select This Folder")); } - get_ok()->set_disabled(_is_open_should_be_disabled()); + get_ok_button()->set_disabled(_is_open_should_be_disabled()); } void FileDialog::_tree_item_activated() { @@ -434,7 +434,7 @@ void FileDialog::update_file_list() { dirs.sort_custom<NaturalNoCaseComparator>(); files.sort_custom<NaturalNoCaseComparator>(); - while (!dirs.empty()) { + while (!dirs.is_empty()) { String &dir_name = dirs.front()->get(); TreeItem *ti = tree->create_item(root); ti->set_text(0, dir_name); @@ -478,8 +478,8 @@ void FileDialog::update_file_list() { String base_dir = dir_access->get_current_dir(); - while (!files.empty()) { - bool match = patterns.empty(); + while (!files.is_empty()) { + bool match = patterns.is_empty(); String match_str; for (List<String>::Element *E = patterns.front(); E; E = E->next()) { @@ -646,35 +646,35 @@ void FileDialog::set_file_mode(FileMode p_mode) { mode = p_mode; switch (mode) { case FILE_MODE_OPEN_FILE: - get_ok()->set_text(RTR("Open")); + get_ok_button()->set_text(RTR("Open")); if (mode_overrides_title) { set_title(RTR("Open a File")); } makedir->hide(); break; case FILE_MODE_OPEN_FILES: - get_ok()->set_text(RTR("Open")); + get_ok_button()->set_text(RTR("Open")); if (mode_overrides_title) { set_title(RTR("Open File(s)")); } makedir->hide(); break; case FILE_MODE_OPEN_DIR: - get_ok()->set_text(RTR("Select Current Folder")); + get_ok_button()->set_text(RTR("Select Current Folder")); if (mode_overrides_title) { set_title(RTR("Open a Directory")); } makedir->show(); break; case FILE_MODE_OPEN_ANY: - get_ok()->set_text(RTR("Open")); + get_ok_button()->set_text(RTR("Open")); if (mode_overrides_title) { set_title(RTR("Open a File or Directory")); } makedir->show(); break; case FILE_MODE_SAVE_FILE: - get_ok()->set_text(RTR("Save")); + get_ok_button()->set_text(RTR("Save")); if (mode_overrides_title) { set_title(RTR("Save a File")); } @@ -808,7 +808,7 @@ void FileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_file_name"), &FileDialog::update_file_name); ClassDB::bind_method(D_METHOD("_update_dir"), &FileDialog::update_dir); ClassDB::bind_method(D_METHOD("_update_file_list"), &FileDialog::update_file_list); - ClassDB::bind_method(D_METHOD("deselect_items"), &FileDialog::deselect_items); + ClassDB::bind_method(D_METHOD("deselect_all"), &FileDialog::deselect_all); ClassDB::bind_method(D_METHOD("invalidate"), &FileDialog::invalidate); @@ -879,6 +879,7 @@ FileDialog::FileDialog() { hbc->add_child(drives); dir = memnew(LineEdit); + dir->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE); hbc->add_child(dir); dir->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -912,6 +913,7 @@ FileDialog::FileDialog() { file_box = memnew(HBoxContainer); file_box->add_child(memnew(Label(RTR("File:")))); file = memnew(LineEdit); + file->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE); file->set_stretch_ratio(4); file->set_h_size_flags(Control::SIZE_EXPAND_FILL); file_box->add_child(file); @@ -930,7 +932,7 @@ FileDialog::FileDialog() { tree->connect("multi_selected", callable_mp(this, &FileDialog::_tree_multi_selected), varray(), CONNECT_DEFERRED); tree->connect("cell_selected", callable_mp(this, &FileDialog::_tree_selected), varray(), CONNECT_DEFERRED); tree->connect("item_activated", callable_mp(this, &FileDialog::_tree_item_activated), varray()); - tree->connect("nothing_selected", callable_mp(this, &FileDialog::deselect_items)); + tree->connect("nothing_selected", callable_mp(this, &FileDialog::deselect_all)); dir->connect("text_entered", callable_mp(this, &FileDialog::_dir_entered)); file->connect("text_entered", callable_mp(this, &FileDialog::_file_entered)); filter->connect("item_selected", callable_mp(this, &FileDialog::_filter_selected)); @@ -947,6 +949,7 @@ FileDialog::FileDialog() { makedialog->add_child(makevb); makedirname = memnew(LineEdit); + makedirname->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE); makevb->add_margin_child(RTR("Name:"), makedirname); add_child(makedialog); makedialog->register_text_enter(makedirname); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 8003650668..626bb78d42 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -170,7 +170,7 @@ public: void invalidate(); - void deselect_items(); + void deselect_all(); FileDialog(); ~FileDialog(); diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index 53d7ead548..d18fff887d 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -237,7 +237,7 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { // Snap to "round" coordinates if holding Ctrl. // Be more precise if holding Shift as well if (mm->get_control()) { - newofs = Math::stepify(newofs, mm->get_shift() ? 0.025 : 0.1); + newofs = Math::snapped(newofs, mm->get_shift() ? 0.025 : 0.1); } else if (mm->get_shift()) { // Snap to nearest point if holding just Shift const float snap_threshold = 0.03; diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h index 6e950703bb..189b33f563 100644 --- a/scene/gui/gradient_edit.h +++ b/scene/gui/gradient_edit.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index ad02aaade5..d5b12b6bb6 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,7 @@ #include "graph_edit.h" #include "core/input/input.h" +#include "core/math/math_funcs.h" #include "core/os/keyboard.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" @@ -44,6 +45,9 @@ #define MIN_ZOOM (((1 / ZOOM_SCALE) / ZOOM_SCALE) / ZOOM_SCALE) #define MAX_ZOOM (1 * ZOOM_SCALE * ZOOM_SCALE * ZOOM_SCALE) +#define MINIMAP_OFFSET 12 +#define MINIMAP_PADDING 5 + bool GraphEditFilter::has_point(const Point2 &p_point) const { return ge->_filter_input(p_point); } @@ -52,6 +56,141 @@ GraphEditFilter::GraphEditFilter(GraphEdit *p_edit) { ge = p_edit; } +void GraphEditMinimap::_bind_methods() { + ClassDB::bind_method(D_METHOD("_gui_input"), &GraphEditMinimap::_gui_input); +} + +GraphEditMinimap::GraphEditMinimap(GraphEdit *p_edit) { + ge = p_edit; + + graph_proportions = Vector2(1, 1); + graph_padding = Vector2(0, 0); + camera_position = Vector2(100, 50); + camera_size = Vector2(200, 200); + minimap_padding = Vector2(MINIMAP_PADDING, MINIMAP_PADDING); + minimap_offset = minimap_padding + _convert_from_graph_position(graph_padding); + + is_pressing = false; + is_resizing = false; +} + +void GraphEditMinimap::update_minimap() { + Vector2 graph_offset = _get_graph_offset(); + Vector2 graph_size = _get_graph_size(); + + camera_position = ge->get_scroll_ofs() - graph_offset; + camera_size = ge->get_size(); + + Vector2 render_size = _get_render_size(); + float target_ratio = render_size.x / render_size.y; + float graph_ratio = graph_size.x / graph_size.y; + + graph_proportions = graph_size; + graph_padding = Vector2(0, 0); + if (graph_ratio > target_ratio) { + graph_proportions.x = graph_size.x; + graph_proportions.y = graph_size.x / target_ratio; + graph_padding.y = Math::abs(graph_size.y - graph_proportions.y) / 2; + } else { + graph_proportions.x = graph_size.y * target_ratio; + graph_proportions.y = graph_size.y; + graph_padding.x = Math::abs(graph_size.x - graph_proportions.x) / 2; + } + + // This centers minimap inside the minimap rectangle. + minimap_offset = minimap_padding + _convert_from_graph_position(graph_padding); +} + +Rect2 GraphEditMinimap::get_camera_rect() { + Vector2 camera_center = _convert_from_graph_position(camera_position + camera_size / 2) + minimap_offset; + Vector2 camera_viewport = _convert_from_graph_position(camera_size); + Vector2 camera_position = (camera_center - camera_viewport / 2); + return Rect2(camera_position, camera_viewport); +} + +Vector2 GraphEditMinimap::_get_render_size() { + if (!is_inside_tree()) { + return Vector2(0, 0); + } + + return get_size() - 2 * minimap_padding; +} + +Vector2 GraphEditMinimap::_get_graph_offset() { + return Vector2(ge->h_scroll->get_min(), ge->v_scroll->get_min()); +} + +Vector2 GraphEditMinimap::_get_graph_size() { + Vector2 graph_size = Vector2(ge->h_scroll->get_max(), ge->v_scroll->get_max()) - Vector2(ge->h_scroll->get_min(), ge->v_scroll->get_min()); + + if (graph_size.x == 0) { + graph_size.x = 1; + } + if (graph_size.y == 0) { + graph_size.y = 1; + } + + return graph_size; +} + +Vector2 GraphEditMinimap::_convert_from_graph_position(const Vector2 &p_position) { + Vector2 map_position = Vector2(0, 0); + Vector2 render_size = _get_render_size(); + + map_position.x = p_position.x * render_size.x / graph_proportions.x; + map_position.y = p_position.y * render_size.y / graph_proportions.y; + + return map_position; +} + +Vector2 GraphEditMinimap::_convert_to_graph_position(const Vector2 &p_position) { + Vector2 graph_position = Vector2(0, 0); + Vector2 render_size = _get_render_size(); + + graph_position.x = p_position.x * graph_proportions.x / render_size.x; + graph_position.y = p_position.y * graph_proportions.y / render_size.y; + + return graph_position; +} + +void GraphEditMinimap::_gui_input(const Ref<InputEvent> &p_ev) { + Ref<InputEventMouseButton> mb = p_ev; + Ref<InputEventMouseMotion> mm = p_ev; + + if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) { + if (mb->is_pressed()) { + is_pressing = true; + + Ref<Texture2D> resizer = get_theme_icon("resizer"); + Rect2 resizer_hitbox = Rect2(Point2(), resizer->get_size()); + if (resizer_hitbox.has_point(mb->get_position())) { + is_resizing = true; + } else { + Vector2 click_position = _convert_to_graph_position(mb->get_position() - minimap_padding) - graph_padding; + _adjust_graph_scroll(click_position); + } + } else { + is_pressing = false; + is_resizing = false; + } + accept_event(); + } else if (mm.is_valid() && is_pressing) { + if (is_resizing) { + ge->set_minimap_size(ge->get_minimap_size() - mm->get_relative()); + update(); + } else { + Vector2 click_position = _convert_to_graph_position(mm->get_position() - minimap_padding) - graph_padding; + _adjust_graph_scroll(click_position); + } + accept_event(); + } +} + +void GraphEditMinimap::_adjust_graph_scroll(const Vector2 &p_offset) { + Vector2 graph_offset = _get_graph_offset(); + ge->set_scroll_ofs(p_offset + graph_offset - camera_size / 2); +} + Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) { if (is_node_connected(p_from, p_from_port, p_to, p_to_port)) { return OK; @@ -64,6 +203,7 @@ Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const S c.activity = 0; connections.push_back(c); top_layer->update(); + minimap->update(); update(); connections_layer->update(); @@ -85,6 +225,7 @@ void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) { connections.erase(E); top_layer->update(); + minimap->update(); update(); connections_layer->update(); return; @@ -118,6 +259,7 @@ void GraphEdit::_scroll_moved(double) { awaiting_scroll_offset_update = true; } top_layer->update(); + minimap->update(); update(); if (!setting_scroll_ofs) { //in godot, signals on change value are avoided as a convention @@ -134,7 +276,7 @@ void GraphEdit::_update_scroll_offset() { continue; } - Point2 pos = gn->get_offset() * zoom; + Point2 pos = gn->get_position_offset() * zoom; pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); gn->set_position(pos); if (gn->get_scale() != Vector2(zoom, zoom)) { @@ -164,7 +306,7 @@ void GraphEdit::_update_scroll() { } Rect2 r; - r.position = gn->get_offset() * zoom; + r.position = gn->get_position_offset() * zoom; r.size = gn->get_size() * zoom; screen = screen.merge(r); } @@ -195,8 +337,8 @@ void GraphEdit::_update_scroll() { Size2 vmin = v_scroll->get_combined_minimum_size(); // Avoid scrollbar overlapping. - h_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, v_scroll->is_visible() ? -vmin.width : 0); - v_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, h_scroll->is_visible() ? -hmin.height : 0); + h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, v_scroll->is_visible() ? -vmin.width : 0); + v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, h_scroll->is_visible() ? -hmin.height : 0); set_block_minimum_size_adjust(false); @@ -234,6 +376,7 @@ void GraphEdit::_graph_node_moved(Node *p_gn) { GraphNode *gn = Object::cast_to<GraphNode>(p_gn); ERR_FAIL_COND(!gn); top_layer->update(); + minimap->update(); update(); connections_layer->update(); } @@ -241,13 +384,15 @@ void GraphEdit::_graph_node_moved(Node *p_gn) { void GraphEdit::add_child_notify(Node *p_child) { Control::add_child_notify(p_child); - top_layer->call_deferred("raise"); //top layer always on top! + top_layer->call_deferred("raise"); // Top layer always on top! + GraphNode *gn = Object::cast_to<GraphNode>(p_child); if (gn) { gn->set_scale(Vector2(zoom, zoom)); - gn->connect("offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved), varray(gn)); + gn->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved), varray(gn)); gn->connect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised), varray(gn)); gn->connect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::update)); + gn->connect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::update)); _graph_node_moved(gn); gn->set_mouse_filter(MOUSE_FILTER_PASS); } @@ -255,14 +400,17 @@ void GraphEdit::add_child_notify(Node *p_child) { void GraphEdit::remove_child_notify(Node *p_child) { Control::remove_child_notify(p_child); + if (is_inside_tree()) { - top_layer->call_deferred("raise"); //top layer always on top! + top_layer->call_deferred("raise"); // Top layer always on top! } + GraphNode *gn = Object::cast_to<GraphNode>(p_child); if (gn) { - gn->disconnect("offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved)); + gn->disconnect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved)); gn->disconnect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised)); gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::update)); + gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::update)); } } @@ -275,20 +423,21 @@ void GraphEdit::_notification(int p_what) { zoom_reset->set_icon(get_theme_icon("reset")); zoom_plus->set_icon(get_theme_icon("more")); snap_button->set_icon(get_theme_icon("snap")); + minimap_button->set_icon(get_theme_icon("minimap")); } if (p_what == NOTIFICATION_READY) { Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); - h_scroll->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 0); - h_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0); - h_scroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -hmin.height); - h_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0); + h_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0); + h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); + h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height); + h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); - v_scroll->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -vmin.width); - v_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0); - v_scroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 0); - v_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0); + v_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width); + v_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); + v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0); + v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); } if (p_what == NOTIFICATION_DRAW) { draw_style_box(get_theme_stylebox("bg"), Rect2(Point2(), get_size())); @@ -338,6 +487,7 @@ void GraphEdit::_notification(int p_what) { if (p_what == NOTIFICATION_RESIZED) { _update_scroll(); top_layer->update(); + minimap->update(); } } @@ -472,6 +622,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { connecting_to = mm->get_position(); connecting_target = false; top_layer->update(); + minimap->update(); connecting_valid = just_disconnected || click_pos.distance_to(connecting_to) > 20.0 * zoom; if (connecting_valid) { @@ -541,6 +692,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { connecting = false; top_layer->update(); + minimap->update(); update(); connections_layer->update(); } @@ -632,12 +784,12 @@ void GraphEdit::_bake_segment2d(Vector<Vector2> &points, Vector<Color> &colors, } } -void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color) { +void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_bezier_ratio = 1.0) { //cubic bezier code float diff = p_to.x - p_from.x; float cp_offset; - int cp_len = get_theme_constant("bezier_len_pos"); - int cp_neg_len = get_theme_constant("bezier_len_neg"); + int cp_len = get_theme_constant("bezier_len_pos") * p_bezier_ratio; + int cp_neg_len = get_theme_constant("bezier_len_neg") * p_bezier_ratio; if (diff > 0) { cp_offset = MIN(cp_len, diff * 0.5); @@ -659,9 +811,9 @@ void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const colors.push_back(p_to_color); #ifdef TOOLS_ENABLED - p_where->draw_polyline_colors(points, colors, Math::floor(2 * EDSCALE)); + p_where->draw_polyline_colors(points, colors, Math::floor(p_width * EDSCALE), lines_antialiased); #else - p_where->draw_polyline_colors(points, colors, 2); + p_where->draw_polyline_colors(points, colors, p_width, lines_antialiased); #endif } @@ -699,16 +851,16 @@ void GraphEdit::_connections_layer_draw() { continue; } - Vector2 frompos = gfrom->get_connection_output_position(E->get().from_port) + gfrom->get_offset() * zoom; + Vector2 frompos = gfrom->get_connection_output_position(E->get().from_port) + gfrom->get_position_offset() * zoom; Color color = gfrom->get_connection_output_color(E->get().from_port); - Vector2 topos = gto->get_connection_input_position(E->get().to_port) + gto->get_offset() * zoom; + Vector2 topos = gto->get_connection_input_position(E->get().to_port) + gto->get_position_offset() * zoom; Color tocolor = gto->get_connection_input_color(E->get().to_port); if (E->get().activity > 0) { color = color.lerp(activity_color, E->get().activity); tocolor = tocolor.lerp(activity_color, E->get().activity); } - _draw_cos_line(connections_layer, frompos, topos, color, tocolor); + _draw_cos_line(connections_layer, frompos, topos, color, tocolor, lines_thickness); } while (to_erase.size()) { @@ -747,7 +899,7 @@ void GraphEdit::_top_layer_draw() { if (!connecting_out) { SWAP(pos, topos); } - _draw_cos_line(top_layer, pos, topos, col, col); + _draw_cos_line(top_layer, pos, topos, col, col, lines_thickness); } if (box_selecting) { @@ -756,6 +908,114 @@ void GraphEdit::_top_layer_draw() { } } +void GraphEdit::_minimap_draw() { + if (!is_minimap_enabled()) { + return; + } + + minimap->update_minimap(); + + // Draw the minimap background. + Rect2 minimap_rect = Rect2(Point2(), minimap->get_size()); + minimap->draw_style_box(minimap->get_theme_stylebox("bg"), minimap_rect); + + Vector2 graph_offset = minimap->_get_graph_offset(); + Vector2 minimap_offset = minimap->minimap_offset; + + // Draw comment graph nodes. + for (int i = get_child_count() - 1; i >= 0; i--) { + GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); + if (!gn || !gn->is_comment()) { + continue; + } + + Vector2 node_position = minimap->_convert_from_graph_position(gn->get_position_offset() * zoom - graph_offset) + minimap_offset; + Vector2 node_size = minimap->_convert_from_graph_position(gn->get_size() * zoom); + Rect2 node_rect = Rect2(node_position, node_size); + + Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox("node")->duplicate(); + + // Override default values with colors provided by the GraphNode's stylebox, if possible. + Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "commentfocus" : "comment"); + if (sbf.is_valid()) { + Color node_color = sbf->get_bg_color(); + sb_minimap->set_bg_color(node_color); + } + + minimap->draw_style_box(sb_minimap, node_rect); + } + + // Draw regular graph nodes. + for (int i = get_child_count() - 1; i >= 0; i--) { + GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); + if (!gn || gn->is_comment()) { + continue; + } + + Vector2 node_position = minimap->_convert_from_graph_position(gn->get_position_offset() * zoom - graph_offset) + minimap_offset; + Vector2 node_size = minimap->_convert_from_graph_position(gn->get_size() * zoom); + Rect2 node_rect = Rect2(node_position, node_size); + + Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox("node")->duplicate(); + + // Override default values with colors provided by the GraphNode's stylebox, if possible. + Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "selectedframe" : "frame"); + if (sbf.is_valid()) { + Color node_color = sbf->get_border_color(); + sb_minimap->set_bg_color(node_color); + } + + minimap->draw_style_box(sb_minimap, node_rect); + } + + // Draw node connections. + Color activity_color = get_theme_color("activity"); + for (List<Connection>::Element *E = connections.front(); E; E = E->next()) { + NodePath fromnp(E->get().from); + + Node *from = get_node(fromnp); + if (!from) { + continue; + } + GraphNode *gfrom = Object::cast_to<GraphNode>(from); + if (!gfrom) { + continue; + } + + NodePath tonp(E->get().to); + Node *to = get_node(tonp); + if (!to) { + continue; + } + GraphNode *gto = Object::cast_to<GraphNode>(to); + if (!gto) { + continue; + } + + Vector2 from_slot_position = gfrom->get_position_offset() * zoom + gfrom->get_connection_output_position(E->get().from_port); + Vector2 from_position = minimap->_convert_from_graph_position(from_slot_position - graph_offset) + minimap_offset; + Color from_color = gfrom->get_connection_output_color(E->get().from_port); + Vector2 to_slot_position = gto->get_position_offset() * zoom + gto->get_connection_input_position(E->get().to_port); + Vector2 to_position = minimap->_convert_from_graph_position(to_slot_position - graph_offset) + minimap_offset; + Color to_color = gto->get_connection_input_color(E->get().to_port); + + if (E->get().activity > 0) { + from_color = from_color.lerp(activity_color, E->get().activity); + to_color = to_color.lerp(activity_color, E->get().activity); + } + _draw_cos_line(minimap, from_position, to_position, from_color, to_color, 1.0, 0.5); + } + + // Draw the "camera" viewport. + Rect2 camera_rect = minimap->get_camera_rect(); + minimap->draw_style_box(minimap->get_theme_stylebox("camera"), camera_rect); + + // Draw the resizer control. + Ref<Texture2D> resizer = minimap->get_theme_icon("resizer"); + Color resizer_color = minimap->get_theme_color("resizer_color"); + minimap->draw_texture(resizer, Point2(), resizer_color); +} + void GraphEdit::set_selected(Node *p_child) { for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -794,7 +1054,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { pos = pos.snapped(Vector2(snap, snap)); } - gn->set_offset(pos); + gn->set_position_offset(pos); } } } @@ -821,13 +1081,13 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { if (!gn->is_selected() && box_selection_mode_additive) { emit_signal("node_selected", gn); } else if (gn->is_selected() && !box_selection_mode_additive) { - emit_signal("node_unselected", gn); + emit_signal("node_deselected", gn); } gn->set_selected(box_selection_mode_additive); } else { bool select = (previus_selected.find(gn) != nullptr); if (gn->is_selected() && !select) { - emit_signal("node_unselected", gn); + emit_signal("node_deselected", gn); } else if (!gn->is_selected() && select) { emit_signal("node_selected", gn); } @@ -836,6 +1096,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { } top_layer->update(); + minimap->update(); } Ref<InputEventMouseButton> b = p_ev; @@ -851,17 +1112,19 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { bool select = (previus_selected.find(gn) != nullptr); if (gn->is_selected() && !select) { - emit_signal("node_unselected", gn); + emit_signal("node_deselected", gn); } else if (!gn->is_selected() && select) { emit_signal("node_selected", gn); } gn->set_selected(select); } top_layer->update(); + minimap->update(); } else { if (connecting) { connecting = false; top_layer->update(); + minimap->update(); } else { emit_signal("popup_request", b->get_global_position()); } @@ -878,7 +1141,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { Rect2 r = gn->get_rect(); r.size *= zoom; if (r.has_point(b->get_position())) { - emit_signal("node_unselected", gn); + emit_signal("node_deselected", gn); gn->set_selected(false); } } @@ -902,6 +1165,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { dragging = false; top_layer->update(); + minimap->update(); update(); connections_layer->update(); } @@ -940,7 +1204,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { o_gn->set_selected(true); } else { if (o_gn->is_selected()) { - emit_signal("node_unselected", o_gn); + emit_signal("node_deselected", o_gn); } o_gn->set_selected(false); } @@ -1000,7 +1264,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { continue; } if (gn2->is_selected()) { - emit_signal("node_unselected", gn2); + emit_signal("node_deselected", gn2); } gn2->set_selected(false); } @@ -1012,6 +1276,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { box_selecting = false; previus_selected.clear(); top_layer->update(); + minimap->update(); } if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) { @@ -1079,6 +1344,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por if (Math::is_equal_approx(E->get().activity, p_activity)) { //update only if changed top_layer->update(); + minimap->update(); connections_layer->update(); } E->get().activity = p_activity; @@ -1089,6 +1355,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por void GraphEdit::clear_connections() { connections.clear(); + minimap->update(); update(); connections_layer->update(); } @@ -1112,6 +1379,7 @@ void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) { top_layer->update(); _update_scroll(); + minimap->update(); connections_layer->update(); if (is_visible_in_tree()) { @@ -1229,6 +1497,63 @@ void GraphEdit::_snap_value_changed(double) { update(); } +void GraphEdit::set_minimap_size(Vector2 p_size) { + minimap->set_size(p_size); + Vector2 minimap_size = minimap->get_size(); // The size might've been adjusted by the minimum size. + + minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT); + minimap->set_offset(Side::SIDE_LEFT, -minimap_size.x - MINIMAP_OFFSET); + minimap->set_offset(Side::SIDE_TOP, -minimap_size.y - MINIMAP_OFFSET); + minimap->set_offset(Side::SIDE_RIGHT, -MINIMAP_OFFSET); + minimap->set_offset(Side::SIDE_BOTTOM, -MINIMAP_OFFSET); + minimap->update(); +} + +Vector2 GraphEdit::get_minimap_size() const { + return minimap->get_size(); +} + +void GraphEdit::set_minimap_opacity(float p_opacity) { + minimap->set_modulate(Color(1, 1, 1, p_opacity)); + minimap->update(); +} + +float GraphEdit::get_minimap_opacity() const { + Color minimap_modulate = minimap->get_modulate(); + return minimap_modulate.a; +} + +void GraphEdit::set_minimap_enabled(bool p_enable) { + minimap_button->set_pressed(p_enable); + minimap->update(); +} + +bool GraphEdit::is_minimap_enabled() const { + return minimap_button->is_pressed(); +} + +void GraphEdit::_minimap_toggled() { + minimap->update(); +} + +void GraphEdit::set_connection_lines_thickness(float p_thickness) { + lines_thickness = p_thickness; + update(); +} + +float GraphEdit::get_connection_lines_thickness() const { + return lines_thickness; +} + +void GraphEdit::set_connection_lines_antialiased(bool p_antialiased) { + lines_antialiased = p_antialiased; + update(); +} + +bool GraphEdit::is_connection_lines_antialiased() const { + return lines_antialiased; +} + HBoxContainer *GraphEdit::get_zoom_hbox() { return zoom_hb; } @@ -1260,6 +1585,20 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_snap", "enable"), &GraphEdit::set_use_snap); ClassDB::bind_method(D_METHOD("is_using_snap"), &GraphEdit::is_using_snap); + ClassDB::bind_method(D_METHOD("set_connection_lines_thickness", "pixels"), &GraphEdit::set_connection_lines_thickness); + ClassDB::bind_method(D_METHOD("get_connection_lines_thickness"), &GraphEdit::get_connection_lines_thickness); + + ClassDB::bind_method(D_METHOD("set_connection_lines_antialiased", "pixels"), &GraphEdit::set_connection_lines_antialiased); + ClassDB::bind_method(D_METHOD("is_connection_lines_antialiased"), &GraphEdit::is_connection_lines_antialiased); + + ClassDB::bind_method(D_METHOD("set_minimap_size", "p_size"), &GraphEdit::set_minimap_size); + ClassDB::bind_method(D_METHOD("get_minimap_size"), &GraphEdit::get_minimap_size); + ClassDB::bind_method(D_METHOD("set_minimap_opacity", "p_opacity"), &GraphEdit::set_minimap_opacity); + ClassDB::bind_method(D_METHOD("get_minimap_opacity"), &GraphEdit::get_minimap_opacity); + + ClassDB::bind_method(D_METHOD("set_minimap_enabled", "enable"), &GraphEdit::set_minimap_enabled); + ClassDB::bind_method(D_METHOD("is_minimap_enabled"), &GraphEdit::is_minimap_enabled); + ClassDB::bind_method(D_METHOD("set_right_disconnects", "enable"), &GraphEdit::set_right_disconnects); ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled); @@ -1275,6 +1614,12 @@ void GraphEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_distance"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_snap"), "set_use_snap", "is_using_snap"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_thickness"), "set_connection_lines_thickness", "get_connection_lines_thickness"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "connection_lines_antialiased"), "set_connection_lines_antialiased", "is_connection_lines_antialiased"); + ADD_GROUP("Minimap", "minimap"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_enabled"), "set_minimap_enabled", "is_minimap_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimap_size"), "set_minimap_size", "get_minimap_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "minimap_opacity"), "set_minimap_opacity", "get_minimap_opacity"); ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"))); ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"))); @@ -1283,7 +1628,7 @@ void GraphEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("copy_nodes_request")); ADD_SIGNAL(MethodInfo("paste_nodes_request")); ADD_SIGNAL(MethodInfo("node_selected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("node_unselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("node_deselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::VECTOR2, "release_position"))); ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"), PropertyInfo(Variant::VECTOR2, "release_position"))); ADD_SIGNAL(MethodInfo("delete_nodes_request")); @@ -1300,7 +1645,7 @@ GraphEdit::GraphEdit() { top_layer = memnew(GraphEditFilter(this)); add_child(top_layer); top_layer->set_mouse_filter(MOUSE_FILTER_PASS); - top_layer->set_anchors_and_margins_preset(Control::PRESET_WIDE); + top_layer->set_anchors_and_offsets_preset(Control::PRESET_WIDE); top_layer->connect("draw", callable_mp(this, &GraphEdit::_top_layer_draw)); top_layer->connect("gui_input", callable_mp(this, &GraphEdit::_top_layer_input)); @@ -1380,6 +1725,32 @@ GraphEdit::GraphEdit() { snap_amount->connect("value_changed", callable_mp(this, &GraphEdit::_snap_value_changed)); zoom_hb->add_child(snap_amount); + minimap_button = memnew(Button); + minimap_button->set_flat(true); + minimap_button->set_toggle_mode(true); + minimap_button->set_tooltip(RTR("Enable grid minimap.")); + minimap_button->connect("pressed", callable_mp(this, &GraphEdit::_minimap_toggled)); + minimap_button->set_pressed(true); + minimap_button->set_focus_mode(FOCUS_NONE); + zoom_hb->add_child(minimap_button); + + Vector2 minimap_size = Vector2(240, 160); + float minimap_opacity = 0.65; + + minimap = memnew(GraphEditMinimap(this)); + top_layer->add_child(minimap); + minimap->set_name("_minimap"); + minimap->set_modulate(Color(1, 1, 1, minimap_opacity)); + minimap->set_mouse_filter(MOUSE_FILTER_STOP); + minimap->set_custom_minimum_size(Vector2(50, 50)); + minimap->set_size(minimap_size); + minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT); + minimap->set_offset(Side::SIDE_LEFT, -minimap_size.x - MINIMAP_OFFSET); + minimap->set_offset(Side::SIDE_TOP, -minimap_size.y - MINIMAP_OFFSET); + minimap->set_offset(Side::SIDE_RIGHT, -MINIMAP_OFFSET); + minimap->set_offset(Side::SIDE_BOTTOM, -MINIMAP_OFFSET); + minimap->connect("draw", callable_mp(this, &GraphEdit::_minimap_draw)); + setting_scroll_ofs = false; just_disconnected = false; set_clip_contents(true); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index d87bd41f27..4525152bd5 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -45,6 +45,7 @@ class GraphEditFilter : public Control { GDCLASS(GraphEditFilter, Control); friend class GraphEdit; + friend class GraphEditMinimap; GraphEdit *ge; virtual bool has_point(const Point2 &p_point) const override; @@ -52,6 +53,45 @@ public: GraphEditFilter(GraphEdit *p_edit); }; +class GraphEditMinimap : public Control { + GDCLASS(GraphEditMinimap, Control); + + friend class GraphEdit; + friend class GraphEditFilter; + GraphEdit *ge; + +protected: + static void _bind_methods(); + +public: + GraphEditMinimap(GraphEdit *p_edit); + + void update_minimap(); + Rect2 get_camera_rect(); + +private: + Vector2 minimap_padding; + Vector2 minimap_offset; + Vector2 graph_proportions; + Vector2 graph_padding; + Vector2 camera_position; + Vector2 camera_size; + + bool is_pressing; + bool is_resizing; + + Vector2 _get_render_size(); + Vector2 _get_graph_offset(); + Vector2 _get_graph_size(); + + Vector2 _convert_from_graph_position(const Vector2 &p_position); + Vector2 _convert_to_graph_position(const Vector2 &p_position); + + void _gui_input(const Ref<InputEvent> &p_ev); + + void _adjust_graph_scroll(const Vector2 &p_offset); +}; + class GraphEdit : public Control { GDCLASS(GraphEdit, Control); @@ -72,6 +112,8 @@ private: Button *snap_button; SpinBox *snap_amount; + Button *minimap_button; + void _zoom_minus(); void _zoom_reset(); void _zoom_plus(); @@ -116,9 +158,12 @@ private: bool awaiting_scroll_offset_update; List<Connection> connections; + float lines_thickness = 2.0f; + bool lines_antialiased = true; + void _bake_segment2d(Vector<Vector2> &points, Vector<Color> &colors, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_min_depth, int p_max_depth, float p_tol, const Color &p_color, const Color &p_to_color, int &lines) const; - void _draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color); + void _draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_bezier_ratio); void _graph_node_raised(Node *p_gn); void _graph_node_moved(Node *p_gn); @@ -129,12 +174,14 @@ private: Control *connections_layer; GraphEditFilter *top_layer; + GraphEditMinimap *minimap; void _top_layer_input(const Ref<InputEvent> &p_ev); bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos); void _top_layer_draw(); void _connections_layer_draw(); + void _minimap_draw(); void _update_scroll_offset(); Array _get_connection_list() const; @@ -171,6 +218,9 @@ private: void _snap_toggled(); void _snap_value_changed(double); + friend class GraphEditMinimap; + void _minimap_toggled(); + bool _check_clickable_control(Control *p_control, const Vector2 &pos); protected: @@ -196,7 +246,16 @@ public: void set_zoom_custom(float p_zoom, const Vector2 &p_center); float get_zoom() const; + void set_minimap_size(Vector2 p_size); + Vector2 get_minimap_size() const; + void set_minimap_opacity(float p_opacity); + float get_minimap_opacity() const; + + void set_minimap_enabled(bool p_enable); + bool is_minimap_enabled() const; + GraphEditFilter *get_top_layer() const { return top_layer; } + GraphEditMinimap *get_minimap() const { return minimap; } void get_connection_list(List<Connection> *r_connections) const; void set_right_disconnects(bool p_enable); @@ -219,6 +278,12 @@ public: int get_snap() const; void set_snap(int p_snap); + void set_connection_lines_thickness(float p_thickness); + float get_connection_lines_thickness() const; + + void set_connection_lines_antialiased(bool p_antialiased); + bool is_connection_lines_antialiased() const; + HBoxContainer *get_zoom_hbox(); GraphEdit(); diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 4454e87017..9f5c87377f 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,13 +30,37 @@ #include "graph_node.h" +#include "core/string/translation.h" + bool GraphNode::_set(const StringName &p_name, const Variant &p_value) { - if (!p_name.operator String().begins_with("slot/")) { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + double value = p_value; + if (value == -1) { + if (opentype_features.has(tag)) { + opentype_features.erase(tag); + _shape(); + update(); + } + } else { + if ((double)opentype_features[tag] != value) { + opentype_features[tag] = value; + _shape(); + update(); + } + } + _change_notify(); + return true; + } + + if (!str.begins_with("slot/")) { return false; } - int idx = p_name.operator String().get_slice("/", 1).to_int(); - String what = p_name.operator String().get_slice("/", 2); + int idx = str.get_slice("/", 1).to_int(); + String what = str.get_slice("/", 2); Slot si; if (slot_info.has(idx)) { @@ -65,12 +89,25 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) { } bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const { - if (!p_name.operator String().begins_with("slot/")) { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + if (opentype_features.has(tag)) { + r_ret = opentype_features[tag]; + return true; + } else { + r_ret = -1; + return true; + } + } + + if (!str.begins_with("slot/")) { return false; } - int idx = p_name.operator String().get_slice("/", 1).to_int(); - String what = p_name.operator String().get_slice("/", 2); + int idx = str.get_slice("/", 1).to_int(); + String what = str.get_slice("/", 2); Slot si; if (slot_info.has(idx)) { @@ -97,6 +134,12 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const { } void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const { + for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { + String name = TS->tag_to_name(*ftr); + p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name)); + } + p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + int idx = 0; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -160,7 +203,7 @@ void GraphNode::_resort() { Size2i size = c->get_combined_minimum_size(); - Rect2 r(sb->get_margin(MARGIN_LEFT), sb->get_margin(MARGIN_TOP) + vofs, w, size.y); + Rect2 r(sb->get_margin(SIDE_LEFT), sb->get_margin(SIDE_TOP) + vofs, w, size.y); fit_child_in_rect(c, r); cache_y.push_back(vofs + size.y * 0.5); @@ -181,7 +224,7 @@ bool GraphNode::has_point(const Point2 &p_point) const { return true; } - if (Rect2(0, 0, get_size().width, comment->get_margin(MARGIN_TOP)).has_point(p_point)) { + if (Rect2(0, 0, get_size().width, comment->get_margin(SIDE_TOP)).has_point(p_point)) { return true; } @@ -213,13 +256,12 @@ void GraphNode::_notification(int p_what) { int close_h_offset = get_theme_constant("close_h_offset"); Color close_color = get_theme_color("close_color"); Color resizer_color = get_theme_color("resizer_color"); - Ref<Font> title_font = get_theme_font("title_font"); int title_offset = get_theme_constant("title_offset"); int title_h_offset = get_theme_constant("title_h_offset"); Color title_color = get_theme_color("title_color"); Point2i icofs = -port->get_size() * 0.5; int edgeofs = get_theme_constant("port_offset"); - icofs.y += sb->get_margin(MARGIN_TOP); + icofs.y += sb->get_margin(SIDE_TOP); draw_style_box(sb, Rect2(Point2(), get_size())); @@ -241,9 +283,10 @@ void GraphNode::_notification(int p_what) { w -= close->get_width(); } - draw_string(title_font, Point2(sb->get_margin(MARGIN_LEFT) + title_h_offset, -title_font->get_height() + title_font->get_ascent() + title_offset), title, title_color, w); + title_buf->set_width(w); + title_buf->draw(get_canvas_item(), Point2(sb->get_margin(SIDE_LEFT) + title_h_offset, -title_buf->get_size().y + title_offset), title_color); if (show_close) { - Vector2 cpos = Point2(w + sb->get_margin(MARGIN_LEFT) + close_h_offset, -close->get_height() + close_offset); + Vector2 cpos = Point2(w + sb->get_margin(SIDE_LEFT) + close_h_offset, -close->get_height() + close_offset); draw_texture(close, cpos, close_color); close_rect.position = cpos; close_rect.size = close->get_size(); @@ -285,12 +328,30 @@ void GraphNode::_notification(int p_what) { _resort(); } break; + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_THEME_CHANGED: { + _shape(); + minimum_size_changed(); + update(); } break; } } +void GraphNode::_shape() { + Ref<Font> font = get_theme_font("title_font"); + int font_size = get_theme_font_size("title_font_size"); + + title_buf->clear(); + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { + title_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + title_buf->set_direction((TextServer::Direction)text_direction); + } + title_buf->add_string(title, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); +} + void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left, const Ref<Texture2D> &p_custom_right) { ERR_FAIL_COND(p_idx < 0); @@ -368,14 +429,12 @@ Color GraphNode::get_slot_color_right(int p_idx) const { } Size2 GraphNode::get_minimum_size() const { - Ref<Font> title_font = get_theme_font("title_font"); - int sep = get_theme_constant("separation"); Ref<StyleBox> sb = get_theme_stylebox("frame"); bool first = true; Size2 minsize; - minsize.x = title_font->get_string_size(title).x; + minsize.x = title_buf->get_size().x; if (show_close) { Ref<Texture2D> close = get_theme_icon("close"); minsize.x += sep + close->get_width(); @@ -410,6 +469,8 @@ void GraphNode::set_title(const String &p_title) { return; } title = p_title; + _shape(); + update(); _change_notify("title"); minimum_size_changed(); @@ -419,14 +480,62 @@ String GraphNode::get_title() const { return title; } -void GraphNode::set_offset(const Vector2 &p_offset) { - offset = p_offset; - emit_signal("offset_changed"); +void GraphNode::set_text_direction(Control::TextDirection p_text_direction) { + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (text_direction != p_text_direction) { + text_direction = p_text_direction; + _shape(); + update(); + } +} + +Control::TextDirection GraphNode::get_text_direction() const { + return text_direction; +} + +void GraphNode::clear_opentype_features() { + opentype_features.clear(); + _shape(); update(); } -Vector2 GraphNode::get_offset() const { - return offset; +void GraphNode::set_opentype_feature(const String &p_name, int p_value) { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { + opentype_features[tag] = p_value; + _shape(); + update(); + } +} + +int GraphNode::get_opentype_feature(const String &p_name) const { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag)) { + return -1; + } + return opentype_features[tag]; +} + +void GraphNode::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + _shape(); + update(); + } +} + +String GraphNode::get_language() const { + return language; +} + +void GraphNode::set_position_offset(const Vector2 &p_offset) { + position_offset = p_offset; + emit_signal("position_offset_changed"); + update(); +} + +Vector2 GraphNode::get_position_offset() const { + return position_offset; } void GraphNode::set_selected(bool p_selected) { @@ -440,9 +549,9 @@ bool GraphNode::is_selected() { void GraphNode::set_drag(bool p_drag) { if (p_drag) { - drag_from = get_offset(); + drag_from = get_position_offset(); } else { - emit_signal("dragged", drag_from, get_offset()); //useful for undo/redo + emit_signal("dragged", drag_from, get_position_offset()); //useful for undo/redo } } @@ -481,7 +590,7 @@ void GraphNode::_connpos_update() { Size2i size = c->get_combined_minimum_size(); - int y = sb->get_margin(MARGIN_TOP) + vofs; + int y = sb->get_margin(SIDE_TOP) + vofs; int h = size.y; if (slot_info.has(idx)) { @@ -658,6 +767,14 @@ bool GraphNode::is_resizable() const { void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_title", "title"), &GraphNode::set_title); ClassDB::bind_method(D_METHOD("get_title"), &GraphNode::get_title); + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &GraphNode::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &GraphNode::get_text_direction); + ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &GraphNode::set_opentype_feature); + ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &GraphNode::get_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_opentype_features"), &GraphNode::clear_opentype_features); + ClassDB::bind_method(D_METHOD("set_language", "language"), &GraphNode::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &GraphNode::get_language); + ClassDB::bind_method(D_METHOD("_gui_input"), &GraphNode::_gui_input); ClassDB::bind_method(D_METHOD("set_slot", "idx", "enable_left", "type_left", "color_left", "enable_right", "type_right", "color_right", "custom_left", "custom_right"), &GraphNode::set_slot, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>())); @@ -670,8 +787,8 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("get_slot_type_right", "idx"), &GraphNode::get_slot_type_right); ClassDB::bind_method(D_METHOD("get_slot_color_right", "idx"), &GraphNode::get_slot_color_right); - ClassDB::bind_method(D_METHOD("set_offset", "offset"), &GraphNode::set_offset); - ClassDB::bind_method(D_METHOD("get_offset"), &GraphNode::get_offset); + ClassDB::bind_method(D_METHOD("set_position_offset", "offset"), &GraphNode::set_position_offset); + ClassDB::bind_method(D_METHOD("get_position_offset"), &GraphNode::get_position_offset); ClassDB::bind_method(D_METHOD("set_comment", "comment"), &GraphNode::set_comment); ClassDB::bind_method(D_METHOD("is_comment"), &GraphNode::is_comment); @@ -699,14 +816,16 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("get_overlay"), &GraphNode::get_overlay); ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position_offset"), "set_position_offset", "get_position_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_close"), "set_show_close_button", "is_close_button_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resizable"), "set_resizable", "is_resizable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selected"), "set_selected", "is_selected"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "comment"), "set_comment", "is_comment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "overlay", PROPERTY_HINT_ENUM, "Disabled,Breakpoint,Position"), "set_overlay", "get_overlay"); - ADD_SIGNAL(MethodInfo("offset_changed")); + ADD_SIGNAL(MethodInfo("position_offset_changed")); ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to"))); ADD_SIGNAL(MethodInfo("raise_request")); ADD_SIGNAL(MethodInfo("close_request")); @@ -718,6 +837,7 @@ void GraphNode::_bind_methods() { } GraphNode::GraphNode() { + title_buf.instance(); overlay = OVERLAY_DISABLED; show_close = false; connpos_dirty = true; diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index 0cf6d9b09a..cf66586e6b 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,6 +32,7 @@ #define GRAPH_NODE_H #include "scene/gui/container.h" +#include "scene/resources/text_line.h" class GraphNode : public Container { GDCLASS(GraphNode, Container); @@ -65,8 +66,14 @@ private: }; String title; + Ref<TextLine> title_buf; + + Dictionary opentype_features; + String language; + TextDirection text_direction = TEXT_DIRECTION_AUTO; + bool show_close; - Vector2 offset; + Vector2 position_offset; bool comment; bool resizable; @@ -93,6 +100,7 @@ private: void _connpos_update(); void _resort(); + void _shape(); Vector2 drag_from; bool selected; @@ -124,8 +132,18 @@ public: void set_title(const String &p_title); String get_title() const; - void set_offset(const Vector2 &p_offset); - Vector2 get_offset() const; + void set_text_direction(TextDirection p_text_direction); + TextDirection get_text_direction() const; + + void set_opentype_feature(const String &p_name, int p_value); + int get_opentype_feature(const String &p_name) const; + void clear_opentype_features(); + + void set_language(const String &p_language); + String get_language() const; + + void set_position_offset(const Vector2 &p_offset); + Vector2 get_position_offset() const; void set_selected(bool p_selected); bool is_selected(); diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index 2f37461c4d..11335db54a 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -141,6 +141,7 @@ void GridContainer::_notification(int p_what) { // Finally, fit the nodes. int col_expand = col_expanded.size() > 0 ? remaining_space.width / col_expanded.size() : 0; int row_expand = row_expanded.size() > 0 ? remaining_space.height / row_expanded.size() : 0; + bool rtl = is_layout_rtl(); int col_ofs = 0; int row_ofs = 0; @@ -156,24 +157,37 @@ void GridContainer::_notification(int p_what) { valid_controls_index++; if (col == 0) { - col_ofs = 0; + if (rtl) { + col_ofs = get_size().width; + } else { + col_ofs = 0; + } if (row > 0) { row_ofs += (row_expanded.has(row - 1) ? row_expand : row_minh[row - 1]) + vsep; } } - Point2 p(col_ofs, row_ofs); - Size2 s(col_expanded.has(col) ? col_expand : col_minw[col], row_expanded.has(row) ? row_expand : row_minh[row]); - - fit_child_in_rect(c, Rect2(p, s)); - - col_ofs += s.width + hsep; + if (rtl) { + Size2 s(col_expanded.has(col) ? col_expand : col_minw[col], row_expanded.has(row) ? row_expand : row_minh[row]); + Point2 p(col_ofs - s.width, row_ofs); + fit_child_in_rect(c, Rect2(p, s)); + col_ofs -= s.width + hsep; + } else { + Point2 p(col_ofs, row_ofs); + Size2 s(col_expanded.has(col) ? col_expand : col_minw[col], row_expanded.has(row) ? row_expand : row_minh[row]); + fit_child_in_rect(c, Rect2(p, s)); + col_ofs += s.width + hsep; + } } } break; case NOTIFICATION_THEME_CHANGED: { minimum_size_changed(); } break; + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + queue_sort(); + } break; } } diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h index 79d4aee284..8045c94b9a 100644 --- a/scene/gui/grid_container.h +++ b/scene/gui/grid_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 6708b18e0a..69ca96b28e 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,24 @@ #include "item_list.h" #include "core/config/project_settings.h" #include "core/os/os.h" +#include "core/string/translation.h" + +void ItemList::_shape(int p_idx) { + Item &item = items.write[p_idx]; + + item.text_buf->clear(); + if (item.text_direction == Control::TEXT_DIRECTION_INHERITED) { + item.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + item.text_buf->set_direction((TextServer::Direction)item.text_direction); + } + item.text_buf->add_string(item.text, get_theme_font("font"), get_theme_font_size("font_size"), item.opentype_features, (item.language != "") ? item.language : TranslationServer::get_singleton()->get_tool_locale()); + if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { + item.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + } else { + item.text_buf->set_flags(TextServer::BREAK_NONE); + } +} void ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bool p_selectable) { Item item; @@ -39,6 +57,7 @@ void ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, b item.icon_region = Rect2i(); item.icon_modulate = Color(1, 1, 1, 1); item.text = p_item; + item.text_buf.instance(); item.selectable = p_selectable; item.selected = false; item.disabled = false; @@ -46,6 +65,8 @@ void ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, b item.custom_bg = Color(0, 0, 0, 0); items.push_back(item); + _shape(items.size() - 1); + update(); shape_changed = true; } @@ -57,6 +78,7 @@ void ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) { item.icon_region = Rect2i(); item.icon_modulate = Color(1, 1, 1, 1); //item.text=p_item; + item.text_buf.instance(); item.selectable = p_selectable; item.selected = false; item.disabled = false; @@ -72,6 +94,7 @@ void ItemList::set_item_text(int p_idx, const String &p_text) { ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].text = p_text; + _shape(p_idx); update(); shape_changed = true; } @@ -81,6 +104,61 @@ String ItemList::get_item_text(int p_idx) const { return items[p_idx].text; } +void ItemList::set_item_text_direction(int p_idx, Control::TextDirection p_text_direction) { + ERR_FAIL_INDEX(p_idx, items.size()); + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (items[p_idx].text_direction != p_text_direction) { + items.write[p_idx].text_direction = p_text_direction; + _shape(p_idx); + update(); + } +} + +Control::TextDirection ItemList::get_item_text_direction(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, items.size(), TEXT_DIRECTION_INHERITED); + return items[p_idx].text_direction; +} + +void ItemList::clear_item_opentype_features(int p_idx) { + ERR_FAIL_INDEX(p_idx, items.size()); + items.write[p_idx].opentype_features.clear(); + _shape(p_idx); + update(); +} + +void ItemList::set_item_opentype_feature(int p_idx, const String &p_name, int p_value) { + ERR_FAIL_INDEX(p_idx, items.size()); + int32_t tag = TS->name_to_tag(p_name); + if (!items[p_idx].opentype_features.has(tag) || (int)items[p_idx].opentype_features[tag] != p_value) { + items.write[p_idx].opentype_features[tag] = p_value; + _shape(p_idx); + update(); + } +} + +int ItemList::get_item_opentype_feature(int p_idx, const String &p_name) const { + ERR_FAIL_INDEX_V(p_idx, items.size(), -1); + int32_t tag = TS->name_to_tag(p_name); + if (!items[p_idx].opentype_features.has(tag)) { + return -1; + } + return items[p_idx].opentype_features[tag]; +} + +void ItemList::set_item_language(int p_idx, const String &p_language) { + ERR_FAIL_INDEX(p_idx, items.size()); + if (items[p_idx].language != p_language) { + items.write[p_idx].language = p_language; + _shape(p_idx); + update(); + } +} + +String ItemList::get_item_language(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, items.size(), ""); + return items[p_idx].language; +} + void ItemList::set_item_tooltip_enabled(int p_idx, const bool p_enabled) { ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].tooltip_enabled = p_enabled; @@ -255,7 +333,7 @@ void ItemList::select(int p_idx, bool p_single) { update(); } -void ItemList::unselect(int p_idx) { +void ItemList::deselect(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); if (select_mode != SELECT_MULTI) { @@ -267,7 +345,7 @@ void ItemList::unselect(int p_idx) { update(); } -void ItemList::unselect_all() { +void ItemList::deselect_all() { if (items.size() < 1) { return; } @@ -361,9 +439,18 @@ bool ItemList::is_same_column_width() const { void ItemList::set_max_text_lines(int p_lines) { ERR_FAIL_COND(p_lines < 1); - max_text_lines = p_lines; - update(); - shape_changed = true; + if (max_text_lines != p_lines) { + max_text_lines = p_lines; + for (int i = 0; i < items.size(); i++) { + if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { + items.write[i].text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + } else { + items.write[i].text_buf->set_flags(TextServer::BREAK_NONE); + } + } + shape_changed = true; + update(); + } } int ItemList::get_max_text_lines() const { @@ -392,9 +479,18 @@ ItemList::SelectMode ItemList::get_select_mode() const { void ItemList::set_icon_mode(IconMode p_mode) { ERR_FAIL_INDEX((int)p_mode, 2); - icon_mode = p_mode; - update(); - shape_changed = true; + if (icon_mode != p_mode) { + icon_mode = p_mode; + for (int i = 0; i < items.size(); i++) { + if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { + items.write[i].text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + } else { + items.write[i].text_buf->set_flags(TextServer::BREAK_NONE); + } + } + shape_changed = true; + update(); + } } ItemList::IconMode ItemList::get_icon_mode() const { @@ -455,6 +551,10 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { pos -= bg->get_offset(); pos.y += scroll_bar->get_value(); + if (is_layout_rtl()) { + pos.x = get_size().width - pos.x; + } + int closest = -1; for (int i = 0; i < items.size(); i++) { @@ -473,7 +573,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { int i = closest; if (select_mode == SELECT_MULTI && items[i].selected && mb->get_command()) { - unselect(i); + deselect(i); emit_signal("multi_selected", i, false); } else if (select_mode == SELECT_MULTI && mb->get_shift() && current >= 0 && current < items.size() && current != i) { @@ -659,7 +759,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { select(current, false); emit_signal("multi_selected", current, true); } else if (items[current].selected) { - unselect(current); + deselect(current); emit_signal("multi_selected", current, false); } } @@ -749,14 +849,22 @@ void ItemList::_notification(int p_what) { update(); } + if ((p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) || (p_what == NOTIFICATION_TRANSLATION_CHANGED) || (p_what == NOTIFICATION_THEME_CHANGED)) { + for (int i = 0; i < items.size(); i++) { + _shape(i); + } + shape_changed = true; + update(); + } + if (p_what == NOTIFICATION_DRAW) { Ref<StyleBox> bg = get_theme_stylebox("bg"); int mw = scroll_bar->get_minimum_size().x; - scroll_bar->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -mw); - scroll_bar->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0); - scroll_bar->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, bg->get_margin(MARGIN_TOP)); - scroll_bar->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -bg->get_margin(MARGIN_BOTTOM)); + scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -mw); + scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); + scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, bg->get_margin(SIDE_TOP)); + scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bg->get_margin(SIDE_BOTTOM)); Size2 size = get_size(); @@ -774,19 +882,11 @@ void ItemList::_notification(int p_what) { Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox("selected_focus") : get_theme_stylebox("selected"); Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox("cursor") : get_theme_stylebox("cursor_unfocused"); + bool rtl = is_layout_rtl(); - Ref<Font> font = get_theme_font("font"); Color guide_color = get_theme_color("guide_color"); Color font_color = get_theme_color("font_color"); Color font_color_selected = get_theme_color("font_color_selected"); - int font_height = font->get_height(); - Vector<int> line_size_cache; - Vector<int> line_limit_cache; - - if (max_text_lines) { - line_size_cache.resize(max_text_lines); - line_limit_cache.resize(max_text_lines); - } if (has_focus()) { RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true); @@ -817,13 +917,13 @@ void ItemList::_notification(int p_what) { } if (items[i].text != "") { - Size2 s = font->get_string_size(items[i].text); + Size2 s = items[i].text_buf->get_size(); //s.width=MIN(s.width,fixed_column_width); if (icon_mode == ICON_MODE_TOP) { minsize.x = MAX(minsize.x, s.width); if (max_text_lines > 0) { - minsize.y += (font_height + line_separation) * max_text_lines; + minsize.y += s.height + line_separation * max_text_lines; } else { minsize.y += s.height; } @@ -986,6 +1086,10 @@ void ItemList::_notification(int p_what) { r.position.x -= hseparation / 2; r.size.x += hseparation; + if (rtl) { + r.position.x = size.width - r.position.x - r.size.x; + } + draw_style_box(sbsel, r); } if (items[i].custom_bg.a > 0.001) { @@ -998,6 +1102,10 @@ void ItemList::_notification(int p_what) { r.position.x -= hseparation / 2; r.size.x += hseparation; + if (rtl) { + r.position.x = size.width - r.position.x - r.size.x; + } + draw_rect(r, items[i].custom_bg); } @@ -1049,17 +1157,25 @@ void ItemList::_notification(int p_what) { } Rect2 region = (items[i].icon_region.size.x == 0 || items[i].icon_region.size.y == 0) ? Rect2(Vector2(), items[i].icon->get_size()) : Rect2(items[i].icon_region); + + if (rtl) { + draw_rect.position.x = size.width - draw_rect.position.x - draw_rect.size.x; + } draw_texture_rect_region(items[i].icon, draw_rect, region, modulate, items[i].icon_transposed); } if (items[i].tag_icon.is_valid()) { - draw_texture(items[i].tag_icon, items[i].rect_cache.position + base_ofs); + Point2 draw_pos = items[i].rect_cache.position; + if (rtl) { + draw_pos.x = size.width - draw_pos.x - items[i].tag_icon->get_width(); + } + draw_texture(items[i].tag_icon, draw_pos + base_ofs); } if (items[i].text != "") { int max_len = -1; - Vector2 size2 = font->get_string_size(items[i].text); + Vector2 size2 = items[i].text_buf->get_size(); if (fixed_column_width) { max_len = fixed_column_width; } else if (same_column_width) { @@ -1074,45 +1190,18 @@ void ItemList::_notification(int p_what) { } if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { - int ss = items[i].text.length(); - float ofs = 0; - int line = 0; - for (int j = 0; j <= ss; j++) { - int cs = j < ss ? font->get_char_size(items[i].text[j], items[i].text[j + 1]).x : 0; - if (ofs + cs > max_len || j == ss) { - line_limit_cache.write[line] = j; - line_size_cache.write[line] = ofs; - line++; - ofs = 0; - if (line >= max_text_lines) { - break; - } - } else { - ofs += cs; - } - } - - line = 0; - ofs = 0; - - text_ofs.y += font->get_ascent(); text_ofs = text_ofs.floor(); text_ofs += base_ofs; text_ofs += items[i].rect_cache.position; - FontDrawer drawer(font, Color(1, 1, 1)); - for (int j = 0; j < ss; j++) { - if (j == line_limit_cache[line]) { - line++; - ofs = 0; - if (line >= max_text_lines) { - break; - } - } - ofs += drawer.draw_char(get_canvas_item(), text_ofs + Vector2(ofs + (max_len - line_size_cache[line]) / 2, line * (font_height + line_separation)).floor(), items[i].text[j], items[i].text[j + 1], modulate); + if (rtl) { + text_ofs.x = size.width - text_ofs.x - max_len; } - //special multiline mode + items.write[i].text_buf->set_width(max_len); + items.write[i].text_buf->set_align(HALIGN_CENTER); + + items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate); } else { if (fixed_column_width > 0) { size2.x = MIN(size2.x, fixed_column_width); @@ -1124,12 +1213,22 @@ void ItemList::_notification(int p_what) { text_ofs.y += (items[i].rect_cache.size.height - size2.y) / 2; } - text_ofs.y += font->get_ascent(); text_ofs = text_ofs.floor(); text_ofs += base_ofs; text_ofs += items[i].rect_cache.position; - draw_string(font, text_ofs, items[i].text, modulate, max_len + 1); + if (rtl) { + text_ofs.x = size.width - text_ofs.x - max_len; + } + + items.write[i].text_buf->set_width(max_len); + + if (rtl) { + items.write[i].text_buf->set_align(HALIGN_RIGHT); + } else { + items.write[i].text_buf->set_align(HALIGN_LEFT); + } + items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate); } } @@ -1140,6 +1239,11 @@ void ItemList::_notification(int p_what) { r.size.y += vseparation; r.position.x -= hseparation / 2; r.size.x += hseparation; + + if (rtl) { + r.position.x = size.width - r.position.x - r.size.x; + } + draw_style_box(cursor, r); } } @@ -1166,7 +1270,7 @@ void ItemList::_notification(int p_what) { } const int y = base_ofs.y + separators[i]; - draw_line(Vector2(bg->get_margin(MARGIN_LEFT), y), Vector2(width, y), guide_color); + draw_line(Vector2(bg->get_margin(SIDE_LEFT), y), Vector2(width, y), guide_color); } } } @@ -1181,6 +1285,10 @@ int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const { pos -= bg->get_offset(); pos.y += scroll_bar->get_value(); + if (is_layout_rtl()) { + pos.x = get_size().width - pos.x; + } + int closest = -1; int closest_dist = 0x7FFFFFFF; @@ -1206,7 +1314,7 @@ int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const { } bool ItemList::is_pos_at_end_of_items(const Point2 &p_pos) const { - if (items.empty()) { + if (items.is_empty()) { return true; } @@ -1215,6 +1323,10 @@ bool ItemList::is_pos_at_end_of_items(const Point2 &p_pos) const { pos -= bg->get_offset(); pos.y += scroll_bar->get_value(); + if (is_layout_rtl()) { + pos.x = get_size().width - pos.x; + } + Rect2 endrect = items[items.size() - 1].rect_cache; return (pos.y > endrect.position.y + endrect.size.y); } @@ -1366,6 +1478,16 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &ItemList::set_item_icon); ClassDB::bind_method(D_METHOD("get_item_icon", "idx"), &ItemList::get_item_icon); + ClassDB::bind_method(D_METHOD("set_item_text_direction", "idx", "direction"), &ItemList::set_item_text_direction); + ClassDB::bind_method(D_METHOD("get_item_text_direction", "idx"), &ItemList::get_item_text_direction); + + ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "idx", "tag", "value"), &ItemList::set_item_opentype_feature); + ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "idx", "tag"), &ItemList::get_item_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "idx"), &ItemList::clear_item_opentype_features); + + ClassDB::bind_method(D_METHOD("set_item_language", "idx", "language"), &ItemList::set_item_language); + ClassDB::bind_method(D_METHOD("get_item_language", "idx"), &ItemList::get_item_language); + ClassDB::bind_method(D_METHOD("set_item_icon_transposed", "idx", "transposed"), &ItemList::set_item_icon_transposed); ClassDB::bind_method(D_METHOD("is_item_icon_transposed", "idx"), &ItemList::is_item_icon_transposed); @@ -1397,8 +1519,8 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &ItemList::get_item_tooltip); ClassDB::bind_method(D_METHOD("select", "idx", "single"), &ItemList::select, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("unselect", "idx"), &ItemList::unselect); - ClassDB::bind_method(D_METHOD("unselect_all"), &ItemList::unselect_all); + ClassDB::bind_method(D_METHOD("deselect", "idx"), &ItemList::deselect); + ClassDB::bind_method(D_METHOD("deselect_all"), &ItemList::deselect_all); ClassDB::bind_method(D_METHOD("is_selected", "idx"), &ItemList::is_selected); ClassDB::bind_method(D_METHOD("get_selected_items"), &ItemList::get_selected_items); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 03f477940c..d08823c398 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ #include "scene/gui/control.h" #include "scene/gui/scroll_bar.h" +#include "scene/resources/text_paragraph.h" class ItemList : public Control { GDCLASS(ItemList, Control); @@ -56,6 +57,11 @@ private: Color icon_modulate; Ref<Texture2D> tag_icon; String text; + Ref<TextParagraph> text_buf; + Dictionary opentype_features; + String language; + TextDirection text_direction = TEXT_DIRECTION_AUTO; + bool selectable; bool selected; bool disabled; @@ -117,6 +123,7 @@ private: void _scroll_changed(double); void _gui_input(const Ref<InputEvent> &p_event); + void _shape(int p_idx); protected: void _notification(int p_what); @@ -129,6 +136,16 @@ public: void set_item_text(int p_idx, const String &p_text); String get_item_text(int p_idx) const; + void set_item_text_direction(int p_idx, TextDirection p_text_direction); + TextDirection get_item_text_direction(int p_idx) const; + + void set_item_opentype_feature(int p_idx, const String &p_name, int p_value); + int get_item_opentype_feature(int p_idx, const String &p_name) const; + void clear_item_opentype_features(int p_idx); + + void set_item_language(int p_idx, const String &p_language); + String get_item_language(int p_idx) const; + void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon); Ref<Texture2D> get_item_icon(int p_idx) const; @@ -166,8 +183,8 @@ public: Color get_item_custom_fg_color(int p_idx) const; void select(int p_idx, bool p_single = true); - void unselect(int p_idx); - void unselect_all(); + void deselect(int p_idx); + void deselect_all(); bool is_selected(int p_idx) const; Vector<int> get_selected_items(); bool is_anything_selected(); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 9df63a3c71..bd89fe441c 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,13 +34,13 @@ #include "core/string/print_string.h" #include "core/string/translation.h" +#include "servers/text_server.h" + void Label::set_autowrap(bool p_autowrap) { - if (autowrap == p_autowrap) { - return; + if (autowrap != p_autowrap) { + autowrap = p_autowrap; + lines_dirty = true; } - - autowrap = p_autowrap; - word_cache_dirty = true; update(); if (clip) { @@ -54,7 +54,8 @@ bool Label::has_autowrap() const { void Label::set_uppercase(bool p_uppercase) { uppercase = p_uppercase; - word_cache_dirty = true; + dirty = true; + update(); } @@ -62,8 +63,97 @@ bool Label::is_uppercase() const { return uppercase; } -int Label::get_line_height() const { - return get_theme_font("font")->get_height(); +int Label::get_line_height(int p_line) const { + Ref<Font> font = get_theme_font("font"); + if (p_line >= 0 && p_line < lines_rid.size()) { + return TS->shaped_text_get_size(lines_rid[p_line]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM); + } else if (lines_rid.size() > 0) { + int h = 0; + for (int i = 0; i < lines_rid.size(); i++) { + h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y) + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM); + } + return h; + } else { + return font->get_height(get_theme_font_size("font_size")); + } +} + +void Label::_shape() { + Ref<StyleBox> style = get_theme_stylebox("normal", "Label"); + int width = (get_size().width - style->get_minimum_size().width); + + if (dirty) { + TS->shaped_text_clear(text_rid); + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { + TS->shaped_text_set_direction(text_rid, is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + TS->shaped_text_set_direction(text_rid, (TextServer::Direction)text_direction); + } + TS->shaped_text_add_string(text_rid, (uppercase) ? xl_text.to_upper() : xl_text, get_theme_font("font")->get_rids(), get_theme_font_size("font_size"), opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); + TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, xl_text)); + dirty = false; + lines_dirty = true; + } + if (lines_dirty) { + for (int i = 0; i < lines_rid.size(); i++) { + TS->free(lines_rid[i]); + } + lines_rid.clear(); + + Vector<Vector2i> lines = TS->shaped_text_get_line_breaks(text_rid, width, 0, (autowrap) ? (TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) : TextServer::BREAK_MANDATORY); + for (int i = 0; i < lines.size(); i++) { + RID line = TS->shaped_text_substr(text_rid, lines[i].x, lines[i].y - lines[i].x); + lines_rid.push_back(line); + } + } + + if (xl_text.length() == 0) { + minsize = Size2(1, get_line_height()); + return; + } + if (!autowrap) { + minsize.width = 0.0f; + for (int i = 0; i < lines_rid.size(); i++) { + if (minsize.width < TS->shaped_text_get_size(lines_rid[i]).x) { + minsize.width = TS->shaped_text_get_size(lines_rid[i]).x; + } + } + } + + if (lines_dirty) { // Fill after min_size calculation. + if (align == ALIGN_FILL) { + for (int i = 0; i < lines_rid.size(); i++) { + TS->shaped_text_fit_to_width(lines_rid.write[i], width); + } + } + lines_dirty = false; + } + + _update_visible(); + + if (!autowrap || !clip) { + minimum_size_changed(); + } +} + +void Label::_update_visible() { + int line_spacing = get_theme_constant("line_spacing", "Label"); + Ref<StyleBox> style = get_theme_stylebox("normal", "Label"); + Ref<Font> font = get_theme_font("font"); + int lines_visible = lines_rid.size(); + + if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { + lines_visible = max_lines_visible; + } + + minsize.height = 0; + int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); + for (int64_t i = lines_skipped; i < last_line; i++) { + minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing; + if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) { + break; + } + } } void Label::_notification(int p_what) { @@ -73,8 +163,8 @@ void Label::_notification(int p_what) { return; //nothing new } xl_text = new_text; + dirty = true; - regenerate_word_cache(); update(); } @@ -83,8 +173,8 @@ void Label::_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); } - if (word_cache_dirty) { - regenerate_word_cache(); + if (dirty || lines_dirty) { + _shape(); } RID ci = get_canvas_item(); @@ -95,51 +185,59 @@ void Label::_notification(int p_what) { Ref<Font> font = get_theme_font("font"); Color font_color = get_theme_color("font_color"); Color font_color_shadow = get_theme_color("font_color_shadow"); - bool use_outline = get_theme_constant("shadow_as_outline"); Point2 shadow_ofs(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y")); int line_spacing = get_theme_constant("line_spacing"); Color font_outline_modulate = get_theme_color("font_outline_modulate"); + int outline_size = get_theme_constant("outline_size"); + int shadow_outline_size = get_theme_constant("shadow_outline_size"); + bool rtl = is_layout_rtl(); style->draw(ci, Rect2(Point2(0, 0), get_size())); - RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(get_canvas_item(), font.is_valid() && font->is_distance_field_hint()); - - int font_h = font->get_height() + line_spacing; - - int lines_visible = (size.y + line_spacing) / font_h; + float total_h = 0; + int lines_visible = 0; - real_t space_w = font->get_char_size(' ').width; - int chars_total = 0; - - int vbegin = 0, vsep = 0; - - if (lines_visible > line_count) { - lines_visible = line_count; + // Get number of lines to fit to the height. + for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing; + if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { + break; + } + lines_visible++; } if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { lines_visible = max_lines_visible; } + int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); + + // Get real total height. + total_h = 0; + for (int64_t i = lines_skipped; i < last_line; i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing; + } + + int vbegin = 0, vsep = 0; if (lines_visible > 0) { switch (valign) { case VALIGN_TOP: { //nothing } break; case VALIGN_CENTER: { - vbegin = (size.y - (lines_visible * font_h - line_spacing)) / 2; + vbegin = (size.y - (total_h - line_spacing)) / 2; vsep = 0; } break; case VALIGN_BOTTOM: { - vbegin = size.y - (lines_visible * font_h - line_spacing); + vbegin = size.y - (total_h - line_spacing); vsep = 0; } break; case VALIGN_FILL: { vbegin = 0; if (lines_visible > 1) { - vsep = (size.y - (lines_visible * font_h - line_spacing)) / (lines_visible - 1); + vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1); } else { vsep = 0; } @@ -148,138 +246,113 @@ void Label::_notification(int p_what) { } } - WordCache *wc = word_cache; - if (!wc) { - return; - } - - int line = 0; - int line_to = lines_skipped + (lines_visible > 0 ? lines_visible : 1); - FontDrawer drawer(font, font_outline_modulate); - while (wc) { - /* handle lines not meant to be drawn quickly */ - if (line >= line_to) { - break; - } - if (line < lines_skipped) { - while (wc && wc->char_pos >= 0) { - wc = wc->next; - } - if (wc) { - wc = wc->next; - } - line++; - continue; - } - - /* handle lines normally */ - - if (wc->char_pos < 0) { - //empty line - wc = wc->next; - line++; - continue; - } - - WordCache *from = wc; - WordCache *to = wc; - - int taken = 0; - int spaces = 0; - while (to && to->char_pos >= 0) { - taken += to->pixel_width; - if (to->space_count) { - spaces += to->space_count; + int visible_glyphs = -1; + int glyhps_drawn = 0; + if (percent_visible < 1) { + int total_glyphs = 0; + for (int i = lines_skipped; i < last_line; i++) { + const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(lines_rid[i]); + const TextServer::Glyph *glyphs = visual.ptr(); + int gl_size = visual.size(); + for (int j = 0; j < gl_size; j++) { + if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + total_glyphs++; + } } - to = to->next; } + visible_glyphs = total_glyphs * percent_visible; + } - bool can_fill = to && to->char_pos == WordCache::CHAR_WRAPLINE; - - float x_ofs = 0; - + Vector2 ofs; + ofs.y = style->get_offset().y + vbegin; + for (int i = lines_skipped; i < last_line; i++) { + ofs.x = 0; + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(Font::SPACING_TOP); switch (align) { case ALIGN_FILL: case ALIGN_LEFT: { - x_ofs = style->get_offset().x; + if (rtl) { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - TS->shaped_text_get_size(lines_rid[i]).x); + } else { + ofs.x = style->get_offset().x; + } } break; case ALIGN_CENTER: { - x_ofs = int(size.width - (taken + spaces * space_w)) / 2; + ofs.x = int(size.width - TS->shaped_text_get_size(lines_rid[i]).x) / 2; } break; case ALIGN_RIGHT: { - x_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (taken + spaces * space_w)); + if (rtl) { + ofs.x = style->get_offset().x; + } else { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - TS->shaped_text_get_size(lines_rid[i]).x); + } } break; } - float y_ofs = style->get_offset().y; - y_ofs += (line - lines_skipped) * font_h + font->get_ascent(); - y_ofs += vbegin + line * vsep; - - while (from != to) { - // draw a word - int pos = from->char_pos; - if (from->char_pos < 0) { - ERR_PRINT("BUG"); - return; - } - if (from->space_count) { - /* spacing */ - x_ofs += space_w * from->space_count; - if (can_fill && align == ALIGN_FILL && spaces) { - x_ofs += int((size.width - (taken + space_w * spaces)) / spaces); + const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(lines_rid[i]); + const TextServer::Glyph *glyphs = visual.ptr(); + int gl_size = visual.size(); + + float x = ofs.x; + int outlines_drawn = glyhps_drawn; + for (int j = 0; j < gl_size; j++) { + for (int k = 0; k < glyphs[j].repeat; k++) { + if (glyphs[j].font_rid != RID()) { + if (font_color_shadow.a > 0) { + TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + shadow_ofs, glyphs[j].index, font_color_shadow); + if (shadow_outline_size > 0) { + //draw shadow + TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, shadow_outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + Vector2(-shadow_ofs.x, shadow_ofs.y), glyphs[j].index, font_color_shadow); + TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, shadow_outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + Vector2(shadow_ofs.x, -shadow_ofs.y), glyphs[j].index, font_color_shadow); + TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, shadow_outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + Vector2(-shadow_ofs.x, -shadow_ofs.y), glyphs[j].index, font_color_shadow); + } + } + if (font_outline_modulate.a != 0.0 && outline_size > 0) { + TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off), glyphs[j].index, font_outline_modulate); + } } + ofs.x += glyphs[j].advance; } - - if (font_color_shadow.a > 0) { - int chars_total_shadow = chars_total; //save chars drawn - float x_ofs_shadow = x_ofs; - for (int i = 0; i < from->word_len; i++) { - if (visible_chars < 0 || chars_total_shadow < visible_chars) { - char32_t c = xl_text[i + pos]; - char32_t n = xl_text[i + pos + 1]; - if (uppercase) { - c = String::char_uppercase(c); - n = String::char_uppercase(n); - } - - float move = drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + shadow_ofs, c, n, font_color_shadow); - if (use_outline) { - drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, shadow_ofs.y), c, n, font_color_shadow); - drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow); - drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow); - } - x_ofs_shadow += move; - chars_total_shadow++; + if (visible_glyphs != -1) { + if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + outlines_drawn++; + if (outlines_drawn >= visible_glyphs) { + break; } } } - for (int i = 0; i < from->word_len; i++) { - if (visible_chars < 0 || chars_total < visible_chars) { - char32_t c = xl_text[i + pos]; - char32_t n = xl_text[i + pos + 1]; - if (uppercase) { - c = String::char_uppercase(c); - n = String::char_uppercase(n); + } + ofs.x = x; + + for (int j = 0; j < gl_size; j++) { + for (int k = 0; k < glyphs[j].repeat; k++) { + if (glyphs[j].font_rid != RID()) { + TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off), glyphs[j].index, font_color); + } else if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + TS->draw_hex_code_box(ci, glyphs[j].font_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off), glyphs[j].index, font_color); + } + ofs.x += glyphs[j].advance; + } + if (visible_glyphs != -1) { + if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + glyhps_drawn++; + if (glyhps_drawn >= visible_glyphs) { + return; } - - x_ofs += drawer.draw_char(ci, Point2(x_ofs, y_ofs), c, n, font_color); - chars_total++; } } - from = from->next; } - wc = to ? to->next : nullptr; - line++; + ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(Font::SPACING_BOTTOM); } } if (p_what == NOTIFICATION_THEME_CHANGED) { - word_cache_dirty = true; + dirty = true; update(); } if (p_what == NOTIFICATION_RESIZED) { - word_cache_dirty = true; + lines_dirty = true; } } @@ -287,8 +360,8 @@ Size2 Label::get_minimum_size() const { Size2 min_style = get_theme_stylebox("normal")->get_minimum_size(); // don't want to mutable everything - if (word_cache_dirty) { - const_cast<Label *>(this)->regenerate_word_cache(); + if (dirty || lines_dirty) { + const_cast<Label *>(this)->_shape(); } if (autowrap) { @@ -302,230 +375,50 @@ Size2 Label::get_minimum_size() const { } } -int Label::get_longest_line_width() const { - Ref<Font> font = get_theme_font("font"); - real_t max_line_width = 0; - real_t line_width = 0; - - for (int i = 0; i < xl_text.size(); i++) { - char32_t current = xl_text[i]; - if (uppercase) { - current = String::char_uppercase(current); - } - - if (current < 32) { - if (current == '\n') { - if (line_width > max_line_width) { - max_line_width = line_width; - } - line_width = 0; - } - } else { - real_t char_width = font->get_char_size(current, xl_text[i + 1]).width; - line_width += char_width; - } - } - - if (line_width > max_line_width) { - max_line_width = line_width; - } - - // ceiling to ensure autowrapping does not cut text - return Math::ceil(max_line_width); -} - int Label::get_line_count() const { if (!is_inside_tree()) { return 1; } - if (word_cache_dirty) { - const_cast<Label *>(this)->regenerate_word_cache(); + if (dirty || lines_dirty) { + const_cast<Label *>(this)->_shape(); } - return line_count; + return lines_rid.size(); } int Label::get_visible_line_count() const { - int line_spacing = get_theme_constant("line_spacing"); - int font_h = get_theme_font("font")->get_height() + line_spacing; - int lines_visible = (get_size().height - get_theme_stylebox("normal")->get_minimum_size().height + line_spacing) / font_h; - - if (lines_visible > line_count) { - lines_visible = line_count; - } - - if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { - lines_visible = max_lines_visible; - } - - return lines_visible; -} - -void Label::regenerate_word_cache() { - while (word_cache) { - WordCache *current = word_cache; - word_cache = current->next; - memdelete(current); - } - - int width; - if (autowrap) { - Ref<StyleBox> style = get_theme_stylebox("normal"); - width = MAX(get_size().width, get_custom_minimum_size().width) - style->get_minimum_size().width; - } else { - width = get_longest_line_width(); - } - Ref<Font> font = get_theme_font("font"); - - real_t current_word_size = 0; - int word_pos = 0; - real_t line_width = 0; - int space_count = 0; - real_t space_width = font->get_char_size(' ').width; + Ref<StyleBox> style = get_theme_stylebox("normal"); int line_spacing = get_theme_constant("line_spacing"); - line_count = 1; - total_char_cache = 0; - - WordCache *last = nullptr; - - for (int i = 0; i <= xl_text.length(); i++) { - char32_t current = i < xl_text.length() ? xl_text[i] : L' '; //always a space at the end, so the algo works - - if (uppercase) { - current = String::char_uppercase(current); - } - - // ranges taken from http://www.unicodemap.org/ - // if your language is not well supported, consider helping improve - // the unicode support in Godot. - bool separatable = (current >= 0x2E08 && current <= 0xFAFF) || (current >= 0xFE30 && current <= 0xFE4F); - //current>=33 && (current < 65||current >90) && (current<97||current>122) && (current<48||current>57); - bool insert_newline = false; - real_t char_width = 0; - - if (current < 33) { - if (current_word_size > 0) { - WordCache *wc = memnew(WordCache); - if (word_cache) { - last->next = wc; - } else { - word_cache = wc; - } - last = wc; - - wc->pixel_width = current_word_size; - wc->char_pos = word_pos; - wc->word_len = i - word_pos; - wc->space_count = space_count; - current_word_size = 0; - space_count = 0; - } else if ((i == xl_text.length() || current == '\n') && last != nullptr && space_count != 0) { - //in case there are trailing white spaces we add a placeholder word cache with just the spaces - WordCache *wc = memnew(WordCache); - if (word_cache) { - last->next = wc; - } else { - word_cache = wc; - } - last = wc; - - wc->pixel_width = 0; - wc->char_pos = 0; - wc->word_len = 0; - wc->space_count = space_count; - current_word_size = 0; - space_count = 0; - } - - if (current == '\n') { - insert_newline = true; - } else if (current != ' ') { - total_char_cache++; - } - - if (i < xl_text.length() && xl_text[i] == ' ') { - if (line_width > 0 || last == nullptr || last->char_pos != WordCache::CHAR_WRAPLINE) { - space_count++; - line_width += space_width; - } else { - space_count = 0; - } - } - - } else { - // latin characters - if (current_word_size == 0) { - word_pos = i; - } - char_width = font->get_char_size(current, xl_text[i + 1]).width; - current_word_size += char_width; - line_width += char_width; - total_char_cache++; - - // allow autowrap to cut words when they exceed line width - if (autowrap && (current_word_size > width)) { - separatable = true; - } - } - - if ((autowrap && (line_width >= width) && ((last && last->char_pos >= 0) || separatable)) || insert_newline) { - if (separatable) { - if (current_word_size > 0) { - WordCache *wc = memnew(WordCache); - if (word_cache) { - last->next = wc; - } else { - word_cache = wc; - } - last = wc; - - wc->pixel_width = current_word_size - char_width; - wc->char_pos = word_pos; - wc->word_len = i - word_pos; - wc->space_count = space_count; - current_word_size = char_width; - word_pos = i; - } - } - - WordCache *wc = memnew(WordCache); - if (word_cache) { - last->next = wc; - } else { - word_cache = wc; - } - last = wc; - - wc->pixel_width = 0; - wc->char_pos = insert_newline ? WordCache::CHAR_NEWLINE : WordCache::CHAR_WRAPLINE; - - line_width = current_word_size; - line_count++; - space_count = 0; + int lines_visible = 0; + float total_h = 0; + for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing; + if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { + break; } + lines_visible++; } - if (!autowrap) { - minsize.width = width; + if (lines_visible > lines_rid.size()) { + lines_visible = lines_rid.size(); } - if (max_lines_visible > 0 && line_count > max_lines_visible) { - minsize.height = (font->get_height() * max_lines_visible) + (line_spacing * (max_lines_visible - 1)); - } else { - minsize.height = (font->get_height() * line_count) + (line_spacing * (line_count - 1)); + if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { + lines_visible = max_lines_visible; } - if (!autowrap || !clip) { - //helps speed up some labels that may change a lot, as no resizing is requested. Do not change. - minimum_size_changed(); - } - word_cache_dirty = false; + return lines_visible; } void Label::set_align(Align p_align) { ERR_FAIL_INDEX((int)p_align, 4); - align = p_align; + if (align != p_align) { + if (align == ALIGN_FILL || p_align == ALIGN_FILL) { + lines_dirty = true; // Reshape lines. + } + align = p_align; + } update(); } @@ -549,13 +442,83 @@ void Label::set_text(const String &p_string) { } text = p_string; xl_text = tr(p_string); - word_cache_dirty = true; + dirty = true; if (percent_visible < 1) { visible_chars = get_total_character_count() * percent_visible; } update(); } +void Label::set_text_direction(Control::TextDirection p_text_direction) { + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (text_direction != p_text_direction) { + text_direction = p_text_direction; + dirty = true; + update(); + } +} + +void Label::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { + if (st_parser != p_parser) { + st_parser = p_parser; + dirty = true; + update(); + } +} + +Control::StructuredTextParser Label::get_structured_text_bidi_override() const { + return st_parser; +} + +void Label::set_structured_text_bidi_override_options(Array p_args) { + st_args = p_args; + dirty = true; + update(); +} + +Array Label::get_structured_text_bidi_override_options() const { + return st_args; +} + +Control::TextDirection Label::get_text_direction() const { + return text_direction; +} + +void Label::clear_opentype_features() { + opentype_features.clear(); + dirty = true; + update(); +} + +void Label::set_opentype_feature(const String &p_name, int p_value) { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { + opentype_features[tag] = p_value; + dirty = true; + update(); + } +} + +int Label::get_opentype_feature(const String &p_name) const { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag)) { + return -1; + } + return opentype_features[tag]; +} + +void Label::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + dirty = true; + update(); + } +} + +String Label::get_language() const { + return language; +} + void Label::set_clip_text(bool p_clip) { clip = p_clip; update(); @@ -573,7 +536,7 @@ String Label::get_text() const { void Label::set_visible_characters(int p_amount) { visible_chars = p_amount; if (get_total_character_count() > 0) { - percent_visible = (float)p_amount / (float)total_char_cache; + percent_visible = (float)p_amount / (float)get_total_character_count(); } _change_notify("percent_visible"); update(); @@ -602,6 +565,7 @@ float Label::get_percent_visible() const { void Label::set_lines_skipped(int p_lines) { lines_skipped = p_lines; + _update_visible(); update(); } @@ -611,6 +575,7 @@ int Label::get_lines_skipped() const { void Label::set_max_lines_visible(int p_lines) { max_lines_visible = p_lines; + _update_visible(); update(); } @@ -619,11 +584,61 @@ int Label::get_max_lines_visible() const { } int Label::get_total_character_count() const { - if (word_cache_dirty) { - const_cast<Label *>(this)->regenerate_word_cache(); + if (dirty || lines_dirty) { + const_cast<Label *>(this)->_shape(); + } + + return xl_text.length(); +} + +bool Label::_set(const StringName &p_name, const Variant &p_value) { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + double value = p_value; + if (value == -1) { + if (opentype_features.has(tag)) { + opentype_features.erase(tag); + dirty = true; + update(); + } + } else { + if ((double)opentype_features[tag] != value) { + opentype_features[tag] = value; + dirty = true; + update(); + } + } + _change_notify(); + return true; } - return total_char_cache; + return false; +} + +bool Label::_get(const StringName &p_name, Variant &r_ret) const { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + if (opentype_features.has(tag)) { + r_ret = opentype_features[tag]; + return true; + } else { + r_ret = -1; + return true; + } + } + return false; +} + +void Label::_get_property_list(List<PropertyInfo> *p_list) const { + for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { + String name = TS->tag_to_name(*ftr); + p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name)); + } + p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); } void Label::_bind_methods() { @@ -633,13 +648,20 @@ void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("get_valign"), &Label::get_valign); ClassDB::bind_method(D_METHOD("set_text", "text"), &Label::set_text); ClassDB::bind_method(D_METHOD("get_text"), &Label::get_text); + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &Label::get_text_direction); + ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &Label::set_opentype_feature); + ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &Label::get_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_opentype_features"), &Label::clear_opentype_features); + ClassDB::bind_method(D_METHOD("set_language", "language"), &Label::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &Label::get_language); ClassDB::bind_method(D_METHOD("set_autowrap", "enable"), &Label::set_autowrap); ClassDB::bind_method(D_METHOD("has_autowrap"), &Label::has_autowrap); ClassDB::bind_method(D_METHOD("set_clip_text", "enable"), &Label::set_clip_text); ClassDB::bind_method(D_METHOD("is_clipping_text"), &Label::is_clipping_text); ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &Label::set_uppercase); ClassDB::bind_method(D_METHOD("is_uppercase"), &Label::is_uppercase); - ClassDB::bind_method(D_METHOD("get_line_height"), &Label::get_line_height); + ClassDB::bind_method(D_METHOD("get_line_height", "line"), &Label::get_line_height, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_line_count"), &Label::get_line_count); ClassDB::bind_method(D_METHOD("get_visible_line_count"), &Label::get_visible_line_count); ClassDB::bind_method(D_METHOD("get_total_character_count"), &Label::get_total_character_count); @@ -651,6 +673,10 @@ void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("get_lines_skipped"), &Label::get_lines_skipped); ClassDB::bind_method(D_METHOD("set_max_lines_visible", "lines_visible"), &Label::set_max_lines_visible); ClassDB::bind_method(D_METHOD("get_max_lines_visible"), &Label::get_max_lines_visible); + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &Label::set_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &Label::get_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label::set_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label::get_structured_text_bidi_override_options); BIND_ENUM_CONSTANT(ALIGN_LEFT); BIND_ENUM_CONSTANT(ALIGN_CENTER); @@ -663,6 +689,8 @@ void Label::_bind_methods() { BIND_ENUM_CONSTANT(VALIGN_FILL); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align"); ADD_PROPERTY(PropertyInfo(Variant::INT, "valign", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_valign", "get_valign"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autowrap"), "set_autowrap", "has_autowrap"); @@ -672,18 +700,23 @@ void Label::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible"); + ADD_GROUP("Structured Text", "structured_text_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); } Label::Label(const String &p_text) { + text_rid = TS->create_shaped_text(); + set_mouse_filter(MOUSE_FILTER_IGNORE); set_text(p_text); set_v_size_flags(SIZE_SHRINK_CENTER); } Label::~Label() { - while (word_cache) { - WordCache *current = word_cache; - word_cache = current->next; - memdelete(current); + for (int i = 0; i < lines_rid.size(); i++) { + TS->free(lines_rid[i]); } + lines_rid.clear(); + TS->free(text_rid); } diff --git a/scene/gui/label.h b/scene/gui/label.h index df78a1b34c..219c6af39e 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -59,39 +59,37 @@ private: bool autowrap = false; bool clip = false; Size2 minsize; - int line_count = 0; bool uppercase = false; - int get_longest_line_width() const; - - struct WordCache { - enum { - CHAR_NEWLINE = -1, - CHAR_WRAPLINE = -2 - }; - int char_pos = 0; // if -1, then newline - int word_len = 0; - int pixel_width = 0; - int space_count = 0; - WordCache *next = nullptr; - }; + bool lines_dirty = true; + bool dirty = true; + RID text_rid; + Vector<RID> lines_rid; - bool word_cache_dirty = true; - void regenerate_word_cache(); + Dictionary opentype_features; + String language; + TextDirection text_direction = TEXT_DIRECTION_AUTO; + Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + Array st_args; float percent_visible = 1; - WordCache *word_cache = nullptr; - int total_char_cache = 0; int visible_chars = -1; int lines_skipped = 0; int max_lines_visible = -1; + void _update_visible(); + void _shape(); + protected: void _notification(int p_what); static void _bind_methods(); - // bind helpers + + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + public: virtual Size2 get_minimum_size() const override; @@ -104,6 +102,22 @@ public: void set_text(const String &p_string); String get_text() const; + void set_text_direction(TextDirection p_text_direction); + TextDirection get_text_direction() const; + + void set_opentype_feature(const String &p_name, int p_value); + int get_opentype_feature(const String &p_name) const; + void clear_opentype_features(); + + void set_language(const String &p_language); + String get_language() const; + + void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); + Control::StructuredTextParser get_structured_text_bidi_override() const; + + void set_structured_text_bidi_override_options(Array p_args); + Array get_structured_text_bidi_override_options() const; + void set_autowrap(bool p_autowrap); bool has_autowrap() const; @@ -126,7 +140,7 @@ public: void set_max_lines_visible(int p_lines); int get_max_lines_visible() const; - int get_line_height() const; + int get_line_height(int p_line = -1) const; int get_line_count() const; int get_visible_line_count() const; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 857c96bea3..5f0bb453f3 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,19 +37,21 @@ #include "core/string/translation.h" #include "label.h" #include "servers/display_server.h" +#include "servers/text_server.h" #ifdef TOOLS_ENABLED #include "editor/editor_scale.h" #include "editor/editor_settings.h" #endif #include "scene/main/window.h" -static bool _is_text_char(char32_t c) { - return !is_symbol(c); -} void LineEdit::_gui_input(Ref<InputEvent> p_event) { Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { + if (ime_text.length() != 0) { + // Ignore mouse clicks in IME input mode. + return; + } if (b->is_pressed() && b->get_button_index() == BUTTON_RIGHT && context_menu_enabled) { menu->set_position(get_screen_transform().xform(get_local_mouse_position())); menu->set_size(Vector2(1, 1)); @@ -67,7 +69,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { _reset_caret_blink_timer(); if (b->is_pressed()) { accept_event(); //don't pass event further when clicked on text field - if (!text.empty() && is_editable() && _is_over_clear_button(b->get_position())) { + if (!text.is_empty() && is_editable() && _is_over_clear_button(b->get_position())) { clear_button_status.press_attempt = true; clear_button_status.pressing_inside = true; update(); @@ -104,7 +106,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { update(); } else { - if (!text.empty() && is_editable() && clear_button_enabled) { + if (!text.is_empty() && is_editable() && clear_button_enabled) { bool press_attempt = clear_button_status.press_attempt; clear_button_status.press_attempt = false; if (press_attempt && clear_button_status.pressing_inside && _is_over_clear_button(b->get_position())) { @@ -134,7 +136,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { Ref<InputEventMouseMotion> m = p_event; if (m.is_valid()) { - if (!text.empty() && is_editable() && clear_button_enabled) { + if (!text.is_empty() && is_editable() && clear_button_enabled) { bool last_press_inside = clear_button_status.pressing_inside; clear_button_status.pressing_inside = clear_button_status.press_attempt && _is_over_clear_button(m->get_position()); if (last_press_inside != clear_button_status.pressing_inside) { @@ -200,6 +202,18 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { bool handled = true; switch (code) { + case (KEY_QUOTELEFT): { // Swap current input direction (primary cursor) + + if (input_direction == TEXT_DIRECTION_LTR) { + input_direction = TEXT_DIRECTION_RTL; + } else { + input_direction = TEXT_DIRECTION_LTR; + } + set_cursor_position(get_cursor_position()); + update(); + + } break; + case (KEY_X): { // CUT. if (editable) { @@ -214,6 +228,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } break; + case (KEY_Y): // PASTE (Yank for unix users). case (KEY_V): { // PASTE. if (editable) { @@ -237,20 +252,13 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (editable) { deselect(); text = text.substr(cursor_pos, text.length() - cursor_pos); - update_cached_width(); + _shape(); set_cursor_position(0); _text_changed(); } } break; - case (KEY_Y): { // PASTE (Yank for unix users). - - if (editable) { - paste_text(); - } - - } break; case (KEY_K): { // Delete from cursor_pos to end. if (editable) { @@ -335,17 +343,13 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } else if (k->get_command()) { #endif int cc = cursor_pos; - bool prev_char = false; - - while (cc > 0) { - bool ischar = _is_text_char(text[cc - 1]); - if (prev_char && !ischar) { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = words.size() - 1; i >= 0; i--) { + if (words[i].x < cc) { + cc = words[i].x; break; } - - prev_char = ischar; - cc--; } delete_text(cc, cursor_pos); @@ -390,24 +394,24 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { break; } else if (k->get_command()) { #endif - bool prev_char = false; int cc = cursor_pos; - while (cc > 0) { - bool ischar = _is_text_char(text[cc - 1]); - - if (prev_char && !ischar) { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = words.size() - 1; i >= 0; i--) { + if (words[i].x < cc) { + cc = words[i].x; break; } - - prev_char = ischar; - cc--; } set_cursor_position(cc); } else { - set_cursor_position(get_cursor_position() - 1); + if (mid_grapheme_caret_enabled) { + set_cursor_position(get_cursor_position() - 1); + } else { + set_cursor_position(TS->shaped_text_prev_grapheme_pos(text_rid, get_cursor_position())); + } } shift_selection_check_post(k->get_shift()); @@ -446,24 +450,24 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { break; } else if (k->get_command()) { #endif - bool prev_char = false; int cc = cursor_pos; - while (cc < text.length()) { - bool ischar = _is_text_char(text[cc]); - - if (prev_char && !ischar) { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = 0; i < words.size(); i++) { + if (words[i].y > cc) { + cc = words[i].y; break; } - - prev_char = ischar; - cc++; } set_cursor_position(cc); } else { - set_cursor_position(get_cursor_position() + 1); + if (mid_grapheme_caret_enabled) { + set_cursor_position(get_cursor_position() + 1); + } else { + set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, get_cursor_position())); + } } shift_selection_check_post(k->get_shift()); @@ -516,23 +520,25 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { #endif int cc = cursor_pos; - bool prev_char = false; - - while (cc < text.length()) { - bool ischar = _is_text_char(text[cc]); - - if (prev_char && !ischar) { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = 0; i < words.size(); i++) { + if (words[i].y > cc) { + cc = words[i].y; break; } - prev_char = ischar; - cc++; } delete_text(cursor_pos, cc); } else { - set_cursor_position(cursor_pos + 1); - delete_char(); + if (mid_grapheme_caret_enabled) { + set_cursor_position(cursor_pos + 1); + delete_char(); + } else { + int cc = cursor_pos; + set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, cursor_pos)); + delete_text(cc, cursor_pos); + } } } break; @@ -562,10 +568,10 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } break; case KEY_MENU: { if (context_menu_enabled) { - Point2 pos = Point2(get_cursor_pixel_pos(), (get_size().y + get_theme_font("font")->get_height()) / 2); + Point2 pos = Point2(get_cursor_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2); menu->set_position(get_global_transform().xform(pos)); menu->set_size(Vector2(1, 1)); - // menu->set_scale(get_global_transform().get_scale()); + //menu->set_scale(get_global_transform().get_scale()); menu->popup(); menu->grab_focus(); } @@ -578,7 +584,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (handled) { accept_event(); - } else if (!k->get_command() || (k->get_command() && k->get_alt())) { + } else if (!k->get_command()) { if (k->get_unicode() >= 32 && k->get_keycode() != KEY_DELETE) { if (editable) { selection_delete(); @@ -605,7 +611,10 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { void LineEdit::set_align(Align p_align) { ERR_FAIL_INDEX((int)p_align, 4); - align = p_align; + if (align != p_align) { + align = p_align; + _shape(); + } update(); } @@ -634,14 +643,8 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) { set_cursor_at_pixel_pos(p_point.x); int selected = selection.end - selection.begin; - Ref<Font> font = get_theme_font("font"); - if (font != nullptr) { - for (int i = selection.begin; i < selection.end; i++) { - cached_width -= font->get_char_size(pass ? secret_character[0] : text[i]).width; - } - } - text.erase(selection.begin, selected); + _shape(); append_at_cursor(p_data); selection.begin = cursor_pos - selected; @@ -650,7 +653,7 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) { } Control::CursorShape LineEdit::get_cursor_shape(const Point2 &p_pos) const { - if (!text.empty() && is_editable() && _is_over_clear_button(p_pos)) { + if (!text.is_empty() && is_editable() && _is_over_clear_button(p_pos)) { return CURSOR_ARROW; } return Control::get_cursor_shape(p_pos); @@ -680,13 +683,18 @@ void LineEdit::_notification(int p_what) { } break; #endif case NOTIFICATION_RESIZED: { + _fit_to_width(); scroll_offset = 0; set_cursor_position(get_cursor_position()); - + } break; + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_THEME_CHANGED: { + _shape(); + update(); } break; case NOTIFICATION_TRANSLATION_CHANGED: { placeholder_translated = tr(placeholder); - update_placeholder_width(); + _shape(); update(); } break; case NOTIFICATION_WM_WINDOW_FOCUS_IN: { @@ -705,6 +713,7 @@ void LineEdit::_notification(int p_what) { } int width, height; + bool rtl = is_layout_rtl(); Size2 size = get_size(); width = size.width; @@ -717,7 +726,6 @@ void LineEdit::_notification(int p_what) { style = get_theme_stylebox("read_only"); draw_caret = false; } - Ref<Font> font = get_theme_font("font"); style->draw(ci, Rect2(Point2(), size)); @@ -727,40 +735,45 @@ void LineEdit::_notification(int p_what) { } int x_ofs = 0; - bool using_placeholder = text.empty() && ime_text.empty(); - int cached_text_width = using_placeholder ? cached_placeholder_width : cached_width; + bool using_placeholder = text.is_empty() && ime_text.is_empty(); + float text_width = TS->shaped_text_get_size(text_rid).x; + float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM); switch (align) { case ALIGN_FILL: case ALIGN_LEFT: { - x_ofs = style->get_offset().x; + if (rtl) { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - style->get_margin(SIDE_RIGHT) - (text_width))); + } else { + x_ofs = style->get_offset().x; + } } break; case ALIGN_CENTER: { if (scroll_offset != 0) { x_ofs = style->get_offset().x; } else { - x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - (cached_text_width)) / 2); + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - (text_width)) / 2); } } break; case ALIGN_RIGHT: { - x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_text_width))); + if (rtl) { + x_ofs = style->get_offset().x; + } else { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - style->get_margin(SIDE_RIGHT) - (text_width))); + } } break; } - int ofs_max = width - style->get_margin(MARGIN_RIGHT); - int char_ofs = scroll_offset; + int ofs_max = width - style->get_margin(SIDE_RIGHT); int y_area = height - style->get_minimum_size().height; - int y_ofs = style->get_offset().y + (y_area - font->get_height()) / 2; - - int font_ascent = font->get_ascent(); + int y_ofs = style->get_offset().y + (y_area - text_height) / 2; Color selection_color = get_theme_color("selection_color"); Color font_color = is_editable() ? get_theme_color("font_color") : get_theme_color("font_color_uneditable"); Color font_color_selected = get_theme_color("font_color_selected"); Color cursor_color = get_theme_color("cursor_color"); - const String &t = using_placeholder ? placeholder_translated : text; // Draw placeholder color. if (using_placeholder) { font_color.a *= placeholder_alpha; @@ -778,150 +791,148 @@ void LineEdit::_notification(int p_what) { } } - r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon); + r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(SIDE_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon); if (align == ALIGN_CENTER) { if (scroll_offset == 0) { - x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - cached_text_width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT) * 2) / 2); + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); } } else { - x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_RIGHT)); + x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT)); } ofs_max -= r_icon->get_width(); } - int caret_height = font->get_height() > y_area ? y_area : font->get_height(); - FontDrawer drawer(font, Color(1, 1, 1)); - while (true) { - // End of string, break. - if (char_ofs >= t.length()) { - break; - } - - if (char_ofs == cursor_pos) { - if (ime_text.length() > 0) { - int ofs = 0; - while (true) { - if (ofs >= ime_text.length()) { - break; - } - - char32_t cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs]; - char32_t next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1]; - int im_char_width = font->get_char_size(cchar, next).width; - - if ((x_ofs + im_char_width) > ofs_max) { - break; - } - - bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y; - if (selected) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 3)), font_color); - } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color); - } - - drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color); +#ifdef TOOLS_ENABLED + int caret_width = Math::round(EDSCALE); +#else + int caret_width = 1; +#endif - x_ofs += im_char_width; - ofs++; + // Draw selections rects. + Vector2 ofs = Point2(x_ofs + scroll_offset, y_ofs); + if (selection.enabled) { + Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, selection.begin, selection.end); + for (int i = 0; i < sel.size(); i++) { + Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height); + if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) { + continue; + } + if (rect.position.x < x_ofs) { + rect.size.x -= (x_ofs - rect.position.x); + rect.position.x = x_ofs; + } else if (rect.position.x + rect.size.x > ofs_max) { + rect.size.x = ofs_max - rect.position.x; + } + RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_color); + } + } + const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(text_rid); + const TextServer::Glyph *glyphs = visual.ptr(); + int gl_size = visual.size(); + + // Draw text. + ofs.y += TS->shaped_text_get_ascent(text_rid); + for (int i = 0; i < gl_size; i++) { + bool selected = selection.enabled && glyphs[i].start >= selection.begin && glyphs[i].end <= selection.end; + for (int j = 0; j < glyphs[i].repeat; j++) { + if (ceil(ofs.x) >= x_ofs && (ofs.x + glyphs[i].advance) <= ofs_max) { + if (glyphs[i].font_rid != RID()) { + TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_color_selected : font_color); + } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + TS->draw_hex_code_box(ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_color_selected : font_color); } } + ofs.x += glyphs[i].advance; } - - char32_t cchar = (pass && !text.empty()) ? secret_character[0] : t[char_ofs]; - char32_t next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1]; - int char_width = font->get_char_size(cchar, next).width; - - // End of widget, break. - if ((x_ofs + char_width) > ofs_max) { + if (ofs.x >= ofs_max) { break; } - - bool selected = selection.enabled && char_ofs >= selection.begin && char_ofs < selection.end; - - if (selected) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(char_width, caret_height)), selection_color); - } - - int yofs = y_ofs + (caret_height - font->get_height()) / 2; - drawer.draw_char(ci, Point2(x_ofs, yofs + font_ascent), cchar, next, selected ? font_color_selected : font_color); - - if (char_ofs == cursor_pos && draw_caret && !using_placeholder) { - if (ime_text.length() == 0) { -#ifdef TOOLS_ENABLED - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color); -#else - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(1, caret_height)), cursor_color); -#endif - } - } - - x_ofs += char_width; - char_ofs++; } - if (char_ofs == cursor_pos) { - if (ime_text.length() > 0) { - int ofs = 0; - while (true) { - if (ofs >= ime_text.length()) { - break; + // Draw carets. + ofs.x = x_ofs + scroll_offset; + if (draw_caret) { + if (ime_text.length() == 0) { + // Normal caret. + Rect2 l_caret, t_caret; + TextServer::Direction l_dir, t_dir; + TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir); + + if (l_caret == Rect2() && t_caret == Rect2()) { + // No carets, add one at the start. + int h = get_theme_font("font")->get_height(get_theme_font_size("font_size")); + int y = style->get_offset().y + (y_area - h) / 2; + if (rtl) { + l_dir = TextServer::DIRECTION_RTL; + l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h)); + } else { + l_dir = TextServer::DIRECTION_LTR; + l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h)); } - - char32_t cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs]; - char32_t next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1]; - int im_char_width = font->get_char_size(cchar, next).width; - - if ((x_ofs + im_char_width) > ofs_max) { - break; + RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color); + } else { + if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) { + // Draw extra marker on top of mid caret. + Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width); + trect.position += ofs; + RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, cursor_color); } - bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y; - if (selected) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 3)), font_color); - } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color); - } + l_caret.position += ofs; + l_caret.size.x = caret_width; + RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color); - drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color); + t_caret.position += ofs; + t_caret.size.x = caret_width; - x_ofs += im_char_width; - ofs++; + RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, cursor_color); } - } - } - - if ((char_ofs == cursor_pos || using_placeholder) && draw_caret) { // May be at the end, or placeholder. - if (ime_text.length() == 0) { - int caret_x_ofs = x_ofs; - if (using_placeholder) { - switch (align) { - case ALIGN_LEFT: - case ALIGN_FILL: { - caret_x_ofs = style->get_offset().x; - } break; - case ALIGN_CENTER: { - caret_x_ofs = ofs_max / 2; - } break; - case ALIGN_RIGHT: { - caret_x_ofs = ofs_max; - } break; + } else { + { + // IME intermidiet text range. + Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos, cursor_pos + ime_text.length()); + for (int i = 0; i < sel.size(); i++) { + Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height); + if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) { + continue; + } + if (rect.position.x < x_ofs) { + rect.size.x -= (x_ofs - rect.position.x); + rect.position.x = x_ofs; + } else if (rect.position.x + rect.size.x > ofs_max) { + rect.size.x = ofs_max - rect.position.x; + } + rect.size.y = caret_width; + RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color); + } + } + { + // IME caret. + Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos + ime_selection.x, cursor_pos + ime_selection.x + ime_selection.y); + for (int i = 0; i < sel.size(); i++) { + Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height); + if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) { + continue; + } + if (rect.position.x < x_ofs) { + rect.size.x -= (x_ofs - rect.position.x); + rect.position.x = x_ofs; + } else if (rect.position.x + rect.size.x > ofs_max) { + rect.size.x = ofs_max - rect.position.x; + } + rect.size.y = caret_width * 3; + RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color); } } -#ifdef TOOLS_ENABLED - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(caret_x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color); -#else - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(caret_x_ofs, y_ofs), Size2(1, caret_height)), cursor_color); -#endif } } if (has_focus()) { if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); - DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + Point2(using_placeholder ? 0 : x_ofs, y_ofs + caret_height), get_viewport()->get_window_id()); + DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + Point2(using_placeholder ? 0 : x_ofs, y_ofs + TS->shaped_text_get_size(text_rid).y), get_viewport()->get_window_id()); } } } break; @@ -962,6 +973,8 @@ void LineEdit::_notification(int p_what) { } ime_text = ""; ime_selection = Point2(); + _shape(); + set_cursor_position(cursor_pos); // Update scroll_offset if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_hide(); @@ -972,6 +985,9 @@ void LineEdit::_notification(int p_what) { if (has_focus()) { ime_text = DisplayServer::get_singleton()->ime_get_text(); ime_selection = DisplayServer::get_singleton()->ime_get_selection(); + _shape(); + set_cursor_position(cursor_pos); // Update scroll_offset + update(); } } break; @@ -1023,14 +1039,10 @@ void LineEdit::undo() { undo_stack_pos = undo_stack_pos->prev(); TextOperation op = undo_stack_pos->get(); text = op.text; - cached_width = op.cached_width; scroll_offset = op.scroll_offset; set_cursor_position(op.cursor_pos); - if (expand_to_text_length) { - minimum_size_changed(); - } - + _shape(); _emit_text_change(); } @@ -1044,14 +1056,10 @@ void LineEdit::redo() { undo_stack_pos = undo_stack_pos->next(); TextOperation op = undo_stack_pos->get(); text = op.text; - cached_width = op.cached_width; scroll_offset = op.scroll_offset; set_cursor_position(op.cursor_pos); - if (expand_to_text_length) { - minimum_size_changed(); - } - + _shape(); _emit_text_change(); } @@ -1071,98 +1079,138 @@ void LineEdit::shift_selection_check_post(bool p_shift) { } void LineEdit::set_cursor_at_pixel_pos(int p_x) { - Ref<Font> font = get_theme_font("font"); - int ofs = scroll_offset; Ref<StyleBox> style = get_theme_stylebox("normal"); - int pixel_ofs = 0; - Size2 size = get_size(); - bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled; - int r_icon_width = Control::get_theme_icon("clear")->get_width(); + bool rtl = is_layout_rtl(); + int x_ofs = 0; + float text_width = TS->shaped_text_get_size(text_rid).x; switch (align) { case ALIGN_FILL: case ALIGN_LEFT: { - pixel_ofs = int(style->get_offset().x); + if (rtl) { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width))); + } else { + x_ofs = style->get_offset().x; + } } break; case ALIGN_CENTER: { if (scroll_offset != 0) { - pixel_ofs = int(style->get_offset().x); + x_ofs = style->get_offset().x; } else { - pixel_ofs = int(size.width - (cached_width)) / 2; - } - - if (display_clear_icon) { - pixel_ofs -= int(r_icon_width / 2 + style->get_margin(MARGIN_RIGHT)); + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2); } } break; case ALIGN_RIGHT: { - pixel_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_width)); - - if (display_clear_icon) { - pixel_ofs -= int(r_icon_width + style->get_margin(MARGIN_RIGHT)); + if (rtl) { + x_ofs = style->get_offset().x; + } else { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width))); } } break; } - while (ofs < text.length()) { - int char_w = 0; - if (font != nullptr) { - char_w = font->get_char_size(pass ? secret_character[0] : text[ofs]).width; - } - pixel_ofs += char_w; - - if (pixel_ofs > p_x) { // Found what we look for. - break; + bool using_placeholder = text.is_empty() && ime_text.is_empty(); + bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; + if (right_icon.is_valid() || display_clear_icon) { + Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; + if (align == ALIGN_CENTER) { + if (scroll_offset == 0) { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); + } + } else { + x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT)); } - - ofs++; } + int ofs = TS->shaped_text_hit_test_position(text_rid, p_x - x_ofs - scroll_offset); set_cursor_position(ofs); } -int LineEdit::get_cursor_pixel_pos() { - Ref<Font> font = get_theme_font("font"); - int ofs = scroll_offset; +Vector2i LineEdit::get_cursor_pixel_pos() { Ref<StyleBox> style = get_theme_stylebox("normal"); - int pixel_ofs = 0; - Size2 size = get_size(); - bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled; - int r_icon_width = Control::get_theme_icon("clear")->get_width(); + bool rtl = is_layout_rtl(); + int x_ofs = 0; + float text_width = TS->shaped_text_get_size(text_rid).x; switch (align) { case ALIGN_FILL: case ALIGN_LEFT: { - pixel_ofs = int(style->get_offset().x); + if (rtl) { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width))); + } else { + x_ofs = style->get_offset().x; + } } break; case ALIGN_CENTER: { if (scroll_offset != 0) { - pixel_ofs = int(style->get_offset().x); + x_ofs = style->get_offset().x; } else { - pixel_ofs = int(size.width - (cached_width)) / 2; - } - - if (display_clear_icon) { - pixel_ofs -= int(r_icon_width / 2 + style->get_margin(MARGIN_RIGHT)); + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2); } } break; case ALIGN_RIGHT: { - pixel_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_width)); - - if (display_clear_icon) { - pixel_ofs -= int(r_icon_width + style->get_margin(MARGIN_RIGHT)); + if (rtl) { + x_ofs = style->get_offset().x; + } else { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width))); } } break; } - while (ofs < cursor_pos) { - if (font != nullptr) { - pixel_ofs += font->get_char_size(pass ? secret_character[0] : text[ofs]).width; + bool using_placeholder = text.is_empty() && ime_text.is_empty(); + bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; + if (right_icon.is_valid() || display_clear_icon) { + Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; + if (align == ALIGN_CENTER) { + if (scroll_offset == 0) { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); + } + } else { + x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT)); } - ofs++; } - return pixel_ofs; + Vector2i ret; + Rect2 l_caret, t_caret; + TextServer::Direction l_dir, t_dir; + // Get position of the start of caret. + if (ime_text.length() != 0 && ime_selection.x != 0) { + TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x, l_caret, l_dir, t_caret, t_dir); + } else { + TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir); + } + + if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { + ret.x = x_ofs + l_caret.position.x + scroll_offset; + } else { + ret.x = x_ofs + t_caret.position.x + scroll_offset; + } + + // Get position of the end of caret. + if (ime_text.length() != 0) { + if (ime_selection.y != 0) { + TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir); + } else { + TS->shaped_text_get_carets(text_rid, cursor_pos + ime_text.size(), l_caret, l_dir, t_caret, t_dir); + } + if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { + ret.y = x_ofs + l_caret.position.x + scroll_offset; + } else { + ret.y = x_ofs + t_caret.position.x + scroll_offset; + } + } else { + ret.y = ret.x; + } + + return ret; +} + +void LineEdit::set_mid_grapheme_caret_enabled(const bool p_enabled) { + mid_grapheme_caret_enabled = p_enabled; +} + +bool LineEdit::get_mid_grapheme_caret_enabled() const { + return mid_grapheme_caret_enabled; } bool LineEdit::cursor_get_blink_enabled() const { @@ -1227,49 +1275,26 @@ void LineEdit::delete_char() { return; } - Ref<Font> font = get_theme_font("font"); - if (font != nullptr) { - cached_width -= font->get_char_size(pass ? secret_character[0] : text[cursor_pos - 1]).width; - } - text.erase(cursor_pos - 1, 1); + _shape(); set_cursor_position(get_cursor_position() - 1); - if (align == ALIGN_CENTER || align == ALIGN_RIGHT) { - scroll_offset = CLAMP(scroll_offset - 1, 0, MAX(text.length() - 1, 0)); - } - _text_changed(); } void LineEdit::delete_text(int p_from_column, int p_to_column) { ERR_FAIL_COND_MSG(p_from_column < 0 || p_from_column > p_to_column || p_to_column > text.length(), vformat("Positional parameters (from: %d, to: %d) are inverted or outside the text length (%d).", p_from_column, p_to_column, text.length())); - if (text.size() > 0) { - Ref<Font> font = get_theme_font("font"); - if (font != nullptr) { - for (int i = p_from_column; i < p_to_column; i++) { - cached_width -= font->get_char_size(pass ? secret_character[0] : text[i]).width; - } - } - } else { - cached_width = 0; - } text.erase(p_from_column, p_to_column - p_from_column); + _shape(); + cursor_pos -= CLAMP(cursor_pos - p_from_column, 0, p_to_column - p_from_column); if (cursor_pos >= text.length()) { cursor_pos = text.length(); } - if (scroll_offset > cursor_pos) { - scroll_offset = cursor_pos; - } - - if (align == ALIGN_CENTER || align == ALIGN_RIGHT) { - scroll_offset = CLAMP(scroll_offset - (p_to_column - p_from_column), 0, MAX(text.length() - 1, 0)); - } if (!text_changed_dirty) { if (is_inside_tree()) { @@ -1283,15 +1308,102 @@ void LineEdit::set_text(String p_text) { clear_internal(); append_at_cursor(p_text); - if (expand_to_text_length) { - minimum_size_changed(); - } - update(); cursor_pos = 0; scroll_offset = 0; } +void LineEdit::set_text_direction(Control::TextDirection p_text_direction) { + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (text_direction != p_text_direction) { + text_direction = p_text_direction; + if (text_direction != TEXT_DIRECTION_AUTO && text_direction != TEXT_DIRECTION_INHERITED) { + input_direction = text_direction; + } + _shape(); + + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_AUTO), text_direction == TEXT_DIRECTION_AUTO); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); + update(); + } +} + +Control::TextDirection LineEdit::get_text_direction() const { + return text_direction; +} + +void LineEdit::clear_opentype_features() { + opentype_features.clear(); + _shape(); + update(); +} + +void LineEdit::set_opentype_feature(const String &p_name, int p_value) { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { + opentype_features[tag] = p_value; + _shape(); + update(); + } +} + +int LineEdit::get_opentype_feature(const String &p_name) const { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag)) { + return -1; + } + return opentype_features[tag]; +} + +void LineEdit::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + _shape(); + update(); + } +} + +String LineEdit::get_language() const { + return language; +} + +void LineEdit::set_draw_control_chars(bool p_draw_control_chars) { + if (draw_control_chars != p_draw_control_chars) { + draw_control_chars = p_draw_control_chars; + menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); + _shape(); + update(); + } +} + +bool LineEdit::get_draw_control_chars() const { + return draw_control_chars; +} + +void LineEdit::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { + if (st_parser != p_parser) { + st_parser = p_parser; + _shape(); + update(); + } +} + +Control::StructuredTextParser LineEdit::get_structured_text_bidi_override() const { + return st_parser; +} + +void LineEdit::set_structured_text_bidi_override_options(Array p_args) { + st_args = p_args; + _shape(); + update(); +} + +Array LineEdit::get_structured_text_bidi_override_options() const { + return st_args; +} + void LineEdit::clear() { clear_internal(); _text_changed(); @@ -1304,7 +1416,7 @@ String LineEdit::get_text() const { void LineEdit::set_placeholder(String p_text) { placeholder = p_text; placeholder_translated = tr(placeholder); - update_placeholder_width(); + _shape(); update(); } @@ -1332,57 +1444,68 @@ void LineEdit::set_cursor_position(int p_pos) { cursor_pos = p_pos; + // Fit to window. + if (!is_inside_tree()) { - scroll_offset = cursor_pos; + scroll_offset = 0; return; } Ref<StyleBox> style = get_theme_stylebox("normal"); - Ref<Font> font = get_theme_font("font"); + bool rtl = is_layout_rtl(); - if (cursor_pos <= scroll_offset) { - // Adjust window if cursor goes too much to the left. - set_scroll_offset(MAX(0, cursor_pos - 1)); - } else { - // Adjust window if cursor goes too much to the right. - int window_width = get_size().width - style->get_minimum_size().width; - bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled; - if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; - window_width -= r_icon->get_width(); - } - - if (window_width < 0) { - return; - } - int wp = scroll_offset; - - if (font.is_valid()) { - int accum_width = 0; - - for (int i = cursor_pos; i >= scroll_offset; i--) { - if (i >= text.length()) { - // Do not do this, because if the cursor is at the end, its just fine that it takes no space. - // accum_width = font->get_char_size(' ').width; - } else { - if (pass) { - accum_width += font->get_char_size(secret_character[0], i + 1 < text.length() ? secret_character[0] : 0).width; - } else { - accum_width += font->get_char_size(text[i], i + 1 < text.length() ? text[i + 1] : 0).width; // Anything should do. - } - } - if (accum_width > window_width) { - break; - } + int x_ofs = 0; + float text_width = TS->shaped_text_get_size(text_rid).x; + switch (align) { + case ALIGN_FILL: + case ALIGN_LEFT: { + if (rtl) { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width))); + } else { + x_ofs = style->get_offset().x; + } + } break; + case ALIGN_CENTER: { + if (scroll_offset != 0) { + x_ofs = style->get_offset().x; + } else { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2); + } + } break; + case ALIGN_RIGHT: { + if (rtl) { + x_ofs = style->get_offset().x; + } else { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width))); + } + } break; + } - wp = i; + int ofs_max = get_size().width - style->get_margin(SIDE_RIGHT); + bool using_placeholder = text.is_empty() && ime_text.is_empty(); + bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; + if (right_icon.is_valid() || display_clear_icon) { + Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; + if (align == ALIGN_CENTER) { + if (scroll_offset == 0) { + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); } + } else { + x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT)); } + ofs_max -= r_icon->get_width(); + } - if (wp != scroll_offset) { - set_scroll_offset(wp); - } + // Note: Use too coordinates to fit IME input range. + Vector2i primary_catret_offset = get_cursor_pixel_pos(); + + if (MIN(primary_catret_offset.x, primary_catret_offset.y) <= x_ofs) { + scroll_offset += (x_ofs - MIN(primary_catret_offset.x, primary_catret_offset.y)); + } else if (MAX(primary_catret_offset.x, primary_catret_offset.y) >= ofs_max) { + scroll_offset += (ofs_max - MAX(primary_catret_offset.x, primary_catret_offset.y)); } + scroll_offset = MIN(0, scroll_offset); + update(); } @@ -1406,7 +1529,11 @@ void LineEdit::append_at_cursor(String p_text) { String pre = text.substr(0, cursor_pos); String post = text.substr(cursor_pos, text.length() - cursor_pos); text = pre + p_text + post; - update_cached_width(); + _shape(); + TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, cursor_pos, cursor_pos + p_text.length()); + if (dir != TextServer::DIRECTION_AUTO) { + input_direction = (TextDirection)dir; + } set_cursor_position(cursor_pos + p_text.length()); } else { emit_signal("text_change_rejected"); @@ -1416,39 +1543,39 @@ void LineEdit::append_at_cursor(String p_text) { void LineEdit::clear_internal() { deselect(); _clear_undo_stack(); - cached_width = 0; cursor_pos = 0; scroll_offset = 0; undo_text = ""; text = ""; + _shape(); update(); } Size2 LineEdit::get_minimum_size() const { Ref<StyleBox> style = get_theme_stylebox("normal"); Ref<Font> font = get_theme_font("font"); + int font_size = get_theme_font_size("font_size"); Size2 min_size; // Minimum size of text. - int space_size = font->get_char_size(' ').x; + int space_size = font->get_char_size('m', 0, font_size).x; min_size.width = get_theme_constant("minimum_spaces") * space_size; if (expand_to_text_length) { // Add a space because some fonts are too exact, and because cursor needs a bit more when at the end. - min_size.width = MAX(min_size.width, font->get_string_size(text).x + space_size); + min_size.width = MAX(min_size.width, full_width + space_size); } - min_size.height = font->get_height(); + min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM), font->get_height(font_size)); // Take icons into account. - if (!text.empty() && is_editable() && clear_button_enabled) { - min_size.width = MAX(min_size.width, Control::get_theme_icon("clear")->get_width()); - min_size.height = MAX(min_size.height, Control::get_theme_icon("clear")->get_height()); - } - if (right_icon.is_valid()) { - min_size.width = MAX(min_size.width, right_icon->get_width()); - min_size.height = MAX(min_size.height, right_icon->get_height()); + bool using_placeholder = text.is_empty() && ime_text.is_empty(); + bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; + if (right_icon.is_valid() || display_clear_icon) { + Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; + min_size.width += r_icon->get_width(); + min_size.height = MAX(min_size.height, r_icon->get_height()); } return style->get_minimum_size() + min_size; @@ -1531,8 +1658,10 @@ bool LineEdit::is_editable() const { } void LineEdit::set_secret(bool p_secret) { - pass = p_secret; - update_cached_width(); + if (pass != p_secret) { + pass = p_secret; + _shape(); + } update(); } @@ -1545,8 +1674,10 @@ void LineEdit::set_secret_character(const String &p_string) { // It also wouldn't make sense to use multiple characters as the secret character. ERR_FAIL_COND_MSG(p_string.length() != 1, "Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given)."); - secret_character = p_string; - update_cached_width(); + if (secret_character != p_string) { + secret_character = p_string; + _shape(); + } update(); } @@ -1623,6 +1754,101 @@ void LineEdit::menu_option(int p_option) { if (editable) { redo(); } + } break; + case MENU_DIR_INHERITED: { + set_text_direction(TEXT_DIRECTION_INHERITED); + } break; + case MENU_DIR_AUTO: { + set_text_direction(TEXT_DIRECTION_AUTO); + } break; + case MENU_DIR_LTR: { + set_text_direction(TEXT_DIRECTION_LTR); + } break; + case MENU_DIR_RTL: { + set_text_direction(TEXT_DIRECTION_RTL); + } break; + case MENU_DISPLAY_UCC: { + set_draw_control_chars(!get_draw_control_chars()); + } break; + case MENU_INSERT_LRM: { + if (editable) { + append_at_cursor(String::chr(0x200E)); + } + } break; + case MENU_INSERT_RLM: { + if (editable) { + append_at_cursor(String::chr(0x200F)); + } + } break; + case MENU_INSERT_LRE: { + if (editable) { + append_at_cursor(String::chr(0x202A)); + } + } break; + case MENU_INSERT_RLE: { + if (editable) { + append_at_cursor(String::chr(0x202B)); + } + } break; + case MENU_INSERT_LRO: { + if (editable) { + append_at_cursor(String::chr(0x202D)); + } + } break; + case MENU_INSERT_RLO: { + if (editable) { + append_at_cursor(String::chr(0x202E)); + } + } break; + case MENU_INSERT_PDF: { + if (editable) { + append_at_cursor(String::chr(0x202C)); + } + } break; + case MENU_INSERT_ALM: { + if (editable) { + append_at_cursor(String::chr(0x061C)); + } + } break; + case MENU_INSERT_LRI: { + if (editable) { + append_at_cursor(String::chr(0x2066)); + } + } break; + case MENU_INSERT_RLI: { + if (editable) { + append_at_cursor(String::chr(0x2067)); + } + } break; + case MENU_INSERT_FSI: { + if (editable) { + append_at_cursor(String::chr(0x2068)); + } + } break; + case MENU_INSERT_PDI: { + if (editable) { + append_at_cursor(String::chr(0x2069)); + } + } break; + case MENU_INSERT_ZWJ: { + if (editable) { + append_at_cursor(String::chr(0x200D)); + } + } break; + case MENU_INSERT_ZWNJ: { + if (editable) { + append_at_cursor(String::chr(0x200C)); + } + } break; + case MENU_INSERT_WJ: { + if (editable) { + append_at_cursor(String::chr(0x2060)); + } + } break; + case MENU_INSERT_SHY: { + if (editable) { + append_at_cursor(String::chr(0x00AD)); + } } } } @@ -1649,7 +1875,7 @@ void LineEdit::_editor_settings_changed() { void LineEdit::set_expand_to_text_length(bool p_enabled) { expand_to_text_length = p_enabled; minimum_size_changed(); - set_scroll_offset(0); + set_cursor_position(cursor_pos); } bool LineEdit::get_expand_to_text_length() const { @@ -1661,6 +1887,7 @@ void LineEdit::set_clear_button_enabled(bool p_enabled) { return; } clear_button_enabled = p_enabled; + _fit_to_width(); minimum_size_changed(); update(); } @@ -1706,6 +1933,7 @@ void LineEdit::set_right_icon(const Ref<Texture2D> &p_icon) { return; } right_icon = p_icon; + _fit_to_width(); minimum_size_changed(); update(); } @@ -1715,10 +1943,6 @@ Ref<Texture2D> LineEdit::get_right_icon() { } void LineEdit::_text_changed() { - if (expand_to_text_length) { - minimum_size_changed(); - } - _emit_text_change(); _clear_redo(); } @@ -1729,24 +1953,55 @@ void LineEdit::_emit_text_change() { text_changed_dirty = false; } -void LineEdit::update_cached_width() { - Ref<Font> font = get_theme_font("font"); - cached_width = 0; - if (font != nullptr) { - String text = get_text(); - for (int i = 0; i < text.length(); i++) { - cached_width += font->get_char_size(pass ? secret_character[0] : text[i]).width; +void LineEdit::_shape() { + Size2 old_size = TS->shaped_text_get_size(text_rid); + TS->shaped_text_clear(text_rid); + + String t; + if (text.length() == 0) { + t = placeholder_translated; + } else if (pass) { + t = secret_character.repeat(text.length() + ime_text.length()); + } else { + if (ime_text.length() > 0) { + t = text.substr(0, cursor_pos) + ime_text + text.substr(cursor_pos, text.length()); + } else { + t = text; } } + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { + TS->shaped_text_set_direction(text_rid, is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + TS->shaped_text_set_direction(text_rid, (TextServer::Direction)text_direction); + } + TS->shaped_text_set_preserve_control(text_rid, draw_control_chars); + + const Ref<Font> &font = get_theme_font("font"); + int font_size = get_theme_font_size("font_size"); + TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); + TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, t)); + + full_width = TS->shaped_text_get_size(text_rid).x; + _fit_to_width(); + + Size2 size = TS->shaped_text_get_size(text_rid); + + if ((expand_to_text_length && old_size.x != size.x) || (old_size.y != size.y)) { + minimum_size_changed(); + } } -void LineEdit::update_placeholder_width() { - Ref<Font> font = get_theme_font("font"); - cached_placeholder_width = 0; - if (font != nullptr) { - for (int i = 0; i < placeholder_translated.length(); i++) { - cached_placeholder_width += font->get_char_size(placeholder_translated[i]).width; +void LineEdit::_fit_to_width() { + if (align == ALIGN_FILL) { + Ref<StyleBox> style = get_theme_stylebox("normal"); + int t_width = get_size().width - style->get_margin(SIDE_RIGHT) - style->get_margin(SIDE_LEFT); + bool using_placeholder = text.is_empty() && ime_text.is_empty(); + bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; + if (right_icon.is_valid() || display_clear_icon) { + Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon("clear") : right_icon; + t_width -= r_icon->get_width(); } + TS->shaped_text_fit_to_width(text_rid, MAX(t_width, full_width)); } } @@ -1774,7 +2029,6 @@ void LineEdit::_clear_undo_stack() { void LineEdit::_create_undo_state() { TextOperation op; op.text = text; - op.cached_width = cached_width; op.cursor_pos = cursor_pos; op.scroll_offset = scroll_offset; undo_stack.push_back(op); @@ -1800,6 +2054,63 @@ void LineEdit::_generate_context_menu() { menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_Z : 0); menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z : 0); } + menu->add_separator(); + menu->add_submenu_item(RTR("Text writing direction"), "DirMenu"); + menu->add_separator(); + menu->add_check_item(RTR("Display control characters"), MENU_DISPLAY_UCC); + if (editable) { + menu->add_submenu_item(RTR("Insert control character"), "CTLMenu"); + } +} + +bool LineEdit::_set(const StringName &p_name, const Variant &p_value) { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + double value = p_value; + if (value == -1) { + if (opentype_features.has(tag)) { + opentype_features.erase(tag); + _shape(); + update(); + } + } else { + if ((double)opentype_features[tag] != value) { + opentype_features[tag] = value; + _shape(); + update(); + } + } + _change_notify(); + return true; + } + + return false; +} + +bool LineEdit::_get(const StringName &p_name, Variant &r_ret) const { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + if (opentype_features.has(tag)) { + r_ret = opentype_features[tag]; + return true; + } else { + r_ret = -1; + return true; + } + } + return false; +} + +void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const { + for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { + String name = TS->tag_to_name(*ftr); + p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name)); + } + p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); } void LineEdit::_bind_methods() { @@ -1815,6 +2126,19 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("deselect"), &LineEdit::deselect); ClassDB::bind_method(D_METHOD("set_text", "text"), &LineEdit::set_text); ClassDB::bind_method(D_METHOD("get_text"), &LineEdit::get_text); + ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &LineEdit::get_draw_control_chars); + ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enable"), &LineEdit::set_draw_control_chars); + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &LineEdit::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &LineEdit::get_text_direction); + ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &LineEdit::set_opentype_feature); + ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &LineEdit::get_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_opentype_features"), &LineEdit::clear_opentype_features); + ClassDB::bind_method(D_METHOD("set_language", "language"), &LineEdit::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &LineEdit::get_language); + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &LineEdit::set_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &LineEdit::get_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &LineEdit::set_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &LineEdit::get_structured_text_bidi_override_options); ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &LineEdit::set_placeholder); ClassDB::bind_method(D_METHOD("get_placeholder"), &LineEdit::get_placeholder); ClassDB::bind_method(D_METHOD("set_placeholder_alpha", "alpha"), &LineEdit::set_placeholder_alpha); @@ -1826,6 +2150,8 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_expand_to_text_length"), &LineEdit::get_expand_to_text_length); ClassDB::bind_method(D_METHOD("cursor_set_blink_enabled", "enabled"), &LineEdit::cursor_set_blink_enabled); ClassDB::bind_method(D_METHOD("cursor_get_blink_enabled"), &LineEdit::cursor_get_blink_enabled); + ClassDB::bind_method(D_METHOD("set_mid_grapheme_caret_enabled", "enabled"), &LineEdit::set_mid_grapheme_caret_enabled); + ClassDB::bind_method(D_METHOD("get_mid_grapheme_caret_enabled"), &LineEdit::get_mid_grapheme_caret_enabled); ClassDB::bind_method(D_METHOD("cursor_set_force_displayed", "enabled"), &LineEdit::cursor_set_force_displayed); ClassDB::bind_method(D_METHOD("cursor_get_force_displayed"), &LineEdit::cursor_get_force_displayed); ClassDB::bind_method(D_METHOD("cursor_set_blink_speed", "blink_speed"), &LineEdit::cursor_set_blink_speed); @@ -1872,6 +2198,27 @@ void LineEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_SELECT_ALL); BIND_ENUM_CONSTANT(MENU_UNDO); BIND_ENUM_CONSTANT(MENU_REDO); + BIND_ENUM_CONSTANT(MENU_DIR_INHERITED); + BIND_ENUM_CONSTANT(MENU_DIR_AUTO); + BIND_ENUM_CONSTANT(MENU_DIR_LTR); + BIND_ENUM_CONSTANT(MENU_DIR_RTL); + BIND_ENUM_CONSTANT(MENU_DISPLAY_UCC); + BIND_ENUM_CONSTANT(MENU_INSERT_LRM); + BIND_ENUM_CONSTANT(MENU_INSERT_RLM); + BIND_ENUM_CONSTANT(MENU_INSERT_LRE); + BIND_ENUM_CONSTANT(MENU_INSERT_RLE); + BIND_ENUM_CONSTANT(MENU_INSERT_LRO); + BIND_ENUM_CONSTANT(MENU_INSERT_RLO); + BIND_ENUM_CONSTANT(MENU_INSERT_PDF); + BIND_ENUM_CONSTANT(MENU_INSERT_ALM); + BIND_ENUM_CONSTANT(MENU_INSERT_LRI); + BIND_ENUM_CONSTANT(MENU_INSERT_RLI); + BIND_ENUM_CONSTANT(MENU_INSERT_FSI); + BIND_ENUM_CONSTANT(MENU_INSERT_PDI); + BIND_ENUM_CONSTANT(MENU_INSERT_ZWJ); + BIND_ENUM_CONSTANT(MENU_INSERT_ZWNJ); + BIND_ENUM_CONSTANT(MENU_INSERT_WJ); + BIND_ENUM_CONSTANT(MENU_INSERT_SHY); BIND_ENUM_CONSTANT(MENU_MAX); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); @@ -1887,6 +2234,12 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); + ADD_GROUP("Structured Text", "structured_text_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); ADD_GROUP("Placeholder", "placeholder_"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha"); @@ -1895,50 +2248,67 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_position"), "set_cursor_position", "get_cursor_position"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "cursor_set_force_displayed", "cursor_get_force_displayed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_mid_grapheme_caret_enabled", "get_mid_grapheme_caret_enabled"); } LineEdit::LineEdit() { - undo_stack_pos = nullptr; + text_rid = TS->create_shaped_text(); _create_undo_state(); - align = ALIGN_LEFT; - cached_width = 0; - cached_placeholder_width = 0; - cursor_pos = 0; - scroll_offset = 0; - window_has_focus = true; - max_length = 0; - pass = false; - secret_character = "*"; - text_changed_dirty = false; - placeholder_alpha = 0.6; - clear_button_enabled = false; + clear_button_status.press_attempt = false; clear_button_status.pressing_inside = false; - shortcut_keys_enabled = true; - selecting_enabled = true; deselect(); set_focus_mode(FOCUS_ALL); set_default_cursor_shape(CURSOR_IBEAM); set_mouse_filter(MOUSE_FILTER_STOP); - draw_caret = true; - caret_blink_enabled = false; - caret_force_displayed = false; caret_blink_timer = memnew(Timer); add_child(caret_blink_timer); caret_blink_timer->set_wait_time(0.65); caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret)); cursor_set_blink_enabled(false); - context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); - editable = false; // Initialise to opposite first, so we get past the early-out in set_editable. - set_editable(true); + + menu_dir = memnew(PopupMenu); + menu_dir->set_name("DirMenu"); + menu_dir->add_radio_check_item(RTR("Same as layout direction"), MENU_DIR_INHERITED); + menu_dir->add_radio_check_item(RTR("Auto-detect direction"), MENU_DIR_AUTO); + menu_dir->add_radio_check_item(RTR("Left-to-right"), MENU_DIR_LTR); + menu_dir->add_radio_check_item(RTR("Right-to-left"), MENU_DIR_RTL); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), true); + menu->add_child(menu_dir); + + menu_ctl = memnew(PopupMenu); + menu_ctl->set_name("CTLMenu"); + menu_ctl->add_item(RTR("Left-to-right mark (LRM)"), MENU_INSERT_LRM); + menu_ctl->add_item(RTR("Right-to-left mark (RLM)"), MENU_INSERT_RLM); + menu_ctl->add_item(RTR("Start of left-to-right embedding (LRE)"), MENU_INSERT_LRE); + menu_ctl->add_item(RTR("Start of right-to-left embedding (RLE)"), MENU_INSERT_RLE); + menu_ctl->add_item(RTR("Start of left-to-right override (LRO)"), MENU_INSERT_LRO); + menu_ctl->add_item(RTR("Start of right-to-left override (RLO)"), MENU_INSERT_RLO); + menu_ctl->add_item(RTR("Pop direction formatting (PDF)"), MENU_INSERT_PDF); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Arabic letter mark (ALM)"), MENU_INSERT_ALM); + menu_ctl->add_item(RTR("Left-to-right isolate (LRI)"), MENU_INSERT_LRI); + menu_ctl->add_item(RTR("Right-to-left isolate (RLI)"), MENU_INSERT_RLI); + menu_ctl->add_item(RTR("First strong isolate (FSI)"), MENU_INSERT_FSI); + menu_ctl->add_item(RTR("Pop direction isolate (PDI)"), MENU_INSERT_PDI); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Zero width joiner (ZWJ)"), MENU_INSERT_ZWJ); + menu_ctl->add_item(RTR("Zero width non-joiner (ZWNJ)"), MENU_INSERT_ZWNJ); + menu_ctl->add_item(RTR("Word joiner (WJ)"), MENU_INSERT_WJ); + menu_ctl->add_item(RTR("Soft hyphen (SHY)"), MENU_INSERT_SHY); + menu->add_child(menu_ctl); + + set_editable(true); // Initialise to opposite first, so we get past the early-out in set_editable. menu->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); - expand_to_text_length = false; + menu_dir->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); + menu_ctl->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); } LineEdit::~LineEdit() { + TS->free(text_rid); } diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 5fceedbf26..1e7495e734 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -53,41 +53,76 @@ public: MENU_SELECT_ALL, MENU_UNDO, MENU_REDO, + MENU_DIR_INHERITED, + MENU_DIR_AUTO, + MENU_DIR_LTR, + MENU_DIR_RTL, + MENU_DISPLAY_UCC, + MENU_INSERT_LRM, + MENU_INSERT_RLM, + MENU_INSERT_LRE, + MENU_INSERT_RLE, + MENU_INSERT_LRO, + MENU_INSERT_RLO, + MENU_INSERT_PDF, + MENU_INSERT_ALM, + MENU_INSERT_LRI, + MENU_INSERT_RLI, + MENU_INSERT_FSI, + MENU_INSERT_PDI, + MENU_INSERT_ZWJ, + MENU_INSERT_ZWNJ, + MENU_INSERT_WJ, + MENU_INSERT_SHY, MENU_MAX - }; private: - Align align; + Align align = ALIGN_LEFT; - bool editable; - bool pass; - bool text_changed_dirty; + bool editable = false; + bool pass = false; + bool text_changed_dirty = false; String undo_text; String text; String placeholder; String placeholder_translated; - String secret_character; - float placeholder_alpha; + String secret_character = "*"; + float placeholder_alpha = 0.6; String ime_text; Point2 ime_selection; - bool selecting_enabled; + RID text_rid; + float full_width = 0; + + bool selecting_enabled = true; + + bool context_menu_enabled = true; + PopupMenu *menu = nullptr; + PopupMenu *menu_dir = nullptr; + PopupMenu *menu_ctl = nullptr; - bool context_menu_enabled; - PopupMenu *menu; + bool mid_grapheme_caret_enabled = false; - int cursor_pos; - int scroll_offset; - int max_length; // 0 for no maximum. + int cursor_pos = 0; + int scroll_offset = 0; + int max_length = 0; // 0 for no maximum. - int cached_width; - int cached_placeholder_width; + Dictionary opentype_features; + String language; + TextDirection text_direction = TEXT_DIRECTION_AUTO; + TextDirection input_direction = TEXT_DIRECTION_LTR; + Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + Array st_args; + bool draw_control_chars = false; - bool clear_button_enabled; + bool expand_to_text_length = false; + bool window_has_focus = true; - bool shortcut_keys_enabled; + bool clear_button_enabled = false; + + bool shortcut_keys_enabled = true; bool virtual_keyboard_enabled = true; @@ -110,13 +145,18 @@ private: String text; }; List<TextOperation> undo_stack; - List<TextOperation>::Element *undo_stack_pos; + List<TextOperation>::Element *undo_stack_pos = nullptr; struct ClearButtonStatus { - bool press_attempt; - bool pressing_inside; + bool press_attempt = false; + bool pressing_inside = false; } clear_button_status; + bool caret_blink_enabled = false; + bool caret_force_displayed = false; + bool draw_caret = true; + Timer *caret_blink_timer = nullptr; + bool _is_over_clear_button(const Point2 &p_pos) const; void _clear_undo_stack(); @@ -125,19 +165,10 @@ private: void _generate_context_menu(); - Timer *caret_blink_timer; - + void _shape(); + void _fit_to_width(); void _text_changed(); void _emit_text_change(); - bool expand_to_text_length; - - void update_cached_width(); - void update_placeholder_width(); - - bool caret_blink_enabled; - bool caret_force_displayed; - bool draw_caret; - bool window_has_focus; void shift_selection_check_pre(bool); void shift_selection_check_post(bool); @@ -147,7 +178,7 @@ private: int get_scroll_offset() const; void set_cursor_at_pixel_pos(int p_x); - int get_cursor_pixel_pos(); + Vector2i get_cursor_pixel_pos(); void _reset_caret_blink_timer(); void _toggle_draw_caret(); @@ -163,6 +194,10 @@ private: protected: static void _bind_methods(); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + public: void set_align(Align p_align); Align get_align() const; @@ -185,19 +220,47 @@ public: void delete_char(); void delete_text(int p_from_column, int p_to_column); + void set_text(String p_text); String get_text() const; + + void set_text_direction(TextDirection p_text_direction); + TextDirection get_text_direction() const; + + void set_opentype_feature(const String &p_name, int p_value); + int get_opentype_feature(const String &p_name) const; + void clear_opentype_features(); + + void set_language(const String &p_language); + String get_language() const; + + void set_draw_control_chars(bool p_draw_control_chars); + bool get_draw_control_chars() const; + + void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); + Control::StructuredTextParser get_structured_text_bidi_override() const; + + void set_structured_text_bidi_override_options(Array p_args); + Array get_structured_text_bidi_override_options() const; + void set_placeholder(String p_text); String get_placeholder() const; + void set_placeholder_alpha(float p_alpha); float get_placeholder_alpha() const; + void set_cursor_position(int p_pos); int get_cursor_position() const; + void set_max_length(int p_max_length); int get_max_length() const; + void append_at_cursor(String p_text); void clear(); + void set_mid_grapheme_caret_enabled(const bool p_enabled); + bool get_mid_grapheme_caret_enabled() const; + bool cursor_get_blink_enabled() const; void cursor_set_blink_enabled(const bool p_enabled); diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index 27a60945c8..495529017a 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,17 +29,103 @@ /*************************************************************************/ #include "link_button.h" +#include "core/string/translation.h" + +void LinkButton::_shape() { + Ref<Font> font = get_theme_font("font"); + int font_size = get_theme_font_size("font_size"); + + text_buf->clear(); + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { + text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + text_buf->set_direction((TextServer::Direction)text_direction); + } + TS->shaped_text_set_bidi_override(text_buf->get_rid(), structured_text_parser(st_parser, st_args, text)); + text_buf->add_string(text, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); +} void LinkButton::set_text(const String &p_text) { text = p_text; - update(); + _shape(); minimum_size_changed(); + update(); } String LinkButton::get_text() const { return text; } +void LinkButton::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { + if (st_parser != p_parser) { + st_parser = p_parser; + _shape(); + update(); + } +} + +Control::StructuredTextParser LinkButton::get_structured_text_bidi_override() const { + return st_parser; +} + +void LinkButton::set_structured_text_bidi_override_options(Array p_args) { + st_args = p_args; + _shape(); + update(); +} + +Array LinkButton::get_structured_text_bidi_override_options() const { + return st_args; +} + +void LinkButton::set_text_direction(Control::TextDirection p_text_direction) { + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (text_direction != p_text_direction) { + text_direction = p_text_direction; + _shape(); + update(); + } +} + +Control::TextDirection LinkButton::get_text_direction() const { + return text_direction; +} + +void LinkButton::clear_opentype_features() { + opentype_features.clear(); + _shape(); + update(); +} + +void LinkButton::set_opentype_feature(const String &p_name, int p_value) { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { + opentype_features[tag] = p_value; + _shape(); + update(); + } +} + +int LinkButton::get_opentype_feature(const String &p_name) const { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag)) { + return -1; + } + return opentype_features[tag]; +} + +void LinkButton::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + _shape(); + update(); + } +} + +String LinkButton::get_language() const { + return language; +} + void LinkButton::set_underline_mode(UnderlineMode p_underline_mode) { underline_mode = p_underline_mode; update(); @@ -50,11 +136,20 @@ LinkButton::UnderlineMode LinkButton::get_underline_mode() const { } Size2 LinkButton::get_minimum_size() const { - return get_theme_font("font")->get_string_size(text); + return text_buf->get_size(); } void LinkButton::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + update(); + } break; + case NOTIFICATION_THEME_CHANGED: { + _shape(); + minimum_size_changed(); + update(); + } break; case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); Size2 size = get_size(); @@ -94,38 +189,112 @@ void LinkButton::_notification(int p_what) { style->draw(ci, Rect2(Point2(), size)); } - Ref<Font> font = get_theme_font("font"); + int width = text_buf->get_line_width(); - draw_string(font, Vector2(0, font->get_ascent()), text, color); + if (is_layout_rtl()) { + text_buf->draw(get_canvas_item(), Vector2(size.width - width, 0), color); + } else { + text_buf->draw(get_canvas_item(), Vector2(0, 0), color); + } if (do_underline) { - int underline_spacing = get_theme_constant("underline_spacing") + font->get_underline_position(); - int width = font->get_string_size(text).width; - int y = font->get_ascent() + underline_spacing; + int underline_spacing = get_theme_constant("underline_spacing") + text_buf->get_line_underline_position(); + int y = text_buf->get_line_ascent() + underline_spacing; - draw_line(Vector2(0, y), Vector2(width, y), color, font->get_underline_thickness()); + if (is_layout_rtl()) { + draw_line(Vector2(size.width - width, y), Vector2(size.width, y), color, text_buf->get_line_underline_thickness()); + } else { + draw_line(Vector2(0, y), Vector2(width, y), color, text_buf->get_line_underline_thickness()); + } } } break; } } +bool LinkButton::_set(const StringName &p_name, const Variant &p_value) { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + double value = p_value; + if (value == -1) { + if (opentype_features.has(tag)) { + opentype_features.erase(tag); + _shape(); + update(); + } + } else { + if ((double)opentype_features[tag] != value) { + opentype_features[tag] = value; + _shape(); + update(); + } + } + _change_notify(); + return true; + } + + return false; +} + +bool LinkButton::_get(const StringName &p_name, Variant &r_ret) const { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + if (opentype_features.has(tag)) { + r_ret = opentype_features[tag]; + return true; + } else { + r_ret = -1; + return true; + } + } + return false; +} + +void LinkButton::_get_property_list(List<PropertyInfo> *p_list) const { + for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { + String name = TS->tag_to_name(*ftr); + p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name)); + } + p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); +} + void LinkButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text", "text"), &LinkButton::set_text); ClassDB::bind_method(D_METHOD("get_text"), &LinkButton::get_text); - + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &LinkButton::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &LinkButton::get_text_direction); + ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &LinkButton::set_opentype_feature); + ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &LinkButton::get_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_opentype_features"), &LinkButton::clear_opentype_features); + ClassDB::bind_method(D_METHOD("set_language", "language"), &LinkButton::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &LinkButton::get_language); ClassDB::bind_method(D_METHOD("set_underline_mode", "underline_mode"), &LinkButton::set_underline_mode); ClassDB::bind_method(D_METHOD("get_underline_mode"), &LinkButton::get_underline_mode); + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &LinkButton::set_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &LinkButton::get_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &LinkButton::set_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &LinkButton::get_structured_text_bidi_override_options); BIND_ENUM_CONSTANT(UNDERLINE_MODE_ALWAYS); BIND_ENUM_CONSTANT(UNDERLINE_MODE_ON_HOVER); BIND_ENUM_CONSTANT(UNDERLINE_MODE_NEVER); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode"); + ADD_GROUP("Structured Text", "structured_text_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); } LinkButton::LinkButton() { + text_buf.instance(); underline_mode = UNDERLINE_MODE_ALWAYS; + set_focus_mode(FOCUS_NONE); set_default_cursor_shape(CURSOR_POINTING_HAND); } diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h index b8469b529a..5ab6aba122 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ #include "scene/gui/base_button.h" #include "scene/resources/bit_map.h" +#include "scene/resources/text_line.h" class LinkButton : public BaseButton { GDCLASS(LinkButton, BaseButton); @@ -46,17 +47,46 @@ public: private: String text; + Ref<TextLine> text_buf; UnderlineMode underline_mode; + Dictionary opentype_features; + String language; + TextDirection text_direction = TEXT_DIRECTION_AUTO; + Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + Array st_args; + + void _shape(); + protected: virtual Size2 get_minimum_size() const override; void _notification(int p_what); static void _bind_methods(); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + public: void set_text(const String &p_text); String get_text() const; + void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); + Control::StructuredTextParser get_structured_text_bidi_override() const; + + void set_structured_text_bidi_override_options(Array p_args); + Array get_structured_text_bidi_override_options() const; + + void set_text_direction(TextDirection p_text_direction); + TextDirection get_text_direction() const; + + void set_opentype_feature(const String &p_name, int p_value); + int get_opentype_feature(const String &p_name) const; + void clear_opentype_features(); + + void set_language(const String &p_language); + String get_language() const; + void set_underline_mode(UnderlineMode p_underline_mode); UnderlineMode get_underline_mode() const; diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp index b674b492d8..0e9610d0a3 100644 --- a/scene/gui/margin_container.cpp +++ b/scene/gui/margin_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/margin_container.h b/scene/gui/margin_container.h index 12e230d9d7..b782976ada 100644 --- a/scene/gui/margin_container.h +++ b/scene/gui/margin_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index d65e98ea46..94cb5ef0f4 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,6 +34,10 @@ #include "scene/main/window.h" void MenuButton::_unhandled_key_input(Ref<InputEvent> p_event) { + if (!_is_focus_owner_in_shorcut_context()) { + return; + } + if (disable_shortcuts) { return; } @@ -43,9 +47,6 @@ void MenuButton::_unhandled_key_input(Ref<InputEvent> p_event) { return; } - //bool global_only = (get_viewport()->get_modal_stack_top() && !get_viewport()->get_modal_stack_top()->is_a_parent_of(this)); - //if (popup->activate_item_by_event(p_event, global_only)) - // accept_event(); if (popup->activate_item_by_event(p_event, false)) { accept_event(); } @@ -100,7 +101,6 @@ void MenuButton::_notification(int p_what) { void MenuButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_popup"), &MenuButton::get_popup); - ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &MenuButton::_unhandled_key_input); ClassDB::bind_method(D_METHOD("_set_items"), &MenuButton::_set_items); ClassDB::bind_method(D_METHOD("_get_items"), &MenuButton::_get_items); ClassDB::bind_method(D_METHOD("set_switch_on_hover", "enable"), &MenuButton::set_switch_on_hover); @@ -123,6 +123,7 @@ MenuButton::MenuButton() { set_toggle_mode(true); set_disable_shortcuts(false); set_process_unhandled_key_input(true); + set_focus_mode(FOCUS_NONE); set_action_mode(ACTION_MODE_BUTTON_PRESS); popup = memnew(PopupMenu); diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index 6330899ad3..cd4ed5bcb2 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,7 +42,6 @@ class MenuButton : public Button { bool disable_shortcuts; PopupMenu *popup; - void _unhandled_key_input(Ref<InputEvent> p_event); Array _get_items() const; void _set_items(const Array &p_items); @@ -51,6 +50,7 @@ class MenuButton : public Button { protected: void _notification(int p_what); static void _bind_methods(); + virtual void _unhandled_key_input(Ref<InputEvent> p_event) override; public: virtual void pressed() override; diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp index bc71ae94f5..c274eb5c10 100644 --- a/scene/gui/nine_patch_rect.cpp +++ b/scene/gui/nine_patch_rect.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -44,12 +44,12 @@ void NinePatchRect::_notification(int p_what) { texture->get_rect_region(rect, src_rect, rect, src_rect); RID ci = get_canvas_item(); - RS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[MARGIN_LEFT], margin[MARGIN_TOP]), Vector2(margin[MARGIN_RIGHT], margin[MARGIN_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center); + RS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[SIDE_LEFT], margin[SIDE_TOP]), Vector2(margin[SIDE_RIGHT], margin[SIDE_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center); } } Size2 NinePatchRect::get_minimum_size() const { - return Size2(margin[MARGIN_LEFT] + margin[MARGIN_RIGHT], margin[MARGIN_TOP] + margin[MARGIN_BOTTOM]); + return Size2(margin[SIDE_LEFT] + margin[SIDE_RIGHT], margin[SIDE_TOP] + margin[SIDE_BOTTOM]); } void NinePatchRect::_bind_methods() { @@ -73,10 +73,10 @@ void NinePatchRect::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); ADD_GROUP("Patch Margin", "patch_margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "patch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", SIDE_BOTTOM); ADD_GROUP("Axis Stretch", "axis_stretch_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode"); @@ -105,30 +105,30 @@ Ref<Texture2D> NinePatchRect::get_texture() const { return texture; } -void NinePatchRect::set_patch_margin(Margin p_margin, int p_size) { - ERR_FAIL_INDEX((int)p_margin, 4); - margin[p_margin] = p_size; +void NinePatchRect::set_patch_margin(Side p_side, int p_size) { + ERR_FAIL_INDEX((int)p_side, 4); + margin[p_side] = p_size; update(); minimum_size_changed(); - switch (p_margin) { - case MARGIN_LEFT: + switch (p_side) { + case SIDE_LEFT: _change_notify("patch_margin_left"); break; - case MARGIN_TOP: + case SIDE_TOP: _change_notify("patch_margin_top"); break; - case MARGIN_RIGHT: + case SIDE_RIGHT: _change_notify("patch_margin_right"); break; - case MARGIN_BOTTOM: + case SIDE_BOTTOM: _change_notify("patch_margin_bottom"); break; } } -int NinePatchRect::get_patch_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, 0); - return margin[p_margin]; +int NinePatchRect::get_patch_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); + return margin[p_side]; } void NinePatchRect::set_region_rect(const Rect2 &p_region_rect) { @@ -174,10 +174,10 @@ NinePatchRect::AxisStretchMode NinePatchRect::get_v_axis_stretch_mode() const { } NinePatchRect::NinePatchRect() { - margin[MARGIN_LEFT] = 0; - margin[MARGIN_RIGHT] = 0; - margin[MARGIN_BOTTOM] = 0; - margin[MARGIN_TOP] = 0; + margin[SIDE_LEFT] = 0; + margin[SIDE_RIGHT] = 0; + margin[SIDE_BOTTOM] = 0; + margin[SIDE_TOP] = 0; set_mouse_filter(MOUSE_FILTER_IGNORE); draw_center = true; diff --git a/scene/gui/nine_patch_rect.h b/scene/gui/nine_patch_rect.h index a539ad43c0..fcf25018aa 100644 --- a/scene/gui/nine_patch_rect.h +++ b/scene/gui/nine_patch_rect.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -59,8 +59,8 @@ public: void set_texture(const Ref<Texture2D> &p_tex); Ref<Texture2D> get_texture() const; - void set_patch_margin(Margin p_margin, int p_size); - int get_patch_margin(Margin p_margin) const; + void set_patch_margin(Side p_side, int p_size); + int get_patch_margin(Side p_side) const; void set_region_rect(const Rect2 &p_region_rect); Rect2 get_region_rect() const; diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index f0e69a94a4..4f274595a2 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -77,12 +77,25 @@ void OptionButton::_notification(int p_what) { Size2 size = get_size(); - Point2 ofs(size.width - arrow->get_width() - get_theme_constant("arrow_margin"), int(Math::abs((size.height - arrow->get_height()) / 2))); + Point2 ofs; + if (is_layout_rtl()) { + ofs = Point2(get_theme_constant("arrow_margin"), int(Math::abs((size.height - arrow->get_height()) / 2))); + } else { + ofs = Point2(size.width - arrow->get_width() - get_theme_constant("arrow_margin"), int(Math::abs((size.height - arrow->get_height()) / 2))); + } arrow->draw(ci, ofs, clr); } break; + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { if (has_theme_icon("arrow")) { - _set_internal_margin(MARGIN_RIGHT, Control::get_theme_icon("arrow")->get_width()); + if (is_layout_rtl()) { + _set_internal_margin(SIDE_LEFT, Control::get_theme_icon("arrow")->get_width()); + _set_internal_margin(SIDE_RIGHT, 0.f); + } else { + _set_internal_margin(SIDE_LEFT, 0.f); + _set_internal_margin(SIDE_RIGHT, Control::get_theme_icon("arrow")->get_width()); + } } } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -326,10 +339,16 @@ OptionButton::OptionButton() { current = -1; set_toggle_mode(true); set_text_align(ALIGN_LEFT); - set_action_mode(ACTION_MODE_BUTTON_PRESS); - if (has_theme_icon("arrow")) { - _set_internal_margin(MARGIN_RIGHT, Control::get_theme_icon("arrow")->get_width()); + if (is_layout_rtl()) { + if (has_theme_icon("arrow")) { + _set_internal_margin(SIDE_LEFT, Control::get_theme_icon("arrow")->get_width()); + } + } else { + if (has_theme_icon("arrow")) { + _set_internal_margin(SIDE_RIGHT, Control::get_theme_icon("arrow")->get_width()); + } } + set_action_mode(ACTION_MODE_BUTTON_PRESS); popup = memnew(PopupMenu); popup->hide(); diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index fec7695969..9cb296baa9 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp index acbb6d7ab5..995e985c3a 100644 --- a/scene/gui/panel.cpp +++ b/scene/gui/panel.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -63,6 +63,3 @@ Panel::Panel() { // Has visible stylebox, so stop by default. set_mouse_filter(MOUSE_FILTER_STOP); } - -Panel::~Panel() { -} diff --git a/scene/gui/panel.h b/scene/gui/panel.h index a68c3d3f0c..84fd6aaead 100644 --- a/scene/gui/panel.h +++ b/scene/gui/panel.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -54,7 +54,6 @@ public: Mode get_mode() const; Panel(); - ~Panel(); }; VARIANT_ENUM_CAST(Panel::Mode) diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp index 051b4de825..11d822c5e1 100644 --- a/scene/gui/panel_container.cpp +++ b/scene/gui/panel_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/panel_container.h b/scene/gui/panel_container.h index 92743f2c47..f27ca7fad7 100644 --- a/scene/gui/panel_container.h +++ b/scene/gui/panel_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 791c78e2b4..36bcca61a7 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/popup.h b/scene/gui/popup.h index 48e7ea9452..0355405d7c 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 7baf32173f..e777e6c26b 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,13 +36,11 @@ #include "core/string/print_string.h" #include "core/string/translation.h" -String PopupMenu::_get_accel_text(int p_item) const { - ERR_FAIL_INDEX_V(p_item, items.size(), String()); - - if (items[p_item].shortcut.is_valid()) { - return items[p_item].shortcut->get_as_text(); - } else if (items[p_item].accel) { - return keycode_get_string(items[p_item].accel); +String PopupMenu::_get_accel_text(const Item &p_item) const { + if (p_item.shortcut.is_valid()) { + return p_item.shortcut->get_as_text(); + } else if (p_item.accel) { + return keycode_get_string(p_item.accel); } return String(); } @@ -53,11 +51,9 @@ Size2 PopupMenu::_get_contents_minimum_size() const { Size2 minsize = get_theme_stylebox("panel")->get_minimum_size(); // Accounts for margin in the margin container minsize.x += scroll_container->get_v_scrollbar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content - Ref<Font> font = get_theme_font("font"); float max_w = 0; float icon_w = 0; - int font_h = font->get_height(); int check_w = MAX(get_theme_icon("checked")->get_width(), get_theme_icon("radio_checked")->get_width()) + hseparation; int accel_max_w = 0; bool has_check = false; @@ -66,7 +62,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const { Size2 size; Size2 icon_size = items[i].get_icon_size(); - size.height = MAX(icon_size.height, font_h); + size.height = MAX(icon_size.height, items[i].text_buf->get_size().y); icon_w = MAX(icon_size.width, icon_w); size.width += items[i].h_ofs; @@ -75,15 +71,12 @@ Size2 PopupMenu::_get_contents_minimum_size() const { has_check = true; } - String text = items[i].xl_text; - size.width += font->get_string_size(text).width; - if (i > 0) { - size.height += vseparation; - } + size.width += items[i].text_buf->get_size().x; + size.height += vseparation; if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->is_valid())) { int accel_w = hseparation * 2; - accel_w += font->get_string_size(_get_accel_text(i)).width; + accel_w += items[i].accel_text_buf->get_size().x; accel_max_w = MAX(accel_w, accel_max_w); } @@ -112,13 +105,12 @@ Size2 PopupMenu::_get_contents_minimum_size() const { } int PopupMenu::_get_items_total_height() const { - int font_height = get_theme_font("font")->get_height(); int vsep = get_theme_constant("vseparation"); // Get total height of all items by taking max of icon height and font height int items_total_height = 0; for (int i = 0; i < items.size(); i++) { - items_total_height += MAX(items[i].get_icon_size().height, font_height) + vsep; + items_total_height += MAX(items[i].get_icon_size().height, items[i].text_buf->get_size().y) + vsep; } // Subtract a separator which is not needed for the last item. @@ -150,7 +142,6 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const { Ref<StyleBox> style = get_theme_stylebox("panel"); // Accounts for margin in the margin container int vseparation = get_theme_constant("vseparation"); - float font_h = get_theme_font("font")->get_height(); Point2 ofs = style->get_offset() + Point2(0, vseparation / 2); @@ -159,11 +150,9 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const { } for (int i = 0; i < items.size(); i++) { - if (i > 0) { - ofs.y += vseparation; - } + ofs.y += i > 0 ? vseparation : (float)vseparation / 2; - ofs.y += MAX(items[i].get_icon_size().height, font_h); + ofs.y += MAX(items[i].get_icon_size().height, items[i].text_buf->get_size().y); if (p_over.y - control->get_position().y < ofs.y) { return i; @@ -190,10 +179,19 @@ void PopupMenu::_activate_submenu(int p_over) { float scroll_offset = control->get_position().y; - Point2 submenu_pos = this_pos + Point2(this_rect.size.width, items[p_over]._ofs_cache + scroll_offset); + Point2 submenu_pos; Size2 submenu_size = submenu_popup->get_size(); + if (control->is_layout_rtl()) { + submenu_pos = this_pos + Point2(-submenu_size.width, items[p_over]._ofs_cache + scroll_offset); + } else { + submenu_pos = this_pos + Point2(this_rect.size.width, items[p_over]._ofs_cache + scroll_offset); + } // Fix pos if going outside parent rect + if (submenu_pos.x < get_parent_rect().position.x) { + submenu_pos.x = this_pos.x + submenu_size.width; + } + if (submenu_pos.x + submenu_size.width > get_parent_rect().size.width) { submenu_pos.x = this_pos.x - submenu_size.width; } @@ -230,12 +228,13 @@ void PopupMenu::_submenu_timeout() { } void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { - if (p_event->is_action("ui_down") && p_event->is_pressed() && mouse_over != items.size() - 1) { + if (p_event->is_action("ui_down") && p_event->is_pressed()) { int search_from = mouse_over + 1; if (search_from >= items.size()) { search_from = 0; } + bool match_found = false; for (int i = search_from; i < items.size(); i++) { if (!items[i].separator && !items[i].disabled) { mouse_over = i; @@ -243,15 +242,31 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { _scroll_to_item(i); control->update(); set_input_as_handled(); + match_found = true; break; } } - } else if (p_event->is_action("ui_up") && p_event->is_pressed() && mouse_over != 0) { + + if (!match_found) { + // If the last item is not selectable, try re-searching from the start. + for (int i = 0; i < search_from; i++) { + if (!items[i].separator && !items[i].disabled) { + mouse_over = i; + emit_signal("id_focused", i); + _scroll_to_item(i); + control->update(); + set_input_as_handled(); + break; + } + } + } + } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { int search_from = mouse_over - 1; if (search_from < 0) { search_from = items.size() - 1; } + bool match_found = false; for (int i = search_from; i >= 0; i--) { if (!items[i].separator && !items[i].disabled) { mouse_over = i; @@ -259,9 +274,24 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { _scroll_to_item(i); control->update(); set_input_as_handled(); + match_found = true; break; } } + + if (!match_found) { + // If the first item is not selectable, try re-searching from the end. + for (int i = items.size() - 1; i >= search_from; i--) { + if (!items[i].separator && !items[i].disabled) { + mouse_over = i; + emit_signal("id_focused", i); + _scroll_to_item(i); + control->update(); + set_input_as_handled(); + break; + } + } + } } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { Node *n = get_parent(); if (n && Object::cast_to<PopupMenu>(n)) { @@ -287,7 +317,11 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { // Make an area which does not include v scrollbar, so that items are not activated when dragging scrollbar. Rect2 item_clickable_area = scroll_container->get_rect(); if (scroll_container->get_v_scrollbar()->is_visible_in_tree()) { - item_clickable_area.size.width -= scroll_container->get_v_scrollbar()->get_size().width; + if (is_layout_rtl()) { + item_clickable_area.position.x += scroll_container->get_v_scrollbar()->get_size().width; + } else { + item_clickable_area.size.width -= scroll_container->get_v_scrollbar()->get_size().width; + } } Ref<InputEventMouseButton> b = p_event; @@ -417,13 +451,19 @@ void PopupMenu::_draw_items() { margin_size.width = margin_container->get_theme_constant("margin_right") + margin_container->get_theme_constant("margin_left"); margin_size.height = margin_container->get_theme_constant("margin_top") + margin_container->get_theme_constant("margin_bottom"); + bool rtl = control->is_layout_rtl(); Ref<StyleBox> style = get_theme_stylebox("panel"); Ref<StyleBox> hover = get_theme_stylebox("hover"); - Ref<Font> font = get_theme_font("font"); // In Item::checkable_type enum order (less the non-checkable member) Ref<Texture2D> check[] = { get_theme_icon("checked"), get_theme_icon("radio_checked") }; Ref<Texture2D> uncheck[] = { get_theme_icon("unchecked"), get_theme_icon("radio_unchecked") }; - Ref<Texture2D> submenu = get_theme_icon("submenu"); + Ref<Texture2D> submenu; + if (rtl) { + submenu = get_theme_icon("submenu_mirrored"); + } else { + submenu = get_theme_icon("submenu"); + } + Ref<StyleBox> separator = get_theme_stylebox("separator"); Ref<StyleBox> labeled_separator_left = get_theme_stylebox("labeled_separator_left"); Ref<StyleBox> labeled_separator_right = get_theme_stylebox("labeled_separator_right"); @@ -434,7 +474,7 @@ void PopupMenu::_draw_items() { Color font_color_disabled = get_theme_color("font_color_disabled"); Color font_color_accel = get_theme_color("font_color_accel"); Color font_color_hover = get_theme_color("font_color_hover"); - float font_h = font->get_height(); + Color font_color_separator = get_theme_color("font_color_separator"); float scroll_width = scroll_container->get_v_scrollbar()->is_visible_in_tree() ? scroll_container->get_v_scrollbar()->get_size().width : 0; float display_width = control->get_size().width - scroll_width; @@ -462,17 +502,21 @@ void PopupMenu::_draw_items() { // Loop through all items and draw each. for (int i = 0; i < items.size(); i++) { - // If not the first item, add the separation space between items. - if (i > 0) { - ofs.y += vseparation; - } + // For the first item only add half a separation. For all other items, add a whole separation to the offset. + ofs.y += i > 0 ? vseparation : (float)vseparation / 2; + + _shape_item(i); Point2 item_ofs = ofs; Size2 icon_size = items[i].get_icon_size(); - float h = MAX(icon_size.height, font_h); + float h = MAX(icon_size.height, items[i].text_buf->get_size().y); if (i == mouse_over) { - hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(display_width + hseparation * 2, h + vseparation))); + if (rtl) { + hover->draw(ci, Rect2(item_ofs + Point2(scroll_width, -vseparation / 2), Size2(display_width, h + vseparation))); + } else { + hover->draw(ci, Rect2(item_ofs + Point2(0, -vseparation / 2), Size2(display_width, h + vseparation))); + } } String text = items[i].xl_text; @@ -482,7 +526,7 @@ void PopupMenu::_draw_items() { if (items[i].separator) { int sep_h = separator->get_center_size().height + separator->get_minimum_size().height; if (text != String()) { - int text_size = font->get_string_size(text).width; + int text_size = items[i].text_buf->get_size().width; int text_center = display_width / 2; int text_left = text_center - text_size / 2; int text_right = text_center + text_size / 2; @@ -502,36 +546,54 @@ void PopupMenu::_draw_items() { // Checkboxes if (items[i].checkable_type) { Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr(); - icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color); + if (rtl) { + icon->draw(ci, Size2(control->get_size().width - item_ofs.x - icon->get_width(), item_ofs.y) + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color); + } else { + icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color); + } } // Icon if (!items[i].icon.is_null()) { - items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); + if (rtl) { + items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); + } else { + items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); + } } // Submenu arrow on right hand side if (items[i].submenu != "") { - submenu->draw(ci, Point2(display_width - submenu->get_width(), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); + if (rtl) { + submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); + } else { + submenu->draw(ci, Point2(display_width - style->get_margin(SIDE_RIGHT) - submenu->get_width(), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); + } } // Text - item_ofs.y += font->get_ascent(); if (items[i].separator) { if (text != String()) { - int center = (display_width - font->get_string_size(text).width) / 2; - font->draw(ci, Point2(center, item_ofs.y + Math::floor((h - font_h) / 2.0)), text, font_color_disabled); + int center = (display_width - items[i].text_buf->get_size().width) / 2; + items[i].text_buf->draw(ci, Point2(center, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0)), font_color_separator); } } else { item_ofs.x += icon_ofs + check_ofs; - font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color)); + if (rtl) { + items[i].text_buf->draw(ci, Size2(control->get_size().width - items[i].text_buf->get_size().width - item_ofs.x, item_ofs.y) + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)), items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color)); + } else { + items[i].text_buf->draw(ci, item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)), items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color)); + } } // Accelerator / Shortcut if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->is_valid())) { - String sc_text = _get_accel_text(i); - item_ofs.x = display_width - font->get_string_size(sc_text).width; - font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), sc_text, i == mouse_over ? font_color_hover : font_color_accel); + if (rtl) { + item_ofs.x = scroll_width + style->get_margin(SIDE_LEFT); + } else { + item_ofs.x = display_width - style->get_margin(SIDE_RIGHT) - items[i].accel_text_buf->get_size().x; + } + items[i].accel_text_buf->draw(ci, item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)), i == mouse_over ? font_color_hover : font_color_accel); } // Cache the item vertical offset from the first item and the height @@ -573,6 +635,27 @@ void PopupMenu::_close_pressed() { } } +void PopupMenu::_shape_item(int p_item) { + if (items.write[p_item].dirty) { + items.write[p_item].text_buf->clear(); + + Ref<Font> font = get_theme_font("font"); + int font_size = get_theme_font_size("font_size"); + + if (items[p_item].text_direction == Control::TEXT_DIRECTION_INHERITED) { + items.write[p_item].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + items.write[p_item].text_buf->set_direction((TextServer::Direction)items[p_item].text_direction); + } + items.write[p_item].text_buf->add_string(items.write[p_item].xl_text, font, font_size, items[p_item].opentype_features, (items[p_item].language != "") ? items[p_item].language : TranslationServer::get_singleton()->get_tool_locale()); + + items.write[p_item].accel_text_buf->clear(); + items.write[p_item].accel_text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + items.write[p_item].accel_text_buf->add_string(_get_accel_text(items.write[p_item]), font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + items.write[p_item].dirty = false; + } +} + void PopupMenu::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -583,9 +666,12 @@ void PopupMenu::_notification(int p_what) { set_submenu_popup_delay(pm_delay); } } break; + case NOTIFICATION_THEME_CHANGED: + case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { for (int i = 0; i < items.size(); i++) { items.write[i].xl_text = tr(items[i].text); + items.write[i].dirty = true; } child_controls_changed(); @@ -653,10 +739,10 @@ void PopupMenu::_notification(int p_what) { // Set margin on the margin container Ref<StyleBox> panel_style = get_theme_stylebox("panel"); - margin_container->add_theme_constant_override("margin_top", panel_style->get_margin(Margin::MARGIN_TOP)); - margin_container->add_theme_constant_override("margin_bottom", panel_style->get_margin(Margin::MARGIN_BOTTOM)); - margin_container->add_theme_constant_override("margin_left", panel_style->get_margin(Margin::MARGIN_LEFT)); - margin_container->add_theme_constant_override("margin_right", panel_style->get_margin(Margin::MARGIN_RIGHT)); + margin_container->add_theme_constant_override("margin_top", panel_style->get_margin(Side::SIDE_TOP)); + margin_container->add_theme_constant_override("margin_bottom", panel_style->get_margin(Side::SIDE_BOTTOM)); + margin_container->add_theme_constant_override("margin_left", panel_style->get_margin(Side::SIDE_LEFT)); + margin_container->add_theme_constant_override("margin_right", panel_style->get_margin(Side::SIDE_RIGHT)); } } break; } @@ -676,6 +762,7 @@ void PopupMenu::add_item(const String &p_label, int p_id, uint32_t p_accel) { Item item; ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -685,6 +772,7 @@ void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_labe ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); item.icon = p_icon; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -694,6 +782,7 @@ void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -704,6 +793,7 @@ void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String & item.icon = p_icon; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -713,6 +803,7 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_id, uint32_t p ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -723,6 +814,7 @@ void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const St item.icon = p_icon; item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -733,6 +825,7 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int item.max_states = p_max_states; item.state = p_default_state; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -750,6 +843,7 @@ void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_g Item item; ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -759,6 +853,7 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortc ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); item.icon = p_icon; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -768,6 +863,7 @@ void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bo ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -778,6 +874,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref< item.icon = p_icon; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -787,6 +884,7 @@ void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_ ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -797,6 +895,7 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, cons item.icon = p_icon; item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -808,6 +907,7 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, item.id = p_id == -1 ? items.size() : p_id; item.submenu = p_submenu; items.push_back(item); + _shape_item(items.size() - 1); control->update(); child_controls_changed(); } @@ -821,11 +921,48 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) { ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].text = p_text; items.write[p_idx].xl_text = tr(p_text); + _shape_item(p_idx); control->update(); child_controls_changed(); } +void PopupMenu::set_item_text_direction(int p_item, Control::TextDirection p_text_direction) { + ERR_FAIL_INDEX(p_item, items.size()); + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (items[p_item].text_direction != p_text_direction) { + items.write[p_item].text_direction = p_text_direction; + items.write[p_item].dirty = true; + control->update(); + } +} + +void PopupMenu::clear_item_opentype_features(int p_item) { + ERR_FAIL_INDEX(p_item, items.size()); + items.write[p_item].opentype_features.clear(); + items.write[p_item].dirty = true; + control->update(); +} + +void PopupMenu::set_item_opentype_feature(int p_item, const String &p_name, int p_value) { + ERR_FAIL_INDEX(p_item, items.size()); + int32_t tag = TS->name_to_tag(p_name); + if (!items[p_item].opentype_features.has(tag) || (int)items[p_item].opentype_features[tag] != p_value) { + items.write[p_item].opentype_features[tag] = p_value; + items.write[p_item].dirty = true; + control->update(); + } +} + +void PopupMenu::set_item_language(int p_item, const String &p_language) { + ERR_FAIL_INDEX(p_item, items.size()); + if (items[p_item].language != p_language) { + items.write[p_item].language = p_language; + items.write[p_item].dirty = true; + control->update(); + } +} + void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) { ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].icon = p_icon; @@ -854,6 +991,7 @@ void PopupMenu::set_item_id(int p_idx, int p_id) { void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) { ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].accel = p_accel; + items.write[p_idx].dirty = true; control->update(); child_controls_changed(); @@ -892,6 +1030,25 @@ String PopupMenu::get_item_text(int p_idx) const { return items[p_idx].text; } +Control::TextDirection PopupMenu::get_item_text_direction(int p_item) const { + ERR_FAIL_INDEX_V(p_item, items.size(), Control::TEXT_DIRECTION_INHERITED); + return items[p_item].text_direction; +} + +int PopupMenu::get_item_opentype_feature(int p_item, const String &p_name) const { + ERR_FAIL_INDEX_V(p_item, items.size(), -1); + int32_t tag = TS->name_to_tag(p_name); + if (!items[p_item].opentype_features.has(tag)) { + return -1; + } + return items[p_item].opentype_features[tag]; +} + +String PopupMenu::get_item_language(int p_item) const { + ERR_FAIL_INDEX_V(p_item, items.size(), ""); + return items[p_item].language; +} + int PopupMenu::get_item_idx_from_text(const String &text) const { for (int idx = 0; idx < items.size(); idx++) { if (items[idx].text == text) { @@ -998,6 +1155,7 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bo } items.write[p_idx].shortcut = p_shortcut; items.write[p_idx].shortcut_is_global = p_global; + items.write[p_idx].dirty = true; if (items[p_idx].shortcut.is_valid()) { _ref_shortcut(items[p_idx].shortcut); @@ -1185,10 +1343,10 @@ void PopupMenu::remove_item(int p_idx) { child_controls_changed(); } -void PopupMenu::add_separator(const String &p_text) { +void PopupMenu::add_separator(const String &p_text, int p_id) { Item sep; sep.separator = true; - sep.id = -1; + sep.id = p_id; if (p_text != String()) { sep.text = p_text; sep.xl_text = tr(p_text); @@ -1390,6 +1548,9 @@ void PopupMenu::_bind_methods() { 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_text_direction", "idx", "direction"), &PopupMenu::set_item_text_direction); + ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "idx", "tag", "value"), &PopupMenu::set_item_opentype_feature); + ClassDB::bind_method(D_METHOD("set_item_language", "idx", "language"), &PopupMenu::set_item_language); ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &PopupMenu::set_item_icon); ClassDB::bind_method(D_METHOD("set_item_checked", "idx", "checked"), &PopupMenu::set_item_checked); ClassDB::bind_method(D_METHOD("set_item_id", "idx", "id"), &PopupMenu::set_item_id); @@ -1409,6 +1570,10 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("toggle_item_multistate", "idx"), &PopupMenu::toggle_item_multistate); ClassDB::bind_method(D_METHOD("get_item_text", "idx"), &PopupMenu::get_item_text); + ClassDB::bind_method(D_METHOD("get_item_text_direction", "idx"), &PopupMenu::get_item_text_direction); + ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "idx", "tag"), &PopupMenu::get_item_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "idx"), &PopupMenu::clear_item_opentype_features); + ClassDB::bind_method(D_METHOD("get_item_language", "idx"), &PopupMenu::get_item_language); ClassDB::bind_method(D_METHOD("get_item_icon", "idx"), &PopupMenu::get_item_icon); ClassDB::bind_method(D_METHOD("is_item_checked", "idx"), &PopupMenu::is_item_checked); ClassDB::bind_method(D_METHOD("get_item_id", "idx"), &PopupMenu::get_item_id); @@ -1429,7 +1594,7 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item); - ClassDB::bind_method(D_METHOD("add_separator", "label"), &PopupMenu::add_separator, DEFVAL(String())); + ClassDB::bind_method(D_METHOD("add_separator", "label", "id"), &PopupMenu::add_separator, DEFVAL(String()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear); ClassDB::bind_method(D_METHOD("_set_items"), &PopupMenu::_set_items); @@ -1465,14 +1630,13 @@ void PopupMenu::_bind_methods() { void PopupMenu::popup(const Rect2 &p_bounds) { moved = Vector2(); popup_time_msec = OS::get_singleton()->get_ticks_msec(); - set_as_minsize(); Popup::popup(p_bounds); } PopupMenu::PopupMenu() { // Margin Container margin_container = memnew(MarginContainer); - margin_container->set_anchors_and_margins_preset(Control::PRESET_WIDE); + margin_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE); add_child(margin_container); margin_container->connect("draw", callable_mp(this, &PopupMenu::_draw_background)); @@ -1484,7 +1648,7 @@ PopupMenu::PopupMenu() { // The control which will display the items control = memnew(Control); control->set_clip_contents(false); - control->set_anchors_and_margins_preset(Control::PRESET_WIDE); + control->set_anchors_and_offsets_preset(Control::PRESET_WIDE); control->set_h_size_flags(Control::SIZE_EXPAND_FILL); control->set_v_size_flags(Control::SIZE_EXPAND_FILL); scroll_container->add_child(control); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index a2e7d7e6cd..c050e61d50 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,6 +35,7 @@ #include "scene/gui/popup.h" #include "scene/gui/scroll_container.h" #include "scene/gui/shortcut.h" +#include "scene/resources/text_line.h" class PopupMenu : public Popup { GDCLASS(PopupMenu, Popup); @@ -43,6 +44,13 @@ class PopupMenu : public Popup { Ref<Texture2D> icon; String text; String xl_text; + Ref<TextLine> text_buf; + Ref<TextLine> accel_text_buf; + + Dictionary opentype_features; + String language; + Control::TextDirection text_direction = Control::TEXT_DIRECTION_AUTO; + bool checked; enum { CHECKABLE_TYPE_NONE, @@ -53,6 +61,7 @@ class PopupMenu : public Popup { int state; bool separator; bool disabled; + bool dirty; int id; Variant metadata; String submenu; @@ -71,6 +80,9 @@ class PopupMenu : public Popup { } Item() { + text_buf.instance(); + accel_text_buf.instance(); + dirty = true; checked = false; checkable_type = CHECKABLE_TYPE_NONE; separator = false; @@ -97,13 +109,15 @@ class PopupMenu : public Popup { int mouse_over; int submenu_over; Rect2 parent_rect; - String _get_accel_text(int p_item) const; + String _get_accel_text(const Item &p_item) const; int _get_mouse_over(const Point2 &p_over) const; virtual Size2 _get_contents_minimum_size() const override; int _get_items_total_height() const; void _scroll_to_item(int p_item); + void _shape_item(int p_item); + void _gui_input(const Ref<InputEvent> &p_event); void _activate_submenu(int p_over); void _submenu_timeout(); @@ -161,6 +175,11 @@ public: 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_text_direction(int p_idx, Control::TextDirection p_text_direction); + void set_item_opentype_feature(int p_idx, const String &p_name, int p_value); + void clear_item_opentype_features(int p_idx); + void set_item_language(int p_idx, const String &p_language); void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon); void set_item_checked(int p_idx, bool p_checked); void set_item_id(int p_idx, int p_id); @@ -181,6 +200,9 @@ public: void toggle_item_checked(int p_idx); String get_item_text(int p_idx) const; + Control::TextDirection get_item_text_direction(int p_idx) const; + int get_item_opentype_feature(int p_idx, const String &p_name) const; + String get_item_language(int p_idx) const; int get_item_idx_from_text(const String &text) const; Ref<Texture2D> get_item_icon(int p_idx) const; bool is_item_checked(int p_idx) const; @@ -206,7 +228,7 @@ public: void remove_item(int p_idx); - void add_separator(const String &p_text = String()); + void add_separator(const String &p_text = String(), int p_id = -1); void clear(); diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index 9246f1723d..1b33cc6ffb 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,17 +29,21 @@ /*************************************************************************/ #include "progress_bar.h" +#include "scene/resources/text_line.h" Size2 ProgressBar::get_minimum_size() const { Ref<StyleBox> bg = get_theme_stylebox("bg"); Ref<StyleBox> fg = get_theme_stylebox("fg"); Ref<Font> font = get_theme_font("font"); + int font_size = get_theme_font_size("font_size"); Size2 minimum_size = bg->get_minimum_size(); minimum_size.height = MAX(minimum_size.height, fg->get_minimum_size().height); minimum_size.width = MAX(minimum_size.width, fg->get_minimum_size().width); if (percent_visible) { - minimum_size.height = MAX(minimum_size.height, bg->get_minimum_size().height + font->get_height()); + String txt = "100%"; + TextLine tl = TextLine(txt, font, font_size); + minimum_size.height = MAX(minimum_size.height, bg->get_minimum_size().height + tl.get_size().y); } else { // this is needed, else the progressbar will collapse minimum_size.width = MAX(minimum_size.width, 1); minimum_size.height = MAX(minimum_size.height, 1); @@ -52,6 +56,7 @@ void ProgressBar::_notification(int p_what) { Ref<StyleBox> bg = get_theme_stylebox("bg"); Ref<StyleBox> fg = get_theme_stylebox("fg"); Ref<Font> font = get_theme_font("font"); + int font_size = get_theme_font_size("font_size"); Color font_color = get_theme_color("font_color"); draw_style_box(bg, Rect2(Point2(), get_size())); @@ -59,12 +64,17 @@ void ProgressBar::_notification(int p_what) { int mp = fg->get_minimum_size().width; int p = r * (get_size().width - mp); if (p > 0) { - draw_style_box(fg, Rect2(Point2(), Size2(p + fg->get_minimum_size().width, get_size().height))); + if (is_layout_rtl()) { + draw_style_box(fg, Rect2(Point2(p, 0), Size2(fg->get_minimum_size().width, get_size().height))); + } else { + draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); + } } if (percent_visible) { - String txt = itos(int(get_as_ratio() * 100)) + "%"; - font->draw_halign(get_canvas_item(), Point2(0, font->get_ascent() + (get_size().height - font->get_height()) / 2), HALIGN_CENTER, get_size().width, txt, font_color); + String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign(); + TextLine tl = TextLine(txt, font, font_size); + tl.draw(get_canvas_item(), (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round(), font_color); } } } diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h index f00f993adf..3317846108 100644 --- a/scene/gui/progress_bar.h +++ b/scene/gui/progress_bar.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index bdb9f408f0..b9ac6d7505 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,7 +34,7 @@ String Range::get_configuration_warning() const { String warning = Control::get_configuration_warning(); if (shared->exp_ratio && shared->min <= 0) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0."); diff --git a/scene/gui/range.h b/scene/gui/range.h index 9ba367aaa4..68ff46bd74 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/reference_rect.cpp b/scene/gui/reference_rect.cpp index 773acb2713..6d7f2cfd57 100644 --- a/scene/gui/reference_rect.cpp +++ b/scene/gui/reference_rect.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/reference_rect.h b/scene/gui/reference_rect.h index becbbf47c5..7097e83a15 100644 --- a/scene/gui/reference_rect.h +++ b/scene/gui/reference_rect.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp index 76ca8abcc7..39718a269a 100644 --- a/scene/gui/rich_text_effect.cpp +++ b/scene/gui/rich_text_effect.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -64,11 +64,8 @@ RichTextEffect::RichTextEffect() { } void CharFXTransform::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_relative_index"), &CharFXTransform::get_relative_index); - ClassDB::bind_method(D_METHOD("set_relative_index", "index"), &CharFXTransform::set_relative_index); - - ClassDB::bind_method(D_METHOD("get_absolute_index"), &CharFXTransform::get_absolute_index); - ClassDB::bind_method(D_METHOD("set_absolute_index", "index"), &CharFXTransform::set_absolute_index); + ClassDB::bind_method(D_METHOD("get_range"), &CharFXTransform::get_range); + ClassDB::bind_method(D_METHOD("set_range", "range"), &CharFXTransform::set_range); ClassDB::bind_method(D_METHOD("get_elapsed_time"), &CharFXTransform::get_elapsed_time); ClassDB::bind_method(D_METHOD("set_elapsed_time", "time"), &CharFXTransform::set_elapsed_time); @@ -76,6 +73,9 @@ void CharFXTransform::_bind_methods() { ClassDB::bind_method(D_METHOD("is_visible"), &CharFXTransform::is_visible); ClassDB::bind_method(D_METHOD("set_visibility", "visibility"), &CharFXTransform::set_visibility); + ClassDB::bind_method(D_METHOD("is_outline"), &CharFXTransform::is_outline); + ClassDB::bind_method(D_METHOD("set_outline", "outline"), &CharFXTransform::set_outline); + ClassDB::bind_method(D_METHOD("get_offset"), &CharFXTransform::get_offset); ClassDB::bind_method(D_METHOD("set_offset", "offset"), &CharFXTransform::set_offset); @@ -85,27 +85,24 @@ void CharFXTransform::_bind_methods() { ClassDB::bind_method(D_METHOD("get_environment"), &CharFXTransform::get_environment); ClassDB::bind_method(D_METHOD("set_environment", "environment"), &CharFXTransform::set_environment); - ClassDB::bind_method(D_METHOD("get_character"), &CharFXTransform::get_character); - ClassDB::bind_method(D_METHOD("set_character", "character"), &CharFXTransform::set_character); + ClassDB::bind_method(D_METHOD("get_glyph_index"), &CharFXTransform::get_glyph_index); + ClassDB::bind_method(D_METHOD("set_glyph_index", "glyph_index"), &CharFXTransform::set_glyph_index); + + ClassDB::bind_method(D_METHOD("get_font"), &CharFXTransform::get_font); + ClassDB::bind_method(D_METHOD("set_font", "font"), &CharFXTransform::set_font); - ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_index"), "set_relative_index", "get_relative_index"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "absolute_index"), "set_absolute_index", "get_absolute_index"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "range"), "set_range", "get_range"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "elapsed_time"), "set_elapsed_time", "get_elapsed_time"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visibility", "is_visible"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "outline"), "set_outline", "is_outline"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "env"), "set_environment", "get_environment"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "character"), "set_character", "get_character"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "glyph_index"), "set_glyph_index", "get_glyph_index"); + ADD_PROPERTY(PropertyInfo(Variant::RID, "font"), "set_font", "get_font"); } CharFXTransform::CharFXTransform() { - relative_index = 0; - absolute_index = 0; - visibility = true; - offset = Point2(); - color = Color(); - character = 0; - elapsed_time = 0.0f; } CharFXTransform::~CharFXTransform() { diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h index e6b9f09e4d..f2e2823eff 100644 --- a/scene/gui/rich_text_effect.h +++ b/scene/gui/rich_text_effect.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -54,32 +54,37 @@ protected: static void _bind_methods(); public: - uint64_t relative_index; - uint64_t absolute_index; - bool visibility; + Vector2i range; + bool visibility = true; + bool outline = false; Point2 offset; Color color; - char32_t character; - float elapsed_time; + float elapsed_time = 0.0f; Dictionary environment; + uint32_t glpyh_index = 0; + RID font; CharFXTransform(); ~CharFXTransform(); - uint64_t get_relative_index() { return relative_index; } - void set_relative_index(uint64_t p_index) { relative_index = p_index; } - uint64_t get_absolute_index() { return absolute_index; } - void set_absolute_index(uint64_t p_index) { absolute_index = p_index; } + Vector2i get_range() { return range; } + void set_range(const Vector2i &p_range) { range = p_range; } float get_elapsed_time() { return elapsed_time; } void set_elapsed_time(float p_elapsed_time) { elapsed_time = p_elapsed_time; } bool is_visible() { return visibility; } - void set_visibility(bool p_vis) { visibility = p_vis; } + void set_visibility(bool p_visibility) { visibility = p_visibility; } + bool is_outline() { return outline; } + void set_outline(bool p_outline) { outline = p_outline; } Point2 get_offset() { return offset; } void set_offset(Point2 p_offset) { offset = p_offset; } Color get_color() { return color; } void set_color(Color p_color) { color = p_color; } - int get_character() { return (int)character; } - void set_character(int p_char) { character = (char32_t)p_char; } + + uint32_t get_glyph_index() const { return glpyh_index; }; + void set_glyph_index(uint32_t p_glpyh_index) { glpyh_index = p_glpyh_index; }; + RID get_font() const { return font; }; + void set_font(RID p_font) { font = p_font; }; + Dictionary get_environment() { return environment; } void set_environment(Dictionary p_environment) { environment = p_environment; } }; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index e8acac172c..a1aa72b29a 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -140,741 +140,1114 @@ Rect2 RichTextLabel::_get_text_rect() { return Rect2(style->get_offset(), get_size() - style->get_minimum_size()); } -int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) { - ERR_FAIL_INDEX_V((int)p_mode, 3, 0); - - RID ci; - if (r_outside) { - *r_outside = false; +RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item_from, RichTextLabel::Item *p_item_to, int p_position) { + int offset = 0; + for (Item *it = p_item_from; it && it != p_item_to; it = _get_next_item(it)) { + switch (it->type) { + case ITEM_TEXT: { + ItemText *t = (ItemText *)it; + offset += t->text.length(); + if (offset > p_position) { + return it; + } + } break; + case ITEM_NEWLINE: + case ITEM_IMAGE: + case ITEM_TABLE: { + offset += 1; + } break; + default: + break; + } } - if (p_mode == PROCESS_DRAW) { - ci = get_canvas_item(); + return p_item_from; +} - if (r_click_item) { - *r_click_item = nullptr; - } +String RichTextLabel::_roman(int p_num, bool p_capitalize) const { + if (p_num > 3999) { + return "ERR"; + }; + String s; + if (p_capitalize) { + String M[] = { "", "M", "MM", "MMM" }; + String C[] = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" }; + String X[] = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }; + String I[] = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; + s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10]; + } else { + String M[] = { "", "m", "mm", "mmm" }; + String C[] = { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" }; + String X[] = { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" }; + String I[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" }; + s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10]; } - Line &l = p_frame->lines.write[p_line]; - Item *it = l.from; + return s; +} - int line_ofs = 0; - int margin = _find_margin(it, p_base_font); - Align align = _find_align(it); - int line = 0; - int spaces = 0; +String RichTextLabel::_letters(int p_num, bool p_capitalize) const { + int64_t n = p_num; - int height = get_size().y; + int chars = 0; + do { + n /= 24; + chars++; + } while (n); - if (p_mode != PROCESS_CACHE) { - ERR_FAIL_INDEX_V(line, l.offset_caches.size(), 0); - line_ofs = l.offset_caches[line]; - } + String s; + s.resize(chars + 1); + char32_t *c = s.ptrw(); + c[chars] = 0; + n = p_num; + do { + int mod = ABS(n % 24); + char a = (p_capitalize ? 'A' : 'a'); + c[--chars] = a + mod - 1; - if (p_mode == PROCESS_CACHE) { - l.offset_caches.clear(); - l.height_caches.clear(); - l.ascent_caches.clear(); - l.descent_caches.clear(); - l.char_count = 0; - l.minimum_width = 0; - l.maximum_width = 0; - } + n /= 24; + } while (n); - int wofs = margin; - int spaces_size = 0; - int align_ofs = 0; + return s; +} - if (p_mode != PROCESS_CACHE && align != ALIGN_FILL) { - wofs += line_ofs; - } +void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width) { + ERR_FAIL_COND(p_frame == nullptr); + ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); - int begin = margin; + Line &l = p_frame->lines.write[p_line]; + + l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size); + l.text_buf->set_width(p_width - l.offset.x); - Ref<Font> cfont = _find_font(it); - if (cfont.is_null()) { - cfont = p_base_font; + if (tab_size > 0) { // Align inline tabs. + Vector<float> tabs; + tabs.push_back(tab_size * p_base_font->get_char_size('m', 0, p_base_font_size).width); + l.text_buf->tab_align(tabs); } - //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed - int line_height = cfont->get_height(); - int line_ascent = cfont->get_ascent(); - int line_descent = cfont->get_descent(); + Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { + switch (it->type) { + case ITEM_TABLE: { + ItemTable *table = static_cast<ItemTable *>(it); + int hseparation = get_theme_constant("table_hseparation"); + int vseparation = get_theme_constant("table_vseparation"); + int col_count = table->columns.size(); - int backtrack = 0; // for dynamic hidden content. + for (int i = 0; i < col_count; i++) { + table->columns.write[i].width = 0; + } - int nonblank_line_count = 0; //number of nonblank lines as counted during PROCESS_DRAW + int idx = 0; + for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames. + ItemFrame *frame = static_cast<ItemFrame *>(E->get()); + for (int i = 0; i < frame->lines.size(); i++) { + _resize_line(frame, i, p_base_font, p_base_font_size, 1); + } + idx++; + } - Variant meta; + // Compute minimum width for each cell. + const int available_width = p_width - hseparation * (col_count - 1); -#define RETURN return nonblank_line_count - -#define NEW_LINE \ - { \ - if (p_mode != PROCESS_CACHE) { \ - line++; \ - backtrack = 0; \ - if (!line_is_blank) { \ - nonblank_line_count++; \ - } \ - line_is_blank = true; \ - if (line < l.offset_caches.size()) \ - line_ofs = l.offset_caches[line]; \ - wofs = margin; \ - if (align != ALIGN_FILL) \ - wofs += line_ofs; \ - } else { \ - int used = wofs - margin; \ - switch (align) { \ - case ALIGN_LEFT: \ - l.offset_caches.push_back(0); \ - break; \ - case ALIGN_CENTER: \ - l.offset_caches.push_back(((p_width - margin) - used) / 2); \ - break; \ - case ALIGN_RIGHT: \ - l.offset_caches.push_back(((p_width - margin) - used)); \ - break; \ - case ALIGN_FILL: \ - l.offset_caches.push_back(line_wrapped ? ((p_width - margin) - used) : 0); \ - break; \ - } \ - l.height_caches.push_back(line_height); \ - l.ascent_caches.push_back(line_ascent); \ - l.descent_caches.push_back(line_descent); \ - l.space_caches.push_back(spaces); \ - } \ - line_wrapped = false; \ - y += line_height + get_theme_constant(SceneStringNames::get_singleton()->line_separation); \ - line_height = 0; \ - line_ascent = 0; \ - line_descent = 0; \ - spaces = 0; \ - spaces_size = 0; \ - wofs = begin; \ - align_ofs = 0; \ - if (p_mode != PROCESS_CACHE) { \ - lh = line < l.height_caches.size() ? l.height_caches[line] : 1; \ - line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; \ - line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; \ - if (align != ALIGN_FILL) { \ - if (line < l.offset_caches.size()) { \ - wofs = l.offset_caches[line]; \ - } \ - } \ - } \ - if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x < p_ofs.x + wofs) { \ - if (r_outside) \ - *r_outside = true; \ - *r_click_item = it; \ - *r_click_char = rchar; \ - RETURN; \ - } \ - } - -#define ENSURE_WIDTH(m_width) \ - if (p_mode == PROCESS_CACHE) { \ - l.maximum_width = MAX(l.maximum_width, MIN(p_width, wofs + m_width)); \ - l.minimum_width = MAX(l.minimum_width, m_width); \ - } \ - if (wofs - backtrack + m_width > p_width) { \ - line_wrapped = true; \ - if (p_mode == PROCESS_CACHE) { \ - if (spaces > 0) \ - spaces -= 1; \ - } \ - const bool x_in_range = (p_click_pos.x > p_ofs.x + wofs) && (!p_frame->cell || p_click_pos.x < p_ofs.x + p_width); \ - if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && x_in_range) { \ - if (r_outside) \ - *r_outside = true; \ - *r_click_item = it; \ - *r_click_char = rchar; \ - RETURN; \ - } \ - NEW_LINE \ - } - -#define ADVANCE(m_width) \ - { \ - if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x >= p_ofs.x + wofs && p_click_pos.x < p_ofs.x + wofs + m_width) { \ - if (r_outside) \ - *r_outside = false; \ - *r_click_item = it; \ - *r_click_char = rchar; \ - RETURN; \ - } \ - wofs += m_width; \ - } - -#define CHECK_HEIGHT(m_height) \ - if (m_height > line_height) { \ - line_height = m_height; \ - } - -#define YRANGE_VISIBLE(m_top, m_height) \ - (m_height > 0 && ((m_top >= 0 && m_top < height) || ((m_top + m_height - 1) >= 0 && (m_top + m_height - 1) < height))) - - Color selection_fg; - Color selection_bg; - - if (p_mode == PROCESS_DRAW) { - selection_fg = get_theme_color("font_color_selected"); - selection_bg = get_theme_color("selection_color"); - } - - int rchar = 0; - int lh = 0; - bool line_is_blank = true; - bool line_wrapped = false; - int fh = 0; + // Compute available width and total ratio (for expanders). + int total_ratio = 0; + int remaining_width = available_width; + table->total_width = hseparation; - while (it) { - switch (it->type) { - case ITEM_ALIGN: { - ItemAlign *align_it = static_cast<ItemAlign *>(it); + for (int i = 0; i < col_count; i++) { + remaining_width -= table->columns[i].min_width; + if (table->columns[i].max_width > table->columns[i].min_width) { + table->columns.write[i].expand = true; + } + if (table->columns[i].expand) { + total_ratio += table->columns[i].expand_ratio; + } + } - align = align_it->align; + // Assign actual widths. + for (int i = 0; i < col_count; i++) { + table->columns.write[i].width = table->columns[i].min_width; + if (table->columns[i].expand && total_ratio > 0) { + table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; + } + table->total_width += table->columns[i].width + hseparation; + } - } break; - case ITEM_INDENT: { - if (it != l.from) { - ItemIndent *indent_it = static_cast<ItemIndent *>(it); - - int indent = indent_it->level * tab_size * cfont->get_char_size(' ').width; - margin += indent; - begin += indent; - wofs += indent; + // Resize to max_width if needed and distribute the remaining space. + bool table_need_fit = true; + while (table_need_fit) { + table_need_fit = false; + // Fit slim. + for (int i = 0; i < col_count; i++) { + if (!table->columns[i].expand) { + continue; + } + int dif = table->columns[i].width - table->columns[i].max_width; + if (dif > 0) { + table_need_fit = true; + table->columns.write[i].width = table->columns[i].max_width; + table->total_width -= dif; + total_ratio -= table->columns[i].expand_ratio; + } + } + // Grow. + remaining_width = available_width - table->total_width; + if (remaining_width > 0 && total_ratio > 0) { + for (int i = 0; i < col_count; i++) { + if (table->columns[i].expand) { + int dif = table->columns[i].max_width - table->columns[i].width; + if (dif > 0) { + int slice = table->columns[i].expand_ratio * remaining_width / total_ratio; + int incr = MIN(dif, slice); + table->columns.write[i].width += incr; + table->total_width += incr; + } + } + } + } } + // Update line width and get total height. + idx = 0; + table->total_height = 0; + table->rows.clear(); + + Vector2 offset; + float row_height = 0; + + for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames. + ItemFrame *frame = static_cast<ItemFrame *>(E->get()); + + int column = idx % col_count; + + offset.x += frame->padding.position.x; + float yofs = frame->padding.position.y; + for (int i = 0; i < frame->lines.size(); i++) { + frame->lines.write[i].text_buf->set_width(table->columns[column].width); + table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x)); + + if (i > 0) { + frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y; + } else { + frame->lines.write[i].offset.y = 0; + } + frame->lines.write[i].offset += Vector2(offset.x, offset.y); + + float h = frame->lines[i].text_buf->get_size().y; + if (frame->min_size_over.y > 0) { + h = MAX(h, frame->min_size_over.y); + } + if (frame->max_size_over.y > 0) { + h = MIN(h, frame->max_size_over.y); + } + yofs += h; + } + yofs += frame->padding.size.y; + offset.x += table->columns[column].width + hseparation + frame->padding.size.x; + + row_height = MAX(yofs, row_height); + if (column == col_count - 1) { + offset.x = 0; + row_height += vseparation; + table->total_height += row_height; + offset.y += row_height; + table->rows.push_back(row_height); + row_height = 0; + } + idx++; + } + l.text_buf->resize_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align); } break; - case ITEM_TEXT: { - ItemText *text = static_cast<ItemText *>(it); + default: + break; + } + } + + if (p_line > 0) { + l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y; + } else { + l.offset.y = 0; + } +} + +void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset) { + ERR_FAIL_COND(p_frame == nullptr); + ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); + + Line &l = p_frame->lines.write[p_line]; + + // Clear cache. + l.text_buf->clear(); + l.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES); + l.char_offset = *r_char_offset; + l.char_count = 0; + // Add indent. + l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size); + l.text_buf->set_width(p_width - l.offset.x); + l.text_buf->set_align((HAlign)_find_align(l.from)); + l.text_buf->set_direction(_find_direction(l.from)); + + if (tab_size > 0) { // Align inline tabs. + Vector<float> tabs; + tabs.push_back(tab_size * p_base_font->get_char_size('m', 0, p_base_font_size).width); + l.text_buf->tab_align(tabs); + } + + // Shape current paragraph. + String text; + Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { + if (visible_characters >= 0 && l.char_offset + l.char_count > visible_characters) { + break; + } + switch (it->type) { + case ITEM_DROPCAP: { + // Add dropcap. + const ItemDropcap *dc = (ItemDropcap *)it; + if (dc != nullptr) { + l.text_buf->set_dropcap(dc->text, dc->font, dc->font_size, dc->dropcap_margins); + l.dc_color = dc->color; + l.dc_ol_size = dc->ol_size; + l.dc_ol_color = dc->ol_color; + } else { + l.text_buf->clear_dropcap(); + } + } break; + case ITEM_NEWLINE: { Ref<Font> font = _find_font(it); if (font.is_null()) { font = p_base_font; } + int font_size = _find_font_size(it); + if (font_size == -1) { + font_size = p_base_font_size; + } + l.text_buf->add_string("\n", font, font_size, Dictionary(), ""); + text += "\n"; + l.char_count += 1; + } break; + case ITEM_TEXT: { + ItemText *t = (ItemText *)it; + Ref<Font> font = _find_font(it); + if (font.is_null()) { + font = p_base_font; + } + int font_size = _find_font_size(it); + if (font_size == -1) { + font_size = p_base_font_size; + } + Dictionary font_ftr = _find_font_features(it); + String lang = _find_language(it); + String tx = t->text; + if (visible_characters >= 0 && l.char_offset + l.char_count + tx.length() > visible_characters) { + tx = tx.substr(0, l.char_offset + l.char_count + tx.length() - visible_characters); + } - const char32_t *c = text->text.get_data(); - const char32_t *cf = c; - int ascent = font->get_ascent(); - int descent = font->get_descent(); - - Color color; - Color font_color_shadow; - bool underline = false; - bool strikethrough = false; - ItemFade *fade = nullptr; - int it_char_start = p_char_count; - - Vector<ItemFX *> fx_stack = Vector<ItemFX *>(); - _fetch_item_fx_stack(text, fx_stack); - bool custom_fx_ok = true; - - if (p_mode == PROCESS_DRAW) { - color = _find_color(text, p_base_color); - font_color_shadow = _find_color(text, p_font_color_shadow); - if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) { - underline = true; - } else if (_find_strikethrough(text)) { - strikethrough = true; - } + l.text_buf->add_string(tx, font, font_size, font_ftr, lang); + text += tx; + l.char_count += tx.length(); + } break; + case ITEM_IMAGE: { + ItemImage *img = (ItemImage *)it; + l.text_buf->add_object((uint64_t)it, img->image->get_size(), img->inline_align, 1); + text += String::chr(0xfffc); + } break; + case ITEM_TABLE: { + ItemTable *table = static_cast<ItemTable *>(it); + int hseparation = get_theme_constant("table_hseparation"); + int vseparation = get_theme_constant("table_vseparation"); + int col_count = table->columns.size(); + int t_char_count = 0; + // Set minimums to zero. + for (int i = 0; i < col_count; i++) { + table->columns.write[i].min_width = 0; + table->columns.write[i].max_width = 0; + table->columns.write[i].width = 0; + } + // Compute minimum width for each cell. + const int available_width = p_width - hseparation * (col_count - 1); - Item *fade_item = it; - while (fade_item) { - if (fade_item->type == ITEM_FADE) { - fade = static_cast<ItemFade *>(fade_item); - break; - } - fade_item = fade_item->parent; - } + int idx = 0; + for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames. + ItemFrame *frame = static_cast<ItemFrame *>(E->get()); - } else if (p_mode == PROCESS_CACHE) { - l.char_count += text->text.length(); + int column = idx % col_count; + for (int i = 0; i < frame->lines.size(); i++) { + int char_offset = l.char_offset + l.char_count; + _shape_line(frame, i, p_base_font, p_base_font_size, 1, &char_offset); + int cell_ch = (char_offset - (l.char_offset + l.char_count)); + l.char_count += cell_ch; + t_char_count += cell_ch; + + table->columns.write[column].min_width = MAX(table->columns[column].min_width, ceil(frame->lines[i].text_buf->get_size().x)); + table->columns.write[column].max_width = MAX(table->columns[column].max_width, ceil(frame->lines[i].text_buf->get_non_wraped_size().x)); + } + idx++; } - rchar = 0; - FontDrawer drawer(font, Color(1, 1, 1)); - while (*c) { - int end = 0; - int w = 0; - int fw = 0; + // Compute available width and total ratio (for expanders). + int total_ratio = 0; + int remaining_width = available_width; + table->total_width = hseparation; - lh = 0; + for (int i = 0; i < col_count; i++) { + remaining_width -= table->columns[i].min_width; + if (table->columns[i].max_width > table->columns[i].min_width) { + table->columns.write[i].expand = true; + } + if (table->columns[i].expand) { + total_ratio += table->columns[i].expand_ratio; + } + } - if (p_mode != PROCESS_CACHE) { - lh = line < l.height_caches.size() ? l.height_caches[line] : 1; - line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; - line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; + // Assign actual widths. + for (int i = 0; i < col_count; i++) { + table->columns.write[i].width = table->columns[i].min_width; + if (table->columns[i].expand && total_ratio > 0) { + table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; } - while (c[end] != 0 && !(end && c[end - 1] == ' ' && c[end] != ' ')) { - int cw = font->get_char_size(c[end], c[end + 1]).width; - if (c[end] == '\t') { - cw = tab_size * font->get_char_size(' ').width; - } + table->total_width += table->columns[i].width + hseparation; + } - if (end > 0 && fw + cw + begin > p_width) { - break; //don't allow lines longer than assigned width + // Resize to max_width if needed and distribute the remaining space. + bool table_need_fit = true; + while (table_need_fit) { + table_need_fit = false; + // Fit slim. + for (int i = 0; i < col_count; i++) { + if (!table->columns[i].expand) { + continue; + } + int dif = table->columns[i].width - table->columns[i].max_width; + if (dif > 0) { + table_need_fit = true; + table->columns.write[i].width = table->columns[i].max_width; + table->total_width -= dif; + total_ratio -= table->columns[i].expand_ratio; } - - fw += cw; - - end++; } - CHECK_HEIGHT(fh); - ENSURE_WIDTH(fw); - - line_ascent = MAX(line_ascent, ascent); - line_descent = MAX(line_descent, descent); - fh = line_ascent + line_descent; - - if (end && c[end - 1] == ' ') { - if (p_mode == PROCESS_CACHE) { - spaces_size += font->get_char_size(' ').width; - } else if (align == ALIGN_FILL) { - int ln = MIN(l.offset_caches.size() - 1, line); - if (l.space_caches[ln]) { - align_ofs = spaces * l.offset_caches[ln] / l.space_caches[ln]; + // Grow. + remaining_width = available_width - table->total_width; + if (remaining_width > 0 && total_ratio > 0) { + for (int i = 0; i < col_count; i++) { + if (table->columns[i].expand) { + int dif = table->columns[i].max_width - table->columns[i].width; + if (dif > 0) { + int slice = table->columns[i].expand_ratio * remaining_width / total_ratio; + int incr = MIN(dif, slice); + table->columns.write[i].width += incr; + table->total_width += incr; + } } } - spaces++; } + } - { - int ofs = 0 - backtrack; + // Update line width and get total height. + idx = 0; + table->total_height = 0; + table->rows.clear(); - for (int i = 0; i < end; i++) { - int pofs = wofs + ofs; + Vector2 offset; + float row_height = 0; - if (p_mode == PROCESS_POINTER && r_click_char && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) { - int cw = font->get_char_size(c[i], c[i + 1]).x; + for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames. + ItemFrame *frame = static_cast<ItemFrame *>(E->get()); - if (c[i] == '\t') { - cw = tab_size * font->get_char_size(' ').width; - } + int column = idx % col_count; - if (p_click_pos.x - cw / 2 > p_ofs.x + align_ofs + pofs) { - rchar = int((&c[i]) - cf); - } + offset.x += frame->padding.position.x; + float yofs = frame->padding.position.y; + for (int i = 0; i < frame->lines.size(); i++) { + frame->lines.write[i].text_buf->set_width(table->columns[column].width); + table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x)); - ofs += cw; - } else if (p_mode == PROCESS_DRAW) { - bool selected = false; - Color fx_color = Color(color); - Point2 fx_offset; - char32_t fx_char = c[i]; - - if (selection.active) { - int cofs = (&c[i]) - cf; - if ((text->index > selection.from->index || (text->index == selection.from->index && cofs >= selection.from_char)) && (text->index < selection.to->index || (text->index == selection.to->index && cofs <= selection.to_char))) { - selected = true; - } - } + if (i > 0) { + frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y; + } else { + frame->lines.write[i].offset.y = 0; + } + frame->lines.write[i].offset += Vector2(offset.x, offset.y); - int cw = 0; - int c_item_offset = p_char_count - it_char_start; + float h = frame->lines[i].text_buf->get_size().y; + if (frame->min_size_over.y > 0) { + h = MAX(h, frame->min_size_over.y); + } + if (frame->max_size_over.y > 0) { + h = MIN(h, frame->max_size_over.y); + } + yofs += h; + } + yofs += frame->padding.size.y; + offset.x += table->columns[column].width + hseparation + frame->padding.size.x; - float faded_visibility = 1.0f; - if (fade) { - if (c_item_offset >= fade->starting_index) { - faded_visibility -= (float)(c_item_offset - fade->starting_index) / (float)fade->length; - faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; - } - fx_color.a = faded_visibility; - } + row_height = MAX(yofs, row_height); + if (column == col_count - 1) { + offset.x = 0; + row_height += vseparation; + table->total_height += row_height; + offset.y += row_height; + table->rows.push_back(row_height); + row_height = 0; + } + idx++; + } - bool visible = visible_characters < 0 || ((p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)) && - faded_visibility > 0.0f); + l.text_buf->add_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align, t_char_count); + text += String::chr(0xfffc).repeat(t_char_count); + } break; + default: + break; + } + } - const bool previously_visible = visible; + //Apply BiDi override. + l.text_buf->set_bidi_override(structured_text_parser(_find_stt(l.from), st_args, text)); - for (int j = 0; j < fx_stack.size(); j++) { - ItemFX *item_fx = fx_stack[j]; + *r_char_offset = l.char_offset + l.char_count; - if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) { - ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx); + if (p_line > 0) { + l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y; + } else { + l.offset.y = 0; + } +} - Ref<CharFXTransform> charfx = item_custom->char_fx_transform; - Ref<RichTextEffect> custom_effect = item_custom->custom_effect; +void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &p_shadow_ofs) { + Vector2 off; - if (!custom_effect.is_null()) { - charfx->elapsed_time = item_custom->elapsed_time; - charfx->relative_index = c_item_offset; - charfx->absolute_index = p_char_count; - charfx->visibility = visible; - charfx->offset = fx_offset; - charfx->color = fx_color; - charfx->character = fx_char; + ERR_FAIL_COND(p_frame == nullptr); + ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); - bool effect_status = custom_effect->_process_effect_impl(charfx); - custom_fx_ok = effect_status; + Line &l = p_frame->lines.write[p_line]; - fx_offset += charfx->offset; - fx_color = charfx->color; - visible &= charfx->visibility; - fx_char = charfx->character; - } - } else if (item_fx->type == ITEM_SHAKE) { - ItemShake *item_shake = static_cast<ItemShake *>(item_fx); - - uint64_t char_current_rand = item_shake->offset_random(c_item_offset); - uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset); - uint64_t max_rand = 2147483647; - double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); - double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); - n_time = (n_time > 1.0) ? 1.0 : n_time; - fx_offset += Point2(Math::lerp(Math::sin(previous_offset), - Math::sin(current_offset), - n_time), - Math::lerp(Math::cos(previous_offset), - Math::cos(current_offset), - n_time)) * - (float)item_shake->strength / 10.0f; - } else if (item_fx->type == ITEM_WAVE) { - ItemWave *item_wave = static_cast<ItemWave *>(item_fx); - - double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f); - fx_offset += Point2(0, 1) * value; - } else if (item_fx->type == ITEM_TORNADO) { - ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx); - - double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); - double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); - fx_offset += Point2(torn_x, torn_y); - } else if (item_fx->type == ITEM_RAINBOW) { - ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx); - - fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)), - item_rainbow->saturation, - item_rainbow->value, - fx_color.a); - } - } + Item *it_from = l.from; + Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + Variant meta; - if (visible) { - line_is_blank = false; - w += font->get_char_size(c[i], c[i + 1]).x; - } + if (it_from == nullptr) { + return; + } - if (c[i] == '\t') { - visible = false; - } + RID ci = get_canvas_item(); + bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL); + bool lrtl = is_layout_rtl(); - if (visible) { - if (selected) { - cw = font->get_char_size(fx_char, c[i + 1]).x; - draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg); - } + Vector<int> list_index; + Vector<ItemList *> list_items; + _find_list(l.from, list_index, list_items); - if (p_font_color_shadow.a > 0) { - float x_ofs_shadow = align_ofs + pofs; - float y_ofs_shadow = y + lh - line_descent; - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs + fx_offset, fx_char, c[i + 1], p_font_color_shadow); + String prefix; + for (int i = 0; i < list_index.size(); i++) { + if (rtl) { + prefix = prefix + "."; + } else { + prefix = "." + prefix; + } + String segment; + if (list_items[i]->list_type == LIST_DOTS) { + static const char32_t _prefix[2] = { 0x25CF, 0 }; + prefix = _prefix; + break; + } else if (list_items[i]->list_type == LIST_NUMBERS) { + segment = TS->format_number(itos(list_index[i]), _find_language(l.from)); + } else if (list_items[i]->list_type == LIST_LETTERS) { + segment = _letters(list_index[i], list_items[i]->capitalize); + } else if (list_items[i]->list_type == LIST_ROMAN) { + segment = _roman(list_index[i], list_items[i]->capitalize); + } + if (rtl) { + prefix = prefix + segment; + } else { + prefix = segment + prefix; + } + } + if (prefix != "") { + Ref<Font> font = _find_font(l.from); + if (font.is_null()) { + font = get_theme_font("normal_font"); + } + int font_size = _find_font_size(l.from); + if (font_size == -1) { + font_size = get_theme_font_size("normal_font_size"); + } + if (rtl) { + float offx = 0.0f; + if (!lrtl && p_frame == main) { // Skip Scrollbar. + offx -= scroll_w; + } + font->draw_string(ci, p_ofs + Vector2(p_width - l.offset.x + offx, l.text_buf->get_line_ascent(0)), " " + prefix, HALIGN_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color)); + } else { + float offx = 0.0f; + if (lrtl && p_frame == main) { // Skip Scrollbar. + offx += scroll_w; + } + font->draw_string(ci, p_ofs + Vector2(offx, l.text_buf->get_line_ascent(0)), prefix + " ", HALIGN_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color)); + } + } - if (p_shadow_as_outline) { - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow); - } - } + // Draw dropcap. + int dc_lines = l.text_buf->get_dropcap_lines(); + float h_off = l.text_buf->get_dropcap_size().x; + if (l.dc_ol_size > 0) { + l.text_buf->draw_dropcap_outline(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_ol_size, l.dc_ol_color); + } + l.text_buf->draw_dropcap(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_color); - if (selected) { - drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], override_selected_font_color ? selection_fg : fx_color); - } else { - cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], fx_color); - } - } else if (previously_visible && c[i] != '\t') { - backtrack += font->get_char_size(fx_char, c[i + 1]).x; - } + // Draw text. + for (int line = 0; line < l.text_buf->get_line_count(); line++) { + RID rid = l.text_buf->get_line_rid(line); - p_char_count++; - if (c[i] == '\t') { - cw = tab_size * font->get_char_size(' ').width; - backtrack = MAX(0, backtrack - cw); - } + float width = l.text_buf->get_width(); + float length = TS->shaped_text_get_width(rid); - ofs += cw; - } - } + // Draw line. - if (underline) { - Color uc = color; - uc.a *= 0.5; - int uy = y + lh - line_descent + font->get_underline_position(); - float underline_width = font->get_underline_thickness(); -#ifdef TOOLS_ENABLED - underline_width *= EDSCALE; -#endif - RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, underline_width); - } else if (strikethrough) { - Color uc = color; - uc.a *= 0.5; - int uy = y + lh - (line_ascent + line_descent) / 2; - float strikethrough_width = font->get_underline_thickness(); -#ifdef TOOLS_ENABLED - strikethrough_width *= EDSCALE; -#endif - RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, strikethrough_width); - } - } + if (rtl) { + off.x = p_width - l.offset.x - width; + if (!lrtl && p_frame == main) { // Skip Scrollbar. + off.x -= scroll_w; + } + } else { + off.x = l.offset.x; + if (lrtl && p_frame == main) { // Skip Scrollbar. + off.x += scroll_w; + } + } - ADVANCE(fw); - CHECK_HEIGHT(fh); //must be done somewhere - c = &c[end]; + // Draw text. + switch (l.text_buf->get_align()) { + case HALIGN_FILL: + case HALIGN_LEFT: { + if (rtl) { + off.x += width - length; } - } break; - case ITEM_IMAGE: { - lh = 0; - if (p_mode != PROCESS_CACHE) { - lh = line < l.height_caches.size() ? l.height_caches[line] : 1; - } else { - l.char_count += 1; //images count as chars too + case HALIGN_CENTER: { + off.x += Math::floor((width - length) / 2.0); + } break; + case HALIGN_RIGHT: { + if (!rtl) { + off.x += width - length; } + } break; + } - ItemImage *img = static_cast<ItemImage *>(it); + if (line <= dc_lines) { + if (rtl) { + off.x -= h_off; + } else { + off.x += h_off; + } + } - Ref<Font> font = _find_font(it); - if (font.is_null()) { - font = p_base_font; - } + //draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS + + off.y += TS->shaped_text_get_ascent(rid); + // Draw inlined objects. + Array objects = TS->shaped_text_get_objects(rid); + for (int i = 0; i < objects.size(); i++) { + Item *it = (Item *)(uint64_t)objects[i]; + if (it != nullptr) { + Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]); + //draw_rect(rect, Color(1,0,0), false, 2); //DEBUG_RECTS + switch (it->type) { + case ITEM_IMAGE: { + ItemImage *img = static_cast<ItemImage *>(it); + img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off, rect.size), false, img->color); + } break; + case ITEM_TABLE: { + ItemTable *table = static_cast<ItemTable *>(it); + Color odd_row_bg = get_theme_color("table_odd_row_bg"); + Color even_row_bg = get_theme_color("table_even_row_bg"); + Color border = get_theme_color("table_border"); + int col_count = table->columns.size(); + int row_count = table->rows.size(); + + int idx = 0; + for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { + ItemFrame *frame = static_cast<ItemFrame *>(E->get()); + + int col = idx % col_count; + int row = idx / col_count; + + if (frame->lines.size() != 0 && row < row_count) { + Vector2 coff = frame->lines[0].offset; + if (rtl) { + coff.x = rect.size.width - table->columns[col].width - coff.x; + } + if (row % 2 == 0) { + draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width, table->rows[row])), (frame->odd_row_bg != Color(0, 0, 0, 0) ? frame->odd_row_bg : odd_row_bg), true); + } else { + draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width, table->rows[row])), (frame->even_row_bg != Color(0, 0, 0, 0) ? frame->even_row_bg : even_row_bg), true); + } + draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width, table->rows[row])), (frame->border != Color(0, 0, 0, 0) ? frame->border : border), false); + } - if (p_mode == PROCESS_POINTER && r_click_char) { - *r_click_char = 0; + for (int j = 0; j < frame->lines.size(); j++) { + _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_color_shadow, p_shadow_as_outline, p_shadow_ofs); + } + idx++; + } + } break; + default: + break; } + } + } - ENSURE_WIDTH(img->size.width); + const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid); + const TextServer::Glyph *glyphs = visual.ptr(); + int gl_size = visual.size(); - bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->size.height, img->size.height)); - if (visible) { - line_is_blank = false; - } + Vector2 gloff = off; + // Draw oulines and shadow. + for (int i = 0; i < gl_size; i++) { + Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start); + int size = _find_outline_size(it); + Color font_color = _find_outline_color(it, Color(0, 0, 0, 0)); + if (size <= 0) { + gloff.x += glyphs[i].advance; + continue; + } - if (p_mode == PROCESS_DRAW && visible) { - img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size), false, img->color); + // Get FX. + ItemFade *fade = nullptr; + Item *fade_item = it; + while (fade_item) { + if (fade_item->type == ITEM_FADE) { + fade = static_cast<ItemFade *>(fade_item); + break; } - p_char_count++; + fade_item = fade_item->parent; + } - ADVANCE(img->size.width); - CHECK_HEIGHT((img->size.height + font->get_descent())); + Vector<ItemFX *> fx_stack; + _fetch_item_fx_stack(it, fx_stack); + bool custom_fx_ok = true; - } break; - case ITEM_NEWLINE: { - lh = 0; + Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off); + RID frid = glyphs[i].font_rid; + uint32_t gl = glyphs[i].index; - if (p_mode != PROCESS_CACHE) { - lh = line < l.height_caches.size() ? l.height_caches[line] : 1; - line_is_blank = true; + //Apply fx. + float faded_visibility = 1.0f; + if (fade) { + if (glyphs[i].start >= fade->starting_index) { + faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length; + faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; } + font_color.a = faded_visibility; + } - } break; - case ITEM_TABLE: { - lh = 0; - ItemTable *table = static_cast<ItemTable *>(it); - int hseparation = get_theme_constant("table_hseparation"); - int vseparation = get_theme_constant("table_vseparation"); - Color ccolor = _find_color(table, p_base_color); - Vector2 draw_ofs = Point2(wofs, y); - Color font_color_shadow = get_theme_color("font_color_shadow"); - bool use_outline = get_theme_constant("shadow_as_outline"); - Point2 shadow_ofs2(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y")); - - if (p_mode == PROCESS_CACHE) { - int idx = 0; - //set minimums to zero - for (int i = 0; i < table->columns.size(); i++) { - table->columns.write[i].min_width = 0; - table->columns.write[i].max_width = 0; - table->columns.write[i].width = 0; + bool visible = (font_color.a != 0); + + for (int j = 0; j < fx_stack.size(); j++) { + ItemFX *item_fx = fx_stack[j]; + if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) { + ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx); + + Ref<CharFXTransform> charfx = item_custom->char_fx_transform; + Ref<RichTextEffect> custom_effect = item_custom->custom_effect; + + if (!custom_effect.is_null()) { + charfx->elapsed_time = item_custom->elapsed_time; + charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end); + charfx->visibility = visible; + charfx->outline = true; + charfx->font = frid; + charfx->glpyh_index = gl; + charfx->offset = fx_offset; + charfx->color = font_color; + + bool effect_status = custom_effect->_process_effect_impl(charfx); + custom_fx_ok = effect_status; + + fx_offset += charfx->offset; + font_color = charfx->color; + frid = charfx->font; + gl = charfx->glpyh_index; + visible &= charfx->visibility; } - //compute minimum width for each cell - const int available_width = p_width - hseparation * (table->columns.size() - 1) - wofs; - - for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { - ERR_CONTINUE(E->get()->type != ITEM_FRAME); //children should all be frames - ItemFrame *frame = static_cast<ItemFrame *>(E->get()); - - int column = idx % table->columns.size(); + } else if (item_fx->type == ITEM_SHAKE) { + ItemShake *item_shake = static_cast<ItemShake *>(item_fx); + + uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start); + uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start); + uint64_t max_rand = 2147483647; + double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); + n_time = (n_time > 1.0) ? 1.0 : n_time; + fx_offset += Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f; + } else if (item_fx->type == ITEM_WAVE) { + ItemWave *item_wave = static_cast<ItemWave *>(item_fx); + + double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f); + fx_offset += Point2(0, 1) * value; + } else if (item_fx->type == ITEM_TORNADO) { + ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx); + + double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius); + double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius); + fx_offset += Point2(torn_x, torn_y); + } else if (item_fx->type == ITEM_RAINBOW) { + ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx); + + font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + gloff.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a); + } + } - int ly = 0; + Point2 shadow_ofs(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y")); - for (int i = 0; i < frame->lines.size(); i++) { - _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); - table->columns.write[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); - table->columns.write[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); + // Draw glyph outlines. + for (int j = 0; j < glyphs[i].repeat; j++) { + if (visible) { + if (frid != RID()) { + if (p_shadow_as_outline) { + TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, shadow_ofs.y), gl, p_font_color_shadow); + TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(shadow_ofs.x, -shadow_ofs.y), gl, p_font_color_shadow); + TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, -shadow_ofs.y), gl, p_font_color_shadow); } - idx++; + TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color); } + } + gloff.x += glyphs[i].advance; + } + } - //compute available width and total ratio (for expanders) + // Draw main text. + Color selection_fg = get_theme_color("font_color_selected"); + Color selection_bg = get_theme_color("selection_color"); - int total_ratio = 0; - int remaining_width = available_width; - table->total_width = hseparation; + int sel_start = -1; + int sel_end = -1; - for (int i = 0; i < table->columns.size(); i++) { - remaining_width -= table->columns[i].min_width; - if (table->columns[i].max_width > table->columns[i].min_width) { - table->columns.write[i].expand = true; - } - if (table->columns[i].expand) { - total_ratio += table->columns[i].expand_ratio; - } - } + if (selection.active && (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) <= (l.char_offset + TS->shaped_text_get_range(rid).y) && (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) >= (l.char_offset + TS->shaped_text_get_range(rid).x)) { + sel_start = MAX(TS->shaped_text_get_range(rid).x, (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) - l.char_offset); + sel_end = MIN(TS->shaped_text_get_range(rid).y, (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) - l.char_offset); - //assign actual widths - for (int i = 0; i < table->columns.size(); i++) { - table->columns.write[i].width = table->columns[i].min_width; - if (table->columns[i].expand && total_ratio > 0) { - table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; - } - table->total_width += table->columns[i].width + hseparation; - } + Vector<Vector2> sel = TS->shaped_text_get_selection(rid, sel_start, sel_end); + for (int i = 0; i < sel.size(); i++) { + Rect2 rect = Rect2(sel[i].x + p_ofs.x + off.x, p_ofs.y + off.y - TS->shaped_text_get_ascent(rid), sel[i].y - sel[i].x, TS->shaped_text_get_size(rid).y); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_bg); + } + } - //resize to max_width if needed and distribute the remaining space - bool table_need_fit = true; - while (table_need_fit) { - table_need_fit = false; - //fit slim - for (int i = 0; i < table->columns.size(); i++) { - if (!table->columns[i].expand) { - continue; - } - int dif = table->columns[i].width - table->columns[i].max_width; - if (dif > 0) { - table_need_fit = true; - table->columns.write[i].width = table->columns[i].max_width; - table->total_width -= dif; - total_ratio -= table->columns[i].expand_ratio; - } - } - //grow - remaining_width = available_width - table->total_width; - if (remaining_width > 0 && total_ratio > 0) { - for (int i = 0; i < table->columns.size(); i++) { - if (table->columns[i].expand) { - int dif = table->columns[i].max_width - table->columns[i].width; - if (dif > 0) { - int slice = table->columns[i].expand_ratio * remaining_width / total_ratio; - int incr = MIN(dif, slice); - table->columns.write[i].width += incr; - table->total_width += incr; - } - } - } - } - } + for (int i = 0; i < gl_size; i++) { + bool selected = selection.active && (sel_start != -1) && (glyphs[i].start >= sel_start) && (glyphs[i].end <= sel_end); + Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start); + Color font_color = _find_color(it, p_base_color); + if (_find_underline(it) || (_find_meta(it, &meta) && underline_meta)) { + Color uc = font_color; + uc.a *= 0.5; + float y_off = TS->shaped_text_get_underline_position(rid); + float underline_width = TS->shaped_text_get_underline_thickness(rid); +#ifdef TOOLS_ENABLED + underline_width *= EDSCALE; +#endif + draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width); + } else if (_find_strikethrough(it)) { + Color uc = font_color; + uc.a *= 0.5; + float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2; + float underline_width = TS->shaped_text_get_underline_thickness(rid); +#ifdef TOOLS_ENABLED + underline_width *= EDSCALE; +#endif + draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width); + } - //compute caches properly again with the right width - idx = 0; - for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { - ERR_CONTINUE(E->get()->type != ITEM_FRAME); //children should all be frames - ItemFrame *frame = static_cast<ItemFrame *>(E->get()); + // Get FX. + ItemFade *fade = nullptr; + Item *fade_item = it; + while (fade_item) { + if (fade_item->type == ITEM_FADE) { + fade = static_cast<ItemFade *>(fade_item); + break; + } + fade_item = fade_item->parent; + } - int column = idx % table->columns.size(); + Vector<ItemFX *> fx_stack; + _fetch_item_fx_stack(it, fx_stack); + bool custom_fx_ok = true; - for (int i = 0; i < frame->lines.size(); i++) { - int ly = 0; - _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2); - frame->lines.write[i].height_cache = ly; //actual height - frame->lines.write[i].height_accum_cache = ly; //actual height - } - idx++; + Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off); + RID frid = glyphs[i].font_rid; + uint32_t gl = glyphs[i].index; + + //Apply fx. + float faded_visibility = 1.0f; + if (fade) { + if (glyphs[i].start >= fade->starting_index) { + faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length; + faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; + } + font_color.a = faded_visibility; + } + + bool visible = (font_color.a != 0); + + for (int j = 0; j < fx_stack.size(); j++) { + ItemFX *item_fx = fx_stack[j]; + if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) { + ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx); + + Ref<CharFXTransform> charfx = item_custom->char_fx_transform; + Ref<RichTextEffect> custom_effect = item_custom->custom_effect; + + if (!custom_effect.is_null()) { + charfx->elapsed_time = item_custom->elapsed_time; + charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end); + charfx->visibility = visible; + charfx->outline = false; + charfx->font = frid; + charfx->glpyh_index = gl; + charfx->offset = fx_offset; + charfx->color = font_color; + + bool effect_status = custom_effect->_process_effect_impl(charfx); + custom_fx_ok = effect_status; + + fx_offset += charfx->offset; + font_color = charfx->color; + frid = charfx->font; + gl = charfx->glpyh_index; + visible &= charfx->visibility; } + } else if (item_fx->type == ITEM_SHAKE) { + ItemShake *item_shake = static_cast<ItemShake *>(item_fx); + + uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start); + uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start); + uint64_t max_rand = 2147483647; + double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); + n_time = (n_time > 1.0) ? 1.0 : n_time; + fx_offset += Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f; + } else if (item_fx->type == ITEM_WAVE) { + ItemWave *item_wave = static_cast<ItemWave *>(item_fx); + + double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f); + fx_offset += Point2(0, 1) * value; + } else if (item_fx->type == ITEM_TORNADO) { + ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx); + + double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius); + double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius); + fx_offset += Point2(torn_x, torn_y); + } else if (item_fx->type == ITEM_RAINBOW) { + ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx); + + font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + off.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a); } + } - Point2 offset(align_ofs + hseparation, vseparation); + if (selected) { + font_color = override_selected_font_color ? selection_fg : font_color; + } - int row_height = 0; - //draw using computed caches - int idx = 0; - for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { - ERR_CONTINUE(E->get()->type != ITEM_FRAME); //children should all be frames - ItemFrame *frame = static_cast<ItemFrame *>(E->get()); + // Draw glyphs. + for (int j = 0; j < glyphs[i].repeat; j++) { + if (visible) { + if (frid != RID()) { + TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color); + } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + TS->draw_hex_code_box(ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color); + } + } + off.x += glyphs[i].advance; + } + } + off.y += TS->shaped_text_get_descent(rid); + } +} - int column = idx % table->columns.size(); +void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool *r_outside) { + if (r_click_item) { + *r_click_item = nullptr; + } + if (r_click_char != nullptr) { + *r_click_char = 0; + } + if (r_outside != nullptr) { + *r_outside = true; + } - int ly = 0; - int yofs = 0; + Size2 size = get_size(); + Rect2 text_rect = _get_text_rect(); - int lines_h = frame->lines[frame->lines.size() - 1].height_accum_cache - (frame->lines[0].height_accum_cache - frame->lines[0].height_cache); - int lines_ofs = p_ofs.y + offset.y + draw_ofs.y; + int vofs = vscroll->get_value(); - bool visible = lines_ofs < get_size().height && lines_ofs + lines_h >= 0; - if (visible) { - line_is_blank = false; - } + // Search for the first line. + int from_line = 0; - for (int i = 0; i < frame->lines.size(); i++) { - if (visible) { - if (p_mode == PROCESS_DRAW) { - nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2); - } else if (p_mode == PROCESS_POINTER) { - _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_click_pos, r_click_item, r_click_char, r_outside); - if (r_click_item && *r_click_item) { - RETURN; // exit early - } - } - } + //TODO, change to binary search ? + while (from_line < main->lines.size()) { + if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) { + break; + } + from_line++; + } - yofs += frame->lines[i].height_cache; - if (p_mode == PROCESS_CACHE) { - frame->lines.write[i].height_accum_cache = offset.y + draw_ofs.y + frame->lines[i].height_cache; - } - } + if (from_line >= main->lines.size()) { + return; + } - row_height = MAX(yofs, row_height); - offset.x += table->columns[column].width + hseparation; + Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs); + while (ofs.y < size.height && from_line < main->lines.size()) { + ofs.y += _find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char); + if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) { + if (r_outside != nullptr) { + *r_outside = false; + } + return; + } + from_line++; + } +} - if (column == table->columns.size() - 1) { - offset.y += row_height + vseparation; - offset.x = hseparation; - row_height = 0; - } - idx++; - } +float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char) { + Vector2 off; - int total_height = offset.y; - if (row_height) { - total_height = row_height + vseparation; - } + int char_pos = -1; + Line &l = p_frame->lines.write[p_line]; + bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL); + bool lrtl = is_layout_rtl(); + bool table_hit = false; - ADVANCE(table->total_width); - CHECK_HEIGHT(total_height); + for (int line = 0; line < l.text_buf->get_line_count(); line++) { + RID rid = l.text_buf->get_line_rid(line); - } break; + float width = l.text_buf->get_width(); + float length = TS->shaped_text_get_width(rid); - default: { + if (rtl) { + off.x = p_width - l.offset.x - width; + if (!lrtl && p_frame == main) { // Skip Scrollbar. + off.x -= scroll_w; + } + } else { + off.x = l.offset.x; + if (lrtl && p_frame == main) { // Skip Scrollbar. + off.x += scroll_w; } } - Item *itp = it; + switch (l.text_buf->get_align()) { + case HALIGN_FILL: + case HALIGN_LEFT: { + if (rtl) { + off.x += width - length; + } + } break; + case HALIGN_CENTER: { + off.x += Math::floor((width - length) / 2.0); + } break; + case HALIGN_RIGHT: { + if (!rtl) { + off.x += width - length; + } + } break; + } + + off.y += TS->shaped_text_get_ascent(rid); + + Array objects = TS->shaped_text_get_objects(rid); + for (int i = 0; i < objects.size(); i++) { + Item *it = (Item *)(uint64_t)objects[i]; + if (it != nullptr) { + Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]); + if (rect.has_point(p_click - p_ofs - off)) { + switch (it->type) { + case ITEM_TABLE: { + int hseparation = get_theme_constant("table_hseparation"); + int vseparation = get_theme_constant("table_vseparation"); + + ItemTable *table = static_cast<ItemTable *>(it); - it = _get_next_item(it); + table_hit = true; - if (it && (p_line + 1 < p_frame->lines.size()) && p_frame->lines[p_line + 1].from == it) { - if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) { - //went to next line, but pointer was on the previous one - if (r_outside) { - *r_outside = true; + int idx = 0; + int col_count = table->columns.size(); + int row_count = table->rows.size(); + + for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { + ItemFrame *frame = static_cast<ItemFrame *>(E->get()); + + int col = idx % col_count; + int row = idx / col_count; + + if (frame->lines.size() != 0 && row < row_count) { + Vector2 coff = frame->lines[0].offset; + if (rtl) { + coff.x = rect.size.width - table->columns[col].width - coff.x; + } + Rect2 crect = Rect2(p_ofs + off + rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size); + if (col == col_count - 1) { + if (rtl) { + crect.size.x = crect.position.x + crect.size.x; + crect.position.x = 0; + } else { + crect.size.x = get_size().x; + } + } + if (crect.has_point(p_click)) { + for (int j = 0; j < frame->lines.size(); j++) { + _find_click_in_line(frame, j, p_ofs + off + rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char); + if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) { + return off.y; + } + } + } + } + idx++; + } + } break; + default: + break; + } } - *r_click_item = itp; - *r_click_char = rchar; - RETURN; } + } + Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)), Size2(get_size().x, TS->shaped_text_get_size(rid).y)); - break; + if (rect.has_point(p_click) && !table_hit) { + char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x); } + off.y += TS->shaped_text_get_descent(rid); } - NEW_LINE; - RETURN; + if (char_pos >= 0) { + // Find item. + if (r_click_item != nullptr) { + Item *it = p_frame->lines[p_line].from; + Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + it = _get_item_at_pos(it, it_to, char_pos); + *r_click_item = it; + } + + if (r_click_frame != nullptr) { + *r_click_frame = p_frame; + } + + if (r_click_line != nullptr) { + *r_click_line = p_line; + } -#undef RETURN -#undef NEW_LINE -#undef ENSURE_WIDTH -#undef ADVANCE -#undef CHECK_HEIGHT + if (r_click_char != nullptr) { + *r_click_char = char_pos; + } + } + + return off.y; } void RichTextLabel::_scroll_changed(double) { @@ -903,14 +1276,14 @@ void RichTextLabel::_update_scroll() { scroll_visible = true; scroll_w = vscroll->get_combined_minimum_size().width; vscroll->show(); - vscroll->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -scroll_w); + vscroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_w); } else { scroll_visible = false; scroll_w = 0; vscroll->hide(); } - main->first_invalid_line = 0; //invalidate ALL + main->first_resized_line = 0; //invalidate ALL _validate_line_caches(main); } } @@ -960,10 +1333,11 @@ void RichTextLabel::_notification(int p_what) { } } break; case NOTIFICATION_RESIZED: { - main->first_invalid_line = 0; //invalidate ALL + main->first_resized_line = 0; //invalidate ALL update(); } break; + case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_ENTER_TREE: { if (bbcode != "") { set_bbcode(bbcode); @@ -971,11 +1345,11 @@ void RichTextLabel::_notification(int p_what) { main->first_invalid_line = 0; //invalidate ALL update(); - } break; - case NOTIFICATION_THEME_CHANGED: { + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: { + main->first_invalid_line = 0; //invalidate ALL update(); - } break; case NOTIFICATION_DRAW: { _validate_line_caches(main); @@ -994,36 +1368,38 @@ void RichTextLabel::_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false); } - int ofs = vscroll->get_value(); - - //todo, change to binary search + float vofs = vscroll->get_value(); + // Search for the first line. int from_line = 0; - int total_chars = 0; + + //TODO, change to binary search ? while (from_line < main->lines.size()) { - if (main->lines[from_line].height_accum_cache + _get_text_rect().get_position().y >= ofs) { + if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) { break; } - total_chars += main->lines[from_line].char_count; from_line++; } if (from_line >= main->lines.size()) { break; //nothing to draw } - int y = (main->lines[from_line].height_accum_cache - main->lines[from_line].height_cache) - ofs; Ref<Font> base_font = get_theme_font("normal_font"); Color base_color = get_theme_color("default_color"); + Color outline_color = get_theme_color("outline_color"); + int outline_size = get_theme_constant("outline_size"); Color font_color_shadow = get_theme_color("font_color_shadow"); bool use_outline = get_theme_constant("shadow_as_outline"); Point2 shadow_ofs(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y")); visible_line_count = 0; - while (y < size.height && from_line < main->lines.size()) { - visible_line_count++; - _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), nullptr, nullptr, nullptr, total_chars); - total_chars += main->lines[from_line].char_count; + // New cache draw. + Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs); + while (ofs.y < size.height && from_line < main->lines.size()) { + visible_line_count++; + _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_color_shadow, use_outline, shadow_ofs); + ofs.y += main->lines[from_line].text_buf->get_size().y; from_line++; } } break; @@ -1036,50 +1412,12 @@ void RichTextLabel::_notification(int p_what) { } } -void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item, int *r_click_char, bool *r_outside) { - if (r_click_item) { - *r_click_item = nullptr; - } - - Rect2 text_rect = _get_text_rect(); - int ofs = vscroll->get_value(); - Color font_color_shadow = get_theme_color("font_color_shadow"); - bool use_outline = get_theme_constant("shadow_as_outline"); - Point2 shadow_ofs(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y")); - - //todo, change to binary search - int from_line = 0; - - while (from_line < p_frame->lines.size()) { - if (p_frame->lines[from_line].height_accum_cache >= ofs) { - break; - } - from_line++; - } - - if (from_line >= p_frame->lines.size()) { - return; - } - - int y = (p_frame->lines[from_line].height_accum_cache - p_frame->lines[from_line].height_cache) - ofs; - Ref<Font> base_font = get_theme_font("normal_font"); - Color base_color = get_theme_color("default_color"); - - while (y < text_rect.get_size().height && from_line < p_frame->lines.size()) { - _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, p_click, r_click_item, r_click_char, r_outside); - if (r_click_item && *r_click_item) { - return; - } - from_line++; - } -} - Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const { if (!underline_meta) { return CURSOR_ARROW; } - if (selection.click) { + if (selection.click_item) { return CURSOR_IBEAM; } @@ -1087,10 +1425,13 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const return CURSOR_ARROW; //invalid } - int line = 0; + if (main->first_resized_line < main->lines.size()) { + return CURSOR_ARROW; //invalid + } + Item *item = nullptr; - bool outside; - ((RichTextLabel *)(this))->_find_click(main, p_pos, &item, &line, &outside); + bool outside = true; + ((RichTextLabel *)(this))->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside); if (item && !outside && ((RichTextLabel *)(this))->_find_meta(item, nullptr)) { return CURSOR_POINTING_HAND; @@ -1106,27 +1447,37 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { if (main->first_invalid_line < main->lines.size()) { return; } + if (main->first_resized_line < main->lines.size()) { + return; + } if (b->get_button_index() == BUTTON_LEFT) { if (b->is_pressed() && !b->is_doubleclick()) { scroll_updated = false; - int line = 0; - Item *item = nullptr; - + ItemFrame *c_frame = nullptr; + int c_line = 0; + Item *c_item = nullptr; + int c_index = 0; bool outside; - _find_click(main, b->get_position(), &item, &line, &outside); - if (item) { + _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside); + if (c_item != nullptr) { if (selection.enabled) { - selection.click = item; - selection.click_char = line; + selection.click_frame = c_frame; + selection.click_item = c_item; + selection.click_line = c_line; + selection.click_char = c_index; // Erase previous selection. if (selection.active) { - selection.from = nullptr; - selection.from_char = '\0'; - selection.to = nullptr; - selection.to_char = '\0'; + selection.from_frame = nullptr; + selection.from_line = 0; + selection.from_item = nullptr; + selection.from_char = 0; + selection.to_frame = nullptr; + selection.to_line = 0; + selection.to_item = nullptr; + selection.to_char = 0; selection.active = false; update(); @@ -1135,44 +1486,49 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { } } else if (b->is_pressed() && b->is_doubleclick() && selection.enabled) { //doubleclick: select word - int line = 0; - Item *item = nullptr; + + ItemFrame *c_frame = nullptr; + int c_line = 0; + Item *c_item = nullptr; + int c_index = 0; bool outside; - _find_click(main, b->get_position(), &item, &line, &outside); + _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside); - while (item && item->type != ITEM_TEXT) { - item = _get_next_item(item, true); - } + if (c_frame) { + const Line &l = c_frame->lines[c_line]; + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(l.text_buf->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (c_index >= words[i].x && c_index < words[i].y) { + selection.from_frame = c_frame; + selection.from_line = c_line; + selection.from_item = c_item; + selection.from_char = words[i].x; + + selection.to_frame = c_frame; + selection.to_line = c_line; + selection.to_item = c_item; + selection.to_char = words[i].y; - if (item && item->type == ITEM_TEXT) { - String itext = static_cast<ItemText *>(item)->text; - - int beg, end; - if (select_word(itext, line, beg, end)) { - selection.from = item; - selection.to = item; - selection.from_char = beg; - selection.to_char = end - 1; - selection.active = true; - update(); + selection.active = true; + update(); + break; + } } } } else if (!b->is_pressed()) { - selection.click = nullptr; + selection.click_item = nullptr; if (!b->is_doubleclick() && !scroll_updated) { - int line = 0; - Item *item = nullptr; + Item *c_item = nullptr; - bool outside; - _find_click(main, b->get_position(), &item, &line, &outside); + bool outside = true; + _find_click(main, b->get_position(), nullptr, nullptr, &c_item, nullptr, &outside); - if (item) { + if (c_item) { Variant meta; - if (!outside && _find_meta(item, &meta)) { + if (!outside && _find_meta(c_item, &meta)) { //meta clicked - emit_signal("meta_clicked", meta); } } @@ -1221,13 +1577,13 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { } break; case KEY_UP: { if (vscroll->is_visible_in_tree()) { - vscroll->set_value(vscroll->get_value() - get_theme_font("normal_font")->get_height()); + vscroll->set_value(vscroll->get_value() - get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size"))); handled = true; } } break; case KEY_DOWN: { if (vscroll->is_visible_in_tree()) { - vscroll->set_value(vscroll->get_value() + get_theme_font("normal_font")->get_height()); + vscroll->set_value(vscroll->get_value() + get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size"))); handled = true; } } break; @@ -1265,27 +1621,32 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { if (main->first_invalid_line < main->lines.size()) { return; } + if (main->first_resized_line < main->lines.size()) { + return; + } - int line = 0; - Item *item = nullptr; + ItemFrame *c_frame = nullptr; + int c_line = 0; + Item *c_item = nullptr; + int c_index = 0; bool outside; - _find_click(main, m->get_position(), &item, &line, &outside); - - if (selection.click) { - if (!item) { - return; // do not update - } - selection.from = selection.click; + _find_click(main, m->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside); + if (selection.click_item && c_item) { + selection.from_frame = selection.click_frame; + selection.from_line = selection.click_line; + selection.from_item = selection.click_item; selection.from_char = selection.click_char; - selection.to = item; - selection.to_char = line; + selection.to_frame = c_frame; + selection.to_line = c_line; + selection.to_item = c_item; + selection.to_char = c_index; bool swap = false; - if (selection.from->index > selection.to->index) { + if (selection.from_item->index > selection.to_item->index) { swap = true; - } else if (selection.from->index == selection.to->index) { + } else if (selection.from_item->index == selection.to_item->index) { if (selection.from_char > selection.to_char) { swap = true; } else if (selection.from_char == selection.to_char) { @@ -1295,7 +1656,9 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { } if (swap) { - SWAP(selection.from, selection.to); + SWAP(selection.from_frame, selection.to_frame); + SWAP(selection.from_line, selection.to_line); + SWAP(selection.from_item, selection.to_item); SWAP(selection.from_char, selection.to_char); } @@ -1305,7 +1668,7 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { Variant meta; ItemMeta *item_meta; - if (item && !outside && _find_meta(item, &meta, &item_meta)) { + if (c_item && !outside && _find_meta(c_item, &meta, &item_meta)) { if (meta_hovering != item_meta) { if (meta_hovering) { emit_signal("meta_hover_ended", current_meta); @@ -1322,6 +1685,31 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { } } +void RichTextLabel::_find_frame(Item *p_item, ItemFrame **r_frame, int *r_line) { + if (r_frame != nullptr) { + *r_frame = nullptr; + } + if (r_line != nullptr) { + *r_line = 0; + } + + Item *item = p_item; + + while (item) { + if (item->parent != nullptr && item->parent->type == ITEM_FRAME) { + if (r_frame != nullptr) { + *r_frame = (ItemFrame *)item->parent; + } + if (r_line != nullptr) { + *r_line = item->line; + } + return; + } + + item = item->parent; + } +} + Ref<Font> RichTextLabel::_find_font(Item *p_item) { Item *fontitem = p_item; @@ -1337,10 +1725,116 @@ Ref<Font> RichTextLabel::_find_font(Item *p_item) { return Ref<Font>(); } -int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font) { +int RichTextLabel::_find_font_size(Item *p_item) { + Item *sizeitem = p_item; + + while (sizeitem) { + if (sizeitem->type == ITEM_FONT_SIZE) { + ItemFontSize *fi = static_cast<ItemFontSize *>(sizeitem); + return fi->font_size; + } + + sizeitem = sizeitem->parent; + } + + return -1; +} + +int RichTextLabel::_find_outline_size(Item *p_item) { + Item *sizeitem = p_item; + + while (sizeitem) { + if (sizeitem->type == ITEM_OUTLINE_SIZE) { + ItemOutlineSize *fi = static_cast<ItemOutlineSize *>(sizeitem); + return fi->outline_size; + } + + sizeitem = sizeitem->parent; + } + + return 0; +} + +Dictionary RichTextLabel::_find_font_features(Item *p_item) { + Item *ffitem = p_item; + + while (ffitem) { + if (ffitem->type == ITEM_FONT_FEATURES) { + ItemFontFeatures *fi = static_cast<ItemFontFeatures *>(ffitem); + return fi->opentype_features; + } + + ffitem = ffitem->parent; + } + + return Dictionary(); +} + +RichTextLabel::ItemDropcap *RichTextLabel::_find_dc_item(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ITEM_DROPCAP) { + return static_cast<ItemDropcap *>(item); + } + item = item->parent; + } + + return nullptr; +} + +RichTextLabel::ItemList *RichTextLabel::_find_list_item(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ITEM_LIST) { + return static_cast<ItemList *>(item); + } + item = item->parent; + } + + return nullptr; +} + +int RichTextLabel::_find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list) { + Item *item = p_item; + Item *prev_item = p_item; + + int level = 0; + + while (item) { + if (item->type == ITEM_LIST) { + ItemList *list = static_cast<ItemList *>(item); + + ItemFrame *frame = nullptr; + int line = -1; + _find_frame(list, &frame, &line); + + int index = 1; + if (frame != nullptr) { + for (int i = list->line + 1; i <= prev_item->line; i++) { + if (_find_list_item(frame->lines[i].from) == list) { + index++; + } + } + } + + r_index.push_back(index); + r_list.push_back(list); + + prev_item = item; + } + level++; + item = item->parent; + } + + return level; +} + +int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size) { Item *item = p_item; - int margin = 0; + float margin = 0; while (item) { if (item->type == ITEM_INDENT) { @@ -1348,16 +1842,22 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font) { if (font.is_null()) { font = p_base_font; } - - ItemIndent *indent = static_cast<ItemIndent *>(item); - - margin += indent->level * tab_size * font->get_char_size(' ').width; + int font_size = _find_font_size(item); + if (font_size == -1) { + font_size = p_base_font_size; + } + margin += tab_size * font->get_char_size('m', 0, font_size).width; } else if (item->type == ITEM_LIST) { Ref<Font> font = _find_font(item); if (font.is_null()) { font = p_base_font; } + int font_size = _find_font_size(item); + if (font_size == -1) { + font_size = p_base_font_size; + } + margin += tab_size * font->get_char_size('m', 0, font_size).width; } item = item->parent; @@ -1370,9 +1870,9 @@ RichTextLabel::Align RichTextLabel::_find_align(Item *p_item) { Item *item = p_item; while (item) { - if (item->type == ITEM_ALIGN) { - ItemAlign *align = static_cast<ItemAlign *>(item); - return align->align; + if (item->type == ITEM_PARAGRAPH) { + ItemParagraph *p = static_cast<ItemParagraph *>(item); + return p->align; } item = item->parent; @@ -1381,6 +1881,57 @@ RichTextLabel::Align RichTextLabel::_find_align(Item *p_item) { return default_align; } +TextServer::Direction RichTextLabel::_find_direction(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ITEM_PARAGRAPH) { + ItemParagraph *p = static_cast<ItemParagraph *>(item); + if (p->direction != Control::TEXT_DIRECTION_INHERITED) { + return (TextServer::Direction)p->direction; + } + } + + item = item->parent; + } + + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { + return is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR; + } else { + return (TextServer::Direction)text_direction; + } +} + +Control::StructuredTextParser RichTextLabel::_find_stt(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ITEM_PARAGRAPH) { + ItemParagraph *p = static_cast<ItemParagraph *>(item); + return p->st_parser; + } + + item = item->parent; + } + + return st_parser; +} + +String RichTextLabel::_find_language(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ITEM_PARAGRAPH) { + ItemParagraph *p = static_cast<ItemParagraph *>(item); + return p->language; + } + + item = item->parent; + } + + return language; +} + Color RichTextLabel::_find_color(Item *p_item, const Color &p_default_color) { Item *item = p_item; @@ -1396,25 +1947,26 @@ Color RichTextLabel::_find_color(Item *p_item, const Color &p_default_color) { return p_default_color; } -bool RichTextLabel::_find_underline(Item *p_item) { +Color RichTextLabel::_find_outline_color(Item *p_item, const Color &p_default_color) { Item *item = p_item; while (item) { - if (item->type == ITEM_UNDERLINE) { - return true; + if (item->type == ITEM_OUTLINE_COLOR) { + ItemOutlineColor *color = static_cast<ItemOutlineColor *>(item); + return color->color; } item = item->parent; } - return false; + return p_default_color; } -bool RichTextLabel::_find_strikethrough(Item *p_item) { +bool RichTextLabel::_find_underline(Item *p_item) { Item *item = p_item; while (item) { - if (item->type == ITEM_STRIKETHROUGH) { + if (item->type == ITEM_UNDERLINE) { return true; } @@ -1424,17 +1976,17 @@ bool RichTextLabel::_find_strikethrough(Item *p_item) { return false; } -bool RichTextLabel::_find_by_type(Item *p_item, ItemType p_type) { - ERR_FAIL_INDEX_V((int)p_type, 19, false); - +bool RichTextLabel::_find_strikethrough(Item *p_item) { Item *item = p_item; while (item) { - if (item->type == p_type) { + if (item->type == ITEM_STRIKETHROUGH) { return true; } + item = item->parent; } + return false; } @@ -1449,46 +2001,6 @@ void RichTextLabel::_fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack } } -Color RichTextLabel::_get_color_from_string(const String &p_color_str, const Color &p_default_color) { - if (p_color_str.begins_with("#")) { - return Color::html(p_color_str); - } else if (p_color_str == "aqua") { - return Color(0, 1, 1); - } else if (p_color_str == "black") { - return Color(0, 0, 0); - } else if (p_color_str == "blue") { - return Color(0, 0, 1); - } else if (p_color_str == "fuchsia") { - return Color(1, 0, 1); - } else if (p_color_str == "gray" || p_color_str == "grey") { - return Color(0.5, 0.5, 0.5); - } else if (p_color_str == "green") { - return Color(0, 0.5, 0); - } else if (p_color_str == "lime") { - return Color(0, 1, 0); - } else if (p_color_str == "maroon") { - return Color(0.5, 0, 0); - } else if (p_color_str == "navy") { - return Color(0, 0, 0.5); - } else if (p_color_str == "olive") { - return Color(0.5, 0.5, 0); - } else if (p_color_str == "purple") { - return Color(0.5, 0, 0.5); - } else if (p_color_str == "red") { - return Color(1, 0, 0); - } else if (p_color_str == "silver") { - return Color(0.75, 0.75, 0.75); - } else if (p_color_str == "teal") { - return Color(0, 0.5, 0.5); - } else if (p_color_str == "white") { - return Color(1, 1, 1); - } else if (p_color_str == "yellow") { - return Color(1, 1, 0); - } else { - return p_default_color; - } -} - bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) { Item *item = p_item; @@ -1530,46 +2042,74 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) { void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { if (p_frame->first_invalid_line == p_frame->lines.size()) { + if (p_frame->first_resized_line == p_frame->lines.size()) { + return; + } + + // Resize lines without reshaping. + Size2 size = get_size(); + if (fixed_width != -1) { + size.width = fixed_width; + } + Rect2 text_rect = _get_text_rect(); + + Ref<Font> base_font = get_theme_font("normal_font"); + int base_font_size = get_theme_font_size("normal_font_size"); + + for (int i = p_frame->first_resized_line; i < p_frame->lines.size(); i++) { + _resize_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w); + } + + int total_height = 0; + if (p_frame->lines.size()) { + total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y; + } + + p_frame->first_resized_line = p_frame->lines.size(); + + updating_scroll = true; + vscroll->set_max(total_height); + vscroll->set_page(text_rect.size.height); + if (scroll_follow && scroll_following) { + vscroll->set_value(total_height - size.height); + } + updating_scroll = false; + + if (fit_content_height) { + minimum_size_changed(); + } return; } - //validate invalid lines + // Shape invalid lines. Size2 size = get_size(); if (fixed_width != -1) { size.width = fixed_width; } Rect2 text_rect = _get_text_rect(); - Color font_color_shadow = get_theme_color("font_color_shadow"); - bool use_outline = get_theme_constant("shadow_as_outline"); - Point2 shadow_ofs(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y")); Ref<Font> base_font = get_theme_font("normal_font"); + int base_font_size = get_theme_font_size("normal_font_size"); + int total_chars = (p_frame->first_invalid_line == 0) ? 0 : (p_frame->lines[p_frame->first_invalid_line].char_offset + p_frame->lines[p_frame->first_invalid_line].char_count); for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) { - int y = 0; - _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color(), font_color_shadow, use_outline, shadow_ofs); - p_frame->lines.write[i].height_cache = y; - p_frame->lines.write[i].height_accum_cache = y; - - if (i > 0) { - p_frame->lines.write[i].height_accum_cache += p_frame->lines[i - 1].height_accum_cache; - } + _shape_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w, &total_chars); } int total_height = 0; if (p_frame->lines.size()) { - total_height = p_frame->lines[p_frame->lines.size() - 1].height_accum_cache + get_theme_stylebox("normal")->get_minimum_size().height; + total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y; } - main->first_invalid_line = p_frame->lines.size(); + p_frame->first_invalid_line = p_frame->lines.size(); + p_frame->first_resized_line = p_frame->lines.size(); updating_scroll = true; vscroll->set_max(total_height); - vscroll->set_page(size.height); + vscroll->set_page(text_rect.size.height); if (scroll_follow && scroll_following) { vscroll->set_value(total_height - size.height); } - updating_scroll = false; if (fit_content_height) { @@ -1641,6 +2181,13 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) p_item->parent = current; p_item->E = current->subitems.push_back(p_item); p_item->index = current_idx++; + p_item->char_ofs = current_char_ofs; + if (p_item->type == ITEM_TEXT) { + ItemText *t = (ItemText *)p_item; + current_char_ofs += t->text.length(); + } else if (p_item->type == ITEM_IMAGE) { + current_char_ofs++; + } if (p_enter) { current = p_item; @@ -1686,7 +2233,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub } } -void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color) { +void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, VAlign p_align) { if (current->type == ITEM_TABLE) { return; } @@ -1696,6 +2243,7 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, item->image = p_image; item->color = p_color; + item->inline_align = p_align; if (p_width > 0) { // custom width @@ -1764,11 +2312,29 @@ bool RichTextLabel::remove_line(const int p_line) { main->lines.write[0].from = main; } - main->first_invalid_line = 0; + main->first_invalid_line = 0; // p_line ??? return true; } +void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins, const Color &p_color, int p_ol_size, const Color &p_ol_color) { + ERR_FAIL_COND(current->type == ITEM_TABLE); + ERR_FAIL_COND(p_string.is_empty()); + ERR_FAIL_COND(p_font.is_null()); + ERR_FAIL_COND(p_size <= 0); + + ItemDropcap *item = memnew(ItemDropcap); + + item->text = p_string; + item->font = p_font; + item->font_size = p_size; + item->color = p_color; + item->ol_size = p_ol_size; + item->ol_color = p_ol_color; + item->dropcap_margins = p_dropcap_margins; + _add_item(item, false); +} + void RichTextLabel::push_font(const Ref<Font> &p_font) { ERR_FAIL_COND(current->type == ITEM_TABLE); ERR_FAIL_COND(p_font.is_null()); @@ -1813,6 +2379,30 @@ void RichTextLabel::push_mono() { push_font(mono_font); } +void RichTextLabel::push_font_size(int p_font_size) { + ERR_FAIL_COND(current->type == ITEM_TABLE); + ItemFontSize *item = memnew(ItemFontSize); + + item->font_size = p_font_size; + _add_item(item, true); +} + +void RichTextLabel::push_font_features(const Dictionary &p_features) { + ERR_FAIL_COND(current->type == ITEM_TABLE); + ItemFontFeatures *item = memnew(ItemFontFeatures); + + item->opentype_features = p_features; + _add_item(item, true); +} + +void RichTextLabel::push_outline_size(int p_font_size) { + ERR_FAIL_COND(current->type == ITEM_TABLE); + ItemOutlineSize *item = memnew(ItemOutlineSize); + + item->outline_size = p_font_size; + _add_item(item, true); +} + void RichTextLabel::push_color(const Color &p_color) { ERR_FAIL_COND(current->type == ITEM_TABLE); ItemColor *item = memnew(ItemColor); @@ -1821,6 +2411,14 @@ void RichTextLabel::push_color(const Color &p_color) { _add_item(item, true); } +void RichTextLabel::push_outline_color(const Color &p_color) { + ERR_FAIL_COND(current->type == ITEM_TABLE); + ItemOutlineColor *item = memnew(ItemOutlineColor); + + item->color = p_color; + _add_item(item, true); +} + void RichTextLabel::push_underline() { ERR_FAIL_COND(current->type == ITEM_TABLE); ItemUnderline *item = memnew(ItemUnderline); @@ -1835,11 +2433,14 @@ void RichTextLabel::push_strikethrough() { _add_item(item, true); } -void RichTextLabel::push_align(Align p_align) { +void RichTextLabel::push_paragraph(Align p_align, Control::TextDirection p_direction, const String &p_language, Control::StructuredTextParser p_st_parser) { ERR_FAIL_COND(current->type == ITEM_TABLE); - ItemAlign *item = memnew(ItemAlign); + ItemParagraph *item = memnew(ItemParagraph); item->align = p_align; + item->direction = p_direction; + item->language = p_language; + item->st_parser = p_st_parser; _add_item(item, true, true); } @@ -1852,13 +2453,15 @@ void RichTextLabel::push_indent(int p_level) { _add_item(item, true, true); } -void RichTextLabel::push_list(ListType p_list) { +void RichTextLabel::push_list(int p_level, ListType p_list, bool p_capitalize) { ERR_FAIL_COND(current->type == ITEM_TABLE); - ERR_FAIL_INDEX(p_list, 3); + ERR_FAIL_COND(p_level < 0); ItemList *item = memnew(ItemList); item->list_type = p_list; + item->level = p_level; + item->capitalize = p_capitalize; _add_item(item, true, true); } @@ -1870,17 +2473,18 @@ void RichTextLabel::push_meta(const Variant &p_meta) { _add_item(item, true); } -void RichTextLabel::push_table(int p_columns) { +void RichTextLabel::push_table(int p_columns, VAlign p_align) { ERR_FAIL_COND(p_columns < 1); ItemTable *item = memnew(ItemTable); item->columns.resize(p_columns); item->total_width = 0; + item->inline_align = p_align; for (int i = 0; i < item->columns.size(); i++) { item->columns.write[i].expand = false; item->columns.write[i].expand_ratio = 1; } - _add_item(item, true, true); + _add_item(item, true, false); } void RichTextLabel::push_fade(int p_start_index, int p_length) { @@ -1934,6 +2538,36 @@ void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_r table->columns.write[p_column].expand_ratio = p_ratio; } +void RichTextLabel::set_cell_row_background_color(const Color &p_odd_row_bg, const Color &p_even_row_bg) { + ERR_FAIL_COND(current->type != ITEM_FRAME); + ItemFrame *cell = static_cast<ItemFrame *>(current); + ERR_FAIL_COND(!cell->cell); + cell->odd_row_bg = p_odd_row_bg; + cell->even_row_bg = p_even_row_bg; +} + +void RichTextLabel::set_cell_border_color(const Color &p_color) { + ERR_FAIL_COND(current->type != ITEM_FRAME); + ItemFrame *cell = static_cast<ItemFrame *>(current); + ERR_FAIL_COND(!cell->cell); + cell->border = p_color; +} + +void RichTextLabel::set_cell_size_override(const Size2 &p_min_size, const Size2 &p_max_size) { + ERR_FAIL_COND(current->type != ITEM_FRAME); + ItemFrame *cell = static_cast<ItemFrame *>(current); + ERR_FAIL_COND(!cell->cell); + cell->min_size_over = p_min_size; + cell->max_size_over = p_max_size; +} + +void RichTextLabel::set_cell_padding(const Rect2 &p_padding) { + ERR_FAIL_COND(current->type != ITEM_FRAME); + ItemFrame *cell = static_cast<ItemFrame *>(current); + ERR_FAIL_COND(!cell->cell); + cell->padding = p_padding; +} + void RichTextLabel::push_cell() { ERR_FAIL_COND(current->type != ITEM_TABLE); @@ -1942,10 +2576,9 @@ void RichTextLabel::push_cell() { _add_item(item, true); current_frame = item; item->cell = true; - item->parent_line = item->parent_frame->lines.size() - 1; item->lines.resize(1); item->lines.write[0].from = nullptr; - item->first_invalid_line = 0; + item->first_invalid_line = 0; // parent frame last line ??? } int RichTextLabel::get_current_table_column() const { @@ -1972,9 +2605,13 @@ void RichTextLabel::clear() { main->lines.resize(1); main->first_invalid_line = 0; update(); - selection.click = nullptr; + + selection.click_frame = nullptr; + selection.click_item = nullptr; selection.active = false; + current_idx = 1; + current_char_ofs = 0; if (scroll_follow) { scroll_following = true; } @@ -1986,7 +2623,7 @@ void RichTextLabel::clear() { void RichTextLabel::set_tab_size(int p_spaces) { tab_size = p_spaces; - main->first_invalid_line = 0; + main->first_resized_line = 0; update(); } @@ -2105,7 +2742,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { String bbcode_name; typedef Map<String, String> OptionMap; OptionMap bbcode_options; - if (!split_tag_block.empty()) { + if (!split_tag_block.is_empty()) { bbcode_name = split_tag_block[0]; for (int i = 1; i < split_tag_block.size(); i++) { const String &expr = split_tag_block[i]; @@ -2135,7 +2772,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { if (tag_stack.front()->get() == "i") { in_italics = false; } - if (tag_stack.front()->get() == "indent") { + if ((tag_stack.front()->get() == "indent") || (tag_stack.front()->get() == "ol") || (tag_stack.front()->get() == "ul")) { indent_level--; } @@ -2147,7 +2784,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { tag_stack.pop_front(); pos = brk_end + 1; - if (tag != "/img") { + if (tag != "/img" && tag != "/dropcap") { pop(); } @@ -2177,12 +2814,24 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag.begins_with("table=")) { - int columns = tag.substr(6, tag.length()).to_int(); + Vector<String> subtag = tag.substr(6, tag.length()).split(","); + int columns = subtag[0].to_int(); if (columns < 1) { columns = 1; } - push_table(columns); + VAlign align = VALIGN_TOP; + if (subtag.size() > 1) { + if (subtag[1] == "top" || subtag[1] == "t") { + align = VALIGN_TOP; + } else if (subtag[1] == "center" || subtag[1] == "c") { + align = VALIGN_CENTER; + } else if (subtag[1] == "bottom" || subtag[1] == "b") { + align = VALIGN_BOTTOM; + } + } + + push_table(columns, align); pos = brk_end + 1; tag_stack.push_front("table"); } else if (tag == "cell") { @@ -2197,6 +2846,43 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { set_table_column_expand(get_current_table_column(), true, ratio); push_cell(); + + pos = brk_end + 1; + tag_stack.push_front("cell"); + } else if (tag.begins_with("cell ")) { + Vector<String> subtag = tag.substr(5, tag.length()).split(" "); + + for (int i = 0; i < subtag.size(); i++) { + Vector<String> subtag_a = subtag[i].split("="); + if (subtag_a.size() == 2) { + if (subtag_a[0] == "expand") { + int ratio = subtag_a[1].to_int(); + if (ratio < 1) { + ratio = 1; + } + set_table_column_expand(get_current_table_column(), true, ratio); + } + } + } + push_cell(); + const Color fallback_color = Color(0, 0, 0, 0); + for (int i = 0; i < subtag.size(); i++) { + Vector<String> subtag_a = subtag[i].split("="); + if (subtag_a.size() == 2) { + if (subtag_a[0] == "border") { + Color color = Color::from_string(subtag_a[1], fallback_color); + set_cell_border_color(color); + } else if (subtag_a[0] == "bg") { + Vector<String> subtag_b = subtag_a[1].split(","); + if (subtag_b.size() == 2) { + Color color1 = Color::from_string(subtag_b[0], fallback_color); + Color color2 = Color::from_string(subtag_b[1], fallback_color); + set_cell_row_background_color(color1, color2); + } + } + } + } + pos = brk_end + 1; tag_stack.push_front("cell"); } else if (tag == "u") { @@ -2209,32 +2895,156 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { push_strikethrough(); pos = brk_end + 1; tag_stack.push_front(tag); + } else if (tag == "lrm") { + add_text(String::chr(0x200E)); + pos = brk_end + 1; + } else if (tag == "rlm") { + add_text(String::chr(0x200F)); + pos = brk_end + 1; + } else if (tag == "lre") { + add_text(String::chr(0x202A)); + pos = brk_end + 1; + } else if (tag == "rle") { + add_text(String::chr(0x202B)); + pos = brk_end + 1; + } else if (tag == "lro") { + add_text(String::chr(0x202D)); + pos = brk_end + 1; + } else if (tag == "rlo") { + add_text(String::chr(0x202E)); + pos = brk_end + 1; + } else if (tag == "pdf") { + add_text(String::chr(0x202C)); + pos = brk_end + 1; + } else if (tag == "alm") { + add_text(String::chr(0x061c)); + pos = brk_end + 1; + } else if (tag == "lri") { + add_text(String::chr(0x2066)); + pos = brk_end + 1; + } else if (tag == "rli") { + add_text(String::chr(0x2027)); + pos = brk_end + 1; + } else if (tag == "fsi") { + add_text(String::chr(0x2068)); + pos = brk_end + 1; + } else if (tag == "pdi") { + add_text(String::chr(0x2069)); + pos = brk_end + 1; + } else if (tag == "zwj") { + add_text(String::chr(0x200D)); + pos = brk_end + 1; + } else if (tag == "zwnj") { + add_text(String::chr(0x200C)); + pos = brk_end + 1; + } else if (tag == "wj") { + add_text(String::chr(0x2060)); + pos = brk_end + 1; + } else if (tag == "shy") { + add_text(String::chr(0x00AD)); + pos = brk_end + 1; } else if (tag == "center") { - push_align(ALIGN_CENTER); + push_paragraph(ALIGN_CENTER); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "fill") { - push_align(ALIGN_FILL); + push_paragraph(ALIGN_FILL); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "right") { - push_align(ALIGN_RIGHT); + push_paragraph(ALIGN_RIGHT); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "ul") { - push_list(LIST_DOTS); + indent_level++; + push_list(indent_level, LIST_DOTS, false); pos = brk_end + 1; tag_stack.push_front(tag); - } else if (tag == "ol") { - push_list(LIST_NUMBERS); + } else if ((tag == "ol") || (tag == "ol type=1")) { + indent_level++; + push_list(indent_level, LIST_NUMBERS, false); pos = brk_end + 1; tag_stack.push_front(tag); + } else if (tag == "ol type=a") { + indent_level++; + push_list(indent_level, LIST_LETTERS, false); + pos = brk_end + 1; + tag_stack.push_front("ol"); + } else if (tag == "ol type=A") { + indent_level++; + push_list(indent_level, LIST_LETTERS, true); + pos = brk_end + 1; + tag_stack.push_front("ol"); + } else if (tag == "ol type=i") { + indent_level++; + push_list(indent_level, LIST_ROMAN, false); + pos = brk_end + 1; + tag_stack.push_front("ol"); + } else if (tag == "ol type=I") { + indent_level++; + push_list(indent_level, LIST_ROMAN, true); + pos = brk_end + 1; + tag_stack.push_front("ol"); } else if (tag == "indent") { indent_level++; push_indent(indent_level); pos = brk_end + 1; tag_stack.push_front(tag); - + } else if (tag == "p") { + push_paragraph(ALIGN_LEFT); + pos = brk_end + 1; + tag_stack.push_front("p"); + } else if (tag.begins_with("p ")) { + Vector<String> subtag = tag.substr(2, tag.length()).split(" "); + Align align = ALIGN_LEFT; + Control::TextDirection dir = Control::TEXT_DIRECTION_INHERITED; + String lang; + Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + for (int i = 0; i < subtag.size(); i++) { + Vector<String> subtag_a = subtag[i].split("="); + if (subtag_a.size() == 2) { + if (subtag_a[0] == "align") { + if (subtag_a[1] == "l" || subtag_a[1] == "left") { + align = ALIGN_LEFT; + } else if (subtag_a[1] == "c" || subtag_a[1] == "center") { + align = ALIGN_CENTER; + } else if (subtag_a[1] == "r" || subtag_a[1] == "right") { + align = ALIGN_RIGHT; + } else if (subtag_a[1] == "f" || subtag_a[1] == "fill") { + align = ALIGN_FILL; + } + } else if (subtag_a[0] == "dir" || subtag_a[0] == "direction") { + if (subtag_a[1] == "a" || subtag_a[1] == "auto") { + dir = Control::TEXT_DIRECTION_AUTO; + } else if (subtag_a[1] == "l" || subtag_a[1] == "ltr") { + dir = Control::TEXT_DIRECTION_LTR; + } else if (subtag_a[1] == "r" || subtag_a[1] == "rtl") { + dir = Control::TEXT_DIRECTION_RTL; + } + } else if (subtag_a[0] == "lang" || subtag_a[0] == "language") { + lang = subtag_a[1]; + } else if (subtag_a[0] == "st" || subtag_a[0] == "bidi_override") { + if (subtag_a[1] == "d" || subtag_a[1] == "default") { + st_parser = STRUCTURED_TEXT_DEFAULT; + } else if (subtag_a[1] == "u" || subtag_a[1] == "uri") { + st_parser = STRUCTURED_TEXT_URI; + } else if (subtag_a[1] == "f" || subtag_a[1] == "file") { + st_parser = STRUCTURED_TEXT_FILE; + } else if (subtag_a[1] == "e" || subtag_a[1] == "email") { + st_parser = STRUCTURED_TEXT_EMAIL; + } else if (subtag_a[1] == "l" || subtag_a[1] == "list") { + st_parser = STRUCTURED_TEXT_LIST; + } else if (subtag_a[1] == "n" || subtag_a[1] == "none") { + st_parser = STRUCTURED_TEXT_NONE; + } else if (subtag_a[1] == "c" || subtag_a[1] == "custom") { + st_parser = STRUCTURED_TEXT_CUSTOM; + } + } + } + } + push_paragraph(align, dir, lang, st_parser); + pos = brk_end + 1; + tag_stack.push_front("p"); } else if (tag == "url") { int end = p_bbcode.find("[", brk_end); if (end == -1) { @@ -2251,7 +3061,67 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { push_meta(url); pos = brk_end + 1; tag_stack.push_front("url"); - } else if (bbcode_name == "img") { + } else if (tag.begins_with("dropcap")) { + Vector<String> subtag = tag.substr(5, tag.length()).split(" "); + Ref<Font> f = get_theme_font("normal_font"); + int fs = get_theme_font_size("normal_font_size") * 3; + Color color = get_theme_color("default_color"); + Color outline_color = get_theme_color("outline_color"); + int outline_size = get_theme_constant("outline_size"); + Rect2 dropcap_margins = Rect2(); + + for (int i = 0; i < subtag.size(); i++) { + Vector<String> subtag_a = subtag[i].split("="); + if (subtag_a.size() == 2) { + if (subtag_a[0] == "font" || subtag_a[0] == "f") { + String fnt = subtag_a[1]; + Ref<Font> font = ResourceLoader::load(fnt, "Font"); + if (font.is_valid()) { + f = font; + } + } else if (subtag_a[0] == "font_size") { + fs = subtag_a[1].to_int(); + } else if (subtag_a[0] == "margins") { + Vector<String> subtag_b = subtag_a[1].split(","); + if (subtag_b.size() == 4) { + dropcap_margins.position.x = subtag_b[0].to_float(); + dropcap_margins.position.y = subtag_b[1].to_float(); + dropcap_margins.size.x = subtag_b[2].to_float(); + dropcap_margins.size.y = subtag_b[3].to_float(); + } + } else if (subtag_a[0] == "outline_size") { + outline_size = subtag_a[1].to_int(); + } else if (subtag_a[0] == "color") { + color = Color::from_string(subtag_a[1], color); + } else if (subtag_a[0] == "outline_color") { + outline_color = Color::from_string(subtag_a[1], outline_color); + } + } + } + int end = p_bbcode.find("[", brk_end); + if (end == -1) { + end = p_bbcode.length(); + } + + String txt = p_bbcode.substr(brk_end + 1, end - brk_end - 1); + + push_dropcap(txt, f, fs, dropcap_margins, color, outline_size, outline_color); + + pos = end; + tag_stack.push_front(bbcode_name); + } else if (tag.begins_with("img")) { + VAlign align = VALIGN_TOP; + if (tag.begins_with("img=")) { + String al = tag.substr(4, tag.length()); + if (al == "top" || al == "t") { + align = VALIGN_TOP; + } else if (al == "center" || al == "c") { + align = VALIGN_CENTER; + } else if (al == "bottom" || al == "b") { + align = VALIGN_BOTTOM; + } + } + int end = p_bbcode.find("[", brk_end); if (end == -1) { end = p_bbcode.length(); @@ -2264,12 +3134,12 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { Color color = Color(1.0, 1.0, 1.0); OptionMap::Element *color_option = bbcode_options.find("color"); if (color_option) { - color = _get_color_from_string(color_option->value(), color); + color = Color::from_string(color_option->value(), color); } int width = 0; int height = 0; - if (!bbcode_value.empty()) { + if (!bbcode_value.is_empty()) { int sep = bbcode_value.find("x"); if (sep == -1) { width = bbcode_value.to_int(); @@ -2289,18 +3159,25 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { } } - add_image(texture, width, height, color); + add_image(texture, width, height, color, align); } pos = end; tag_stack.push_front(bbcode_name); } else if (tag.begins_with("color=")) { String color_str = tag.substr(6, tag.length()); - Color color = _get_color_from_string(color_str, base_color); + Color color = Color::from_string(color_str, base_color); push_color(color); pos = brk_end + 1; tag_stack.push_front("color"); + } else if (tag.begins_with("outline_color=")) { + String color_str = tag.substr(14, tag.length()); + Color color = Color::from_string(color_str, base_color); + push_outline_color(color); + pos = brk_end + 1; + tag_stack.push_front("outline_color"); + } else if (tag.begins_with("font=")) { String fnt = tag.substr(5, tag.length()); @@ -2313,6 +3190,54 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { pos = brk_end + 1; tag_stack.push_front("font"); + } else if (tag.begins_with("font_size=")) { + int fnt_size = tag.substr(10, tag.length()).to_int(); + push_font_size(fnt_size); + pos = brk_end + 1; + tag_stack.push_front("font_size"); + } else if (tag.begins_with("opentype_features=")) { + String fnt_ftr = tag.substr(18, tag.length()); + Vector<String> subtag = fnt_ftr.split(","); + Dictionary ftrs; + for (int i = 0; i < subtag.size(); i++) { + Vector<String> subtag_a = subtag[i].split("="); + if (subtag_a.size() == 2) { + ftrs[TS->name_to_tag(subtag_a[0])] = subtag_a[1].to_int(); + } else if (subtag_a.size() == 1) { + ftrs[TS->name_to_tag(subtag_a[0])] = 1; + } + } + push_font_features(ftrs); + pos = brk_end + 1; + tag_stack.push_front("opentype_features"); + } else if (tag.begins_with("font ")) { + Vector<String> subtag = tag.substr(2, tag.length()).split(" "); + + for (int i = 1; i < subtag.size(); i++) { + Vector<String> subtag_a = subtag[i].split("=", true, 2); + if (subtag_a.size() == 2) { + if (subtag_a[0] == "name" || subtag_a[0] == "n") { + String fnt = subtag_a[1]; + Ref<Font> font = ResourceLoader::load(fnt, "Font"); + if (font.is_valid()) { + push_font(font); + } else { + push_font(normal_font); + } + } else if (subtag_a[0] == "size" || subtag_a[0] == "s") { + int fnt_size = subtag_a[1].to_int(); + push_font_size(fnt_size); + } + } + } + + pos = brk_end + 1; + tag_stack.push_front("font"); + } else if (tag.begins_with("outline_size=")) { + int fnt_size = tag.substr(13, tag.length()).to_int(); + push_outline_size(fnt_size); + pos = brk_end + 1; + tag_stack.push_front("outline_size"); } else if (bbcode_name == "fade") { int start_index = 0; @@ -2445,7 +3370,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { void RichTextLabel::scroll_to_line(int p_line) { ERR_FAIL_INDEX(p_line, main->lines.size()); _validate_line_caches(main); - vscroll->set_value(main->lines[p_line].height_accum_cache - main->lines[p_line].height_cache); + vscroll->set_value(main->lines[p_line].offset.y); } int RichTextLabel::get_line_count() const { @@ -2472,95 +3397,160 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) { } } -bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) { - ERR_FAIL_COND_V(!selection.enabled, false); - Item *it = main; - int charidx = 0; - - if (p_from_selection && selection.active) { - it = selection.to; - charidx = selection.to_char + 1; - } - - while (it) { - if (it->type == ITEM_TEXT) { - ItemText *t = static_cast<ItemText *>(it); - int sp = t->text.findn(p_string, charidx); - if (sp != -1) { - selection.from = it; - selection.from_char = sp; - selection.to = it; - selection.to_char = sp + p_string.length() - 1; - selection.active = true; - update(); - - _validate_line_caches(main); +bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p_string, Item *p_from, Item *p_to) { + ERR_FAIL_COND_V(p_frame == nullptr, false); + ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), false); - int fh = _find_font(t).is_valid() ? _find_font(t)->get_height() : get_theme_font("normal_font")->get_height(); + Line &l = p_frame->lines.write[p_line]; - float offset = 0; + String text; + Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { + switch (it->type) { + case ITEM_NEWLINE: { + text += "\n"; + } break; + case ITEM_TEXT: { + ItemText *t = (ItemText *)it; + text += t->text; + } break; + case ITEM_IMAGE: { + text += " "; + } break; + case ITEM_TABLE: { + ItemTable *table = static_cast<ItemTable *>(it); + int idx = 0; + for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames. + ItemFrame *frame = static_cast<ItemFrame *>(E->get()); - int line = t->line; - Item *item = t; - while (item) { - if (item->type == ITEM_FRAME) { - ItemFrame *frame = static_cast<ItemFrame *>(item); - if (line >= 0 && line < frame->lines.size()) { - offset += frame->lines[line].height_accum_cache - frame->lines[line].height_cache; - line = frame->line; + for (int i = 0; i < frame->lines.size(); i++) { + if (_search_line(frame, i, p_string, p_from, p_to)) { + return true; } } - item = item->parent; + idx++; } - vscroll->set_value(offset - fh); + } break; + default: + break; + } + } + int sp = text.findn(p_string, 0); + if (sp != -1) { + selection.from_frame = p_frame; + selection.from_line = p_line; + selection.from_item = _get_item_at_pos(l.from, it_to, sp); + selection.from_char = sp; + selection.to_frame = p_frame; + selection.to_line = p_line; + selection.to_item = _get_item_at_pos(l.from, it_to, sp + p_string.length() - 1); + selection.to_char = sp + p_string.length() - 1; + selection.active = true; + return true; + } + + return false; +} + +bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) { + ERR_FAIL_COND_V(!selection.enabled, false); + if (p_from_selection && selection.active) { + for (int i = 0; i < main->lines.size(); i++) { + if (_search_line(main, i, p_string, selection.from_item, selection.to_item)) { + update(); return true; } } - - if (p_search_previous) { - it = _get_prev_item(it, true); - } else { - it = _get_next_item(it, true); + } else { + for (int i = 0; i < main->lines.size(); i++) { + if (_search_line(main, i, p_string, nullptr, nullptr)) { + update(); + return true; + } } - charidx = 0; } return false; } -String RichTextLabel::get_selected_text() { - if (!selection.active || !selection.enabled) { - return ""; - } - +String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p_selection) { String text; + ERR_FAIL_COND_V(p_frame == nullptr, text); + ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), text); - RichTextLabel::Item *item = selection.from; - - while (item) { - if (item->type == ITEM_TEXT) { - String itext = static_cast<ItemText *>(item)->text; - if (item == selection.from && item == selection.to) { - text += itext.substr(selection.from_char, selection.to_char - selection.from_char + 1); - } else if (item == selection.from) { - text += itext.substr(selection.from_char, itext.size()); - } else if (item == selection.to) { - text += itext.substr(0, selection.to_char + 1); - } else { - text += itext; - } + Line &l = p_frame->lines.write[p_line]; - } else if (item->type == ITEM_NEWLINE) { - text += "\n"; + Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + int end_idx = 0; + if (it_to != nullptr) { + end_idx = it_to->index; + } else { + for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { + end_idx = it->index + 1; + } + } + for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { + if ((p_selection.to_item != nullptr) && (p_selection.to_item->index < l.from->index)) { + break; } - if (item == selection.to) { + if ((p_selection.from_item != nullptr) && (p_selection.from_item->index >= end_idx)) { break; } + switch (it->type) { + case ITEM_NEWLINE: { + text += "\n"; + } break; + case ITEM_TEXT: { + ItemText *t = (ItemText *)it; + text += t->text; + } break; + case ITEM_IMAGE: { + text += " "; + } break; + case ITEM_TABLE: { + ItemTable *table = static_cast<ItemTable *>(it); + int idx = 0; + int col_count = table->columns.size(); + for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { + ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames. + ItemFrame *frame = static_cast<ItemFrame *>(E->get()); + int column = idx % col_count; + + for (int i = 0; i < frame->lines.size(); i++) { + text += _get_line_text(frame, i, p_selection); + } + if (column == col_count - 1) { + text += "\n"; + } else { + text += " "; + } + idx++; + } + } break; + default: + break; + } + } + if ((l.from != nullptr) && (p_frame == p_selection.to_frame) && (p_selection.to_item != nullptr) && (p_selection.to_item->index >= l.from->index) && (p_selection.to_item->index < end_idx)) { + text = text.substr(0, p_selection.to_char); + } + if ((l.from != nullptr) && (p_frame == p_selection.from_frame) && (p_selection.from_item != nullptr) && (p_selection.from_item->index >= l.from->index) && (p_selection.from_item->index < end_idx)) { + text = text.substr(p_selection.from_char, -1); + } + return text; +} - item = _get_next_item(item, true); +String RichTextLabel::get_selected_text() { + if (!selection.active || !selection.enabled) { + return ""; } + String text; + for (int i = 0; i < main->lines.size(); i++) { + text += _get_line_text(main, i, selection); + } return text; } @@ -2611,7 +3601,7 @@ String RichTextLabel::get_text() { text += t->text; } else if (it->type == ITEM_NEWLINE) { text += "\n"; - } else if (it->type == ITEM_INDENT) { + } else if (it->type == ITEM_INDENT || it->type == ITEM_LIST) { text += "\t"; } it = _get_next_item(it, true); @@ -2624,18 +3614,73 @@ void RichTextLabel::set_text(const String &p_string) { add_text(p_string); } -void RichTextLabel::set_percent_visible(float p_percent) { - if (p_percent < 0 || p_percent >= 1) { - visible_characters = -1; - percent_visible = 1; +void RichTextLabel::set_text_direction(Control::TextDirection p_text_direction) { + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (text_direction != p_text_direction) { + text_direction = p_text_direction; + main->first_invalid_line = 0; //invalidate ALL + _validate_line_caches(main); + update(); + } +} - } else { - visible_characters = get_total_character_count() * p_percent; - percent_visible = p_percent; +void RichTextLabel::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { + if (st_parser != p_parser) { + st_parser = p_parser; + main->first_invalid_line = 0; //invalidate ALL + _validate_line_caches(main); + update(); } +} + +Control::StructuredTextParser RichTextLabel::get_structured_text_bidi_override() const { + return st_parser; +} + +void RichTextLabel::set_structured_text_bidi_override_options(Array p_args) { + st_args = p_args; + main->first_invalid_line = 0; //invalidate ALL + _validate_line_caches(main); update(); } +Array RichTextLabel::get_structured_text_bidi_override_options() const { + return st_args; +} + +Control::TextDirection RichTextLabel::get_text_direction() const { + return text_direction; +} + +void RichTextLabel::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + main->first_invalid_line = 0; //invalidate ALL + _validate_line_caches(main); + update(); + } +} + +String RichTextLabel::get_language() const { + return language; +} + +void RichTextLabel::set_percent_visible(float p_percent) { + if (percent_visible != p_percent) { + if (p_percent < 0 || p_percent >= 1) { + visible_characters = -1; + percent_visible = 1; + + } else { + visible_characters = get_total_character_count() * p_percent; + percent_visible = p_percent; + } + main->first_invalid_line = 0; //invalidate ALL + _validate_line_caches(main); + update(); + } +} + float RichTextLabel::get_percent_visible() const { return percent_visible; } @@ -2671,7 +3716,7 @@ void RichTextLabel::install_effect(const Variant effect) { int RichTextLabel::get_content_height() const { int total_height = 0; if (main->lines.size()) { - total_height = main->lines[main->lines.size() - 1].height_accum_cache + get_theme_stylebox("normal")->get_minimum_size().height; + total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y; } return total_height; } @@ -2681,29 +3726,47 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text); ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text); ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text); - ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0))); + ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(VALIGN_TOP)); ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline); ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line); ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font); + ClassDB::bind_method(D_METHOD("push_font_size", "font_size"), &RichTextLabel::push_font_size); + ClassDB::bind_method(D_METHOD("push_font_features", "opentype_features"), &RichTextLabel::push_font_features); ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal); ClassDB::bind_method(D_METHOD("push_bold"), &RichTextLabel::push_bold); ClassDB::bind_method(D_METHOD("push_bold_italics"), &RichTextLabel::push_bold_italics); ClassDB::bind_method(D_METHOD("push_italics"), &RichTextLabel::push_italics); ClassDB::bind_method(D_METHOD("push_mono"), &RichTextLabel::push_mono); ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color); - ClassDB::bind_method(D_METHOD("push_align", "align"), &RichTextLabel::push_align); + ClassDB::bind_method(D_METHOD("push_outline_size", "outline_size"), &RichTextLabel::push_outline_size); + ClassDB::bind_method(D_METHOD("push_outline_color", "color"), &RichTextLabel::push_outline_color); + ClassDB::bind_method(D_METHOD("push_paragraph", "align", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(STRUCTURED_TEXT_DEFAULT)); ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent); - ClassDB::bind_method(D_METHOD("push_list", "type"), &RichTextLabel::push_list); + ClassDB::bind_method(D_METHOD("push_list", "level", "type", "capitalize"), &RichTextLabel::push_list); ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta); ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline); ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough); - ClassDB::bind_method(D_METHOD("push_table", "columns"), &RichTextLabel::push_table); + ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(VALIGN_TOP)); + ClassDB::bind_method(D_METHOD("push_dropcap", "string", "font", "size", "dropcap_margins", "color", "outline_size", "outline_color"), &RichTextLabel::push_dropcap, DEFVAL(Rect2()), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(0, 0, 0, 0))); ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand); + ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color); + ClassDB::bind_method(D_METHOD("set_cell_border_color", "color"), &RichTextLabel::set_cell_border_color); + ClassDB::bind_method(D_METHOD("set_cell_size_override", "min_size", "max_size"), &RichTextLabel::set_cell_size_override); + ClassDB::bind_method(D_METHOD("set_cell_padding", "padding"), &RichTextLabel::set_cell_padding); ClassDB::bind_method(D_METHOD("push_cell"), &RichTextLabel::push_cell); ClassDB::bind_method(D_METHOD("pop"), &RichTextLabel::pop); ClassDB::bind_method(D_METHOD("clear"), &RichTextLabel::clear); + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &RichTextLabel::set_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &RichTextLabel::get_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &RichTextLabel::set_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &RichTextLabel::get_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &RichTextLabel::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &RichTextLabel::get_text_direction); + ClassDB::bind_method(D_METHOD("set_language", "language"), &RichTextLabel::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &RichTextLabel::get_language); + ClassDB::bind_method(D_METHOD("set_meta_underline", "enable"), &RichTextLabel::set_meta_underline); ClassDB::bind_method(D_METHOD("is_meta_underlined"), &RichTextLabel::is_meta_underlined); @@ -2778,6 +3841,13 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, "RichTextEffect", (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + + ADD_GROUP("Structured Text", "structured_text_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); + ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); @@ -2789,6 +3859,7 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(LIST_NUMBERS); BIND_ENUM_CONSTANT(LIST_LETTERS); + BIND_ENUM_CONSTANT(LIST_ROMAN); BIND_ENUM_CONSTANT(LIST_DOTS); BIND_ENUM_CONSTANT(ITEM_FRAME); @@ -2796,10 +3867,14 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(ITEM_IMAGE); BIND_ENUM_CONSTANT(ITEM_NEWLINE); BIND_ENUM_CONSTANT(ITEM_FONT); + BIND_ENUM_CONSTANT(ITEM_FONT_SIZE); + BIND_ENUM_CONSTANT(ITEM_FONT_FEATURES); BIND_ENUM_CONSTANT(ITEM_COLOR); + BIND_ENUM_CONSTANT(ITEM_OUTLINE_SIZE); + BIND_ENUM_CONSTANT(ITEM_OUTLINE_COLOR); BIND_ENUM_CONSTANT(ITEM_UNDERLINE); BIND_ENUM_CONSTANT(ITEM_STRIKETHROUGH); - BIND_ENUM_CONSTANT(ITEM_ALIGN); + BIND_ENUM_CONSTANT(ITEM_PARAGRAPH); BIND_ENUM_CONSTANT(ITEM_INDENT); BIND_ENUM_CONSTANT(ITEM_LIST); BIND_ENUM_CONSTANT(ITEM_TABLE); @@ -2808,8 +3883,9 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(ITEM_WAVE); BIND_ENUM_CONSTANT(ITEM_TORNADO); BIND_ENUM_CONSTANT(ITEM_RAINBOW); - BIND_ENUM_CONSTANT(ITEM_CUSTOMFX); BIND_ENUM_CONSTANT(ITEM_META); + BIND_ENUM_CONSTANT(ITEM_DROPCAP); + BIND_ENUM_CONSTANT(ITEM_CUSTOMFX); } void RichTextLabel::set_visible_characters(int p_visible) { @@ -2929,6 +4005,7 @@ RichTextLabel::RichTextLabel() { main->lines.resize(1); main->lines.write[0].from = main; main->first_invalid_line = 0; + main->first_resized_line = 0; current_frame = main; tab_size = 4; default_align = ALIGN_LEFT; @@ -2948,16 +4025,16 @@ RichTextLabel::RichTextLabel() { add_child(vscroll); vscroll->set_drag_node(String("..")); vscroll->set_step(1); - vscroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 0); - vscroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0); - vscroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0); + vscroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0); + vscroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); + vscroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); vscroll->connect("value_changed", callable_mp(this, &RichTextLabel::_scroll_changed)); vscroll->set_step(1); vscroll->hide(); - current_idx = 1; use_bbcode = false; - selection.click = nullptr; + selection.click_frame = nullptr; + selection.click_item = nullptr; selection.active = false; selection.enabled = false; diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 67a3f466a6..a65cc5a451 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ #include "rich_text_effect.h" #include "scene/gui/scroll_bar.h" +#include "scene/resources/text_paragraph.h" class RichTextLabel : public Control { GDCLASS(RichTextLabel, Control); @@ -48,6 +49,7 @@ public: enum ListType { LIST_NUMBERS, LIST_LETTERS, + LIST_ROMAN, LIST_DOTS }; @@ -57,10 +59,14 @@ public: ITEM_IMAGE, ITEM_NEWLINE, ITEM_FONT, + ITEM_FONT_SIZE, + ITEM_FONT_FEATURES, ITEM_COLOR, + ITEM_OUTLINE_SIZE, + ITEM_OUTLINE_COLOR, ITEM_UNDERLINE, ITEM_STRIKETHROUGH, - ITEM_ALIGN, + ITEM_PARAGRAPH, ITEM_INDENT, ITEM_LIST, ITEM_TABLE, @@ -70,6 +76,7 @@ public: ITEM_TORNADO, ITEM_RAINBOW, ITEM_META, + ITEM_DROPCAP, ITEM_CUSTOMFX }; @@ -80,31 +87,28 @@ private: struct Item; struct Line { - Item *from; - Vector<int> offset_caches; - Vector<int> height_caches; - Vector<int> ascent_caches; - Vector<int> descent_caches; - Vector<int> space_caches; - int height_cache; - int height_accum_cache; - int char_count; - int minimum_width; - int maximum_width; - - Line() { - from = nullptr; - char_count = 0; - } + Item *from = nullptr; + + Ref<TextParagraph> text_buf; + Color dc_color; + int dc_ol_size = 0; + Color dc_ol_color; + + Vector2 offset; + int char_offset = 0; + int char_count = 0; + + Line() { text_buf.instance(); } }; struct Item { - int index; - Item *parent; - ItemType type; + int index = 0; + int char_ofs = 0; + Item *parent = nullptr; + ItemType type = ITEM_FRAME; List<Item *> subitems; - List<Item *>::Element *E; - int line; + List<Item *>::Element *E = nullptr; + int line = 0; void _clear_children() { while (subitems.size()) { @@ -113,29 +117,26 @@ private: } } - Item() { - parent = nullptr; - E = nullptr; - line = 0; - index = 0; - type = ITEM_FRAME; - } virtual ~Item() { _clear_children(); } }; struct ItemFrame : public Item { - int parent_line; - bool cell; + bool cell = false; + Vector<Line> lines; - int first_invalid_line; - ItemFrame *parent_frame; - - ItemFrame() { - type = ITEM_FRAME; - parent_frame = nullptr; - cell = false; - parent_line = 0; - } + int first_invalid_line = 0; + int first_resized_line = 0; + + ItemFrame *parent_frame = nullptr; + + Color odd_row_bg = Color(0, 0, 0, 0); + Color even_row_bg = Color(0, 0, 0, 0); + Color border = Color(0, 0, 0, 0); + Size2 min_size_over = Size2(-1, -1); + Size2 max_size_over = Size2(-1, -1); + Rect2 padding; + + ItemFrame() { type = ITEM_FRAME; } }; struct ItemText : public Item { @@ -143,8 +144,20 @@ private: ItemText() { type = ITEM_TEXT; } }; + struct ItemDropcap : public Item { + String text; + Ref<Font> font; + int font_size; + Color color; + int ol_size; + Color ol_color; + Rect2 dropcap_margins; + ItemDropcap() { type = ITEM_DROPCAP; } + }; + struct ItemImage : public Item { Ref<Texture2D> image; + VAlign inline_align = VALIGN_TOP; Size2 size; Color color; ItemImage() { type = ITEM_IMAGE; } @@ -155,11 +168,31 @@ private: ItemFont() { type = ITEM_FONT; } }; + struct ItemFontSize : public Item { + int font_size = 16; + ItemFontSize() { type = ITEM_FONT_SIZE; } + }; + + struct ItemFontFeatures : public Item { + Dictionary opentype_features; + ItemFontFeatures() { type = ITEM_FONT_FEATURES; } + }; + struct ItemColor : public Item { Color color; ItemColor() { type = ITEM_COLOR; } }; + struct ItemOutlineSize : public Item { + int outline_size = 0; + ItemOutlineSize() { type = ITEM_OUTLINE_SIZE; } + }; + + struct ItemOutlineColor : public Item { + Color color; + ItemOutlineColor() { type = ITEM_OUTLINE_COLOR; } + }; + struct ItemUnderline : public Item { ItemUnderline() { type = ITEM_UNDERLINE; } }; @@ -173,18 +206,23 @@ private: ItemMeta() { type = ITEM_META; } }; - struct ItemAlign : public Item { - Align align; - ItemAlign() { type = ITEM_ALIGN; } + struct ItemParagraph : public Item { + Align align = ALIGN_LEFT; + String language; + Control::TextDirection direction = Control::TEXT_DIRECTION_AUTO; + Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + ItemParagraph() { type = ITEM_PARAGRAPH; } }; struct ItemIndent : public Item { - int level; + int level = 0; ItemIndent() { type = ITEM_INDENT; } }; struct ItemList : public Item { - ListType list_type; + ListType list_type = LIST_DOTS; + bool capitalize = false; + int level = 0; ItemList() { type = ITEM_LIST; } }; @@ -202,37 +240,32 @@ private: }; Vector<Column> columns; - int total_width; + Vector<float> rows; + + int total_width = 0; + int total_height = 0; + VAlign inline_align = VALIGN_TOP; ItemTable() { type = ITEM_TABLE; } }; struct ItemFade : public Item { - int starting_index; - int length; + int starting_index = 0; + int length = 0; ItemFade() { type = ITEM_FADE; } }; struct ItemFX : public Item { - float elapsed_time; - - ItemFX() { - elapsed_time = 0.0f; - } + float elapsed_time = 0.f; }; struct ItemShake : public ItemFX { - int strength; - float rate; - uint64_t _current_rng; - uint64_t _previous_rng; - - ItemShake() { - strength = 0; - rate = 0.0f; - _current_rng = 0; - type = ITEM_SHAKE; - } + int strength = 0; + float rate = 0.0f; + uint64_t _current_rng = 0; + uint64_t _previous_rng = 0; + + ItemShake() { type = ITEM_SHAKE; } void reroll_random() { _previous_rng = _current_rng; @@ -251,38 +284,25 @@ private: }; struct ItemWave : public ItemFX { - float frequency; - float amplitude; + float frequency = 1.0f; + float amplitude = 1.0f; - ItemWave() { - frequency = 1.0f; - amplitude = 1.0f; - type = ITEM_WAVE; - } + ItemWave() { type = ITEM_WAVE; } }; struct ItemTornado : public ItemFX { - float radius; - float frequency; + float radius = 1.0f; + float frequency = 1.0f; - ItemTornado() { - radius = 1.0f; - frequency = 1.0f; - type = ITEM_TORNADO; - } + ItemTornado() { type = ITEM_TORNADO; } }; struct ItemRainbow : public ItemFX { - float saturation; - float value; - float frequency; - - ItemRainbow() { - saturation = 0.8f; - value = 0.8f; - frequency = 1.0f; - type = ITEM_RAINBOW; - } + float saturation = 0.8f; + float value = 0.8f; + float frequency = 1.0f; + + ItemRainbow() { type = ITEM_RAINBOW; } }; struct ItemCustomFX : public ItemFX { @@ -291,7 +311,6 @@ private: ItemCustomFX() { type = ITEM_CUSTOMFX; - char_fx_transform.instance(); } @@ -316,7 +335,8 @@ private: int scroll_w; bool scroll_updated; bool updating_scroll; - int current_idx; + int current_idx = 1; + int current_char_ofs = 0; int visible_line_count; int tab_size; @@ -336,23 +356,25 @@ private: void _add_item(Item *p_item, bool p_enter = false, bool p_ensure_newline = false); void _remove_item(Item *p_item, const int p_line, const int p_subitem_line); - struct ProcessState { - int line_width; - }; - - enum ProcessMode { - PROCESS_CACHE, - PROCESS_DRAW, - PROCESS_POINTER - }; + String language; + TextDirection text_direction = TEXT_DIRECTION_AUTO; + Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + Array st_args; struct Selection { - Item *click; + ItemFrame *click_frame; + int click_line; + Item *click_item; int click_char; - Item *from; + ItemFrame *from_frame; + int from_line; + Item *from_item; int from_char; - Item *to; + + ItemFrame *to_frame; + int to_line; + Item *to_item; int to_char; bool active; // anything selected? i.e. from, to, etc. valid? @@ -364,22 +386,41 @@ private: int visible_characters; float percent_visible; - int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr, int p_char_count = 0); - void _find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr); + void _find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr); + + String _get_line_text(ItemFrame *p_frame, int p_line, Selection p_sel); + bool _search_line(ItemFrame *p_frame, int p_line, const String &p_string, Item *p_from, Item *p_to); + void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset); + void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width); + void _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs); + float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr); + + String _roman(int p_num, bool p_capitalize) const; + String _letters(int p_num, bool p_capitalize) const; + + Item *_get_item_at_pos(Item *p_item_from, Item *p_item_to, int p_position); + void _find_frame(Item *p_item, ItemFrame **r_frame, int *r_line); Ref<Font> _find_font(Item *p_item); - int _find_margin(Item *p_item, const Ref<Font> &p_base_font); + int _find_font_size(Item *p_item); + Dictionary _find_font_features(Item *p_item); + int _find_outline_size(Item *p_item); + ItemList *_find_list_item(Item *p_item); + ItemDropcap *_find_dc_item(Item *p_item); + int _find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list); + int _find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size); Align _find_align(Item *p_item); + TextServer::Direction _find_direction(Item *p_item); + Control::StructuredTextParser _find_stt(Item *p_item); + String _find_language(Item *p_item); Color _find_color(Item *p_item, const Color &p_default_color); + Color _find_outline_color(Item *p_item, const Color &p_default_color); bool _find_underline(Item *p_item); bool _find_strikethrough(Item *p_item); bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = nullptr); bool _find_layout_subitem(Item *from, Item *to); - bool _find_by_type(Item *p_item, ItemType p_type); void _fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack); - static Color _get_color_from_string(const String &p_color_str, const Color &p_default_color); - void _update_scroll(); void _update_fx(ItemFrame *p_frame, float p_delta_time); void _scroll_changed(double); @@ -395,8 +436,6 @@ private: bool use_bbcode; String bbcode; - void _update_all_lines(); - int fixed_width; bool fit_content_height; @@ -407,23 +446,28 @@ protected: public: String get_text(); void add_text(const String &p_text); - void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0)); + void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), VAlign p_align = VALIGN_TOP); void add_newline(); bool remove_line(const int p_line); + void push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Color &p_color = Color(1, 1, 1), int p_ol_size = 0, const Color &p_ol_color = Color(0, 0, 0, 0)); void push_font(const Ref<Font> &p_font); + void push_font_size(int p_font_size); + void push_font_features(const Dictionary &p_features); + void push_outline_size(int p_font_size); void push_normal(); void push_bold(); void push_bold_italics(); void push_italics(); void push_mono(); void push_color(const Color &p_color); + void push_outline_color(const Color &p_color); void push_underline(); void push_strikethrough(); - void push_align(Align p_align); + void push_paragraph(Align p_align, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", Control::StructuredTextParser p_st_parser = STRUCTURED_TEXT_DEFAULT); void push_indent(int p_level); - void push_list(ListType p_list); + void push_list(int p_level, ListType p_list, bool p_capitalize); void push_meta(const Variant &p_meta); - void push_table(int p_columns); + void push_table(int p_columns, VAlign p_align = VALIGN_TOP); void push_fade(int p_start_index, int p_length); void push_shake(int p_strength, float p_rate); void push_wave(float p_frequency, float p_amplitude); @@ -431,6 +475,10 @@ public: void push_rainbow(float p_saturation, float p_value, float p_frequency); void push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment); void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1); + void set_cell_row_background_color(const Color &p_odd_row_bg, const Color &p_even_row_bg); + void set_cell_border_color(const Color &p_color); + void set_cell_size_override(const Size2 &p_min_size, const Size2 &p_max_size); + void set_cell_padding(const Rect2 &p_padding); int get_current_table_column() const; void push_cell(); void pop(); @@ -485,6 +533,18 @@ public: void set_text(const String &p_string); + void set_text_direction(TextDirection p_text_direction); + TextDirection get_text_direction() const; + + void set_language(const String &p_language); + String get_language() const; + + void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); + Control::StructuredTextParser get_structured_text_bidi_override() const; + + void set_structured_text_bidi_override_options(Array p_args); + Array get_structured_text_bidi_override_options() const; + void set_visible_characters(int p_visible); int get_visible_characters() const; int get_total_character_count() const; diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 9340d98ede..79a0de35fc 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -259,11 +259,11 @@ void ScrollBar::_notification(int p_what) { grabber_rect.size.width = get_grabber_size(); grabber_rect.size.height = get_size().height; grabber_rect.position.y = 0; - grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(MARGIN_LEFT); + grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(SIDE_LEFT); } else { grabber_rect.size.width = get_size().width; grabber_rect.size.height = get_grabber_size(); - grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(MARGIN_TOP); + grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(SIDE_TOP); grabber_rect.position.x = 0; } @@ -437,30 +437,18 @@ double ScrollBar::get_area_offset() const { double ofs = 0; if (orientation == VERTICAL) { - ofs += get_theme_stylebox("hscroll")->get_margin(MARGIN_TOP); + ofs += get_theme_stylebox("hscroll")->get_margin(SIDE_TOP); ofs += get_theme_icon("decrement")->get_height(); } if (orientation == HORIZONTAL) { - ofs += get_theme_stylebox("hscroll")->get_margin(MARGIN_LEFT); + ofs += get_theme_stylebox("hscroll")->get_margin(SIDE_LEFT); ofs += get_theme_icon("decrement")->get_width(); } return ofs; } -double ScrollBar::get_click_pos(const Point2 &p_pos) const { - float pos = (orientation == VERTICAL) ? p_pos.y : p_pos.x; - pos -= get_area_offset(); - - float area = get_area_size(); - if (area == 0) { - return 0; - } else { - return pos / area; - } -} - double ScrollBar::get_grabber_offset() const { return (get_area_size()) * get_as_ratio(); } diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h index 6ae76e453a..7dd71cc269 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -53,15 +53,14 @@ class ScrollBar : public Range { struct Drag { bool active = false; - float pos_at_click; - float value_at_click; + float pos_at_click = 0; + float value_at_click = 0; } drag; double get_grabber_size() const; double get_grabber_min_size() const; double get_area_size() const; double get_area_offset() const; - double get_click_pos(const Point2 &p_pos) const; double get_grabber_offset() const; static void set_can_focus_by_default(bool p_can_focus); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 8aad5f262d..52feb2ab23 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -213,21 +213,27 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { } void ScrollContainer::_update_scrollbar_position() { + if (!_updating_scrollbars) { + return; + } + Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); - h_scroll->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 0); - h_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0); - h_scroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -hmin.height); - h_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0); + h_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0); + h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); + h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height); + h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); - v_scroll->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -vmin.width); - v_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0); - v_scroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 0); - v_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0); + v_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width); + v_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); + v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0); + v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); h_scroll->raise(); v_scroll->raise(); + + _updating_scrollbars = false; } void ScrollContainer::_ensure_focused_visible(Control *p_control) { @@ -249,74 +255,89 @@ void ScrollContainer::_ensure_focused_visible(Control *p_control) { float diff = MAX(MIN(other_rect.position.y, global_rect.position.y), other_rect.position.y + other_rect.size.y - global_rect.size.y + bottom_margin); set_v_scroll(get_v_scroll() + (diff - global_rect.position.y)); - diff = MAX(MIN(other_rect.position.x, global_rect.position.x), other_rect.position.x + other_rect.size.x - global_rect.size.x + right_margin); + if (is_layout_rtl()) { + diff = MAX(MIN(other_rect.position.x, global_rect.position.x), other_rect.position.x + other_rect.size.x - global_rect.size.x); + } else { + diff = MAX(MIN(other_rect.position.x, global_rect.position.x), other_rect.position.x + other_rect.size.x - global_rect.size.x + right_margin); + } set_h_scroll(get_h_scroll() + (diff - global_rect.position.x)); } } -void ScrollContainer::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { - call_deferred("_update_scrollbar_position"); - }; +void ScrollContainer::_update_dimensions() { + child_max_size = Size2(0, 0); + Size2 size = get_size(); + Point2 ofs; - if (p_what == NOTIFICATION_READY) { - get_viewport()->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_ensure_focused_visible)); - } + Ref<StyleBox> sb = get_theme_stylebox("bg"); + size -= sb->get_minimum_size(); + ofs += sb->get_offset(); + bool rtl = is_layout_rtl(); - if (p_what == NOTIFICATION_SORT_CHILDREN) { - child_max_size = Size2(0, 0); - Size2 size = get_size(); - Point2 ofs; + if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) { //scrolls may have been moved out for reasons + size.y -= h_scroll->get_minimum_size().y; + } - Ref<StyleBox> sb = get_theme_stylebox("bg"); - size -= sb->get_minimum_size(); - ofs += sb->get_offset(); + if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) { //scrolls may have been moved out for reasons + size.x -= v_scroll->get_minimum_size().x; + } - if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) { //scrolls may have been moved out for reasons - size.y -= h_scroll->get_minimum_size().y; + for (int i = 0; i < get_child_count(); i++) { + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c) { + continue; } - - if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) { //scrolls may have been moved out for reasons - size.x -= v_scroll->get_minimum_size().x; + if (c->is_set_as_top_level()) { + continue; } - - for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c) { - continue; - } - if (c->is_set_as_top_level()) { - continue; - } - if (c == h_scroll || c == v_scroll) { - continue; - } - Size2 minsize = c->get_combined_minimum_size(); - child_max_size.x = MAX(child_max_size.x, minsize.x); - child_max_size.y = MAX(child_max_size.y, minsize.y); - - Rect2 r = Rect2(-scroll, minsize); - if (!scroll_h || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) { - r.position.x = 0; - if (c->get_h_size_flags() & SIZE_EXPAND) { - r.size.width = MAX(size.width, minsize.width); - } else { - r.size.width = minsize.width; - } + if (c == h_scroll || c == v_scroll) { + continue; + } + Size2 minsize = c->get_combined_minimum_size(); + child_max_size.x = MAX(child_max_size.x, minsize.x); + child_max_size.y = MAX(child_max_size.y, minsize.y); + + Rect2 r = Rect2(-scroll, minsize); + if (!scroll_h || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) { + r.position.x = 0; + if (c->get_h_size_flags() & SIZE_EXPAND) { + r.size.width = MAX(size.width, minsize.width); + } else { + r.size.width = minsize.width; } - if (!scroll_v || (!v_scroll->is_visible_in_tree() && c->get_v_size_flags() & SIZE_EXPAND)) { - r.position.y = 0; - if (c->get_v_size_flags() & SIZE_EXPAND) { - r.size.height = MAX(size.height, minsize.height); - } else { - r.size.height = minsize.height; - } + } + if (!scroll_v || (!v_scroll->is_visible_in_tree() && c->get_v_size_flags() & SIZE_EXPAND)) { + r.position.y = 0; + if (c->get_v_size_flags() & SIZE_EXPAND) { + r.size.height = MAX(size.height, minsize.height); + } else { + r.size.height = minsize.height; } - r.position += ofs; - fit_child_in_rect(c, r); } + r.position += ofs; + if (rtl && v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) { + r.position.x += v_scroll->get_minimum_size().x; + } + r.position = r.position.floor(); + fit_child_in_rect(c, r); + } + + update(); +} - update(); +void ScrollContainer::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) { + _updating_scrollbars = true; + call_deferred("_update_scrollbar_position"); + }; + + if (p_what == NOTIFICATION_READY) { + get_viewport()->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_ensure_focused_visible)); + _update_dimensions(); + } + + if (p_what == NOTIFICATION_SORT_CHILDREN) { + _update_dimensions(); }; if (p_what == NOTIFICATION_DRAW) { @@ -416,6 +437,7 @@ void ScrollContainer::update_scrollbars() { v_scroll->set_max(min.height); if (hide_scroll_v) { + v_scroll->set_page(size.height); v_scroll->hide(); scroll.y = 0; } else { @@ -431,6 +453,7 @@ void ScrollContainer::update_scrollbars() { h_scroll->set_max(min.width); if (hide_scroll_h) { + h_scroll->set_page(size.width); h_scroll->hide(); scroll.x = 0; } else { @@ -445,8 +468,8 @@ void ScrollContainer::update_scrollbars() { } // Avoid scrollbar overlapping. - h_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, hide_scroll_v ? 0 : -vmin.width); - v_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, hide_scroll_h ? 0 : -hmin.height); + h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, hide_scroll_v ? 0 : -vmin.width); + v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, hide_scroll_h ? 0 : -hmin.height); } void ScrollContainer::_scroll_moved(float) { @@ -540,7 +563,7 @@ String ScrollContainer::get_configuration_warning() const { } if (found != 1) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("ScrollContainer is intended to work with a single child control.\nUse a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually."); diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index b28d66ed53..7e32cbed1c 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -69,11 +69,13 @@ protected: Size2 get_minimum_size() const override; void _gui_input(const Ref<InputEvent> &p_gui_input); + void _update_dimensions(); void _notification(int p_what); void _scroll_moved(float); static void _bind_methods(); + bool _updating_scrollbars = false; void _update_scrollbar_position(); void _ensure_focused_visible(Control *p_node); diff --git a/scene/gui/separator.cpp b/scene/gui/separator.cpp index 4f7e5720b8..3cb8ccf135 100644 --- a/scene/gui/separator.cpp +++ b/scene/gui/separator.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/separator.h b/scene/gui/separator.h index eec989cfea..1ac0f2c503 100644 --- a/scene/gui/separator.h +++ b/scene/gui/separator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/shortcut.cpp b/scene/gui/shortcut.cpp index f8c7bc44a7..cbbcf9e069 100644 --- a/scene/gui/shortcut.cpp +++ b/scene/gui/shortcut.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/shortcut.h b/scene/gui/shortcut.h index 176958b397..ea91f29b5d 100644 --- a/scene/gui/shortcut.h +++ b/scene/gui/shortcut.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 3dd5e022f0..0edf1856de 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/slider.h b/scene/gui/slider.h index b4b56f2856..292931b7b5 100644 --- a/scene/gui/slider.h +++ b/scene/gui/slider.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index ae2f99e91d..037a60810e 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,7 +40,7 @@ Size2 SpinBox::get_minimum_size() const { } void SpinBox::_value_changed(double) { - String value = String::num(get_value(), Math::range_step_decimals(get_step())); + String value = TS->format_number(String::num(get_value(), Math::range_step_decimals(get_step()))); if (prefix != "") { value = prefix + " " + value; } @@ -53,8 +53,10 @@ void SpinBox::_value_changed(double) { void SpinBox::_text_entered(const String &p_string) { Ref<Expression> expr; expr.instance(); + + String num = TS->parse_number(p_string); // Ignore the prefix and suffix in the expression - Error err = expr->parse(p_string.trim_prefix(prefix + " ").trim_suffix(" " + suffix)); + Error err = expr->parse(num.trim_prefix(prefix + " ").trim_suffix(" " + suffix)); if (err != OK) { return; } @@ -170,8 +172,9 @@ void SpinBox::_line_edit_focus_exit() { inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) { int w = icon->get_width(); - if (w != last_w) { - line_edit->set_margin(MARGIN_RIGHT, -w); + if ((w != last_w)) { + line_edit->set_offset(SIDE_LEFT, 0); + line_edit->set_offset(SIDE_RIGHT, -w); last_w = w; } } @@ -185,16 +188,24 @@ void SpinBox::_notification(int p_what) { RID ci = get_canvas_item(); Size2i size = get_size(); - updown->draw(ci, Point2i(size.width - updown->get_width(), (size.height - updown->get_height()) / 2)); + if (is_layout_rtl()) { + updown->draw(ci, Point2i(0, (size.height - updown->get_height()) / 2)); + } else { + updown->draw(ci, Point2i(size.width - updown->get_width(), (size.height - updown->get_height()) / 2)); + } } else if (p_what == NOTIFICATION_FOCUS_EXIT) { //_value_changed(0); } else if (p_what == NOTIFICATION_ENTER_TREE) { _adjust_width_for_icon(get_theme_icon("updown")); _value_changed(0); + } else if (p_what == NOTIFICATION_TRANSLATION_CHANGED) { + _value_changed(0); } else if (p_what == NOTIFICATION_THEME_CHANGED) { call_deferred("minimum_size_changed"); get_line_edit()->call_deferred("minimum_size_changed"); + } else if (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) { + update(); } } @@ -261,8 +272,10 @@ SpinBox::SpinBox() { line_edit = memnew(LineEdit); add_child(line_edit); - line_edit->set_anchors_and_margins_preset(Control::PRESET_WIDE); + line_edit->set_anchors_and_offsets_preset(Control::PRESET_WIDE); line_edit->set_mouse_filter(MOUSE_FILTER_PASS); + line_edit->set_align(LineEdit::ALIGN_LEFT); + //connect("value_changed",this,"_value_changed"); line_edit->connect("text_entered", callable_mp(this, &SpinBox::_text_entered), Vector<Variant>(), CONNECT_DEFERRED); line_edit->connect("focus_exited", callable_mp(this, &SpinBox::_line_edit_focus_exit), Vector<Variant>(), CONNECT_DEFERRED); diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index 1b3dc9d79e..0647ec005b 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index 6508be1e43..d6f2df1d8c 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -112,9 +112,16 @@ void SplitContainer::_resort() { int sofs = middle_sep + sep; fit_child_in_rect(second, Rect2(Point2(0, sofs), Size2(get_size().width, get_size().height - sofs))); } else { - fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(middle_sep, get_size().height))); - int sofs = middle_sep + sep; - fit_child_in_rect(second, Rect2(Point2(sofs, 0), Size2(get_size().width - sofs, get_size().height))); + if (is_layout_rtl()) { + middle_sep = get_size().width - middle_sep - sep; + fit_child_in_rect(second, Rect2(Point2(0, 0), Size2(middle_sep, get_size().height))); + int sofs = middle_sep + sep; + fit_child_in_rect(first, Rect2(Point2(sofs, 0), Size2(get_size().width - sofs, get_size().height))); + } else { + fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(middle_sep, get_size().height))); + int sofs = middle_sep + sep; + fit_child_in_rect(second, Rect2(Point2(sofs, 0), Size2(get_size().width - sofs, get_size().height))); + } } update(); @@ -157,6 +164,10 @@ Size2 SplitContainer::get_minimum_size() const { void SplitContainer::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + queue_sort(); + } break; case NOTIFICATION_SORT_CHILDREN: { _resort(); } break; @@ -247,7 +258,11 @@ void SplitContainer::_gui_input(const Ref<InputEvent> &p_event) { return; } - split_offset = drag_ofs + ((vertical ? mm->get_position().y : mm->get_position().x) - drag_from); + if (!vertical && is_layout_rtl()) { + split_offset = drag_ofs + (drag_from - (vertical ? mm->get_position().y : mm->get_position().x)); + } else { + split_offset = drag_ofs + ((vertical ? mm->get_position().y : mm->get_position().x) - drag_from); + } should_clamp_split_offset = true; queue_sort(); emit_signal("dragged", get_split_offset()); diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h index e345016f3d..46a6792206 100644 --- a/scene/gui/split_container.h +++ b/scene/gui/split_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index c5f56fe8e2..68eef0f96b 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index e82ad772ce..91fc29d377 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index d92f41af2d..64a2a1843d 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,8 @@ #include "tab_container.h" #include "core/object/message_queue.h" +#include "core/string/translation.h" + #include "scene/gui/box_container.h" #include "scene/gui/label.h" #include "scene/gui/texture_rect.h" @@ -48,11 +50,12 @@ int TabContainer::_get_top_margin() const { int tab_height = MAX(MAX(tab_bg->get_minimum_size().height, tab_fg->get_minimum_size().height), tab_disabled->get_minimum_size().height); // Font height or higher icon wins. - Ref<Font> font = get_theme_font("font"); - int content_height = font->get_height(); + int content_height = 0; Vector<Control *> tabs = _get_tabs(); for (int i = 0; i < tabs.size(); i++) { + content_height = MAX(content_height, text_buf[i]->get_size().y); + Control *c = tabs[i]; if (!c->has_meta("_tab_icon")) { continue; @@ -78,23 +81,36 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { Size2 size = get_size(); // Click must be on tabs in the tab header area. - if (pos.x < tabs_ofs_cache || pos.y > _get_top_margin()) { + if (pos.y > _get_top_margin()) { return; } // Handle menu button. Ref<Texture2D> menu = get_theme_icon("menu"); - if (popup && pos.x > size.width - menu->get_width()) { - emit_signal("pre_popup_pressed"); + if (is_layout_rtl()) { + if (popup && pos.x < menu->get_width()) { + emit_signal("pre_popup_pressed"); - Vector2 popup_pos = get_screen_position(); - popup_pos.x += size.width - popup->get_size().width; - popup_pos.y += menu->get_height(); + Vector2 popup_pos = get_screen_position(); + popup_pos.y += menu->get_height(); - popup->set_position(popup_pos); - popup->popup(); - return; + popup->set_position(popup_pos); + popup->popup(); + return; + } + } else { + if (popup && pos.x > size.width - menu->get_width()) { + emit_signal("pre_popup_pressed"); + + Vector2 popup_pos = get_screen_position(); + popup_pos.x += size.width - popup->get_size().width; + popup_pos.y += menu->get_height(); + + popup->set_position(popup_pos); + popup->popup(); + return; + } } // Do not activate tabs when tabs is empty. @@ -113,22 +129,46 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { Ref<Texture2D> increment = get_theme_icon("increment"); Ref<Texture2D> decrement = get_theme_icon("decrement"); - if (pos.x > size.width - increment->get_width() - popup_ofs) { - if (last_tab_cache < tabs.size() - 1) { - first_tab_cache += 1; - update(); + if (is_layout_rtl()) { + if (pos.x < popup_ofs + decrement->get_width()) { + if (last_tab_cache < tabs.size() - 1) { + first_tab_cache += 1; + update(); + } + return; + } else if (pos.x < popup_ofs + increment->get_width() + decrement->get_width()) { + if (first_tab_cache > 0) { + first_tab_cache -= 1; + update(); + } + return; } - return; - } else if (pos.x > size.width - increment->get_width() - decrement->get_width() - popup_ofs) { - if (first_tab_cache > 0) { - first_tab_cache -= 1; - update(); + } else { + if (pos.x > size.width - increment->get_width() - popup_ofs && pos.x) { + if (last_tab_cache < tabs.size() - 1) { + first_tab_cache += 1; + update(); + } + return; + } else if (pos.x > size.width - increment->get_width() - decrement->get_width() - popup_ofs) { + if (first_tab_cache > 0) { + first_tab_cache -= 1; + update(); + } + return; } - return; } } // Activate the clicked tab. + if (is_layout_rtl()) { + pos.x = size.width - pos.x; + } + + if (pos.x < tabs_ofs_cache) { + return; + } + pos.x -= tabs_ofs_cache; for (int i = first_tab_cache; i <= last_tab_cache; i++) { if (get_tab_hidden(i)) { @@ -152,7 +192,7 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { Size2 size = get_size(); // Mouse must be on tabs in the tab header area. - if (pos.x < tabs_ofs_cache || pos.y > _get_top_margin()) { + if (pos.y > _get_top_margin()) { if (menu_hovered || highlight_arrow > -1) { menu_hovered = false; highlight_arrow = -1; @@ -163,16 +203,30 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { Ref<Texture2D> menu = get_theme_icon("menu"); if (popup) { - if (pos.x >= size.width - menu->get_width()) { - if (!menu_hovered) { - menu_hovered = true; - highlight_arrow = -1; + if (is_layout_rtl()) { + if (pos.x <= menu->get_width()) { + if (!menu_hovered) { + menu_hovered = true; + highlight_arrow = -1; + update(); + return; + } + } else if (menu_hovered) { + menu_hovered = false; + update(); + } + } else { + if (pos.x >= size.width - menu->get_width()) { + if (!menu_hovered) { + menu_hovered = true; + highlight_arrow = -1; + update(); + return; + } + } else if (menu_hovered) { + menu_hovered = false; update(); - return; } - } else if (menu_hovered) { - menu_hovered = false; - update(); } if (menu_hovered) { @@ -194,29 +248,43 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { Ref<Texture2D> increment = get_theme_icon("increment"); Ref<Texture2D> decrement = get_theme_icon("decrement"); - if (pos.x >= size.width - increment->get_width() - popup_ofs) { - if (highlight_arrow != 1) { - highlight_arrow = 1; + + if (is_layout_rtl()) { + if (pos.x <= popup_ofs + decrement->get_width()) { + if (highlight_arrow != 1) { + highlight_arrow = 1; + update(); + } + } else if (pos.x <= popup_ofs + increment->get_width() + decrement->get_width()) { + if (highlight_arrow != 0) { + highlight_arrow = 0; + update(); + } + } else if (highlight_arrow > -1) { + highlight_arrow = -1; update(); } - } else if (pos.x >= size.width - increment->get_width() - decrement->get_width() - popup_ofs) { - if (highlight_arrow != 0) { - highlight_arrow = 0; + } else { + if (pos.x >= size.width - increment->get_width() - popup_ofs) { + if (highlight_arrow != 1) { + highlight_arrow = 1; + update(); + } + } else if (pos.x >= size.width - increment->get_width() - decrement->get_width() - popup_ofs) { + if (highlight_arrow != 0) { + highlight_arrow = 0; + update(); + } + } else if (highlight_arrow > -1) { + highlight_arrow = -1; update(); } - } else if (highlight_arrow > -1) { - highlight_arrow = -1; - update(); } } } void TabContainer::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_TRANSLATION_CHANGED: { - minimum_size_changed(); - update(); - } break; case NOTIFICATION_RESIZED: { Vector<Control *> tabs = _get_tabs(); int side_margin = get_theme_constant("side_margin"); @@ -259,6 +327,7 @@ void TabContainer::_notification(int p_what) { case NOTIFICATION_DRAW: { RID canvas = get_canvas_item(); Size2 size = get_size(); + bool rtl = is_layout_rtl(); // Draw only the tab area if the header is hidden. Ref<StyleBox> panel = get_theme_stylebox("panel"); @@ -277,7 +346,6 @@ void TabContainer::_notification(int p_what) { Ref<Texture2D> decrement_hl = get_theme_icon("decrement_highlight"); Ref<Texture2D> menu = get_theme_icon("menu"); Ref<Texture2D> menu_hl = get_theme_icon("menu_highlight"); - Ref<Font> font = get_theme_font("font"); Color font_color_fg = get_theme_color("font_color_fg"); Color font_color_bg = get_theme_color("font_color_bg"); Color font_color_disabled = get_theme_color("font_color_disabled"); @@ -347,65 +415,117 @@ void TabContainer::_notification(int p_what) { break; } + if (all_tabs_in_front) { + // Draw the tab area. + panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); + } + // Draw unselected tabs in back int x = 0; int x_current = 0; + int index = 0; for (int i = 0; i < tab_widths.size(); i++) { - if (get_tab_hidden(i)) { + index = i + first_tab_cache; + if (get_tab_hidden(index)) { continue; } int tab_width = tab_widths[i]; - if (get_tab_disabled(i + first_tab_cache)) { - _draw_tab(tab_disabled, font_color_disabled, i, tabs_ofs_cache + x); - } else if (i + first_tab_cache == current) { + if (get_tab_disabled(index)) { + if (rtl) { + _draw_tab(tab_disabled, font_color_disabled, index, size.width - (tabs_ofs_cache + x) - tab_width); + } else { + _draw_tab(tab_disabled, font_color_disabled, index, tabs_ofs_cache + x); + } + } else if (index == current) { x_current = x; } else { - _draw_tab(tab_bg, font_color_bg, i, tabs_ofs_cache + x); + if (rtl) { + _draw_tab(tab_bg, font_color_bg, index, size.width - (tabs_ofs_cache + x) - tab_width); + } else { + _draw_tab(tab_bg, font_color_bg, index, tabs_ofs_cache + x); + } } x += tab_width; - last_tab_cache = i + first_tab_cache; + last_tab_cache = index; } - // Draw the tab area. - panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); + if (!all_tabs_in_front) { + // Draw the tab area. + panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); + } - // Draw selected tab in front. Need to check tabs.size() in case of no contents at all. - if (tabs.size() > 0) { - _draw_tab(tab_fg, font_color_fg, current, tabs_ofs_cache + x_current); + // Draw selected tab in front. only draw selected tab when it's in visible range. + if (tabs.size() > 0 && current - first_tab_cache < tab_widths.size() && current >= first_tab_cache) { + if (rtl) { + _draw_tab(tab_fg, font_color_fg, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]); + } else { + _draw_tab(tab_fg, font_color_fg, current, tabs_ofs_cache + x_current); + } } // Draw the popup menu. - x = get_size().width; + if (rtl) { + x = 0; + } else { + x = get_size().width; + } if (popup) { - x -= menu->get_width(); + if (!rtl) { + x -= menu->get_width(); + } if (menu_hovered) { menu_hl->draw(get_canvas_item(), Size2(x, (header_height - menu_hl->get_height()) / 2)); } else { menu->draw(get_canvas_item(), Size2(x, (header_height - menu->get_height()) / 2)); } + if (rtl) { + x += menu->get_width(); + } } // Draw the navigation buttons. if (buttons_visible_cache) { - x -= increment->get_width(); - if (last_tab_cache < tabs.size() - 1) { - draw_texture(highlight_arrow == 1 ? increment_hl : increment, Point2(x, (header_height - increment->get_height()) / 2)); - } else { - draw_texture(increment, Point2(x, (header_height - increment->get_height()) / 2), Color(1, 1, 1, 0.5)); - } - - x -= decrement->get_width(); - if (first_tab_cache > 0) { - draw_texture(highlight_arrow == 0 ? decrement_hl : decrement, Point2(x, (header_height - decrement->get_height()) / 2)); + if (rtl) { + if (last_tab_cache < tabs.size() - 1) { + draw_texture(highlight_arrow == 1 ? decrement_hl : decrement, Point2(x, (header_height - increment->get_height()) / 2)); + } else { + draw_texture(decrement, Point2(x, (header_height - increment->get_height()) / 2), Color(1, 1, 1, 0.5)); + } + x += increment->get_width(); + + if (first_tab_cache > 0) { + draw_texture(highlight_arrow == 0 ? increment_hl : increment, Point2(x, (header_height - decrement->get_height()) / 2)); + } else { + draw_texture(increment, Point2(x, (header_height - decrement->get_height()) / 2), Color(1, 1, 1, 0.5)); + } + x += decrement->get_width(); } else { - draw_texture(decrement, Point2(x, (header_height - decrement->get_height()) / 2), Color(1, 1, 1, 0.5)); + x -= increment->get_width(); + if (last_tab_cache < tabs.size() - 1) { + draw_texture(highlight_arrow == 1 ? increment_hl : increment, Point2(x, (header_height - increment->get_height()) / 2)); + } else { + draw_texture(increment, Point2(x, (header_height - increment->get_height()) / 2), Color(1, 1, 1, 0.5)); + } + + x -= decrement->get_width(); + if (first_tab_cache > 0) { + draw_texture(highlight_arrow == 0 ? decrement_hl : decrement, Point2(x, (header_height - decrement->get_height()) / 2)); + } else { + draw_texture(decrement, Point2(x, (header_height - decrement->get_height()) / 2), Color(1, 1, 1, 0.5)); + } } } } break; + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { - minimum_size_changed(); + Vector<Control *> tabs = _get_tabs(); + for (int i = 0; i < tabs.size(); i++) { + text_buf.write[i]->clear(); + } + _theme_changing = true; call_deferred("_on_theme_changed"); // Wait until all changed theme. } break; } @@ -424,11 +544,11 @@ void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, in p_tab_style->draw(canvas, tab_rect); // Draw the tab contents. - Control *control = Object::cast_to<Control>(tabs[p_index + first_tab_cache]); + Control *control = Object::cast_to<Control>(tabs[p_index]); String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(tr(control->get_name())); - int x_content = tab_rect.position.x + p_tab_style->get_margin(MARGIN_LEFT); - int top_margin = p_tab_style->get_margin(MARGIN_TOP); + int x_content = tab_rect.position.x + p_tab_style->get_margin(SIDE_LEFT); + int top_margin = p_tab_style->get_margin(SIDE_TOP); int y_center = top_margin + (tab_rect.size.y - p_tab_style->get_minimum_size().y) / 2; // Draw the tab icon. @@ -444,15 +564,40 @@ void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, in } // Draw the tab text. - Point2i text_pos(x_content, y_center - (font->get_height() / 2) + font->get_ascent()); - font->draw(canvas, text_pos, text, p_font_color); + Point2i text_pos(x_content, y_center - text_buf[p_index]->get_size().y / 2); + text_buf[p_index]->draw(canvas, text_pos, p_font_color); +} + +void TabContainer::_refresh_texts() { + text_buf.clear(); + Vector<Control *> tabs = _get_tabs(); + bool rtl = is_layout_rtl(); + Ref<Font> font = get_theme_font("font"); + int font_size = get_theme_font_size("font_size"); + for (int i = 0; i < tabs.size(); i++) { + Control *control = Object::cast_to<Control>(tabs[i]); + String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(tr(control->get_name())); + Ref<TextLine> name; + name.instance(); + name->set_direction(rtl ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + name->add_string(text, font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + text_buf.push_back(name); + } } void TabContainer::_on_theme_changed() { + if (!_theme_changing) { + return; + } + + _refresh_texts(); + + minimum_size_changed(); if (get_tab_count() > 0) { _repaint(); update(); } + _theme_changing = false; } void TabContainer::_repaint() { @@ -462,14 +607,14 @@ void TabContainer::_repaint() { Control *c = tabs[i]; if (i == current) { c->show(); - c->set_anchors_and_margins_preset(Control::PRESET_WIDE); + c->set_anchors_and_offsets_preset(Control::PRESET_WIDE); if (tabs_visible) { - c->set_margin(MARGIN_TOP, _get_top_margin()); + c->set_offset(SIDE_TOP, _get_top_margin()); } - c->set_margin(Margin(MARGIN_TOP), c->get_margin(Margin(MARGIN_TOP)) + sb->get_margin(Margin(MARGIN_TOP))); - c->set_margin(Margin(MARGIN_LEFT), c->get_margin(Margin(MARGIN_LEFT)) + sb->get_margin(Margin(MARGIN_LEFT))); - c->set_margin(Margin(MARGIN_RIGHT), c->get_margin(Margin(MARGIN_RIGHT)) - sb->get_margin(Margin(MARGIN_RIGHT))); - c->set_margin(Margin(MARGIN_BOTTOM), c->get_margin(Margin(MARGIN_BOTTOM)) - sb->get_margin(Margin(MARGIN_BOTTOM))); + c->set_offset(Side(SIDE_TOP), c->get_offset(Side(SIDE_TOP)) + sb->get_margin(Side(SIDE_TOP))); + c->set_offset(Side(SIDE_LEFT), c->get_offset(Side(SIDE_LEFT)) + sb->get_margin(Side(SIDE_LEFT))); + c->set_offset(Side(SIDE_RIGHT), c->get_offset(Side(SIDE_RIGHT)) - sb->get_margin(Side(SIDE_RIGHT))); + c->set_offset(Side(SIDE_BOTTOM), c->get_offset(Side(SIDE_BOTTOM)) - sb->get_margin(Side(SIDE_BOTTOM))); } else { c->hide(); @@ -494,8 +639,9 @@ int TabContainer::_get_tab_width(int p_index) const { // Get the width of the text displayed on the tab. Ref<Font> font = get_theme_font("font"); + int font_size = get_theme_font_size("font_size"); String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(control->get_name()); - int width = font->get_string_size(text).width; + int width = font->get_string_size(text, font_size).width; // Add space for a tab icon. if (control->has_meta("_tab_icon")) { @@ -537,6 +683,7 @@ Vector<Control *> TabContainer::_get_tabs() const { } void TabContainer::_child_renamed_callback() { + _refresh_texts(); update(); } @@ -551,9 +698,12 @@ void TabContainer::add_child_notify(Node *p_child) { return; } + Vector<Control *> tabs = _get_tabs(); + _refresh_texts(); + bool first = false; - if (get_tab_count() != 1) { + if (tabs.size() != 1) { c->hide(); } else { c->show(); @@ -562,16 +712,15 @@ void TabContainer::add_child_notify(Node *p_child) { current = 0; previous = 0; } - c->set_anchors_and_margins_preset(Control::PRESET_WIDE); + c->set_anchors_and_offsets_preset(Control::PRESET_WIDE); if (tabs_visible) { - c->set_margin(MARGIN_TOP, _get_top_margin()); + c->set_offset(SIDE_TOP, _get_top_margin()); } Ref<StyleBox> sb = get_theme_stylebox("panel"); - c->set_margin(Margin(MARGIN_TOP), c->get_margin(Margin(MARGIN_TOP)) + sb->get_margin(Margin(MARGIN_TOP))); - c->set_margin(Margin(MARGIN_LEFT), c->get_margin(Margin(MARGIN_LEFT)) + sb->get_margin(Margin(MARGIN_LEFT))); - c->set_margin(Margin(MARGIN_RIGHT), c->get_margin(Margin(MARGIN_RIGHT)) - sb->get_margin(Margin(MARGIN_RIGHT))); - c->set_margin(Margin(MARGIN_BOTTOM), c->get_margin(Margin(MARGIN_BOTTOM)) - sb->get_margin(Margin(MARGIN_BOTTOM))); - + c->set_offset(Side(SIDE_TOP), c->get_offset(Side(SIDE_TOP)) + sb->get_margin(Side(SIDE_TOP))); + c->set_offset(Side(SIDE_LEFT), c->get_offset(Side(SIDE_LEFT)) + sb->get_margin(Side(SIDE_LEFT))); + c->set_offset(Side(SIDE_RIGHT), c->get_offset(Side(SIDE_RIGHT)) - sb->get_margin(Side(SIDE_RIGHT))); + c->set_offset(Side(SIDE_BOTTOM), c->get_offset(Side(SIDE_BOTTOM)) - sb->get_margin(Side(SIDE_BOTTOM))); update(); p_child->connect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback)); if (first && is_inside_tree()) { @@ -579,6 +728,13 @@ void TabContainer::add_child_notify(Node *p_child) { } } +void TabContainer::move_child_notify(Node *p_child) { + Container::move_child_notify(p_child); + call_deferred("_update_current_tab"); + _refresh_texts(); + update(); +} + int TabContainer::get_tab_count() const { return _get_tabs().size(); } @@ -633,6 +789,10 @@ Control *TabContainer::get_current_tab_control() const { void TabContainer::remove_child_notify(Node *p_child) { Container::remove_child_notify(p_child); + if (!Object::cast_to<Control>(p_child)) { + return; + } + call_deferred("_update_current_tab"); p_child->disconnect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback)); @@ -641,7 +801,10 @@ void TabContainer::remove_child_notify(Node *p_child) { } void TabContainer::_update_current_tab() { - int tc = get_tab_count(); + Vector<Control *> tabs = _get_tabs(); + _refresh_texts(); + + int tc = tabs.size(); if (current >= tc) { current = tc - 1; } @@ -757,30 +920,38 @@ int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const { } // must be on tabs in the tab header area. - if (p_point.x < tabs_ofs_cache || p_point.y > _get_top_margin()) { + if (p_point.y > _get_top_margin()) { return -1; } Size2 size = get_size(); - int right_ofs = 0; + int button_ofs = 0; + int px = p_point.x; + + if (is_layout_rtl()) { + px = size.width - px; + } + + if (px < tabs_ofs_cache) { + return -1; + } Popup *popup = get_popup(); if (popup) { Ref<Texture2D> menu = get_theme_icon("menu"); - right_ofs += menu->get_width(); + button_ofs += menu->get_width(); } if (buttons_visible_cache) { Ref<Texture2D> increment = get_theme_icon("increment"); Ref<Texture2D> decrement = get_theme_icon("decrement"); - right_ofs += increment->get_width() + decrement->get_width(); + button_ofs += increment->get_width() + decrement->get_width(); } - if (p_point.x > size.width - right_ofs) { + if (px > size.width - button_ofs) { return -1; } // get the tab at the point Vector<Control *> tabs = _get_tabs(); - int px = p_point.x; px -= tabs_ofs_cache; for (int i = first_tab_cache; i <= last_tab_cache; i++) { int tab_width = _get_tab_width(i); @@ -815,9 +986,9 @@ void TabContainer::set_tabs_visible(bool p_visible) { for (int i = 0; i < tabs.size(); i++) { Control *c = tabs[i]; if (p_visible) { - c->set_margin(MARGIN_TOP, _get_top_margin()); + c->set_offset(SIDE_TOP, _get_top_margin()); } else { - c->set_margin(MARGIN_TOP, 0); + c->set_offset(SIDE_TOP, 0); } } @@ -829,6 +1000,20 @@ bool TabContainer::are_tabs_visible() const { return tabs_visible; } +void TabContainer::set_all_tabs_in_front(bool p_in_front) { + if (p_in_front == all_tabs_in_front) { + return; + } + + all_tabs_in_front = p_in_front; + + update(); +} + +bool TabContainer::is_all_tabs_in_front() const { + return all_tabs_in_front; +} + Control *TabContainer::_get_tab(int p_idx) const { return get_tab_control(p_idx); } @@ -953,7 +1138,7 @@ Size2 TabContainer::get_minimum_size() const { if (tabs_visible) { ms.y += MAX(MAX(tab_bg->get_minimum_size().y, tab_fg->get_minimum_size().y), tab_disabled->get_minimum_size().y); - ms.y += font->get_height(); + ms.y += _get_top_margin(); } Ref<StyleBox> sb = get_theme_stylebox("panel"); @@ -1020,6 +1205,8 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_align"), &TabContainer::get_tab_align); ClassDB::bind_method(D_METHOD("set_tabs_visible", "visible"), &TabContainer::set_tabs_visible); ClassDB::bind_method(D_METHOD("are_tabs_visible"), &TabContainer::are_tabs_visible); + ClassDB::bind_method(D_METHOD("set_all_tabs_in_front", "is_front"), &TabContainer::set_all_tabs_in_front); + ClassDB::bind_method(D_METHOD("is_all_tabs_in_front"), &TabContainer::is_all_tabs_in_front); ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &TabContainer::set_tab_title); ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &TabContainer::get_tab_title); ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabContainer::set_tab_icon); @@ -1046,6 +1233,7 @@ void TabContainer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align"); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "all_tabs_in_front"), "set_all_tabs_in_front", "is_all_tabs_in_front"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hidden_tabs_for_min_size"), "set_use_hidden_tabs_for_min_size", "get_use_hidden_tabs_for_min_size"); @@ -1065,6 +1253,7 @@ TabContainer::TabContainer() { previous = 0; align = ALIGN_CENTER; tabs_visible = true; + all_tabs_in_front = false; drag_to_rearrange_enabled = false; tabs_rearrange_group = -1; use_hidden_tabs_for_min_size = false; diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index f82f594875..58742c5be8 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,8 @@ #include "scene/gui/container.h" #include "scene/gui/popup.h" +#include "scene/resources/text_line.h" + class TabContainer : public Container { GDCLASS(TabContainer, Container); @@ -50,6 +52,7 @@ private: int current; int previous; bool tabs_visible; + bool all_tabs_in_front; bool buttons_visible_cache; bool menu_hovered; int highlight_arrow; @@ -61,19 +64,23 @@ private: bool use_hidden_tabs_for_min_size; int tabs_rearrange_group; + Vector<Ref<TextLine>> text_buf; Vector<Control *> _get_tabs() const; int _get_tab_width(int p_index) const; + bool _theme_changing = false; void _on_theme_changed(); void _repaint(); void _on_mouse_exited(); void _update_current_tab(); void _draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x); + void _refresh_texts(); protected: void _child_renamed_callback(); void _gui_input(const Ref<InputEvent> &p_event); void _notification(int p_what); virtual void add_child_notify(Node *p_child) override; + virtual void move_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; Variant get_drag_data(const Point2 &p_point) override; @@ -90,6 +97,9 @@ public: void set_tabs_visible(bool p_visible); bool are_tabs_visible() const; + void set_all_tabs_in_front(bool p_is_front); + bool is_all_tabs_in_front() const; + void set_tab_title(int p_tab, const String &p_title); String get_tab_title(int p_tab) const; diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index eefe8cc3bc..3bf71d6c01 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,8 @@ #include "tabs.h" #include "core/object/message_queue.h" +#include "core/string/translation.h" + #include "scene/gui/box_container.h" #include "scene/gui/label.h" #include "scene/gui/texture_rect.h" @@ -39,9 +41,10 @@ Size2 Tabs::get_minimum_size() const { Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg"); Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg"); Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled"); - Ref<Font> font = get_theme_font("font"); - Size2 ms(0, MAX(MAX(tab_bg->get_minimum_size().height, tab_fg->get_minimum_size().height), tab_disabled->get_minimum_size().height) + font->get_height()); + int y_margin = MAX(MAX(tab_bg->get_minimum_size().height, tab_fg->get_minimum_size().height), tab_disabled->get_minimum_size().height); + + Size2 ms(0, 0); for (int i = 0; i < tabs.size(); i++) { Ref<Texture2D> tex = tabs[i].icon; @@ -52,7 +55,8 @@ Size2 Tabs::get_minimum_size() const { } } - ms.width += Math::ceil(font->get_string_size(tabs[i].xl_text).width); + ms.width += Math::ceil(tabs[i].text_buf->get_size().x); + ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin); if (tabs[i].disabled) { ms.width += tab_disabled->get_minimum_size().width; @@ -94,12 +98,19 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { Ref<Texture2D> incr = get_theme_icon("increment"); Ref<Texture2D> decr = get_theme_icon("decrement"); - int limit = get_size().width - incr->get_width() - decr->get_width(); - - if (pos.x > limit + decr->get_width()) { - highlight_arrow = 1; - } else if (pos.x > limit) { - highlight_arrow = 0; + if (is_layout_rtl()) { + if (pos.x < decr->get_width()) { + highlight_arrow = 1; + } else if (pos.x < incr->get_width() + decr->get_width()) { + highlight_arrow = 0; + } + } else { + int limit = get_size().width - incr->get_width() - decr->get_width(); + if (pos.x > limit + decr->get_width()) { + highlight_arrow = 1; + } else if (pos.x > limit) { + highlight_arrow = 0; + } } } @@ -142,7 +153,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { if (cb_hover != -1) { //pressed - emit_signal("tab_close", cb_hover); + emit_signal("tab_closed", cb_hover); } cb_pressing = false; @@ -157,20 +168,35 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { Ref<Texture2D> incr = get_theme_icon("increment"); Ref<Texture2D> decr = get_theme_icon("decrement"); - int limit = get_size().width - incr->get_width() - decr->get_width(); - - if (pos.x > limit + decr->get_width()) { - if (missing_right) { - offset++; - update(); + if (is_layout_rtl()) { + if (pos.x < decr->get_width()) { + if (missing_right) { + offset++; + update(); + } + return; + } else if (pos.x < incr->get_width() + decr->get_width()) { + if (offset > 0) { + offset--; + update(); + } + return; } - return; - } else if (pos.x > limit) { - if (offset > 0) { - offset--; - update(); + } else { + int limit = get_size().width - incr->get_width() - decr->get_width(); + if (pos.x > limit + decr->get_width()) { + if (missing_right) { + offset++; + update(); + } + return; + } else if (pos.x > limit) { + if (offset > 0) { + offset--; + update(); + } + return; } - return; } } @@ -188,7 +214,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { return; } - if (pos.x >= tabs[i].ofs_cache && pos.x < tabs[i].ofs_cache + tabs[i].size_cache) { + if (pos.x >= get_tab_rect(i).position.x && pos.x < get_tab_rect(i).position.x + tabs[i].size_cache) { if (!tabs[i].disabled) { found = i; } @@ -204,12 +230,32 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { } } +void Tabs::_shape(int p_tab) { + Ref<Font> font = get_theme_font("font"); + int font_size = get_theme_font_size("font_size"); + + tabs.write[p_tab].xl_text = tr(tabs[p_tab].text); + tabs.write[p_tab].text_buf->clear(); + if (tabs[p_tab].text_direction == Control::TEXT_DIRECTION_INHERITED) { + tabs.write[p_tab].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction); + } + + tabs.write[p_tab].text_buf->add_string(tabs.write[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, (tabs[p_tab].language != "") ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale()); +} + void Tabs::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + _update_cache(); + update(); + } break; case NOTIFICATION_TRANSLATION_CHANGED: { for (int i = 0; i < tabs.size(); ++i) { - tabs.write[i].xl_text = tr(tabs[i].text); + _shape(i); } + _update_cache(); minimum_size_changed(); update(); } break; @@ -225,11 +271,12 @@ void Tabs::_notification(int p_what) { Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg"); Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg"); Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled"); - Ref<Font> font = get_theme_font("font"); Color color_fg = get_theme_color("font_color_fg"); Color color_bg = get_theme_color("font_color_bg"); Color color_disabled = get_theme_color("font_color_disabled"); Ref<Texture2D> close = get_theme_icon("close"); + Vector2 size = get_size(); + bool rtl = is_layout_rtl(); int h = get_size().height; int w = 0; @@ -286,21 +333,34 @@ void Tabs::_notification(int p_what) { max_drawn_tab = i; } - Rect2 sb_rect = Rect2(w, 0, tabs[i].size_cache, h); + Rect2 sb_rect; + if (rtl) { + sb_rect = Rect2(size.width - w - tabs[i].size_cache, 0, tabs[i].size_cache, h); + } else { + sb_rect = Rect2(w, 0, tabs[i].size_cache, h); + } sb->draw(ci, sb_rect); - w += sb->get_margin(MARGIN_LEFT); + w += sb->get_margin(SIDE_LEFT); Size2i sb_ms = sb->get_minimum_size(); Ref<Texture2D> icon = tabs[i].icon; if (icon.is_valid()) { - icon->draw(ci, Point2i(w, sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); + if (rtl) { + icon->draw(ci, Point2i(size.width - w - icon->get_width(), sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); + } else { + icon->draw(ci, Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); + } if (tabs[i].text != "") { w += icon->get_width() + get_theme_constant("hseparation"); } } - font->draw(ci, Point2i(w, sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - font->get_height()) / 2 + font->get_ascent()), tabs[i].xl_text, col, tabs[i].size_text); + if (rtl) { + tabs[i].text_buf->draw(ci, Point2i(size.width - w - tabs[i].text_buf->get_size().x, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2), col); + } else { + tabs[i].text_buf->draw(ci, Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2), col); + } w += tabs[i].size_text; @@ -312,8 +372,12 @@ void Tabs::_notification(int p_what) { Rect2 rb_rect; rb_rect.size = style->get_minimum_size() + rb->get_size(); - rb_rect.position.x = w; - rb_rect.position.y = sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2; + if (rtl) { + rb_rect.position.x = size.width - w - rb_rect.size.x; + } else { + rb_rect.position.x = w; + } + rb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2; if (rb_hover == i) { if (rb_pressing) { @@ -323,7 +387,11 @@ void Tabs::_notification(int p_what) { } } - rb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), rb_rect.position.y + style->get_margin(MARGIN_TOP))); + if (rtl) { + rb->draw(ci, Point2i(size.width - w - rb_rect.size.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP))); + } else { + rb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP))); + } w += rb->get_width(); tabs.write[i].rb_rect = rb_rect; } @@ -336,8 +404,12 @@ void Tabs::_notification(int p_what) { Rect2 cb_rect; cb_rect.size = style->get_minimum_size() + cb->get_size(); - cb_rect.position.x = w; - cb_rect.position.y = sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2; + if (rtl) { + cb_rect.position.x = size.width - w - cb_rect.size.x; + } else { + cb_rect.position.x = w; + } + cb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2; if (!tabs[i].disabled && cb_hover == i) { if (cb_pressing) { @@ -347,27 +419,45 @@ void Tabs::_notification(int p_what) { } } - cb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), cb_rect.position.y + style->get_margin(MARGIN_TOP))); + if (rtl) { + cb->draw(ci, Point2i(size.width - w - cb_rect.size.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP))); + } else { + cb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP))); + } w += cb->get_width(); tabs.write[i].cb_rect = cb_rect; } - w += sb->get_margin(MARGIN_RIGHT); + w += sb->get_margin(SIDE_RIGHT); } if (offset > 0 || missing_right) { int vofs = (get_size().height - incr->get_size().height) / 2; - if (offset > 0) { - draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit, vofs)); - } else { - draw_texture(decr, Point2(limit, vofs), Color(1, 1, 1, 0.5)); - } + if (rtl) { + if (missing_right) { + draw_texture(highlight_arrow == 1 ? decr_hl : decr, Point2(0, vofs)); + } else { + draw_texture(decr, Point2(0, vofs), Color(1, 1, 1, 0.5)); + } - if (missing_right) { - draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit + decr->get_size().width, vofs)); + if (offset > 0) { + draw_texture(highlight_arrow == 0 ? incr_hl : incr, Point2(incr->get_size().width, vofs)); + } else { + draw_texture(incr, Point2(incr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + } } else { - draw_texture(incr, Point2(limit + decr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + if (offset > 0) { + draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit, vofs)); + } else { + draw_texture(decr, Point2(limit, vofs), Color(1, 1, 1, 0.5)); + } + + if (missing_right) { + draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit + decr->get_size().width, vofs)); + } else { + draw_texture(incr, Point2(limit + decr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + } } buttons_visible = true; @@ -422,6 +512,7 @@ void Tabs::set_tab_title(int p_tab, const String &p_title) { ERR_FAIL_INDEX(p_tab, tabs.size()); tabs.write[p_tab].text = p_title; tabs.write[p_tab].xl_text = tr(p_title); + _shape(p_tab); update(); minimum_size_changed(); } @@ -431,6 +522,61 @@ String Tabs::get_tab_title(int p_tab) const { return tabs[p_tab].text; } +void Tabs::set_tab_text_direction(int p_tab, Control::TextDirection p_text_direction) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (tabs[p_tab].text_direction != p_text_direction) { + tabs.write[p_tab].text_direction = p_text_direction; + _shape(p_tab); + update(); + } +} + +Control::TextDirection Tabs::get_tab_text_direction(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), Control::TEXT_DIRECTION_INHERITED); + return tabs[p_tab].text_direction; +} + +void Tabs::clear_tab_opentype_features(int p_tab) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + tabs.write[p_tab].opentype_features.clear(); + _shape(p_tab); + update(); +} + +void Tabs::set_tab_opentype_feature(int p_tab, const String &p_name, int p_value) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + int32_t tag = TS->name_to_tag(p_name); + if (!tabs[p_tab].opentype_features.has(tag) || (int)tabs[p_tab].opentype_features[tag] != p_value) { + tabs.write[p_tab].opentype_features[tag] = p_value; + _shape(p_tab); + update(); + } +} + +int Tabs::get_tab_opentype_feature(int p_tab, const String &p_name) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), -1); + int32_t tag = TS->name_to_tag(p_name); + if (!tabs[p_tab].opentype_features.has(tag)) { + return -1; + } + return tabs[p_tab].opentype_features[tag]; +} + +void Tabs::set_tab_language(int p_tab, const String &p_language) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + if (tabs[p_tab].language != p_language) { + tabs.write[p_tab].language = p_language; + _shape(p_tab); + update(); + } +} + +String Tabs::get_tab_language(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), ""); + return tabs[p_tab].language; +} + void Tabs::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) { ERR_FAIL_INDEX(p_tab, tabs.size()); tabs.write[p_tab].icon = p_icon; @@ -495,7 +641,7 @@ void Tabs::_update_hover() { } if (hover != hover_now) { hover = hover_now; - emit_signal("tab_hover", hover); + emit_signal("tab_hovered", hover); } if (hover_buttons == -1) { // no hover @@ -508,7 +654,6 @@ void Tabs::_update_cache() { Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled"); Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg"); Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg"); - Ref<Font> font = get_theme_font("font"); Ref<Texture2D> incr = get_theme_icon("increment"); Ref<Texture2D> decr = get_theme_icon("decrement"); int limit = get_size().width - incr->get_width() - decr->get_width(); @@ -520,7 +665,8 @@ void Tabs::_update_cache() { for (int i = 0; i < tabs.size(); i++) { tabs.write[i].ofs_cache = mw; tabs.write[i].size_cache = get_tab_width(i); - tabs.write[i].size_text = Math::ceil(font->get_string_size(tabs[i].xl_text).width); + tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x); + tabs.write[i].text_buf->set_width(-1); mw += tabs[i].size_cache; if (tabs[i].size_cache <= min_width || i == current) { size_fixed += tabs[i].size_cache; @@ -545,7 +691,7 @@ void Tabs::_update_cache() { int slen = tabs[i].size_text; if (min_width > 0 && mw > limit && i != current) { if (lsize > m_width) { - slen = m_width - (sb->get_margin(MARGIN_LEFT) + sb->get_margin(MARGIN_RIGHT)); + slen = m_width - (sb->get_margin(SIDE_LEFT) + sb->get_margin(SIDE_RIGHT)); if (tabs[i].icon.is_valid()) { slen -= tabs[i].icon->get_width(); slen -= get_theme_constant("hseparation"); @@ -562,6 +708,7 @@ void Tabs::_update_cache() { tabs.write[i].ofs_cache = w; tabs.write[i].size_cache = lsize; tabs.write[i].size_text = slen; + tabs.write[i].text_buf->set_width(slen); w += lsize; } } @@ -578,6 +725,9 @@ void Tabs::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) { Tab t; t.text = p_str; t.xl_text = tr(p_str); + t.text_buf.instance(); + t.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + t.text_buf->add_string(t.xl_text, get_theme_font("font"), get_theme_font_size("font_size"), Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); t.icon = p_icon; t.disabled = false; t.ofs_cache = 0; @@ -771,7 +921,6 @@ int Tabs::get_tab_width(int p_idx) const { Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg"); Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg"); Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled"); - Ref<Font> font = get_theme_font("font"); int x = 0; @@ -783,7 +932,7 @@ int Tabs::get_tab_width(int p_idx) const { } } - x += Math::ceil(font->get_string_size(tabs[p_idx].xl_text).width); + x += Math::ceil(tabs[p_idx].text_buf->get_size().x); if (tabs[p_idx].disabled) { x += tab_disabled->get_minimum_size().width; @@ -869,7 +1018,11 @@ void Tabs::ensure_tab_visible(int p_idx) { Rect2 Tabs::get_tab_rect(int p_tab) const { ERR_FAIL_INDEX_V(p_tab, tabs.size(), Rect2()); - return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height); + if (is_layout_rtl()) { + return Rect2(get_size().width - tabs[p_tab].ofs_cache - tabs[p_tab].size_cache, 0, tabs[p_tab].size_cache, get_size().height); + } else { + return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height); + } } void Tabs::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) { @@ -927,6 +1080,13 @@ void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("get_previous_tab"), &Tabs::get_previous_tab); ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &Tabs::set_tab_title); ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &Tabs::get_tab_title); + ClassDB::bind_method(D_METHOD("set_tab_text_direction", "tab_idx", "direction"), &Tabs::set_tab_text_direction); + ClassDB::bind_method(D_METHOD("get_tab_text_direction", "tab_idx"), &Tabs::get_tab_text_direction); + ClassDB::bind_method(D_METHOD("set_tab_opentype_feature", "tab_idx", "tag", "values"), &Tabs::set_tab_opentype_feature); + ClassDB::bind_method(D_METHOD("get_tab_opentype_feature", "tab_idx", "tag"), &Tabs::get_tab_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_tab_opentype_features", "tab_idx"), &Tabs::clear_tab_opentype_features); + ClassDB::bind_method(D_METHOD("set_tab_language", "tab_idx", "language"), &Tabs::set_tab_language); + ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &Tabs::get_tab_language); ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &Tabs::set_tab_icon); ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &Tabs::get_tab_icon); ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &Tabs::set_tab_disabled); @@ -954,8 +1114,8 @@ void Tabs::_bind_methods() { ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab"))); - ADD_SIGNAL(MethodInfo("tab_close", PropertyInfo(Variant::INT, "tab"))); - ADD_SIGNAL(MethodInfo("tab_hover", PropertyInfo(Variant::INT, "tab"))); + ADD_SIGNAL(MethodInfo("tab_closed", PropertyInfo(Variant::INT, "tab"))); + ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("reposition_active_tab_request", PropertyInfo(Variant::INT, "idx_to"))); ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab"))); diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h index 62142e1cde..4396981004 100644 --- a/scene/gui/tabs.h +++ b/scene/gui/tabs.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,6 +32,7 @@ #define TABS_H #include "scene/gui/control.h" +#include "scene/resources/text_line.h" class Tabs : public Control { GDCLASS(Tabs, Control); @@ -55,6 +56,12 @@ private: struct Tab { String text; String xl_text; + + Dictionary opentype_features; + String language; + Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; + + Ref<TextLine> text_buf; Ref<Texture2D> icon; int ofs_cache; bool disabled; @@ -101,6 +108,8 @@ private: void _on_mouse_exited(); + void _shape(int p_tab); + protected: void _gui_input(const Ref<InputEvent> &p_event); void _notification(int p_what); @@ -117,6 +126,16 @@ public: void set_tab_title(int p_tab, const String &p_title); String get_tab_title(int p_tab) const; + void set_tab_text_direction(int p_tab, TextDirection p_text_direction); + TextDirection get_tab_text_direction(int p_tab) const; + + void set_tab_opentype_feature(int p_tab, const String &p_name, int p_value); + int get_tab_opentype_feature(int p_tab, const String &p_name) const; + void clear_tab_opentype_features(int p_tab); + + void set_tab_language(int p_tab, const String &p_language); + String get_tab_language(int p_tab) const; + void set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon); Ref<Texture2D> get_tab_icon(int p_tab) const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 77ac3d6702..7557d36298 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,6 +36,8 @@ #include "core/object/script_language.h" #include "core/os/keyboard.h" #include "core/os/os.h" +#include "core/string/translation.h" + #include "scene/main/window.h" #ifdef TOOLS_ENABLED @@ -113,63 +115,118 @@ void TextEdit::Text::set_font(const Ref<Font> &p_font) { font = p_font; } +void TextEdit::Text::set_font_size(int p_font_size) { + font_size = p_font_size; +} + void TextEdit::Text::set_indent_size(int p_indent_size) { indent_size = p_indent_size; } -void TextEdit::Text::_update_line_cache(int p_line) const { - int w = 0; +void TextEdit::Text::set_font_features(const Dictionary &p_features) { + opentype_features = p_features; +} - int len = text[p_line].data.length(); - const char32_t *str = text[p_line].data.get_data(); +void TextEdit::Text::set_direction_and_language(TextServer::Direction p_direction, String p_language) { + direction = p_direction; + language = p_language; +} - // Update width. +void TextEdit::Text::set_draw_control_chars(bool p_draw_control_chars) { + draw_control_chars = p_draw_control_chars; +} - for (int i = 0; i < len; i++) { - w += get_char_width(str[i], str[i + 1], w); - } +int TextEdit::Text::get_line_width(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + return text[p_line].data_buf->get_size().x; +} + +int TextEdit::Text::get_line_height(int p_line, int p_wrap_index) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); - text.write[p_line].width_cache = w; - text.write[p_line].wrap_amount_cache = -1; + return text[p_line].data_buf->get_line_size(p_wrap_index).y; } -int TextEdit::Text::get_line_width(int p_line) const { - ERR_FAIL_INDEX_V(p_line, text.size(), -1); +void TextEdit::Text::set_width(float p_width) { + width = p_width; +} + +int TextEdit::Text::get_line_wrap_amount(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + + return text[p_line].data_buf->get_line_count() - 1; +} - if (text[p_line].width_cache == -1) { - _update_line_cache(p_line); +Vector<Vector2i> TextEdit::Text::get_line_wrap_ranges(int p_line) const { + Vector<Vector2i> ret; + ERR_FAIL_INDEX_V(p_line, text.size(), ret); + + for (int i = 0; i < text[p_line].data_buf->get_line_count(); i++) { + ret.push_back(text[p_line].data_buf->get_line_range(i)); } + return ret; +} + +const Ref<TextParagraph> TextEdit::Text::get_line_data(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), Ref<TextParagraph>()); + return text[p_line].data_buf; +} - return text[p_line].width_cache; +_FORCE_INLINE_ const String &TextEdit::Text::operator[](int p_line) const { + return text[p_line].data; } -void TextEdit::Text::set_line_wrap_amount(int p_line, int p_wrap_amount) const { +void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Vector<Vector2i> &p_bidi_override) { ERR_FAIL_INDEX(p_line, text.size()); - text.write[p_line].wrap_amount_cache = p_wrap_amount; -} + if (font.is_null() || font_size <= 0) { + return; // Not in tree? + } -int TextEdit::Text::get_line_wrap_amount(int p_line) const { - ERR_FAIL_INDEX_V(p_line, text.size(), -1); + text.write[p_line].data_buf->clear(); + text.write[p_line].data_buf->set_width(width); + text.write[p_line].data_buf->set_direction((TextServer::Direction)direction); + text.write[p_line].data_buf->set_preserve_control(draw_control_chars); + if (p_ime_text.length() > 0) { + text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language); + if (!p_bidi_override.is_empty()) { + TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), p_bidi_override); + } + } else { + text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language); + if (!text[p_line].bidi_override.is_empty()) { + TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), text[p_line].bidi_override); + } + } - return text[p_line].wrap_amount_cache; + // Apply tab align. + if (indent_size > 0) { + Vector<float> tabs; + tabs.push_back(font->get_char_size('m', 0, font_size).width * indent_size); + text.write[p_line].data_buf->tab_align(tabs); + } } -void TextEdit::Text::clear_width_cache() { +void TextEdit::Text::invalidate_all_lines() { for (int i = 0; i < text.size(); i++) { - text.write[i].width_cache = -1; + text.write[i].data_buf->set_width(width); + if (indent_size > 0) { + Vector<float> tabs; + tabs.push_back(font->get_char_size('m', 0, font_size).width * indent_size); + text.write[i].data_buf->tab_align(tabs); + } } } -void TextEdit::Text::clear_wrap_cache() { +void TextEdit::Text::invalidate_all() { for (int i = 0; i < text.size(); i++) { - text.write[i].wrap_amount_cache = -1; + invalidate_cache(i); } } void TextEdit::Text::clear() { text.clear(); - insert(0, ""); + insert(0, "", Vector<Vector2i>()); } int TextEdit::Text::get_max_width(bool p_exclude_hidden) const { @@ -184,46 +241,30 @@ int TextEdit::Text::get_max_width(bool p_exclude_hidden) const { return max; } -void TextEdit::Text::set(int p_line, const String &p_text) { +void TextEdit::Text::set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override) { ERR_FAIL_INDEX(p_line, text.size()); - text.write[p_line].width_cache = -1; - text.write[p_line].wrap_amount_cache = -1; text.write[p_line].data = p_text; + text.write[p_line].bidi_override = p_bidi_override; + invalidate_cache(p_line); } -void TextEdit::Text::insert(int p_at, const String &p_text) { +void TextEdit::Text::insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override) { Line line; line.gutters.resize(gutter_count); line.marked = false; line.hidden = false; - line.width_cache = -1; - line.wrap_amount_cache = -1; line.data = p_text; + line.bidi_override = p_bidi_override; text.insert(p_at, line); + + invalidate_cache(p_at); } void TextEdit::Text::remove(int p_at) { text.remove(p_at); } -int TextEdit::Text::get_char_width(char32_t c, char32_t next_c, int px) const { - int tab_w = font->get_char_size(' ').width * indent_size; - int w = 0; - - if (c == '\t') { - int left = px % tab_w; - if (left == 0) { - w = tab_w; - } else { - w = tab_w - px % tab_w; // Is right. - } - } else { - w = font->get_char_size(c, next_c).width; - } - return w; -} - void TextEdit::Text::add_gutter(int p_at) { for (int i = 0; i < text.size(); i++) { if (p_at < 0 || p_at > gutter_count) { @@ -255,8 +296,8 @@ void TextEdit::_update_scrollbars() { Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); - v_scroll->set_begin(Point2(size.width - vmin.width, cache.style_normal->get_margin(MARGIN_TOP))); - v_scroll->set_end(Point2(size.width, size.height - cache.style_normal->get_margin(MARGIN_TOP) - cache.style_normal->get_margin(MARGIN_BOTTOM))); + v_scroll->set_begin(Point2(size.width - vmin.width, cache.style_normal->get_margin(SIDE_TOP))); + v_scroll->set_end(Point2(size.width, size.height - cache.style_normal->get_margin(SIDE_TOP) - cache.style_normal->get_margin(SIDE_BOTTOM))); h_scroll->set_begin(Point2(0, size.height - hmin.height)); h_scroll->set_end(Point2(size.width - vmin.width, size.height)); @@ -338,9 +379,17 @@ void TextEdit::_click_selection_held() { } } +Point2 TextEdit::_get_local_mouse_pos() const { + Point2 mp = get_local_mouse_position(); + if (is_layout_rtl()) { + mp.x = get_size().width - mp.x; + } + return mp; +} + void TextEdit::_update_selection_mode_pointer() { dragging_selection = true; - Point2 mp = get_local_mouse_position(); + Point2 mp = _get_local_mouse_pos(); int row, col; _get_mouse_pos(Point2i(mp.x, mp.y), row, col); @@ -356,7 +405,7 @@ void TextEdit::_update_selection_mode_pointer() { void TextEdit::_update_selection_mode_word() { dragging_selection = true; - Point2 mp = get_local_mouse_position(); + Point2 mp = _get_local_mouse_pos(); int row, col; _get_mouse_pos(Point2i(mp.x, mp.y), row, col); @@ -413,7 +462,7 @@ void TextEdit::_update_selection_mode_word() { void TextEdit::_update_selection_mode_line() { dragging_selection = true; - Point2 mp = get_local_mouse_position(); + Point2 mp = _get_local_mouse_pos(); int row, col; _get_mouse_pos(Point2i(mp.x, mp.y), row, col); @@ -438,9 +487,9 @@ void TextEdit::_update_selection_mode_line() { } void TextEdit::_update_minimap_click() { - Point2 mp = get_local_mouse_position(); + Point2 mp = _get_local_mouse_pos(); - int xmargin_end = get_size().width - cache.style_normal->get_margin(MARGIN_RIGHT); + int xmargin_end = get_size().width - cache.style_normal->get_margin(SIDE_RIGHT); if (!dragging_minimap && (mp.x < xmargin_end - minimap_width || mp.y > xmargin_end)) { minimap_clicked = false; return; @@ -479,7 +528,8 @@ void TextEdit::_update_minimap_drag() { control_height = scroll_height; } - Point2 mp = get_local_mouse_position(); + Point2 mp = _get_local_mouse_pos(); + double diff = (mp.y - minimap_scroll_click_pos) / control_height; v_scroll->set_as_ratio(minimap_scroll_ratio + diff); } @@ -494,7 +544,7 @@ void TextEdit::_notification(int p_what) { if (text_changed_dirty) { MessageQueue::get_singleton()->push_call(this, "_text_changed_emit"); } - _update_wrap_at(); + _update_wrap_at(true); } break; case NOTIFICATION_RESIZED: { _update_scrollbars(); @@ -506,9 +556,11 @@ void TextEdit::_notification(int p_what) { call_deferred("_update_wrap_at"); } } break; + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_THEME_CHANGED: { _update_caches(); - _update_wrap_at(); + _update_wrap_at(true); } break; case NOTIFICATION_WM_WINDOW_FOCUS_IN: { window_has_focus = true; @@ -556,6 +608,7 @@ void TextEdit::_notification(int p_what) { } Size2 size = get_size(); + bool rtl = is_layout_rtl(); if ((!has_focus() && !menu->has_focus()) || !window_has_focus) { draw_caret = false; } @@ -569,9 +622,9 @@ void TextEdit::_notification(int p_what) { RID ci = get_canvas_item(); RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); - int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + gutters_width + gutter_padding; + int xmargin_beg = cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding; - int xmargin_end = size.width - cache.style_normal->get_margin(MARGIN_RIGHT) - cache.minimap_width; + int xmargin_end = size.width - cache.style_normal->get_margin(SIDE_RIGHT) - cache.minimap_width; // Let's do it easy for now. cache.style_normal->draw(ci, Rect2(Point2(), size)); if (readonly) { @@ -582,8 +635,6 @@ void TextEdit::_notification(int p_what) { cache.style_focus->draw(ci, Rect2(Point2(), size)); } - int ascent = cache.font->get_ascent(); - int visible_rows = get_visible_rows() + 1; Color color = readonly ? cache.font_color_readonly : cache.font_color; @@ -593,17 +644,25 @@ void TextEdit::_notification(int p_what) { } if (line_length_guidelines) { - const int hard_x = xmargin_beg + (int)cache.font->get_char_size('0').width * line_length_guideline_hard_col - cursor.x_ofs; + const int hard_x = xmargin_beg + (int)cache.font->get_char_size('0', 0, cache.font_size).width * line_length_guideline_hard_col - cursor.x_ofs; if (hard_x > xmargin_beg && hard_x < xmargin_end) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(hard_x, 0), Point2(hard_x, size.height), cache.line_length_guideline_color); + if (rtl) { + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(size.width - hard_x, 0), Point2(size.width - hard_x, size.height), cache.line_length_guideline_color); + } else { + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(hard_x, 0), Point2(hard_x, size.height), cache.line_length_guideline_color); + } } // Draw a "Soft" line length guideline, less visible than the hard line length guideline. // It's usually set to a lower column compared to the hard line length guideline. // Only drawn if its column differs from the hard line length guideline. - const int soft_x = xmargin_beg + (int)cache.font->get_char_size('0').width * line_length_guideline_soft_col - cursor.x_ofs; + const int soft_x = xmargin_beg + (int)cache.font->get_char_size('0', 0, cache.font_size).width * line_length_guideline_soft_col - cursor.x_ofs; if (hard_x != soft_x && soft_x > xmargin_beg && soft_x < xmargin_end) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(soft_x, 0), Point2(soft_x, size.height), cache.line_length_guideline_color * Color(1, 1, 1, 0.5)); + if (rtl) { + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(size.width - soft_x, 0), Point2(size.width - soft_x, size.height), cache.line_length_guideline_color * Color(1, 1, 1, 0.5)); + } else { + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(soft_x, 0), Point2(soft_x, size.height), cache.line_length_guideline_color * Color(1, 1, 1, 0.5)); + } } } @@ -762,7 +821,7 @@ void TextEdit::_notification(int p_what) { int cursor_wrap_index = get_cursor_wrap_index(); - FontDrawer drawer(cache.font, Color(1, 1, 1)); + //FontDrawer drawer(cache.font, Color(1, 1, 1)); int first_visible_line = get_first_visible_line() - 1; int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0); @@ -791,7 +850,11 @@ void TextEdit::_notification(int p_what) { // draw the minimap Color viewport_color = (cache.background_color.get_v() < 0.5) ? Color(1, 1, 1, 0.1) : Color(0, 0, 0, 0.1); - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), viewport_offset_y, cache.minimap_width, viewport_height), viewport_color); + if (rtl) { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - cache.minimap_width, viewport_offset_y, cache.minimap_width, viewport_height), viewport_color); + } else { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), viewport_offset_y, cache.minimap_width, viewport_height), viewport_color); + } for (int i = 0; i < minimap_draw_amount; i++) { minimap_line++; @@ -841,7 +904,11 @@ void TextEdit::_notification(int p_what) { } if (minimap_line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), i * 3, cache.minimap_width, 2), cache.current_line_color); + if (rtl) { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - cache.minimap_width, i * 3, cache.minimap_width, 2), cache.current_line_color); + } else { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), i * 3, cache.minimap_width, 2), cache.current_line_color); + } } Color previous_color; @@ -888,7 +955,11 @@ void TextEdit::_notification(int p_what) { // take one for zero indexing, and if we hit whitespace / the end of a word. int chars = MAX(0, (j - (characters - 1)) - (is_whitespace ? 1 : 0)) + 1; int char_x_ofs = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * chars)) + tabs; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_x_ofs, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color); + if (rtl) { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(size.width - char_x_ofs - minimap_char_size.x * characters, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color); + } else { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_x_ofs, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color); + } } if (out_of_bounds) { @@ -907,6 +978,7 @@ void TextEdit::_notification(int p_what) { } // draw main text + int row_height = get_row_height(); int line = first_visible_line; for (int i = 0; i < draw_amount; i++) { line++; @@ -926,20 +998,17 @@ void TextEdit::_notification(int p_what) { continue; } - const String &fullstr = text[line]; - Dictionary color_map = _get_line_syntax_highlighting(line); // Ensure we at least use the font color. Color current_color = readonly ? cache.font_color_readonly : cache.font_color; - bool underlined = false; + const Ref<TextParagraph> ldata = text.get_line_data(line); Vector<String> wrap_rows = get_wrap_rows_text(line); int line_wrap_amount = times_line_wraps(line); - int last_wrap_column = 0; - for (int line_wrap_index = 0; line_wrap_index < line_wrap_amount + 1; line_wrap_index++) { + for (int line_wrap_index = 0; line_wrap_index <= line_wrap_amount; line_wrap_index++) { if (line_wrap_index != 0) { i++; if (i >= draw_amount) { @@ -948,18 +1017,7 @@ void TextEdit::_notification(int p_what) { } const String &str = wrap_rows[line_wrap_index]; - int indent_px = line_wrap_index != 0 ? get_indent_level(line) * cache.font->get_char_size(' ').width : 0; - if (indent_px >= wrap_at) { - indent_px = 0; - } - - if (line_wrap_index > 0) { - last_wrap_column += wrap_rows[line_wrap_index - 1].length(); - } - int char_margin = xmargin_beg - cursor.x_ofs; - char_margin += indent_px; - int char_ofs = 0; int ofs_readonly = 0; int ofs_x = 0; @@ -968,55 +1026,52 @@ void TextEdit::_notification(int p_what) { ofs_x = cache.style_readonly->get_offset().x / 2; } - int ofs_y = (i * get_row_height() + cache.line_spacing / 2) + ofs_readonly; - ofs_y -= cursor.wrap_ofs * get_row_height(); - ofs_y -= get_v_scroll_offset() * get_row_height(); - - // Check if line contains highlighted word. - int highlighted_text_col = -1; - int search_text_col = -1; - int highlighted_word_col = -1; - - if (!search_text.empty()) { - search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0); - } - - if (highlighted_text.length() != 0 && highlighted_text != search_text) { - highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); - } - - if (select_identifiers_enabled && highlighted_word.length() != 0) { - if (_is_char(highlighted_word[0]) || highlighted_word[0] == '.') { - highlighted_word_col = _get_column_pos_of_word(highlighted_word, fullstr, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); - } - } + int ofs_y = (i * row_height + cache.line_spacing / 2) + ofs_readonly; + ofs_y -= cursor.wrap_ofs * row_height; + ofs_y -= get_v_scroll_offset() * row_height; if (text.is_marked(line)) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color); + if (rtl) { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end - xmargin_beg, row_height), cache.mark_color); + } else { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, row_height), cache.mark_color); + } } if (str.length() == 0) { // Draw line background if empty as we won't loop at at all. if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, get_row_height()), cache.current_line_color); + if (rtl) { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), cache.current_line_color); + } else { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), cache.current_line_color); + } } // Give visual indication of empty selected line. if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) { - int char_w = cache.font->get_char_size(' ').width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, get_row_height()), cache.selection_color); + int char_w = cache.font->get_char_size('m', 0, cache.font_size).width; + if (rtl) { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), cache.selection_color); + } else { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, row_height), cache.selection_color); + } } } else { // If it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later. if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg + ofs_x, get_row_height()), cache.current_line_color); + if (rtl) { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), cache.current_line_color); + } else { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), cache.current_line_color); + } } } if (line_wrap_index == 0) { // Only do these if we are on the first wrapped part of a line. - int gutter_offset = cache.style_normal->get_margin(MARGIN_LEFT); + int gutter_offset = cache.style_normal->get_margin(SIDE_LEFT); for (int g = 0; g < gutters.size(); g++) { const GutterInfo gutter = gutters[g]; @@ -1031,8 +1086,12 @@ void TextEdit::_notification(int p_what) { break; } - int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2; - cache.font->draw(ci, Point2(gutter_offset + ofs_x, yofs + cache.font->get_ascent()), text, get_line_gutter_item_color(line, g)); + Ref<TextLine> tl; + tl.instance(); + tl->add_string(text, cache.font, cache.font_size); + + int yofs = ofs_y + (row_height - tl->get_size().y) / 2; + tl->draw(ci, Point2(gutter_offset + ofs_x, yofs), get_line_gutter_item_color(line, g)); } break; case GUTTER_TPYE_ICON: { const Ref<Texture2D> icon = get_line_gutter_icon(line, g); @@ -1040,7 +1099,7 @@ void TextEdit::_notification(int p_what) { break; } - Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, get_row_height())); + Rect2 gutter_rect = Rect2(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, row_height)); int horizontal_padding = gutter_rect.size.x / 6; int vertical_padding = gutter_rect.size.y / 6; @@ -1048,13 +1107,28 @@ void TextEdit::_notification(int p_what) { gutter_rect.position += Point2(horizontal_padding, vertical_padding); gutter_rect.size -= Point2(horizontal_padding, vertical_padding) * 2; + // Correct icon aspect ratio. + float icon_ratio = icon->get_width() / icon->get_height(); + float gutter_ratio = gutter_rect.size.x / gutter_rect.size.y; + if (gutter_ratio > icon_ratio) { + gutter_rect.size.x = floor(icon->get_width() * (gutter_rect.size.y / icon->get_height())); + } else { + gutter_rect.size.y = floor(icon->get_height() * (gutter_rect.size.x / icon->get_width())); + } + if (rtl) { + gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x; + } + icon->draw_rect(ci, gutter_rect, false, get_line_gutter_item_color(line, g)); } break; case GUTTER_TPYE_CUSTOM: { if (gutter.custom_draw_obj.is_valid()) { Object *cdo = ObjectDB::get_instance(gutter.custom_draw_obj); if (cdo) { - Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, get_row_height())); + Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, row_height)); + if (rtl) { + gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x; + } cdo->call(gutter.custom_draw_callback, line, g, Rect2(gutter_rect)); } } @@ -1065,296 +1139,302 @@ void TextEdit::_notification(int p_what) { } } - // Loop through characters in one line. - int j = 0; - for (; j < str.length(); j++) { - if (color_map.has(last_wrap_column + j)) { - current_color = color_map[last_wrap_column + j].get("color"); - if (readonly && current_color.a > cache.font_color_readonly.a) { - current_color.a = cache.font_color_readonly.a; - } - } - color = current_color; + // Draw line. + RID rid = ldata->get_line_rid(line_wrap_index); + float text_height = TS->shaped_text_get_size(rid).y + cache.font->get_spacing(Font::SPACING_TOP) + cache.font->get_spacing(Font::SPACING_BOTTOM); - int char_w; - - // Handle tabulator. - char_w = text.get_char_width(str[j], str[j + 1], char_ofs); - - if ((char_ofs + char_margin) < xmargin_beg) { - char_ofs += char_w; + if (rtl) { + char_margin = size.width - char_margin - TS->shaped_text_get_size(rid).x; + } - // Line highlighting handle horizontal clipping. - if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { - if (j == str.length() - 1) { - // End of line when last char is skipped. - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color); - } else if ((char_ofs + char_margin) > xmargin_beg) { - // Char next to margin is skipped. - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, (char_ofs + char_margin) - (xmargin_beg + ofs_x), get_row_height()), cache.current_line_color); - } + if (selection.active && line >= selection.from_line && line <= selection.to_line) { // Selection + int sel_from = (line > selection.from_line) ? TS->shaped_text_get_range(rid).x : selection.from_column; + int sel_to = (line < selection.to_line) ? TS->shaped_text_get_range(rid).y : selection.to_column; + Vector<Vector2> sel = TS->shaped_text_get_selection(rid, sel_from, sel_to); + for (int j = 0; j < sel.size(); j++) { + Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, row_height); + if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) { + continue; } - continue; - } - - if ((char_ofs + char_margin + char_w) >= xmargin_end) { - break; + if (rect.position.x < xmargin_beg) { + rect.size.x -= (xmargin_beg - rect.position.x); + rect.position.x = xmargin_beg; + } else if (rect.position.x + rect.size.x > xmargin_end) { + rect.size.x = xmargin_end - rect.position.x; + } + draw_rect(rect, cache.selection_color, true); } + } - bool in_search_result = false; - - if (search_text_col != -1) { - // If we are at the end check for new search result on same line. - if (j >= search_text_col + search_text.length()) { - search_text_col = _get_column_pos_of_word(search_text, str, search_flags, j); + int start = TS->shaped_text_get_range(rid).x; + if (!search_text.is_empty()) { // Search highhlight + int search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0); + while (search_text_col != -1) { + Vector<Vector2> sel = TS->shaped_text_get_selection(rid, search_text_col + start, search_text_col + search_text.length() + start); + for (int j = 0; j < sel.size(); j++) { + Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, row_height); + if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) { + continue; + } + if (rect.position.x < xmargin_beg) { + rect.size.x -= (xmargin_beg - rect.position.x); + rect.position.x = xmargin_beg; + } else if (rect.position.x + rect.size.x > xmargin_end) { + rect.size.x = xmargin_end - rect.position.x; + } + draw_rect(rect, cache.search_result_color, true); + draw_rect(rect, cache.search_result_border_color, false); } - in_search_result = j >= search_text_col && j < search_text_col + search_text.length(); - - if (in_search_result) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.search_result_color); - } + search_text_col = _get_column_pos_of_word(search_text, str, search_flags, search_text_col + 1); } + } - // Current line highlighting. - bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || last_wrap_column + j >= selection.from_column) && (line < selection.to_line || last_wrap_column + j < selection.to_column)); - - if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { - // Draw the wrap indent offset highlight. - if (line_wrap_index != 0 && j == 0) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + ofs_x - indent_px, ofs_y, indent_px, get_row_height()), cache.current_line_color); - } - // If its the last char draw to end of the line. - if (j == str.length() - 1) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w + ofs_x, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color); - } - // Actual text. - if (!in_selection) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color); + if (highlight_all_occurrences && !only_whitespaces_highlighted && !highlighted_text.is_empty()) { // Highlight + int highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); + while (highlighted_text_col != -1) { + Vector<Vector2> sel = TS->shaped_text_get_selection(rid, highlighted_text_col + start, highlighted_text_col + highlighted_text.length() + start); + for (int j = 0; j < sel.size(); j++) { + Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, row_height); + if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) { + continue; + } + if (rect.position.x < xmargin_beg) { + rect.size.x -= (xmargin_beg - rect.position.x); + rect.position.x = xmargin_beg; + } else if (rect.position.x + rect.size.x > xmargin_end) { + rect.size.x = xmargin_end - rect.position.x; + } + draw_rect(rect, cache.word_highlighted_color); } - } - if (in_selection) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color); + highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_text_col + 1); } + } - if (in_search_result) { - Color border_color = (line == search_result_line && j >= search_result_col && j < search_result_col + search_text.length()) ? cache.font_color : cache.search_result_border_color; - - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, 1)), border_color); - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y + get_row_height() - 1), Size2i(char_w, 1)), border_color); + if (select_identifiers_enabled && highlighted_word.length() != 0) { // Highlight word + if (_is_char(highlighted_word[0]) || highlighted_word[0] == '.') { + int highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); + while (highlighted_word_col != -1) { + Vector<Vector2> sel = TS->shaped_text_get_selection(rid, highlighted_word_col + start, highlighted_word_col + highlighted_word.length() + start); + for (int j = 0; j < sel.size(); j++) { + Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, row_height); + if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) { + continue; + } + if (rect.position.x < xmargin_beg) { + rect.size.x -= (xmargin_beg - rect.position.x); + rect.position.x = xmargin_beg; + } else if (rect.position.x + rect.size.x > xmargin_end) { + rect.size.x = xmargin_end - rect.position.x; + } + rect.position.y = TS->shaped_text_get_ascent(rid) + cache.font->get_underline_position(cache.font_size); + rect.size.y = cache.font->get_underline_thickness(cache.font_size); + draw_rect(rect, cache.font_color_selected); + } - if (j == search_text_col) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(1, get_row_height())), border_color); - } - if (j == search_text_col + search_text.length() - 1) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + char_w + ofs_x - 1, ofs_y), Size2i(1, get_row_height())), border_color); + highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_word_col + 1); } } + } - if (highlight_all_occurrences && !only_whitespaces_highlighted) { - if (highlighted_text_col != -1) { - // If we are at the end check for new word on same line. - if (j > highlighted_text_col + highlighted_text.length()) { - highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j); - } - - bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col + highlighted_text.length()); + ofs_y += (row_height - text_height) / 2; - // If this is the original highlighted text we don't want to highlight it again. - if (cursor.line == line && cursor_wrap_index == line_wrap_index && (cursor.column >= highlighted_text_col && cursor.column <= highlighted_text_col + highlighted_text.length())) { - in_highlighted_word = false; - } + const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid); + const TextServer::Glyph *glyphs = visual.ptr(); + int gl_size = visual.size(); - if (in_highlighted_word) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.word_highlighted_color); - } + ofs_y += ldata->get_line_ascent(line_wrap_index); + int char_ofs = 0; + for (int j = 0; j < gl_size; j++) { + if (color_map.has(glyphs[j].start)) { + current_color = color_map[glyphs[j].start].get("color"); + if (readonly && current_color.a > cache.font_color_readonly.a) { + current_color.a = cache.font_color_readonly.a; } } - if (highlighted_word_col != -1) { - if (j + last_wrap_column > highlighted_word_col + highlighted_word.length()) { - highlighted_word_col = _get_column_pos_of_word(highlighted_word, fullstr, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j + last_wrap_column); + if (selection.active && line >= selection.from_line && line <= selection.to_line) { // Selection + int sel_from = (line > selection.from_line) ? TS->shaped_text_get_range(rid).x : selection.from_column; + int sel_to = (line < selection.to_line) ? TS->shaped_text_get_range(rid).y : selection.to_column; + + if (glyphs[j].start >= sel_from && glyphs[j].end <= sel_to && override_selected_font_color) { + current_color = cache.font_color_selected; } - underlined = (j + last_wrap_column >= highlighted_word_col && j + last_wrap_column < highlighted_word_col + highlighted_word.length()); } if (brace_matching_enabled) { - int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2; - if ((brace_open_match_line == line && brace_open_match_column == last_wrap_column + j) || - (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) { + if ((brace_open_match_line == line && brace_open_match_column == glyphs[j].start) || + (cursor.column == glyphs[j].start && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) { if (brace_open_mismatch) { - color = cache.brace_mismatch_color; + current_color = cache.brace_mismatch_color; } - drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_color_selected : color); + Rect2 rect = Rect2(char_ofs + char_margin + ofs_x, ofs_y + cache.font->get_underline_position(cache.font_size), glyphs[j].advance * glyphs[j].repeat, cache.font->get_underline_thickness(cache.font_size)); + draw_rect(rect, current_color); } - if ((brace_close_match_line == line && brace_close_match_column == last_wrap_column + j) || - (cursor.column == last_wrap_column + j + 1 && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_close_matching || brace_close_mismatch))) { + if ((brace_close_match_line == line && brace_close_match_column == glyphs[j].start) || + (cursor.column == glyphs[j].start + 1 && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_close_matching || brace_close_mismatch))) { if (brace_close_mismatch) { - color = cache.brace_mismatch_color; + current_color = cache.brace_mismatch_color; } - drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_color_selected : color); + Rect2 rect = Rect2(char_ofs + char_margin + ofs_x, ofs_y + cache.font->get_underline_position(cache.font_size), glyphs[j].advance * glyphs[j].repeat, cache.font->get_underline_thickness(cache.font_size)); + draw_rect(rect, current_color); } } - - if (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index) { - cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y); - cursor_pos.y += (get_row_height() - cache.font->get_height()) / 2; - - if (insert_mode) { - cursor_insert_offset_y = (cache.font->get_height() - 3); - cursor_pos.y += cursor_insert_offset_y; + if (draw_tabs && ((glyphs[j].flags & TextServer::GRAPHEME_IS_TAB) == TextServer::GRAPHEME_IS_TAB)) { + int yofs = (text_height - cache.tab_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); + cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), current_color); + } else if (draw_spaces && ((glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE)) { + int yofs = (text_height - cache.space_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); + int xofs = (glyphs[j].advance * glyphs[j].repeat - cache.space_icon->get_width()) / 2; + cache.space_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x + xofs, ofs_y + yofs), current_color); + } + for (int k = 0; k < glyphs[j].repeat; k++) { + if ((char_ofs + char_margin) >= xmargin_beg && (char_ofs + glyphs[j].advance + char_margin) <= xmargin_end) { + if (glyphs[j].font_rid != RID()) { + TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, current_color); + } else if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + TS->draw_hex_code_box(ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, current_color); + } } + char_ofs += glyphs[j].advance; + } + if ((char_ofs + char_margin) >= xmargin_end) { + break; + } + } - int caret_w = (str[j] == '\t') ? cache.font->get_char_size(' ').width : char_w; - if (ime_text.length() > 0) { - int ofs = 0; - while (true) { - if (ofs >= ime_text.length()) { - break; - } - - char32_t cchar = ime_text[ofs]; - char32_t next = ime_text[ofs + 1]; - int im_char_width = cache.font->get_char_size(cchar, next).width; - - if ((char_ofs + char_margin + im_char_width) >= xmargin_end) { - break; - } - - bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y; - if (selected) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color); - } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color); - } - - drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color); + if (line_wrap_index == line_wrap_amount && is_folded(line)) { + int yofs = (text_height - cache.folded_eol_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); + int xofs = cache.folded_eol_icon->get_width() / 2; + Color eol_color = cache.code_folding_color; + eol_color.a = 1; + cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs + ofs_x, ofs_y + yofs), eol_color); + } - char_ofs += im_char_width; - ofs++; - } - } - if (ime_text.length() == 0) { - if (draw_caret) { - if (insert_mode) { + // Carets #ifdef TOOLS_ENABLED - int caret_h = (block_caret) ? 4 : 2 * EDSCALE; + int caret_width = Math::round(EDSCALE); #else - int caret_h = (block_caret) ? 4 : 2; + int caret_width = 1; #endif - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, caret_h)), cache.caret_color); - } else { -#ifdef TOOLS_ENABLED - caret_w = (block_caret) ? caret_w : 2 * EDSCALE; -#else - caret_w = (block_caret) ? caret_w : 2; -#endif - - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, cache.font->get_height())), cache.caret_color); - } + if (cursor.line == line && ((line_wrap_index == line_wrap_amount) || (cursor.column != TS->shaped_text_get_range(rid).y))) { + cursor_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index); + if (ime_text.length() == 0) { + Rect2 l_caret, t_caret; + TextServer::Direction l_dir, t_dir; + if (str.length() != 0) { + // Get carets. + TS->shaped_text_get_carets(rid, cursor.column, l_caret, l_dir, t_caret, t_dir); + } else { + // No carets, add one at the start. + int h = cache.font->get_height(cache.font_size); + if (rtl) { + l_dir = TextServer::DIRECTION_RTL; + l_caret = Rect2(Vector2(xmargin_end - char_margin + ofs_x, -h / 2), Size2(caret_width * 4, h)); + } else { + l_dir = TextServer::DIRECTION_LTR; + l_caret = Rect2(Vector2(char_ofs, -h / 2), Size2(caret_width * 4, h)); } } - } - - if (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && block_caret && draw_caret && !insert_mode) { - color = cache.caret_background_color; - } else if (block_caret) { - color = readonly ? cache.font_color_readonly : cache.font_color; - } - if (str[j] >= 32) { - int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2; - int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_color_selected : color); - if (underlined) { - float line_width = cache.font->get_underline_thickness(); -#ifdef TOOLS_ENABLED - line_width *= EDSCALE; -#endif - - draw_rect(Rect2(char_ofs + char_margin + ofs_x, yofs + ascent + cache.font->get_underline_position(), w, line_width), in_selection && override_selected_font_color ? cache.font_color_selected : color); + if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { + cursor_pos.x = char_margin + ofs_x + l_caret.position.x; + } else { + cursor_pos.x = char_margin + ofs_x + t_caret.position.x; } - } else if (draw_tabs && str[j] == '\t') { - int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2; - cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_color_selected : color); - } - if (draw_spaces && str[j] == ' ') { - int yofs = (get_row_height() - cache.space_icon->get_height()) / 2; - cache.space_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_color_selected : color); - } - - char_ofs += char_w; - - if (line_wrap_index == line_wrap_amount && j == str.length() - 1 && is_folded(line)) { - int yofs = (get_row_height() - cache.folded_eol_icon->get_height()) / 2; - int xofs = cache.folded_eol_icon->get_width() / 2; - Color eol_color = cache.code_folding_color; - eol_color.a = 1; - cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs + ofs_x, ofs_y + yofs), eol_color); - } - } + if (draw_caret) { + if (block_caret || insert_mode) { + //Block or underline caret, draw trailing carets at full height. + int h = cache.font->get_height(cache.font_size); + + if (t_caret != Rect2()) { + if (insert_mode) { + t_caret.position.y = TS->shaped_text_get_descent(rid); + t_caret.size.y = caret_width; + } else { + t_caret.position.y = -TS->shaped_text_get_ascent(rid); + t_caret.size.y = h; + } + t_caret.position += Vector2(char_margin + ofs_x, ofs_y); + + draw_rect(t_caret, cache.caret_color, false); + } else { // End of the line. + if (insert_mode) { + l_caret.position.y = TS->shaped_text_get_descent(rid); + l_caret.size.y = caret_width; + } else { + l_caret.position.y = -TS->shaped_text_get_ascent(rid); + l_caret.size.y = h; + } + l_caret.position += Vector2(char_margin + ofs_x, ofs_y); + l_caret.size.x = cache.font->get_char_size('m', 0, cache.font_size).x; - if (cursor.column == (last_wrap_column + j) && cursor.line == line && cursor_wrap_index == line_wrap_index && (char_ofs + char_margin) >= xmargin_beg) { - cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y); - cursor_pos.y += (get_row_height() - cache.font->get_height()) / 2; + draw_rect(l_caret, cache.caret_color, false); + } + } else { + // Normal caret. + if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) { + // Draw extra marker on top of mid caret. + Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width); + trect.position += Vector2(char_margin + ofs_x, ofs_y); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, cache.caret_color); + } + l_caret.position += Vector2(char_margin + ofs_x, ofs_y); + l_caret.size.x = caret_width; - if (insert_mode) { - cursor_insert_offset_y = cache.font->get_height() - 3; - cursor_pos.y += cursor_insert_offset_y; - } - if (ime_text.length() > 0) { - int ofs = 0; - while (true) { - if (ofs >= ime_text.length()) { - break; - } + draw_rect(l_caret, cache.caret_color); - char32_t cchar = ime_text[ofs]; - char32_t next = ime_text[ofs + 1]; - int im_char_width = cache.font->get_char_size(cchar, next).width; + t_caret.position += Vector2(char_margin + ofs_x, ofs_y); + t_caret.size.x = caret_width; - if ((char_ofs + char_margin + im_char_width) >= xmargin_end) { - break; + draw_rect(t_caret, cache.caret_color); } - - bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y; - if (selected) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color); - } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color); + } + } else { + { + // IME intermidiet text range. + Vector<Vector2> sel = TS->shaped_text_get_selection(rid, cursor.column, cursor.column + ime_text.length()); + for (int j = 0; j < sel.size(); j++) { + Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, text_height); + if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) { + continue; + } + if (rect.position.x < xmargin_beg) { + rect.size.x -= (xmargin_beg - rect.position.x); + rect.position.x = xmargin_beg; + } else if (rect.position.x + rect.size.x > xmargin_end) { + rect.size.x = xmargin_end - rect.position.x; + } + rect.size.y = caret_width; + draw_rect(rect, cache.caret_color); + cursor_pos.x = rect.position.x; } - - drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color); - - char_ofs += im_char_width; - ofs++; } - } - if (ime_text.length() == 0) { - if (draw_caret) { - if (insert_mode) { - int char_w = cache.font->get_char_size(' ').width; -#ifdef TOOLS_ENABLED - int caret_h = (block_caret) ? 4 : 2 * EDSCALE; -#else - int caret_h = (block_caret) ? 4 : 2; -#endif - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(char_w, caret_h)), cache.caret_color); - } else { - int char_w = cache.font->get_char_size(' ').width; -#ifdef TOOLS_ENABLED - int caret_w = (block_caret) ? char_w : 2 * EDSCALE; -#else - int caret_w = (block_caret) ? char_w : 2; -#endif - - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, cache.font->get_height())), cache.caret_color); + { + // IME caret. + Vector<Vector2> sel = TS->shaped_text_get_selection(rid, cursor.column + ime_selection.x, cursor.column + ime_selection.x + ime_selection.y); + for (int j = 0; j < sel.size(); j++) { + Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, text_height); + if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) { + continue; + } + if (rect.position.x < xmargin_beg) { + rect.size.x -= (xmargin_beg - rect.position.x); + rect.position.x = xmargin_beg; + } else if (rect.position.x + rect.size.x > xmargin_end) { + rect.size.x = xmargin_end - rect.position.x; + } + rect.size.y = caret_width * 3; + draw_rect(rect, cache.caret_color); + cursor_pos.x = rect.position.x; } } } } + ofs_y += ldata->get_line_descent(line_wrap_index); } } @@ -1363,19 +1443,19 @@ void TextEdit::_notification(int p_what) { // Code completion box. Ref<StyleBox> csb = get_theme_stylebox("completion"); int maxlines = get_theme_constant("completion_lines"); - int cmax_width = get_theme_constant("completion_max_width") * cache.font->get_char_size('x').x; + int cmax_width = get_theme_constant("completion_max_width") * cache.font->get_char_size('x', 0, cache.font_size).x; int scrollw = get_theme_constant("completion_scroll_width"); Color scrollc = get_theme_color("completion_scroll_color"); const int completion_options_size = completion_options.size(); int lines = MIN(completion_options_size, maxlines); int w = 0; - int h = lines * get_row_height(); - int nofs = cache.font->get_string_size(completion_base).width; + int h = lines * row_height; + int nofs = cache.font->get_string_size(completion_base, cache.font_size).width; if (completion_options_size < 50) { for (int i = 0; i < completion_options_size; i++) { - int w2 = MIN(cache.font->get_string_size(completion_options[i].display).x, cmax_width); + int w2 = MIN(cache.font->get_string_size(completion_options[i].display, cache.font_size).x, cmax_width); if (w2 > w) { w = w2; } @@ -1386,7 +1466,7 @@ void TextEdit::_notification(int p_what) { // Add space for completion icons. const int icon_hsep = get_theme_constant("hseparation", "ItemList"); - Size2 icon_area_size(get_row_height(), get_row_height()); + Size2 icon_area_size(row_height, row_height); w += icon_area_size.width + icon_hsep; int line_from = CLAMP(completion_index - lines / 2, 0, completion_options_size - lines); @@ -1402,10 +1482,10 @@ void TextEdit::_notification(int p_what) { int th = h + csb->get_minimum_size().y; - if (cursor_pos.y + get_row_height() + th > get_size().height) { + if (cursor_pos.y + row_height + th > get_size().height) { completion_rect.position.y = cursor_pos.y - th - (cache.line_spacing / 2.0f) - cursor_insert_offset_y; } else { - completion_rect.position.y = cursor_pos.y + cache.font->get_height() + (cache.line_spacing / 2.0f) + csb->get_offset().y - cursor_insert_offset_y; + completion_rect.position.y = cursor_pos.y + cache.font->get_height(cache.font_size) + (cache.line_spacing / 2.0f) + csb->get_offset().y - cursor_insert_offset_y; completion_below = true; } @@ -1427,17 +1507,23 @@ void TextEdit::_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(completion_rect.position, completion_rect.size + Size2(scrollw, 0)), cache.completion_background_color); } RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(completion_rect.position.x, completion_rect.position.y + (completion_index - line_from) * get_row_height()), Size2(completion_rect.size.width, get_row_height())), cache.completion_selected_color); + draw_rect(Rect2(completion_rect.position + Vector2(icon_area_size.x + icon_hsep, 0), Size2(MIN(nofs, completion_rect.size.width - (icon_area_size.x + icon_hsep)), completion_rect.size.height)), cache.completion_existing_color); for (int i = 0; i < lines; i++) { int l = line_from + i; ERR_CONTINUE(l < 0 || l >= completion_options_size); - int yofs = (get_row_height() - cache.font->get_height()) / 2; - Point2 title_pos(completion_rect.position.x, completion_rect.position.y + i * get_row_height() + cache.font->get_ascent() + yofs); + + Ref<TextLine> tl; + tl.instance(); + tl->add_string(completion_options[l].display, cache.font, cache.font_size); + + int yofs = (row_height - tl->get_size().y) / 2; + Point2 title_pos(completion_rect.position.x, completion_rect.position.y + i * row_height + yofs); // Draw completion icon if it is valid. Ref<Texture2D> icon = completion_options[l].icon; - Rect2 icon_area(completion_rect.position.x, completion_rect.position.y + i * get_row_height(), icon_area_size.width, icon_area_size.height); + Rect2 icon_area(completion_rect.position.x, completion_rect.position.y + i * row_height, icon_area_size.width, icon_area_size.height); if (icon.is_valid()) { const real_t max_scale = 0.7f; const real_t side = max_scale * icon_area.size.width; @@ -1448,11 +1534,20 @@ void TextEdit::_notification(int p_what) { title_pos.x = icon_area.position.x + icon_area.size.width + icon_hsep; - if (completion_options[l].default_value.get_type() == Variant::COLOR) { - draw_rect(Rect2(Point2(completion_rect.position.x + completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size), (Color)completion_options[l].default_value); - } + tl->set_width(completion_rect.size.width - (icon_area_size.x + icon_hsep)); - draw_string(cache.font, title_pos, completion_options[l].display, completion_options[l].font_color, completion_rect.size.width - (icon_area_size.x + icon_hsep)); + if (rtl) { + if (completion_options[l].default_value.get_type() == Variant::COLOR) { + draw_rect(Rect2(Point2(completion_rect.position.x, icon_area.position.y), icon_area_size), (Color)completion_options[l].default_value); + } + tl->set_align(HALIGN_RIGHT); + } else { + if (completion_options[l].default_value.get_type() == Variant::COLOR) { + draw_rect(Rect2(Point2(completion_rect.position.x + completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size), (Color)completion_options[l].default_value); + } + tl->set_align(HALIGN_LEFT); + } + tl->draw(ci, title_pos, completion_options[l].font_color); } if (scrollw) { @@ -1490,16 +1585,16 @@ void TextEdit::_notification(int p_what) { int spacing = 0; for (int i = 0; i < sc; i++) { String l = completion_hint.get_slice("\n", i); - int len = font->get_string_size(l).x; + int len = font->get_string_size(l, cache.font_size).x; max_w = MAX(len, max_w); if (i == 0) { - offset = font->get_string_size(l.substr(0, l.find(String::chr(0xFFFF)))).x; + offset = font->get_string_size(l.substr(0, l.find(String::chr(0xFFFF))), cache.font_size).x; } else { spacing += cache.line_spacing; } } - Size2 size2 = Size2(max_w, sc * font->get_height() + spacing); + Size2 size2 = Size2(max_w, sc * font->get_height(cache.font_size) + spacing); Size2 minsize = size2 + sb->get_minimum_size(); if (completion_hint_offset == -0xFFFF) { @@ -1509,7 +1604,7 @@ void TextEdit::_notification(int p_what) { Point2 hint_ofs = Vector2(completion_hint_offset, cursor_pos.y) + callhint_offset; if (callhint_below) { - hint_ofs.y += get_row_height() + sb->get_offset().y; + hint_ofs.y += row_height + sb->get_offset().y; } else { hint_ofs.y -= minsize.y + sb->get_offset().y; } @@ -1523,15 +1618,15 @@ void TextEdit::_notification(int p_what) { String l = completion_hint.get_slice("\n", i); if (l.find(String::chr(0xFFFF)) != -1) { - begin = font->get_string_size(l.substr(0, l.find(String::chr(0xFFFF)))).x; - end = font->get_string_size(l.substr(0, l.rfind(String::chr(0xFFFF)))).x; + begin = font->get_string_size(l.substr(0, l.find(String::chr(0xFFFF))), cache.font_size).x; + end = font->get_string_size(l.substr(0, l.rfind(String::chr(0xFFFF))), cache.font_size).x; } - Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent() + font->get_height() * i + spacing); + Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent(cache.font_size) + font->get_height(cache.font_size) * i + spacing); round_ofs = round_ofs.round(); - draw_string(font, round_ofs, l.replace(String::chr(0xFFFF), ""), font_color); + draw_string(font, round_ofs, l.replace(String::chr(0xFFFF), ""), HALIGN_LEFT, -1, cache.font_size, font_color); if (end > 0) { - Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font->get_height() + font->get_height() * i + spacing - 1); + Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font->get_height(cache.font_size) + font->get_height(cache.font_size) * i + spacing - 1); draw_line(b, b + Vector2(end - begin, 0), font_color); } spacing += cache.line_spacing; @@ -1541,7 +1636,7 @@ void TextEdit::_notification(int p_what) { if (has_focus()) { if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); - DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos + Point2(0, get_row_height()), get_viewport()->get_window_id()); + DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id()); } } } break; @@ -1554,21 +1649,23 @@ void TextEdit::_notification(int p_what) { if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); - Point2 cursor_pos = Point2(cursor_get_column(), cursor_get_line()) * get_row_height(); - DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id()); + DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + _get_cursor_pixel_pos(false), get_viewport()->get_window_id()); } if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { - String text = _base_get_text(0, 0, selection.selecting_line, selection.selecting_column); - int cursor_start = text.length(); + int cursor_start = -1; int cursor_end = -1; - if (selection.active) { - String selected_text = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); + if (!selection.active) { + String full_text = _base_get_text(0, 0, cursor.line, cursor.column); - if (selected_text.length() > 0) { - cursor_end = cursor_start + selected_text.length(); - } + cursor_start = full_text.length(); + } else { + String pre_text = _base_get_text(0, 0, selection.from_line, selection.from_column); + String post_text = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); + + cursor_start = pre_text.length(); + cursor_end = cursor_start + post_text.length(); } DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true, -1, cursor_start, cursor_end); @@ -1585,6 +1682,7 @@ void TextEdit::_notification(int p_what) { } ime_text = ""; ime_selection = Point2(); + text.invalidate_cache(cursor.line, cursor.column, ime_text); if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_hide(); @@ -1594,6 +1692,15 @@ void TextEdit::_notification(int p_what) { if (has_focus()) { ime_text = DisplayServer::get_singleton()->ime_get_text(); ime_selection = DisplayServer::get_singleton()->ime_get_selection(); + + String t; + if (cursor.column >= 0) { + t = text[cursor.line].substr(0, cursor.column) + ime_text + text[cursor.line].substr(cursor.column, text[cursor.line].length()); + } else { + t = ime_text; + } + + text.invalidate_cache(cursor.line, cursor.column, t, structured_text_parser(st_parser, st_args, t)); update(); } } break; @@ -1927,7 +2034,7 @@ int TextEdit::_calculate_spaces_till_next_right_indent(int column) { void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const { float rows = p_mouse.y; - rows -= cache.style_normal->get_margin(MARGIN_TOP); + rows -= cache.style_normal->get_margin(SIDE_TOP); rows /= get_row_height(); rows += get_v_scroll_offset(); int first_vis_line = get_first_visible_line(); @@ -1944,7 +2051,7 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co } if (row < 0) { - row = 0; // TODO. + row = 0; } int col = 0; @@ -1953,67 +2060,52 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co row = text.size() - 1; col = text[row].size(); } else { - int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + gutters_width + gutter_padding); + int colx = p_mouse.x - (cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding); colx += cursor.x_ofs; - col = get_char_pos_for_line(colx, row, wrap_index); - if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) { - // Move back one if we are at the end of the row. - Vector<String> rows2 = get_wrap_rows_text(row); - int row_end_col = 0; - for (int i = 0; i < wrap_index + 1; i++) { - row_end_col += rows2[i].length(); - } - if (col >= row_end_col) { - col -= 1; - } + + RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index); + if (is_layout_rtl()) { + colx = TS->shaped_text_get_size(text_rid).x - colx; } + col = TS->shaped_text_hit_test_position(text_rid, colx); } r_row = row; r_col = col; } -Vector2i TextEdit::_get_cursor_pixel_pos() { - adjust_viewport_to_cursor(); - int row = (cursor.line - get_first_visible_line() - cursor.wrap_ofs); - // Correct for hidden and wrapped lines - for (int i = get_first_visible_line(); i < cursor.line; i++) { - if (is_line_hidden(i)) { - row -= 1; - continue; - } - row += times_line_wraps(i); +Vector2i TextEdit::_get_cursor_pixel_pos(bool p_adjust_viewport) { + if (p_adjust_viewport) { + adjust_viewport_to_cursor(); } - // Row might be wrapped. Adjust row and r_column - Vector<String> rows2 = get_wrap_rows_text(cursor.line); - while (rows2.size() > 1) { - if (cursor.column >= rows2[0].length()) { - cursor.column -= rows2[0].length(); - rows2.remove(0); - row++; - } else { - break; + int row = 1; + for (int i = get_first_visible_line(); i < cursor.line; i++) { + if (!is_line_hidden(i)) { + row += times_line_wraps(i) + 1; } } + row += cursor.wrap_ofs; // Calculate final pixel position - int y = (row - get_v_scroll_offset() + 1 /*Bottom of line*/) * get_row_height(); - int x = cache.style_normal->get_margin(MARGIN_LEFT) + gutters_width + gutter_padding - cursor.x_ofs; - int ix = 0; - while (ix < rows2[0].size() && ix < cursor.column) { - if (cache.font != nullptr) { - x += cache.font->get_char_size(rows2[0].get(ix)).width; - } - ix++; + int y = (row - get_v_scroll_offset()) * get_row_height(); + int x = cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding - cursor.x_ofs; + + Rect2 l_caret, t_caret; + TextServer::Direction l_dir, t_dir; + RID text_rid = text.get_line_data(cursor.line)->get_line_rid(cursor.wrap_ofs); + TS->shaped_text_get_carets(text_rid, cursor.column, l_caret, l_dir, t_caret, t_dir); + if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { + x += l_caret.position.x; + } else { + x += t_caret.position.x; } - x += get_indent_level(cursor.line) * cache.font->get_char_size(' ').width; return Vector2i(x, y); } void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const { float rows = p_mouse.y; - rows -= cache.style_normal->get_margin(MARGIN_TOP); + rows -= cache.style_normal->get_margin(SIDE_TOP); rows /= (minimap_char_size.y + minimap_line_spacing); rows += get_v_scroll_offset(); @@ -2071,7 +2163,15 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { Ref<InputEventMouseButton> mb = p_gui_input; if (mb.is_valid()) { - if (completion_active && completion_rect.has_point(mb->get_position())) { + Vector2i mpos = mb->get_position(); + if (is_layout_rtl()) { + mpos.x = get_size().x - mpos.x; + } + if (ime_text.length() != 0) { + // Ignore mouse clicks in IME input mode. + return; + } + if (completion_active && completion_rect.has_point(mpos)) { if (!mb->is_pressed()) { return; } @@ -2092,7 +2192,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } if (mb->get_button_index() == BUTTON_LEFT) { - completion_index = CLAMP(completion_line_ofs + (mb->get_position().y - completion_rect.position.y) / get_row_height(), 0, completion_options.size() - 1); + completion_index = CLAMP(completion_line_ofs + (mpos.y - completion_rect.position.y) / get_row_height(), 0, completion_options.size() - 1); completion_current = completion_options[completion_index]; update(); @@ -2131,15 +2231,15 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { _reset_caret_blink_timer(); int row, col; - _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col); + _get_mouse_pos(Point2i(mpos.x, mpos.y), row, col); - int left_margin = cache.style_normal->get_margin(MARGIN_LEFT); + int left_margin = cache.style_normal->get_margin(SIDE_LEFT); for (int i = 0; i < gutters.size(); i++) { if (!gutters[i].draw || gutters[i].width <= 0) { continue; } - if (mb->get_position().x > left_margin && mb->get_position().x <= (left_margin + gutters[i].width) - 3) { + if (mpos.x > left_margin && mpos.x <= (left_margin + gutters[i].width) - 3) { emit_signal("gutter_clicked", row, i); return; } @@ -2150,7 +2250,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { // Unfold on folded icon click. if (is_folded(row)) { left_margin += gutter_padding + text.get_line_width(row) - cursor.x_ofs; - if (mb->get_position().x > left_margin && mb->get_position().x <= left_margin + cache.folded_eol_icon->get_width() + 3) { + if (mpos.x > left_margin && mpos.x <= left_margin + cache.folded_eol_icon->get_width() + 3) { unfold_line(row); return; } @@ -2241,7 +2341,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { _reset_caret_blink_timer(); int row, col; - _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col); + _get_mouse_pos(Point2i(mpos.x, mpos.y), row, col); if (is_right_click_moving_caret()) { if (is_selection_active()) { @@ -2261,7 +2361,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } - menu->set_position(get_screen_transform().xform(get_local_mouse_position())); + menu->set_position(get_screen_transform().xform(mpos)); menu->set_size(Vector2(1, 1)); // menu->set_scale(get_global_transform().get_scale()); menu->popup(); @@ -2271,7 +2371,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->get_button_index() == BUTTON_LEFT) { if (mb->get_command() && highlighted_word != String()) { int row, col; - _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col); + _get_mouse_pos(Point2i(mpos.x, mpos.y), row, col); emit_signal("symbol_lookup", highlighted_word, row, col); return; @@ -2307,9 +2407,13 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { Ref<InputEventMouseMotion> mm = p_gui_input; if (mm.is_valid()) { + Vector2i mpos = mm->get_position(); + if (is_layout_rtl()) { + mpos.x = get_size().x - mpos.x; + } if (select_identifiers_enabled) { if (!dragging_minimap && !dragging_selection && mm->get_command() && mm->get_button_mask() == 0) { - String new_word = get_word_at_pos(mm->get_position()); + String new_word = get_word_at_pos(mpos); if (new_word != highlighted_word) { emit_signal("symbol_validate", new_word); } @@ -2363,7 +2467,8 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { #endif if (select_identifiers_enabled) { if (k->is_pressed() && !dragging_minimap && !dragging_selection) { - emit_signal("symbol_validate", get_word_at_pos(get_local_mouse_position())); + Point2 mp = _get_local_mouse_pos(); + emit_signal("symbol_validate", get_word_at_pos(mp)); } else { set_highlighted_word(String()); } @@ -2701,7 +2806,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { continue; } - if (indent_char_found && is_line_comment(i)) { + if (indent_char_found && is_line_comment(cursor.line)) { should_indent = true; break; } else if (indent_char_found && !_is_whitespace(c)) { @@ -2844,34 +2949,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { int line = cursor.line; int column = cursor.column; - // Check if we are removing a single whitespace, if so remove it and the next char type, - // else we just remove the whitespace. - bool only_whitespace = false; - if (_is_whitespace(text[line][column - 1]) && _is_whitespace(text[line][column - 2])) { - only_whitespace = true; - } else if (_is_whitespace(text[line][column - 1])) { - // Remove the single whitespace. - column--; - } - - // Check if its a text char. - bool only_char = (_is_text_char(text[line][column - 1]) && !only_whitespace); - - // If its not whitespace or char then symbol. - bool only_symbols = !(only_whitespace || only_char); - - while (column > 0) { - bool is_whitespace = _is_whitespace(text[line][column - 1]); - bool is_text_char = _is_text_char(text[line][column - 1]); - - if (only_whitespace && !is_whitespace) { - break; - } else if (only_char && !is_text_char) { - break; - } else if (only_symbols && (is_whitespace || is_text_char)) { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); + for (int i = words.size() - 1; i >= 0; i--) { + if (words[i].x < column) { + column = words[i].x; break; } - column--; } _remove_text(line, column, cursor.line, cursor.column); @@ -2941,17 +3024,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { cursor_set_line(cursor.line - 1); cursor_set_column(text[cursor.line].length()); } else { - bool prev_char = false; - - while (cc > 0) { - bool ischar = _is_text_char(text[cursor.line][cc - 1]); - - if (prev_char && !ischar) { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid()); + for (int i = words.size() - 1; i >= 0; i--) { + if (words[i].x < cc) { + cc = words[i].x; break; } - - prev_char = ischar; - cc--; } cursor_set_column(cc); } @@ -2962,7 +3040,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { cursor_set_column(text[cursor.line].length()); } } else { - cursor_set_column(cursor_get_column() - 1); + if (mid_grapheme_caret_enabled) { + cursor_set_column(cursor_get_column() - 1); + } else { + cursor_set_column(TS->shaped_text_prev_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column())); + } } if (k->get_shift()) { @@ -3004,16 +3086,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { cursor_set_line(cursor.line + 1); cursor_set_column(0); } else { - bool prev_char = false; - - while (cc < text[cursor.line].length()) { - bool ischar = _is_text_char(text[cursor.line][cc]); - - if (prev_char && !ischar) { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].y > cc) { + cc = words[i].y; break; } - prev_char = ischar; - cc++; } cursor_set_column(cc); } @@ -3024,7 +3102,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { cursor_set_column(0); } } else { - cursor_set_column(cursor_get_column() + 1); + if (mid_grapheme_caret_enabled) { + cursor_set_column(cursor_get_column() + 1); + } else { + cursor_set_column(TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column())); + } } if (k->get_shift()) { @@ -3163,34 +3245,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { int line = cursor.line; int column = cursor.column; - // Check if we are removing a single whitespace, if so remove it and the next char type, - // else we just remove the whitespace. - bool only_whitespace = false; - if (_is_whitespace(text[line][column]) && _is_whitespace(text[line][column + 1])) { - only_whitespace = true; - } else if (_is_whitespace(text[line][column])) { - // Remove the single whitespace. - column++; - } - - // Check if its a text char. - bool only_char = (_is_text_char(text[line][column]) && !only_whitespace); - - // If its not whitespace or char then symbol. - bool only_symbols = !(only_whitespace || only_char); - - while (column < curline_len) { - bool is_whitespace = _is_whitespace(text[line][column]); - bool is_text_char = _is_text_char(text[line][column]); - - if (only_whitespace && !is_whitespace) { - break; - } else if (only_char && !is_text_char) { - break; - } else if (only_symbols && (is_whitespace || is_text_char)) { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].y > column) { + column = words[i].y; break; } - column++; } next_line = line; @@ -3201,7 +3261,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { next_line = cursor.line; #endif } else { - next_column = cursor.column < curline_len ? (cursor.column + 1) : 0; + if (mid_grapheme_caret_enabled) { + next_column = cursor.column < curline_len ? (cursor.column + 1) : 0; + } else { + next_column = cursor.column < curline_len ? TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), (cursor.column)) : 0; + } } _remove_text(cursor.line, cursor.column, next_line, next_column); @@ -3431,6 +3495,20 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { completion_hint = ""; #endif } break; + case (KEY_QUOTELEFT): { // Swap current input direction (primary cursor) + if (!k->get_command()) { + keycode_handled = false; + break; + } + + if (input_direction == TEXT_DIRECTION_LTR) { + input_direction = TEXT_DIRECTION_RTL; + } else { + input_direction = TEXT_DIRECTION_LTR; + } + cursor_set_column(cursor.column); + update(); + } break; case KEY_X: { if (readonly) { break; @@ -3518,9 +3596,8 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { case KEY_MENU: { if (context_menu_enabled) { - menu->set_position(get_global_transform().xform(_get_cursor_pixel_pos())); + menu->set_position(get_screen_transform().xform(_get_cursor_pixel_pos())); menu->set_size(Vector2(1, 1)); - // menu->set_scale(get_global_transform().get_scale()); menu->popup(); menu->grab_focus(); } @@ -3541,7 +3618,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } - if (!keycode_handled && (!k->get_command() || (k->get_command() && k->get_alt()))) { // For German keyboards. + if (!keycode_handled && !k->get_command()) { // For German keyboards. if (k->get_unicode() >= 32) { if (readonly) { @@ -3714,7 +3791,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i /* STEP 2: Add spaces if the char is greater than the end of the line. */ while (p_char > text[p_line].length()) { - text.set(p_line, text[p_line] + String::chr(' ')); + text.set(p_line, text[p_line] + String::chr(' '), structured_text_parser(st_parser, st_args, text[p_line] + String::chr(' '))); } /* STEP 3: Separate dest string in pre and post text. */ @@ -3726,13 +3803,13 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i // Insert the substrings. if (j == 0) { - text.set(p_line, preinsert_text + substrings[j]); + text.set(p_line, preinsert_text + substrings[j], structured_text_parser(st_parser, st_args, preinsert_text + substrings[j])); } else { - text.insert(p_line + j, substrings[j]); + text.insert(p_line + j, substrings[j], structured_text_parser(st_parser, st_args, substrings[j])); } if (j == substrings.size() - 1) { - text.set(p_line + j, text[p_line + j] + postinsert_text); + text.set(p_line + j, text[p_line + j] + postinsert_text, structured_text_parser(st_parser, st_args, text[p_line + j] + postinsert_text)); } } @@ -3743,11 +3820,16 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i text.set_hidden(p_line, false); } - text.set_line_wrap_amount(p_line, -1); + text.invalidate_cache(p_line); r_end_line = p_line + substrings.size() - 1; r_end_column = text[r_end_line].length() - postinsert_text.length(); + TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text.get_line_data(r_end_line)->get_rid(), (r_end_line == p_line) ? cursor.column : 0, r_end_column); + if (dir != TextServer::DIRECTION_AUTO) { + input_direction = (TextDirection)dir; + } + if (!text_changed_dirty && !setting_text) { if (is_inside_tree()) { MessageQueue::get_singleton()->push_call(this, "_text_changed_emit"); @@ -3794,9 +3876,10 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li for (int i = p_from_line; i < p_to_line; i++) { text.remove(p_from_line + 1); } - text.set(p_from_line, pre_text + post_text); + text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text)); - text.set_line_wrap_amount(p_from_line, -1); + //text.set_line_wrap_amount(p_from_line, -1); + text.invalidate_cache(p_from_line); if (!text_changed_dirty && !setting_text) { if (is_inside_tree()) { @@ -3970,6 +4053,13 @@ void TextEdit::_generate_context_menu() { menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_Z : 0); menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z : 0); } + menu->add_separator(); + menu->add_submenu_item(RTR("Text writing direction"), "DirMenu"); + menu->add_separator(); + menu->add_check_item(RTR("Display control characters"), MENU_DISPLAY_UCC); + if (!readonly) { + menu->add_submenu_item(RTR("Insert control character"), "CTLMenu"); + } } int TextEdit::get_visible_rows() const { @@ -3997,19 +4087,27 @@ int TextEdit::get_total_visible_rows() const { return total_rows; } -void TextEdit::_update_wrap_at() { - wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding - cache.minimap_width - wrap_right_offset; - update_cursor_wrap_offset(); - text.clear_wrap_cache(); +void TextEdit::_update_wrap_at(bool p_force) { + int new_wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding; + if (draw_minimap) { + new_wrap_at -= minimap_width; + } + if (v_scroll->is_visible_in_tree()) { + new_wrap_at -= v_scroll->get_combined_minimum_size().width; + } + new_wrap_at -= wrap_right_offset; // Give it a little more space. - for (int i = 0; i < text.size(); i++) { - // Update all values that wrap. - if (!line_wraps(i)) { - continue; + if ((wrap_at != new_wrap_at) || p_force) { + wrap_at = new_wrap_at; + if (wrap_enabled) { + text.set_width(wrap_at); + } else { + text.set_width(-1); } - Vector<String> rows = get_wrap_rows_text(i); - text.set_line_wrap_amount(i, rows.size() - 1); + text.invalidate_all_lines(); } + + update_cursor_wrap_offset(); } void TextEdit::adjust_viewport_to_cursor() { @@ -4041,14 +4139,32 @@ void TextEdit::adjust_viewport_to_cursor() { if (!is_wrap_enabled()) { // Adjust x offset. - int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]); + Vector2i cursor_pos; + + // Get position of the start of caret. + if (ime_text.length() != 0 && ime_selection.x != 0) { + cursor_pos.x = get_column_x_offset_for_line(cursor.column + ime_selection.x, cursor.line); + } else { + cursor_pos.x = get_column_x_offset_for_line(cursor.column, cursor.line); + } + + // Get position of the end of caret. + if (ime_text.length() != 0) { + if (ime_selection.y != 0) { + cursor_pos.y = get_column_x_offset_for_line(cursor.column + ime_selection.x + ime_selection.y, cursor.line); + } else { + cursor_pos.y = get_column_x_offset_for_line(cursor.column + ime_text.size(), cursor.line); + } + } else { + cursor_pos.y = cursor_pos.x; + } - if (cursor_x > (cursor.x_ofs + visible_width)) { - cursor.x_ofs = cursor_x - visible_width + 1; + if (MAX(cursor_pos.x, cursor_pos.y) > (cursor.x_ofs + visible_width)) { + cursor.x_ofs = MAX(cursor_pos.x, cursor_pos.y) - visible_width + 1; } - if (cursor_x < cursor.x_ofs) { - cursor.x_ofs = cursor_x; + if (MIN(cursor_pos.x, cursor_pos.y) < cursor.x_ofs) { + cursor.x_ofs = MIN(cursor_pos.x, cursor_pos.y); } } else { cursor.x_ofs = 0; @@ -4076,14 +4192,33 @@ void TextEdit::center_viewport_to_cursor() { if (is_wrap_enabled()) { // Center x offset. - int cursor_x = get_column_x_offset_for_line(cursor.column, cursor.line); - if (cursor_x > (cursor.x_ofs + visible_width)) { - cursor.x_ofs = cursor_x - visible_width + 1; + Vector2i cursor_pos; + + // Get position of the start of caret. + if (ime_text.length() != 0 && ime_selection.x != 0) { + cursor_pos.x = get_column_x_offset_for_line(cursor.column + ime_selection.x, cursor.line); + } else { + cursor_pos.x = get_column_x_offset_for_line(cursor.column, cursor.line); } - if (cursor_x < cursor.x_ofs) { - cursor.x_ofs = cursor_x; + // Get position of the end of caret. + if (ime_text.length() != 0) { + if (ime_selection.y != 0) { + cursor_pos.y = get_column_x_offset_for_line(cursor.column + ime_selection.x + ime_selection.y, cursor.line); + } else { + cursor_pos.y = get_column_x_offset_for_line(cursor.column + ime_text.size(), cursor.line); + } + } else { + cursor_pos.y = cursor_pos.x; + } + + if (MAX(cursor_pos.x, cursor_pos.y) > (cursor.x_ofs + visible_width)) { + cursor.x_ofs = MAX(cursor_pos.x, cursor_pos.y) - visible_width + 1; + } + + if (MIN(cursor_pos.x, cursor_pos.y) < cursor.x_ofs) { + cursor.x_ofs = MIN(cursor_pos.x, cursor_pos.y); } } else { cursor.x_ofs = 0; @@ -4108,24 +4243,17 @@ bool TextEdit::line_wraps(int line) const { if (!is_wrap_enabled()) { return false; } - return text.get_line_width(line) > wrap_at; + return text.get_line_wrap_amount(line) > 0; } int TextEdit::times_line_wraps(int line) const { ERR_FAIL_INDEX_V(line, text.size(), 0); + if (!line_wraps(line)) { return 0; } - int wrap_amount = text.get_line_wrap_amount(line); - if (wrap_amount == -1) { - // Update the value. - Vector<String> rows = get_wrap_rows_text(line); - wrap_amount = rows.size() - 1; - text.set_line_wrap_amount(line, wrap_amount); - } - - return wrap_amount; + return text.get_line_wrap_amount(line); } Vector<String> TextEdit::get_wrap_rows_text(int p_line) const { @@ -4137,65 +4265,11 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const { return lines; } - int px = 0; - int col = 0; - String line_text = text[p_line]; - String wrap_substring = ""; - - int word_px = 0; - String word_str = ""; - int cur_wrap_index = 0; - - int tab_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width; - if (tab_offset_px >= wrap_at) { - tab_offset_px = 0; - } - - while (col < line_text.length()) { - char32_t c = line_text[col]; - int w = text.get_char_width(c, line_text[col + 1], px + word_px); - - int indent_ofs = (cur_wrap_index != 0 ? tab_offset_px : 0); - - if (indent_ofs + word_px + w > wrap_at) { - // Not enough space to add this char; start next line. - wrap_substring += word_str; - lines.push_back(wrap_substring); - cur_wrap_index++; - wrap_substring = ""; - px = 0; - - word_str = ""; - word_str += c; - word_px = w; - } else { - word_str += c; - word_px += w; - if (c == ' ') { - // End of a word; add this word to the substring. - wrap_substring += word_str; - px += word_px; - word_str = ""; - word_px = 0; - } - - if (indent_ofs + px + word_px > wrap_at) { - // This word will be moved to the next line. - lines.push_back(wrap_substring); - // Reset for next wrap. - cur_wrap_index++; - wrap_substring = ""; - px = 0; - } - } - col++; + const String &line_text = text[p_line]; + Vector<Vector2i> line_ranges = text.get_line_wrap_ranges(p_line); + for (int i = 0; i < line_ranges.size(); i++) { + lines.push_back(line_text.substr(line_ranges[i].x, line_ranges[i].y - line_ranges[i].x)); } - // Line ends before hit wrap_at; add this word to the substring. - wrap_substring += word_str; - lines.push_back(wrap_substring); - - // Update cache. - text.set_line_wrap_amount(p_line, lines.size() - 1); return lines; } @@ -4226,6 +4300,14 @@ int TextEdit::get_line_wrap_index_at_col(int p_line, int p_column) const { return wrap_index; } +void TextEdit::set_mid_grapheme_caret_enabled(const bool p_enabled) { + mid_grapheme_caret_enabled = p_enabled; +} + +bool TextEdit::get_mid_grapheme_caret_enabled() const { + return mid_grapheme_caret_enabled; +} + void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) { if (p_col < 0) { p_col = 0; @@ -4371,7 +4453,7 @@ void TextEdit::set_selection_mode(SelectionMode p_mode, int p_line, int p_column selection.selecting_line = p_line; } if (p_column >= 0) { - ERR_FAIL_INDEX(p_line, text[selection.selecting_line].length()); + ERR_FAIL_INDEX(p_column, text[selection.selecting_line].length()); selection.selecting_column = p_column; } } @@ -4423,101 +4505,47 @@ void TextEdit::_scroll_moved(double p_to_val) { } int TextEdit::get_row_height() const { - return cache.font->get_height() + cache.line_spacing; + int height = cache.font->get_height(cache.font_size); + for (int i = 0; i < text.size(); i++) { + for (int j = 0; j <= text.get_line_wrap_amount(i); j++) { + height = MAX(height, text.get_line_height(i, j)); + } + } + return height + cache.line_spacing; } int TextEdit::get_char_pos_for_line(int p_px, int p_line, int p_wrap_index) const { ERR_FAIL_INDEX_V(p_line, text.size(), 0); + p_wrap_index = MIN(p_wrap_index, text.get_line_data(p_line)->get_line_count() - 1); - if (line_wraps(p_line)) { - int line_wrap_amount = times_line_wraps(p_line); - int wrap_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width; - if (wrap_offset_px >= wrap_at) { - wrap_offset_px = 0; - } - if (p_wrap_index > line_wrap_amount) { - p_wrap_index = line_wrap_amount; - } - if (p_wrap_index > 0) { - p_px -= wrap_offset_px; - } else { - p_wrap_index = 0; - } - Vector<String> rows = get_wrap_rows_text(p_line); - int c_pos = get_char_pos_for(p_px, rows[p_wrap_index]); - for (int i = 0; i < p_wrap_index; i++) { - String s = rows[i]; - c_pos += s.length(); - } - - return c_pos; - } else { - return get_char_pos_for(p_px, text[p_line]); + RID text_rid = text.get_line_data(p_line)->get_line_rid(p_wrap_index); + if (is_layout_rtl()) { + p_px = TS->shaped_text_get_size(text_rid).x - p_px; } + return TS->shaped_text_hit_test_position(text_rid, p_px); } int TextEdit::get_column_x_offset_for_line(int p_char, int p_line) const { ERR_FAIL_INDEX_V(p_line, text.size(), 0); - if (line_wraps(p_line)) { - int n_char = p_char; - int col = 0; - Vector<String> rows = get_wrap_rows_text(p_line); - int wrap_index = 0; - for (int i = 0; i < rows.size(); i++) { - wrap_index = i; - String s = rows[wrap_index]; - col += s.length(); - if (col > p_char) { - break; - } - n_char -= s.length(); - } - int px = get_column_x_offset(n_char, rows[wrap_index]); - - int wrap_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width; - if (wrap_offset_px >= wrap_at) { - wrap_offset_px = 0; - } - if (wrap_index != 0) { - px += wrap_offset_px; - } - - return px; - } else { - return get_column_x_offset(p_char, text[p_line]); - } -} - -int TextEdit::get_char_pos_for(int p_px, String p_str) const { - int px = 0; - int c = 0; - - while (c < p_str.length()) { - int w = text.get_char_width(p_str[c], p_str[c + 1], px); - - if (p_px < (px + w / 2)) { + int row = 0; + Vector<Vector2i> rows2 = text.get_line_wrap_ranges(p_line); + for (int i = 0; i < rows2.size(); i++) { + if ((p_char >= rows2[i].x) && (p_char < rows2[i].y)) { + row = i; break; } - px += w; - c++; } - return c; -} - -int TextEdit::get_column_x_offset(int p_char, String p_str) const { - int px = 0; - - for (int i = 0; i < p_char; i++) { - if (i >= p_str.length()) { - break; - } - - px += text.get_char_width(p_str[i], p_str[i + 1], px); + Rect2 l_caret, t_caret; + TextServer::Direction l_dir, t_dir; + RID text_rid = text.get_line_data(p_line)->get_line_rid(row); + TS->shaped_text_get_carets(text_rid, cursor.column, l_caret, l_dir, t_caret, t_dir); + if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { + return l_caret.position.x; + } else { + return t_caret.position.x; } - - return px; } void TextEdit::insert_text_at_cursor(const String &p_text) { @@ -4546,7 +4574,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { int row, col; _get_mouse_pos(p_pos, row, col); - int left_margin = cache.style_normal->get_margin(MARGIN_LEFT); + int left_margin = cache.style_normal->get_margin(SIDE_LEFT); int gutter = left_margin + gutters_width; if (p_pos.x < gutter) { for (int i = 0; i < gutters.size(); i++) { @@ -4564,7 +4592,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { return CURSOR_ARROW; } - int xmargin_end = get_size().width - cache.style_normal->get_margin(MARGIN_RIGHT); + int xmargin_end = get_size().width - cache.style_normal->get_margin(SIDE_RIGHT); if (draw_minimap && p_pos.x > xmargin_end - minimap_width && p_pos.x <= xmargin_end) { return CURSOR_ARROW; } @@ -4603,7 +4631,7 @@ void TextEdit::set_text(String p_text) { update(); setting_text = false; -}; +} String TextEdit::get_text() { String longthing; @@ -4616,11 +4644,124 @@ String TextEdit::get_text() { } return longthing; -}; +} + +void TextEdit::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { + if (st_parser != p_parser) { + st_parser = p_parser; + for (int i = 0; i < text.size(); i++) { + text.set(i, text[i], structured_text_parser(st_parser, st_args, text[i])); + } + update(); + } +} + +Control::StructuredTextParser TextEdit::get_structured_text_bidi_override() const { + return st_parser; +} + +void TextEdit::set_structured_text_bidi_override_options(Array p_args) { + st_args = p_args; + for (int i = 0; i < text.size(); i++) { + text.set(i, text[i], structured_text_parser(st_parser, st_args, text[i])); + } + update(); +} + +Array TextEdit::get_structured_text_bidi_override_options() const { + return st_args; +} + +void TextEdit::set_text_direction(Control::TextDirection p_text_direction) { + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (text_direction != p_text_direction) { + text_direction = p_text_direction; + if (text_direction != TEXT_DIRECTION_AUTO && text_direction != TEXT_DIRECTION_INHERITED) { + input_direction = text_direction; + } + TextServer::Direction dir; + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { + dir = is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR; + } else { + dir = (TextServer::Direction)text_direction; + } + text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); + text.invalidate_all(); + + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_AUTO), text_direction == TEXT_DIRECTION_AUTO); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); + update(); + } +} + +Control::TextDirection TextEdit::get_text_direction() const { + return text_direction; +} + +void TextEdit::clear_opentype_features() { + opentype_features.clear(); + text.set_font_features(opentype_features); + text.invalidate_all(); + update(); +} + +void TextEdit::set_opentype_feature(const String &p_name, int p_value) { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { + opentype_features[tag] = p_value; + text.set_font_features(opentype_features); + text.invalidate_all(); + update(); + } +} + +int TextEdit::get_opentype_feature(const String &p_name) const { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag)) { + return -1; + } + return opentype_features[tag]; +} + +void TextEdit::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + TextServer::Direction dir; + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { + dir = is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR; + } else { + dir = (TextServer::Direction)text_direction; + } + text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); + text.invalidate_all(); + update(); + } +} + +String TextEdit::get_language() const { + return language; +} + +void TextEdit::set_draw_control_chars(bool p_draw_control_chars) { + if (draw_control_chars != p_draw_control_chars) { + draw_control_chars = p_draw_control_chars; + menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); + text.set_draw_control_chars(draw_control_chars); + text.invalidate_all(); + update(); + } +} + +bool TextEdit::get_draw_control_chars() const { + return draw_control_chars; +} String TextEdit::get_text_for_lookup_completion() { int row, col; - _get_mouse_pos(get_local_mouse_position(), row, col); + Point2i mp = _get_local_mouse_pos(); + _get_mouse_pos(mp, row, col); String longthing; int len = text.size(); @@ -4695,32 +4836,6 @@ void TextEdit::set_readonly(bool p_readonly) { readonly = p_readonly; _generate_context_menu(); - // Reorganize context menu. - menu->clear(); - - if (!readonly) { - menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); - menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z); - } - - if (!readonly) { - menu->add_separator(); - menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X); - } - - menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C); - - if (!readonly) { - menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V); - } - - menu->add_separator(); - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A); - - if (!readonly) { - menu->add_item(RTR("Clear"), MENU_CLEAR); - } - update(); } @@ -4729,7 +4844,10 @@ bool TextEdit::is_readonly() const { } void TextEdit::set_wrap_enabled(bool p_wrap_enabled) { - wrap_enabled = p_wrap_enabled; + if (wrap_enabled != p_wrap_enabled) { + wrap_enabled = p_wrap_enabled; + _update_wrap_at(true); + } } bool TextEdit::is_wrap_enabled() const { @@ -4771,6 +4889,7 @@ void TextEdit::_update_caches() { cache.completion_existing_color = get_theme_color("completion_existing_color"); cache.completion_font_color = get_theme_color("completion_font_color"); cache.font = get_theme_font("font"); + cache.font_size = get_theme_font_size("font_size"); cache.caret_color = get_theme_color("caret_color"); cache.caret_background_color = get_theme_color("caret_background_color"); cache.font_color = get_theme_color("font_color"); @@ -4791,12 +4910,22 @@ void TextEdit::_update_caches() { #else cache.line_spacing = get_theme_constant("line_spacing"); #endif - cache.row_height = cache.font->get_height() + cache.line_spacing; cache.tab_icon = get_theme_icon("tab"); cache.space_icon = get_theme_icon("space"); cache.folded_eol_icon = get_theme_icon("GuiEllipsis", "EditorIcons"); + + TextServer::Direction dir; + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { + dir = is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR; + } else { + dir = (TextServer::Direction)text_direction; + } + text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); + text.set_font_features(opentype_features); + text.set_draw_control_chars(draw_control_chars); text.set_font(cache.font); - text.clear_width_cache(); + text.set_font_size(cache.font_size); + text.invalidate_all(); if (syntax_highlighter.is_valid()) { syntax_highlighter->set_text_edit(this); @@ -5067,7 +5196,7 @@ void TextEdit::paste() { cursor_set_line(selection.from_line); cursor_set_column(selection.from_column); - } else if (!cut_copy_line.empty() && cut_copy_line == clipboard) { + } else if (!cut_copy_line.is_empty() && cut_copy_line == clipboard) { cursor_set_column(0); String ins = "\n"; clipboard += ins; @@ -5203,27 +5332,13 @@ String TextEdit::get_selection_text() const { } String TextEdit::get_word_under_cursor() const { - int prev_cc = cursor.column; - while (prev_cc > 0) { - bool is_char = _is_text_char(text[cursor.line][prev_cc - 1]); - if (!is_char) { - break; + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].x <= cursor.column && words[i].y > cursor.column) { + return text[cursor.line].substr(words[i].x, words[i].y - words[i].x); } - --prev_cc; } - - int next_cc = cursor.column; - while (next_cc < text[cursor.line].length()) { - bool is_char = _is_text_char(text[cursor.line][next_cc]); - if (!is_char) { - break; - } - ++next_cc; - } - if (prev_cc == cursor.column || next_cc == cursor.column) { - return ""; - } - return text[cursor.line].substr(prev_cc, next_cc - prev_cc); + return ""; } void TextEdit::set_search_text(const String &p_search_text) { @@ -5519,7 +5634,7 @@ int TextEdit::num_lines_from_rows(int p_line_from, int p_wrap_index_from, int vi break; } } - wrap_index = times_line_wraps(MIN(i, text.size() - 1)) - (num_visible - visible_amount); + wrap_index = times_line_wraps(MIN(i, text.size() - 1)) - MAX(0, num_visible - visible_amount); } else { visible_amount = ABS(visible_amount); int i; @@ -5534,7 +5649,7 @@ int TextEdit::num_lines_from_rows(int p_line_from, int p_wrap_index_from, int vi break; } } - wrap_index = (num_visible - visible_amount); + wrap_index = MAX(0, num_visible - visible_amount); } wrap_index = MAX(wrap_index, 0); return num_total; @@ -5909,8 +6024,11 @@ bool TextEdit::is_indent_using_spaces() const { void TextEdit::set_indent_size(const int p_size) { ERR_FAIL_COND_MSG(p_size <= 0, "Indend size must be greater than 0."); - indent_size = p_size; - text.set_indent_size(p_size); + if (indent_size != p_size) { + indent_size = p_size; + text.set_indent_size(p_size); + text.invalidate_all_lines(); + } space_indent = ""; for (int i = 0; i < p_size; i++) { @@ -5935,6 +6053,7 @@ bool TextEdit::is_drawing_tabs() const { void TextEdit::set_draw_spaces(bool p_draw) { draw_spaces = p_draw; + update(); } bool TextEdit::is_drawing_spaces() const { @@ -6349,7 +6468,7 @@ void TextEdit::query_code_comple() { c--; } - bool ignored = completion_active && !completion_options.empty(); + bool ignored = completion_active && !completion_options.is_empty(); if (ignored) { ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_PLAIN_TEXT; const ScriptCodeCompletionOption *previous_option = nullptr; @@ -6504,7 +6623,10 @@ void TextEdit::set_line_length_guideline_hard_column(int p_column) { } void TextEdit::set_draw_minimap(bool p_draw) { - draw_minimap = p_draw; + if (draw_minimap != p_draw) { + draw_minimap = p_draw; + _update_wrap_at(); + } update(); } @@ -6513,7 +6635,10 @@ bool TextEdit::is_drawing_minimap() const { } void TextEdit::set_minimap_width(int p_minimap_width) { - minimap_width = p_minimap_width; + if (minimap_width != p_minimap_width) { + minimap_width = p_minimap_width; + _update_wrap_at(); + } update(); } @@ -6574,6 +6699,101 @@ void TextEdit::menu_option(int p_option) { } break; case MENU_REDO: { redo(); + } break; + case MENU_DIR_INHERITED: { + set_text_direction(TEXT_DIRECTION_INHERITED); + } break; + case MENU_DIR_AUTO: { + set_text_direction(TEXT_DIRECTION_AUTO); + } break; + case MENU_DIR_LTR: { + set_text_direction(TEXT_DIRECTION_LTR); + } break; + case MENU_DIR_RTL: { + set_text_direction(TEXT_DIRECTION_RTL); + } break; + case MENU_DISPLAY_UCC: { + set_draw_control_chars(!get_draw_control_chars()); + } break; + case MENU_INSERT_LRM: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x200E)); + } + } break; + case MENU_INSERT_RLM: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x200F)); + } + } break; + case MENU_INSERT_LRE: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x202A)); + } + } break; + case MENU_INSERT_RLE: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x202B)); + } + } break; + case MENU_INSERT_LRO: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x202D)); + } + } break; + case MENU_INSERT_RLO: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x202E)); + } + } break; + case MENU_INSERT_PDF: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x202C)); + } + } break; + case MENU_INSERT_ALM: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x061C)); + } + } break; + case MENU_INSERT_LRI: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x2066)); + } + } break; + case MENU_INSERT_RLI: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x2067)); + } + } break; + case MENU_INSERT_FSI: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x2068)); + } + } break; + case MENU_INSERT_PDI: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x2069)); + } + } break; + case MENU_INSERT_ZWJ: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x200D)); + } + } break; + case MENU_INSERT_ZWNJ: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x200C)); + } + } break; + case MENU_INSERT_WJ: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x2060)); + } + } break; + case MENU_INSERT_SHY: { + if (!readonly) { + insert_text_at_cursor(String::chr(0x00AD)); + } } } } @@ -6635,11 +6855,64 @@ PopupMenu *TextEdit::get_menu() const { return menu; } +bool TextEdit::_set(const StringName &p_name, const Variant &p_value) { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + double value = p_value; + if (value == -1) { + if (opentype_features.has(tag)) { + opentype_features.erase(tag); + text.set_font_features(opentype_features); + text.invalidate_all(); + update(); + } + } else { + if ((double)opentype_features[tag] != value) { + opentype_features[tag] = value; + text.set_font_features(opentype_features); + text.invalidate_all(); + ; + update(); + } + } + _change_notify(); + return true; + } + + return false; +} + +bool TextEdit::_get(const StringName &p_name, Variant &r_ret) const { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + if (opentype_features.has(tag)) { + r_ret = opentype_features[tag]; + return true; + } else { + r_ret = -1; + return true; + } + } + return false; +} + +void TextEdit::_get_property_list(List<PropertyInfo> *p_list) const { + for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { + String name = TS->tag_to_name(*ftr); + p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name)); + } + p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); +} + void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &TextEdit::_gui_input); ClassDB::bind_method(D_METHOD("_cursor_changed_emit"), &TextEdit::_cursor_changed_emit); ClassDB::bind_method(D_METHOD("_text_changed_emit"), &TextEdit::_text_changed_emit); - ClassDB::bind_method(D_METHOD("_update_wrap_at"), &TextEdit::_update_wrap_at); + ClassDB::bind_method(D_METHOD("_update_wrap_at", "force"), &TextEdit::_update_wrap_at, DEFVAL(false)); BIND_ENUM_CONSTANT(SEARCH_MATCH_CASE); BIND_ENUM_CONSTANT(SEARCH_WHOLE_WORDS); @@ -6656,6 +6929,16 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("delete_line"),&TextEdit::delete_line); */ + ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &TextEdit::get_draw_control_chars); + ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enable"), &TextEdit::set_draw_control_chars); + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextEdit::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &TextEdit::get_text_direction); + ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &TextEdit::set_opentype_feature); + ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &TextEdit::get_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_opentype_features"), &TextEdit::clear_opentype_features); + ClassDB::bind_method(D_METHOD("set_language", "language"), &TextEdit::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &TextEdit::get_language); + ClassDB::bind_method(D_METHOD("set_text", "text"), &TextEdit::set_text); ClassDB::bind_method(D_METHOD("insert_text_at_cursor", "text"), &TextEdit::insert_text_at_cursor); @@ -6664,6 +6947,11 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line); ClassDB::bind_method(D_METHOD("set_line", "line", "new_text"), &TextEdit::set_line); + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &TextEdit::set_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &TextEdit::get_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &TextEdit::set_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &TextEdit::get_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("center_viewport_to_cursor"), &TextEdit::center_viewport_to_cursor); ClassDB::bind_method(D_METHOD("cursor_set_column", "column", "adjust_viewport"), &TextEdit::cursor_set_column, DEFVAL(true)); ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport", "can_be_hidden", "wrap_index"), &TextEdit::cursor_set_line, DEFVAL(true), DEFVAL(true), DEFVAL(0)); @@ -6677,6 +6965,9 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("cursor_set_block_mode", "enable"), &TextEdit::cursor_set_block_mode); ClassDB::bind_method(D_METHOD("cursor_is_block_mode"), &TextEdit::cursor_is_block_mode); + ClassDB::bind_method(D_METHOD("set_mid_grapheme_caret_enabled", "enabled"), &TextEdit::set_mid_grapheme_caret_enabled); + ClassDB::bind_method(D_METHOD("get_mid_grapheme_caret_enabled"), &TextEdit::get_mid_grapheme_caret_enabled); + ClassDB::bind_method(D_METHOD("set_right_click_moves_caret", "enable"), &TextEdit::set_right_click_moves_caret); ClassDB::bind_method(D_METHOD("is_right_click_moving_caret"), &TextEdit::is_right_click_moving_caret); @@ -6801,6 +7092,9 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_minimap_width"), &TextEdit::get_minimap_width); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "readonly"), "set_readonly", "is_readonly"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs"); @@ -6829,6 +7123,11 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_moving_by_right_click"), "set_right_click_moves_caret", "is_right_click_moving_caret"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_mid_grapheme_caret_enabled", "get_mid_grapheme_caret_enabled"); + + ADD_GROUP("Structured Text", "structured_text_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); ADD_SIGNAL(MethodInfo("cursor_changed")); ADD_SIGNAL(MethodInfo("text_changed")); @@ -6847,6 +7146,27 @@ void TextEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_SELECT_ALL); BIND_ENUM_CONSTANT(MENU_UNDO); BIND_ENUM_CONSTANT(MENU_REDO); + BIND_ENUM_CONSTANT(MENU_DIR_INHERITED); + BIND_ENUM_CONSTANT(MENU_DIR_AUTO); + BIND_ENUM_CONSTANT(MENU_DIR_LTR); + BIND_ENUM_CONSTANT(MENU_DIR_RTL); + BIND_ENUM_CONSTANT(MENU_DISPLAY_UCC); + BIND_ENUM_CONSTANT(MENU_INSERT_LRM); + BIND_ENUM_CONSTANT(MENU_INSERT_RLM); + BIND_ENUM_CONSTANT(MENU_INSERT_LRE); + BIND_ENUM_CONSTANT(MENU_INSERT_RLE); + BIND_ENUM_CONSTANT(MENU_INSERT_LRO); + BIND_ENUM_CONSTANT(MENU_INSERT_RLO); + BIND_ENUM_CONSTANT(MENU_INSERT_PDF); + BIND_ENUM_CONSTANT(MENU_INSERT_ALM); + BIND_ENUM_CONSTANT(MENU_INSERT_LRI); + BIND_ENUM_CONSTANT(MENU_INSERT_RLI); + BIND_ENUM_CONSTANT(MENU_INSERT_FSI); + BIND_ENUM_CONSTANT(MENU_INSERT_PDI); + BIND_ENUM_CONSTANT(MENU_INSERT_ZWJ); + BIND_ENUM_CONSTANT(MENU_INSERT_ZWNJ); + BIND_ENUM_CONSTANT(MENU_INSERT_WJ); + BIND_ENUM_CONSTANT(MENU_INSERT_SHY); BIND_ENUM_CONSTANT(MENU_MAX); GLOBAL_DEF("gui/timers/text_edit_idle_detect_sec", 3); @@ -6868,8 +7188,8 @@ TextEdit::TextEdit() { wrap_right_offset = 10; set_focus_mode(FOCUS_ALL); _update_caches(); - cache.row_height = 1; cache.line_spacing = 1; + cache.font_size = 16; set_default_cursor_shape(CURSOR_IBEAM); indent_size = 4; @@ -6969,9 +7289,43 @@ TextEdit::TextEdit() { shortcut_keys_enabled = true; menu = memnew(PopupMenu); add_child(menu); + + menu_dir = memnew(PopupMenu); + menu_dir->set_name("DirMenu"); + menu_dir->add_radio_check_item(RTR("Same as layout direction"), MENU_DIR_INHERITED); + menu_dir->add_radio_check_item(RTR("Auto-detect direction"), MENU_DIR_AUTO); + menu_dir->add_radio_check_item(RTR("Left-to-right"), MENU_DIR_LTR); + menu_dir->add_radio_check_item(RTR("Right-to-left"), MENU_DIR_RTL); + menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), true); + menu->add_child(menu_dir); + + menu_ctl = memnew(PopupMenu); + menu_ctl->set_name("CTLMenu"); + menu_ctl->add_item(RTR("Left-to-right mark (LRM)"), MENU_INSERT_LRM); + menu_ctl->add_item(RTR("Right-to-left mark (RLM)"), MENU_INSERT_RLM); + menu_ctl->add_item(RTR("Start of left-to-right embedding (LRE)"), MENU_INSERT_LRE); + menu_ctl->add_item(RTR("Start of right-to-left embedding (RLE)"), MENU_INSERT_RLE); + menu_ctl->add_item(RTR("Start of left-to-right override (LRO)"), MENU_INSERT_LRO); + menu_ctl->add_item(RTR("Start of right-to-left override (RLO)"), MENU_INSERT_RLO); + menu_ctl->add_item(RTR("Pop direction formatting (PDF)"), MENU_INSERT_PDF); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Arabic letter mark (ALM)"), MENU_INSERT_ALM); + menu_ctl->add_item(RTR("Left-to-right isolate (LRI)"), MENU_INSERT_LRI); + menu_ctl->add_item(RTR("Right-to-left isolate (RLI)"), MENU_INSERT_RLI); + menu_ctl->add_item(RTR("First strong isolate (FSI)"), MENU_INSERT_FSI); + menu_ctl->add_item(RTR("Pop direction isolate (PDI)"), MENU_INSERT_PDI); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Zero width joiner (ZWJ)"), MENU_INSERT_ZWJ); + menu_ctl->add_item(RTR("Zero width non-joiner (ZWNJ)"), MENU_INSERT_ZWNJ); + menu_ctl->add_item(RTR("Word joiner (WJ)"), MENU_INSERT_WJ); + menu_ctl->add_item(RTR("Soft hyphen (SHY)"), MENU_INSERT_SHY); + menu->add_child(menu_ctl); + readonly = true; // Initialise to opposite first, so we get past the early-out in set_readonly. set_readonly(false); menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); + menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); + menu_ctl->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); first_draw = true; } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 5cfa70bc55..d5b9b46fe2 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,6 +36,7 @@ #include "scene/gui/scroll_bar.h" #include "scene/main/timer.h" #include "scene/resources/syntax_highlighter.h" +#include "scene/resources/text_paragraph.h" class TextEdit : public Control { GDCLASS(TextEdit, Control); @@ -87,47 +88,68 @@ private: struct Line { Vector<Gutter> gutters; - int32_t width_cache; + String data; + Vector<Vector2i> bidi_override; + Ref<TextParagraph> data_buf; + bool marked; bool hidden; - int32_t wrap_amount_cache; - String data; + Line() { - width_cache = 0; + data_buf.instance(); + marked = false; hidden = false; - wrap_amount_cache = 0; } }; private: mutable Vector<Line> text; Ref<Font> font; + int font_size = -1; + + Dictionary opentype_features; + String language; + TextServer::Direction direction = TextServer::DIRECTION_AUTO; + bool draw_control_chars = false; + + int width = -1; + int indent_size = 4; int gutter_count = 0; - void _update_line_cache(int p_line) const; - public: void set_indent_size(int p_indent_size); void set_font(const Ref<Font> &p_font); + void set_font_size(int p_font_size); + void set_font_features(const Dictionary &p_features); + void set_direction_and_language(TextServer::Direction p_direction, String p_language); + void set_draw_control_chars(bool p_draw_control_chars); + + int get_line_height(int p_line, int p_wrap_index) const; int get_line_width(int p_line) const; int get_max_width(bool p_exclude_hidden = false) const; - int get_char_width(char32_t c, char32_t next_c, int px) const; - void set_line_wrap_amount(int p_line, int p_wrap_amount) const; + + void set_width(float p_width); int get_line_wrap_amount(int p_line) const; - void set(int p_line, const String &p_text); + Vector<Vector2i> get_line_wrap_ranges(int p_line) const; + const Ref<TextParagraph> get_line_data(int p_line) const; + + void set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override); void set_marked(int p_line, bool p_marked) { text.write[p_line].marked = p_marked; } bool is_marked(int p_line) const { return text[p_line].marked; } void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; } bool is_hidden(int p_line) const { return text[p_line].hidden; } - void insert(int p_at, const String &p_text); + void insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override); void remove(int p_at); int size() const { return text.size(); } void clear(); - void clear_width_cache(); - void clear_wrap_cache(); - _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; } + + void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Vector<Vector2i> &p_bidi_override = Vector<Vector2i>()); + void invalidate_all(); + void invalidate_all_lines(); + + _FORCE_INLINE_ const String &operator[](int p_line) const; /* Gutters. */ void add_gutter(int p_at); @@ -260,6 +282,14 @@ private: // data Text text; + Dictionary opentype_features; + String language; + TextDirection text_direction = TEXT_DIRECTION_AUTO; + TextDirection input_direction = TEXT_DIRECTION_LTR; + Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + Array st_args; + bool draw_control_chars = false; + uint32_t version; uint32_t saved_version; @@ -275,6 +305,7 @@ private: bool window_has_focus; bool block_caret; bool right_click_moves_caret; + bool mid_grapheme_caret_enabled = false; bool wrap_enabled; int wrap_at; @@ -356,7 +387,7 @@ private: int _get_minimap_visible_rows() const; void update_cursor_wrap_offset(); - void _update_wrap_at(); + void _update_wrap_at(bool p_force = false); bool line_wraps(int line) const; int times_line_wraps(int line) const; Vector<String> get_wrap_rows_text(int p_line) const; @@ -376,8 +407,6 @@ private: int get_char_pos_for_line(int p_px, int p_line, int p_wrap_index = 0) const; int get_column_x_offset_for_line(int p_char, int p_line) const; - int get_char_pos_for(int p_px, String p_str) const; - int get_column_x_offset(int p_char, String p_str) const; void adjust_viewport_to_cursor(); double get_scroll_line_diff() const; @@ -405,6 +434,8 @@ private: Size2 get_minimum_size() const override; int _get_control_height() const; + Point2 _get_local_mouse_pos() const; + void _reset_caret_blink_timer(); void _toggle_draw_caret(); @@ -425,6 +456,8 @@ private: Dictionary _search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const; PopupMenu *menu; + PopupMenu *menu_dir; + PopupMenu *menu_ctl; void _clear(); void _cancel_completion(); @@ -444,6 +477,7 @@ protected: Ref<StyleBox> style_focus; Ref<StyleBox> style_readonly; Ref<Font> font; + int font_size; Color completion_background_color; Color completion_selected_color; Color completion_existing_color; @@ -464,11 +498,9 @@ protected: Color search_result_border_color; Color background_color; - int row_height; int line_spacing; int minimap_width; Cache() { - row_height = 0; line_spacing = 0; minimap_width = 0; } @@ -487,6 +519,10 @@ protected: static void _bind_methods(); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + public: /* Syntax Highlighting. */ Ref<SyntaxHighlighter> get_syntax_highlighter(); @@ -541,6 +577,27 @@ public: MENU_SELECT_ALL, MENU_UNDO, MENU_REDO, + MENU_DIR_INHERITED, + MENU_DIR_AUTO, + MENU_DIR_LTR, + MENU_DIR_RTL, + MENU_DISPLAY_UCC, + MENU_INSERT_LRM, + MENU_INSERT_RLM, + MENU_INSERT_LRE, + MENU_INSERT_RLE, + MENU_INSERT_LRO, + MENU_INSERT_RLO, + MENU_INSERT_PDF, + MENU_INSERT_ALM, + MENU_INSERT_LRI, + MENU_INSERT_RLI, + MENU_INSERT_FSI, + MENU_INSERT_PDI, + MENU_INSERT_ZWJ, + MENU_INSERT_ZWNJ, + MENU_INSERT_WJ, + MENU_INSERT_SHY, MENU_MAX }; @@ -564,6 +621,25 @@ public: bool is_insert_text_operation(); + void set_text_direction(TextDirection p_text_direction); + TextDirection get_text_direction() const; + + void set_opentype_feature(const String &p_name, int p_value); + int get_opentype_feature(const String &p_name) const; + void clear_opentype_features(); + + void set_language(const String &p_language); + String get_language() const; + + void set_draw_control_chars(bool p_draw_control_chars); + bool get_draw_control_chars() const; + + void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); + Control::StructuredTextParser get_structured_text_bidi_override() const; + + void set_structured_text_bidi_override_options(Array p_args); + Array get_structured_text_bidi_override_options() const; + void set_highlighted_word(const String &new_word); void set_text(String p_text); void insert_text_at_cursor(const String &p_text); @@ -616,12 +692,15 @@ public: void center_viewport_to_cursor(); + void set_mid_grapheme_caret_enabled(const bool p_enabled); + bool get_mid_grapheme_caret_enabled() const; + void cursor_set_column(int p_col, bool p_adjust_viewport = true); void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true, int p_wrap_index = 0); int cursor_get_column() const; int cursor_get_line() const; - Vector2i _get_cursor_pixel_pos(); + Vector2i _get_cursor_pixel_pos(bool p_adjust_viewport = true); bool cursor_get_blink_enabled() const; void cursor_set_blink_enabled(const bool p_enabled); diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index 4187d77083..23c48b0906 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h index 6f7ee65ae4..4b750c3fb1 100644 --- a/scene/gui/texture_button.h +++ b/scene/gui/texture_button.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress_bar.cpp index e0d98d1c22..76564e2b17 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* texture_progress.cpp */ +/* texture_progress_bar.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,21 +28,21 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "texture_progress.h" +#include "texture_progress_bar.h" #include "core/config/engine.h" -void TextureProgress::set_under_texture(const Ref<Texture2D> &p_texture) { +void TextureProgressBar::set_under_texture(const Ref<Texture2D> &p_texture) { under = p_texture; update(); minimum_size_changed(); } -Ref<Texture2D> TextureProgress::get_under_texture() const { +Ref<Texture2D> TextureProgressBar::get_under_texture() const { return under; } -void TextureProgress::set_over_texture(const Ref<Texture2D> &p_texture) { +void TextureProgressBar::set_over_texture(const Ref<Texture2D> &p_texture) { over = p_texture; update(); if (under.is_null()) { @@ -50,35 +50,35 @@ void TextureProgress::set_over_texture(const Ref<Texture2D> &p_texture) { } } -Ref<Texture2D> TextureProgress::get_over_texture() const { +Ref<Texture2D> TextureProgressBar::get_over_texture() const { return over; } -void TextureProgress::set_stretch_margin(Margin p_margin, int p_size) { - ERR_FAIL_INDEX((int)p_margin, 4); - stretch_margin[p_margin] = p_size; +void TextureProgressBar::set_stretch_margin(Side p_side, int p_size) { + ERR_FAIL_INDEX((int)p_side, 4); + stretch_margin[p_side] = p_size; update(); minimum_size_changed(); } -int TextureProgress::get_stretch_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, 0); - return stretch_margin[p_margin]; +int TextureProgressBar::get_stretch_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); + return stretch_margin[p_side]; } -void TextureProgress::set_nine_patch_stretch(bool p_stretch) { +void TextureProgressBar::set_nine_patch_stretch(bool p_stretch) { nine_patch_stretch = p_stretch; update(); minimum_size_changed(); } -bool TextureProgress::get_nine_patch_stretch() const { +bool TextureProgressBar::get_nine_patch_stretch() const { return nine_patch_stretch; } -Size2 TextureProgress::get_minimum_size() const { +Size2 TextureProgressBar::get_minimum_size() const { if (nine_patch_stretch) { - return Size2(stretch_margin[MARGIN_LEFT] + stretch_margin[MARGIN_RIGHT], stretch_margin[MARGIN_TOP] + stretch_margin[MARGIN_BOTTOM]); + return Size2(stretch_margin[SIDE_LEFT] + stretch_margin[SIDE_RIGHT], stretch_margin[SIDE_TOP] + stretch_margin[SIDE_BOTTOM]); } else if (under.is_valid()) { return under->get_size(); } else if (over.is_valid()) { @@ -90,44 +90,44 @@ Size2 TextureProgress::get_minimum_size() const { return Size2(1, 1); } -void TextureProgress::set_progress_texture(const Ref<Texture2D> &p_texture) { +void TextureProgressBar::set_progress_texture(const Ref<Texture2D> &p_texture) { progress = p_texture; update(); minimum_size_changed(); } -Ref<Texture2D> TextureProgress::get_progress_texture() const { +Ref<Texture2D> TextureProgressBar::get_progress_texture() const { return progress; } -void TextureProgress::set_tint_under(const Color &p_tint) { +void TextureProgressBar::set_tint_under(const Color &p_tint) { tint_under = p_tint; update(); } -Color TextureProgress::get_tint_under() const { +Color TextureProgressBar::get_tint_under() const { return tint_under; } -void TextureProgress::set_tint_progress(const Color &p_tint) { +void TextureProgressBar::set_tint_progress(const Color &p_tint) { tint_progress = p_tint; update(); } -Color TextureProgress::get_tint_progress() const { +Color TextureProgressBar::get_tint_progress() const { return tint_progress; } -void TextureProgress::set_tint_over(const Color &p_tint) { +void TextureProgressBar::set_tint_over(const Color &p_tint) { tint_over = p_tint; update(); } -Color TextureProgress::get_tint_over() const { +Color TextureProgressBar::get_tint_over() const { return tint_over; } -Point2 TextureProgress::unit_val_to_uv(float val) { +Point2 TextureProgressBar::unit_val_to_uv(float val) { if (progress.is_null()) { return Point2(); } @@ -191,7 +191,7 @@ Point2 TextureProgress::unit_val_to_uv(float val) { return (p + t1 * dir); } -Point2 TextureProgress::get_relative_center() { +Point2 TextureProgressBar::get_relative_center() { if (progress.is_null()) { return Point2(); } @@ -204,10 +204,10 @@ Point2 TextureProgress::get_relative_center() { return p; } -void TextureProgress::draw_nine_patch_stretched(const Ref<Texture2D> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate) { +void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate) { Vector2 texture_size = p_texture->get_size(); - Vector2 topleft = Vector2(stretch_margin[MARGIN_LEFT], stretch_margin[MARGIN_TOP]); - Vector2 bottomright = Vector2(stretch_margin[MARGIN_RIGHT], stretch_margin[MARGIN_BOTTOM]); + Vector2 topleft = Vector2(stretch_margin[SIDE_LEFT], stretch_margin[SIDE_TOP]); + Vector2 bottomright = Vector2(stretch_margin[SIDE_RIGHT], stretch_margin[SIDE_BOTTOM]); Rect2 src_rect = Rect2(Point2(), texture_size); Rect2 dst_rect = Rect2(Point2(), get_size()); @@ -306,7 +306,7 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture2D> &p_texture, RS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright, RS::NINE_PATCH_STRETCH, RS::NINE_PATCH_STRETCH, true, p_modulate); } -void TextureProgress::_notification(int p_what) { +void TextureProgressBar::_notification(int p_what) { const float corners[12] = { -0.125, -0.375, -0.625, -0.875, 0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875 }; switch (p_what) { case NOTIFICATION_DRAW: { @@ -428,17 +428,17 @@ void TextureProgress::_notification(int p_what) { } } -void TextureProgress::set_fill_mode(int p_fill) { +void TextureProgressBar::set_fill_mode(int p_fill) { ERR_FAIL_INDEX(p_fill, 9); mode = (FillMode)p_fill; update(); } -int TextureProgress::get_fill_mode() { +int TextureProgressBar::get_fill_mode() { return mode; } -void TextureProgress::set_radial_initial_angle(float p_angle) { +void TextureProgressBar::set_radial_initial_angle(float p_angle) { while (p_angle > 360) { p_angle -= 360; } @@ -449,64 +449,64 @@ void TextureProgress::set_radial_initial_angle(float p_angle) { update(); } -float TextureProgress::get_radial_initial_angle() { +float TextureProgressBar::get_radial_initial_angle() { return rad_init_angle; } -void TextureProgress::set_fill_degrees(float p_angle) { +void TextureProgressBar::set_fill_degrees(float p_angle) { rad_max_degrees = CLAMP(p_angle, 0, 360); update(); } -float TextureProgress::get_fill_degrees() { +float TextureProgressBar::get_fill_degrees() { return rad_max_degrees; } -void TextureProgress::set_radial_center_offset(const Point2 &p_off) { +void TextureProgressBar::set_radial_center_offset(const Point2 &p_off) { rad_center_off = p_off; update(); } -Point2 TextureProgress::get_radial_center_offset() { +Point2 TextureProgressBar::get_radial_center_offset() { return rad_center_off; } -void TextureProgress::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_under_texture", "tex"), &TextureProgress::set_under_texture); - ClassDB::bind_method(D_METHOD("get_under_texture"), &TextureProgress::get_under_texture); +void TextureProgressBar::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_under_texture", "tex"), &TextureProgressBar::set_under_texture); + ClassDB::bind_method(D_METHOD("get_under_texture"), &TextureProgressBar::get_under_texture); - ClassDB::bind_method(D_METHOD("set_progress_texture", "tex"), &TextureProgress::set_progress_texture); - ClassDB::bind_method(D_METHOD("get_progress_texture"), &TextureProgress::get_progress_texture); + ClassDB::bind_method(D_METHOD("set_progress_texture", "tex"), &TextureProgressBar::set_progress_texture); + ClassDB::bind_method(D_METHOD("get_progress_texture"), &TextureProgressBar::get_progress_texture); - ClassDB::bind_method(D_METHOD("set_over_texture", "tex"), &TextureProgress::set_over_texture); - ClassDB::bind_method(D_METHOD("get_over_texture"), &TextureProgress::get_over_texture); + ClassDB::bind_method(D_METHOD("set_over_texture", "tex"), &TextureProgressBar::set_over_texture); + ClassDB::bind_method(D_METHOD("get_over_texture"), &TextureProgressBar::get_over_texture); - ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &TextureProgress::set_fill_mode); - ClassDB::bind_method(D_METHOD("get_fill_mode"), &TextureProgress::get_fill_mode); + ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &TextureProgressBar::set_fill_mode); + ClassDB::bind_method(D_METHOD("get_fill_mode"), &TextureProgressBar::get_fill_mode); - ClassDB::bind_method(D_METHOD("set_tint_under", "tint"), &TextureProgress::set_tint_under); - ClassDB::bind_method(D_METHOD("get_tint_under"), &TextureProgress::get_tint_under); + ClassDB::bind_method(D_METHOD("set_tint_under", "tint"), &TextureProgressBar::set_tint_under); + ClassDB::bind_method(D_METHOD("get_tint_under"), &TextureProgressBar::get_tint_under); - ClassDB::bind_method(D_METHOD("set_tint_progress", "tint"), &TextureProgress::set_tint_progress); - ClassDB::bind_method(D_METHOD("get_tint_progress"), &TextureProgress::get_tint_progress); + ClassDB::bind_method(D_METHOD("set_tint_progress", "tint"), &TextureProgressBar::set_tint_progress); + ClassDB::bind_method(D_METHOD("get_tint_progress"), &TextureProgressBar::get_tint_progress); - ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgress::set_tint_over); - ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgress::get_tint_over); + ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgressBar::set_tint_over); + ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgressBar::get_tint_over); - ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgress::set_radial_initial_angle); - ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgress::get_radial_initial_angle); + ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgressBar::set_radial_initial_angle); + ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgressBar::get_radial_initial_angle); - ClassDB::bind_method(D_METHOD("set_radial_center_offset", "mode"), &TextureProgress::set_radial_center_offset); - ClassDB::bind_method(D_METHOD("get_radial_center_offset"), &TextureProgress::get_radial_center_offset); + ClassDB::bind_method(D_METHOD("set_radial_center_offset", "mode"), &TextureProgressBar::set_radial_center_offset); + ClassDB::bind_method(D_METHOD("get_radial_center_offset"), &TextureProgressBar::get_radial_center_offset); - ClassDB::bind_method(D_METHOD("set_fill_degrees", "mode"), &TextureProgress::set_fill_degrees); - ClassDB::bind_method(D_METHOD("get_fill_degrees"), &TextureProgress::get_fill_degrees); + ClassDB::bind_method(D_METHOD("set_fill_degrees", "mode"), &TextureProgressBar::set_fill_degrees); + ClassDB::bind_method(D_METHOD("get_fill_degrees"), &TextureProgressBar::get_fill_degrees); - ClassDB::bind_method(D_METHOD("set_stretch_margin", "margin", "value"), &TextureProgress::set_stretch_margin); - ClassDB::bind_method(D_METHOD("get_stretch_margin", "margin"), &TextureProgress::get_stretch_margin); + ClassDB::bind_method(D_METHOD("set_stretch_margin", "margin", "value"), &TextureProgressBar::set_stretch_margin); + ClassDB::bind_method(D_METHOD("get_stretch_margin", "margin"), &TextureProgressBar::get_stretch_margin); - ClassDB::bind_method(D_METHOD("set_nine_patch_stretch", "stretch"), &TextureProgress::set_nine_patch_stretch); - ClassDB::bind_method(D_METHOD("get_nine_patch_stretch"), &TextureProgress::get_nine_patch_stretch); + ClassDB::bind_method(D_METHOD("set_nine_patch_stretch", "stretch"), &TextureProgressBar::set_nine_patch_stretch); + ClassDB::bind_method(D_METHOD("get_nine_patch_stretch"), &TextureProgressBar::get_nine_patch_stretch); ADD_GROUP("Textures", "texture_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_under_texture", "get_under_texture"); @@ -523,10 +523,10 @@ void TextureProgress::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "radial_center_offset"), "set_radial_center_offset", "get_radial_center_offset"); ADD_GROUP("Stretch", "stretch_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "nine_patch_stretch"), "set_nine_patch_stretch", "get_nine_patch_stretch"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", MARGIN_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_stretch_margin", "get_stretch_margin", SIDE_BOTTOM); BIND_ENUM_CONSTANT(FILL_LEFT_TO_RIGHT); BIND_ENUM_CONSTANT(FILL_RIGHT_TO_LEFT); @@ -539,7 +539,7 @@ void TextureProgress::_bind_methods() { BIND_ENUM_CONSTANT(FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE); } -TextureProgress::TextureProgress() { +TextureProgressBar::TextureProgressBar() { mode = FILL_LEFT_TO_RIGHT; rad_init_angle = 0; rad_center_off = Point2(); @@ -547,10 +547,10 @@ TextureProgress::TextureProgress() { set_mouse_filter(MOUSE_FILTER_PASS); nine_patch_stretch = false; - stretch_margin[MARGIN_LEFT] = 0; - stretch_margin[MARGIN_RIGHT] = 0; - stretch_margin[MARGIN_BOTTOM] = 0; - stretch_margin[MARGIN_TOP] = 0; + stretch_margin[SIDE_LEFT] = 0; + stretch_margin[SIDE_RIGHT] = 0; + stretch_margin[SIDE_BOTTOM] = 0; + stretch_margin[SIDE_TOP] = 0; tint_under = tint_progress = tint_over = Color(1, 1, 1); } diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress_bar.h index 5e29fca21f..ee33a915f7 100644 --- a/scene/gui/texture_progress.h +++ b/scene/gui/texture_progress_bar.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* texture_progress.h */ +/* texture_progress_bar.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TEXTURE_PROGRESS_H -#define TEXTURE_PROGRESS_H +#ifndef TEXTURE_PROGRESS_BAR_H +#define TEXTURE_PROGRESS_BAR_H #include "scene/gui/range.h" -class TextureProgress : public Range { - GDCLASS(TextureProgress, Range); +class TextureProgressBar : public Range { + GDCLASS(TextureProgressBar, Range); Ref<Texture2D> under; Ref<Texture2D> progress; @@ -78,8 +78,8 @@ public: void set_over_texture(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_over_texture() const; - void set_stretch_margin(Margin p_margin, int p_size); - int get_stretch_margin(Margin p_margin) const; + void set_stretch_margin(Side p_side, int p_size); + int get_stretch_margin(Side p_side) const; void set_nine_patch_stretch(bool p_stretch); bool get_nine_patch_stretch() const; @@ -95,7 +95,7 @@ public: Size2 get_minimum_size() const override; - TextureProgress(); + TextureProgressBar(); private: FillMode mode; @@ -111,6 +111,6 @@ private: void draw_nine_patch_stretched(const Ref<Texture2D> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate); }; -VARIANT_ENUM_CAST(TextureProgress::FillMode); +VARIANT_ENUM_CAST(TextureProgressBar::FillMode); -#endif // TEXTURE_PROGRESS_H +#endif // TEXTURE_PROGRESS_BAR_H diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp index 58e7249284..beb06b7739 100644 --- a/scene/gui/texture_rect.cpp +++ b/scene/gui/texture_rect.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h index e39545f679..0f3172f5bd 100644 --- a/scene/gui/texture_rect.h +++ b/scene/gui/texture_rect.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 31030765e0..a968a83dad 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,6 +36,7 @@ #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/string/print_string.h" +#include "core/string/translation.h" #include "scene/main/window.h" #include "box_container.h" @@ -129,6 +130,7 @@ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { c.checked = false; c.icon = Ref<Texture2D>(); c.text = ""; + c.dirty = true; c.icon_max_w = 0; _changed_notify(p_column); } @@ -153,6 +155,7 @@ bool TreeItem::is_checked(int p_column) const { void TreeItem::set_text(int p_column, String p_text) { ERR_FAIL_INDEX(p_column, cells.size()); cells.write[p_column].text = p_text; + cells.write[p_column].dirty = true; if (cells[p_column].mode == TreeItem::CELL_MODE_RANGE) { Vector<String> strings = p_text.split(","); @@ -160,7 +163,7 @@ void TreeItem::set_text(int p_column, String p_text) { cells.write[p_column].max = INT_MIN; for (int i = 0; i < strings.size(); i++) { int value = i; - if (!strings[i].get_slicec(':', 1).empty()) { + if (!strings[i].get_slicec(':', 1).is_empty()) { value = strings[i].get_slicec(':', 1).to_int(); } cells.write[p_column].min = MIN(cells[p_column].min, value); @@ -176,6 +179,87 @@ String TreeItem::get_text(int p_column) const { return cells[p_column].text; } +void TreeItem::set_text_direction(int p_column, Control::TextDirection p_text_direction) { + ERR_FAIL_INDEX(p_column, cells.size()); + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (cells[p_column].text_direction != p_text_direction) { + cells.write[p_column].text_direction = p_text_direction; + cells.write[p_column].dirty = true; + _changed_notify(p_column); + } +} + +Control::TextDirection TreeItem::get_text_direction(int p_column) const { + ERR_FAIL_INDEX_V(p_column, cells.size(), Control::TEXT_DIRECTION_INHERITED); + return cells[p_column].text_direction; +} + +void TreeItem::clear_opentype_features(int p_column) { + ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].opentype_features.clear(); + cells.write[p_column].dirty = true; + _changed_notify(p_column); +} + +void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_value) { + ERR_FAIL_INDEX(p_column, cells.size()); + int32_t tag = TS->name_to_tag(p_name); + if (!cells[p_column].opentype_features.has(tag) || (int)cells[p_column].opentype_features[tag] != p_value) { + cells.write[p_column].opentype_features[tag] = p_value; + cells.write[p_column].dirty = true; + _changed_notify(p_column); + } +} + +int TreeItem::get_opentype_feature(int p_column, const String &p_name) const { + ERR_FAIL_INDEX_V(p_column, cells.size(), -1); + int32_t tag = TS->name_to_tag(p_name); + if (!cells[p_column].opentype_features.has(tag)) { + return -1; + } + return cells[p_column].opentype_features[tag]; +} + +void TreeItem::set_structured_text_bidi_override(int p_column, Control::StructuredTextParser p_parser) { + ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].st_parser != p_parser) { + cells.write[p_column].st_parser = p_parser; + cells.write[p_column].dirty = true; + _changed_notify(p_column); + } +} + +Control::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_column) const { + ERR_FAIL_INDEX_V(p_column, cells.size(), Control::STRUCTURED_TEXT_NONE); + return cells[p_column].st_parser; +} + +void TreeItem::set_structured_text_bidi_override_options(int p_column, Array p_args) { + ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].st_args = p_args; + cells.write[p_column].dirty = true; + _changed_notify(p_column); +} + +Array TreeItem::get_structured_text_bidi_override_options(int p_column) const { + ERR_FAIL_INDEX_V(p_column, cells.size(), Array()); + return cells[p_column].st_args; +} + +void TreeItem::set_language(int p_column, const String &p_language) { + ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].language != p_language) { + cells.write[p_column].language = p_language; + cells.write[p_column].dirty = true; + _changed_notify(p_column); + } +} + +String TreeItem::get_language(int p_column) const { + ERR_FAIL_INDEX_V(p_column, cells.size(), ""); + return cells[p_column].language; +} + void TreeItem::set_suffix(int p_column, String p_suffix) { ERR_FAIL_INDEX(p_column, cells.size()); cells.write[p_column].suffix = p_suffix; @@ -236,7 +320,7 @@ int TreeItem::get_icon_max_width(int p_column) const { void TreeItem::set_range(int p_column, double p_value) { ERR_FAIL_INDEX(p_column, cells.size()); if (cells[p_column].step > 0) { - p_value = Math::stepify(p_value, cells[p_column].step); + p_value = Math::snapped(p_value, cells[p_column].step); } if (p_value < cells[p_column].min) { p_value = cells[p_column].min; @@ -246,6 +330,7 @@ void TreeItem::set_range(int p_column, double p_value) { } cells.write[p_column].val = p_value; + cells.write[p_column].dirty = true; _changed_notify(p_column); } @@ -512,12 +597,6 @@ String TreeItem::get_button_tooltip(int p_column, int p_idx) const { return cells[p_column].buttons[p_idx].tooltip; } -int TreeItem::get_button_id(int p_column, int p_idx) const { - ERR_FAIL_INDEX_V(p_column, cells.size(), -1); - ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), -1); - return cells[p_column].buttons[p_idx].id; -} - void TreeItem::erase_button(int p_column, int p_idx) { ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); @@ -719,6 +798,22 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text", "column", "text"), &TreeItem::set_text); ClassDB::bind_method(D_METHOD("get_text", "column"), &TreeItem::get_text); + ClassDB::bind_method(D_METHOD("set_text_direction", "column", "direction"), &TreeItem::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction", "column"), &TreeItem::get_text_direction); + + ClassDB::bind_method(D_METHOD("set_opentype_feature", "column", "tag", "value"), &TreeItem::set_opentype_feature); + ClassDB::bind_method(D_METHOD("get_opentype_feature", "column", "tag"), &TreeItem::get_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_opentype_features", "column"), &TreeItem::clear_opentype_features); + + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "column", "parser"), &TreeItem::set_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override", "column"), &TreeItem::get_structured_text_bidi_override); + + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "column", "args"), &TreeItem::set_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options", "column"), &TreeItem::get_structured_text_bidi_override_options); + + ClassDB::bind_method(D_METHOD("set_language", "column", "language"), &TreeItem::set_language); + ClassDB::bind_method(D_METHOD("get_language", "column"), &TreeItem::get_language); + ClassDB::bind_method(D_METHOD("set_suffix", "column", "text"), &TreeItem::set_suffix); ClassDB::bind_method(D_METHOD("get_suffix", "column"), &TreeItem::get_suffix); @@ -896,7 +991,9 @@ TreeItem::~TreeItem() { void Tree::update_cache() { cache.font = get_theme_font("font"); + cache.font_size = get_theme_font_size("font_size"); cache.tb_font = get_theme_font("title_button_font"); + cache.tb_font_size = get_theme_font_size("title_button_font_size"); cache.bg = get_theme_stylebox("bg"); cache.selected = get_theme_stylebox("selected"); cache.selected_focus = get_theme_stylebox("selected_focus"); @@ -906,7 +1003,11 @@ void Tree::update_cache() { cache.checked = get_theme_icon("checked"); cache.unchecked = get_theme_icon("unchecked"); - cache.arrow_collapsed = get_theme_icon("arrow_collapsed"); + if (is_layout_rtl()) { + cache.arrow_collapsed = get_theme_icon("arrow_collapsed_mirrored"); + } else { + cache.arrow_collapsed = get_theme_icon("arrow_collapsed"); + } cache.arrow = get_theme_icon("arrow"); cache.select_arrow = get_theme_icon("select_arrow"); cache.updown = get_theme_icon("updown"); @@ -935,7 +1036,7 @@ void Tree::update_cache() { cache.title_button_hover = get_theme_stylebox("title_button_hover"); cache.title_button_color = get_theme_color("title_button_color"); - v_scroll->set_custom_step(cache.font->get_height()); + v_scroll->set_custom_step(cache.font->get_height(cache.font_size)); } int Tree::compute_item_height(TreeItem *p_item) const { @@ -944,9 +1045,13 @@ int Tree::compute_item_height(TreeItem *p_item) const { } ERR_FAIL_COND_V(cache.font.is_null(), 0); - int height = cache.font->get_height(); + int height = 0; for (int i = 0; i < columns.size(); i++) { + if (p_item->cells[i].dirty) { + const_cast<Tree *>(this)->update_item_cell(p_item, i); + } + height = MAX(height, p_item->cells[i].text_buf->get_size().y); for (int j = 0; j < p_item->cells[i].buttons.size(); j++) { Size2i s; // = cache.button_pressed->get_minimum_size(); s += p_item->cells[i].buttons[j].texture->get_size(); @@ -1013,39 +1118,53 @@ int Tree::get_item_height(TreeItem *p_item) const { return height; } -void Tree::draw_item_rect(const TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color) { +void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color) { ERR_FAIL_COND(cache.font.is_null()); Rect2i rect = p_rect; - Ref<Font> font = cache.font; - String text = p_cell.text; - if (p_cell.suffix != String()) { - text += " " + p_cell.suffix; - } + Size2 ts = p_cell.text_buf->get_size(); + bool rtl = is_layout_rtl(); int w = 0; if (!p_cell.icon.is_null()) { Size2i bmsize = p_cell.get_icon_size(); - if (p_cell.icon_max_w > 0 && bmsize.width > p_cell.icon_max_w) { bmsize.width = p_cell.icon_max_w; } w += bmsize.width + cache.hseparation; + if (rect.size.width > 0 && (w + ts.width) > rect.size.width) { + ts.width = rect.size.width - w; + } } - w += font->get_string_size(text).width; + w += ts.width; switch (p_cell.text_align) { case TreeItem::ALIGN_LEFT: - break; //do none + if (rtl) { + rect.position.x += MAX(0, (rect.size.width - w)); + } + break; case TreeItem::ALIGN_CENTER: rect.position.x += MAX(0, (rect.size.width - w) / 2); - break; //do none + break; case TreeItem::ALIGN_RIGHT: - rect.position.x += MAX(0, (rect.size.width - w)); - break; //do none + if (!rtl) { + rect.position.x += MAX(0, (rect.size.width - w)); + } + break; } RID ci = get_canvas_item(); + + if (rtl) { + Point2 draw_pos = rect.position; + draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) / 2.0); + p_cell.text_buf->set_width(MAX(0, rect.size.width)); + p_cell.text_buf->draw(ci, draw_pos, p_color); + rect.position.x += ts.width + cache.hseparation; + rect.size.x -= ts.width + cache.hseparation; + } + if (!p_cell.icon.is_null()) { Size2i bmsize = p_cell.get_icon_size(); @@ -1059,8 +1178,80 @@ void Tree::draw_item_rect(const TreeItem::Cell &p_cell, const Rect2i &p_rect, co rect.size.x -= bmsize.x + cache.hseparation; } - rect.position.y += Math::floor((rect.size.y - font->get_height()) / 2.0) + font->get_ascent(); - font->draw(ci, rect.position, text, p_color, MAX(0, rect.size.width)); + if (!rtl) { + Point2 draw_pos = rect.position; + draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) / 2.0); + p_cell.text_buf->set_width(MAX(0, rect.size.width)); + p_cell.text_buf->draw(ci, draw_pos, p_color); + } +} + +void Tree::update_column(int p_col) { + columns.write[p_col].text_buf->clear(); + if (columns[p_col].text_direction == Control::TEXT_DIRECTION_INHERITED) { + columns.write[p_col].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + columns.write[p_col].text_buf->set_direction((TextServer::Direction)columns[p_col].text_direction); + } + columns.write[p_col].text_buf->add_string(columns[p_col].title, cache.font, cache.font_size, columns[p_col].opentype_features, (columns[p_col].language != "") ? columns[p_col].language : TranslationServer::get_singleton()->get_tool_locale()); +} + +void Tree::update_item_cell(TreeItem *p_item, int p_col) { + String valtext; + + p_item->cells.write[p_col].text_buf->clear(); + if (p_item->cells[p_col].mode == TreeItem::CELL_MODE_RANGE) { + if (p_item->cells[p_col].text != "") { + if (!p_item->cells[p_col].editable) { + return; + } + + int option = (int)p_item->cells[p_col].val; + + valtext = RTR("(Other)"); + Vector<String> strings = p_item->cells[p_col].text.split(","); + for (int j = 0; j < strings.size(); j++) { + int value = j; + if (!strings[j].get_slicec(':', 1).is_empty()) { + value = strings[j].get_slicec(':', 1).to_int(); + } + if (option == value) { + valtext = strings[j].get_slicec(':', 0); + break; + } + } + + } else { + valtext = String::num(p_item->cells[p_col].val, Math::range_step_decimals(p_item->cells[p_col].step)); + } + } else { + valtext = p_item->cells[p_col].text; + } + + if (p_item->cells[p_col].suffix != String()) { + valtext += " " + p_item->cells[p_col].suffix; + } + + if (p_item->cells[p_col].text_direction == Control::TEXT_DIRECTION_INHERITED) { + p_item->cells.write[p_col].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + p_item->cells.write[p_col].text_buf->set_direction((TextServer::Direction)p_item->cells[p_col].text_direction); + } + p_item->cells.write[p_col].text_buf->add_string(valtext, cache.font, cache.font_size, p_item->cells[p_col].opentype_features, (p_item->cells[p_col].language != "") ? p_item->cells[p_col].language : TranslationServer::get_singleton()->get_tool_locale()); + TS->shaped_text_set_bidi_override(p_item->cells[p_col].text_buf->get_rid(), structured_text_parser(p_item->cells[p_col].st_parser, p_item->cells[p_col].st_args, valtext)); + p_item->cells.write[p_col].dirty = false; +} + +void Tree::update_item_cache(TreeItem *p_item) { + for (int i = 0; i < p_item->cells.size(); i++) { + update_item_cell(p_item, i); + } + + TreeItem *c = p_item->children; + while (c) { + update_item_cache(c); + c = c->next; + } } int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item) { @@ -1073,6 +1264,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int htotal = 0; int label_h = compute_item_height(p_item); + bool rtl = is_layout_rtl(); /* Calculate height of the label part */ label_h += cache.vseparation; @@ -1086,9 +1278,6 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 //if (p_item->get_parent()!=root || !hide_root) ERR_FAIL_COND_V(cache.font.is_null(), -1); - Ref<Font> font = cache.font; - - int font_ascent = font->get_ascent(); int ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); int skip2 = 0; @@ -1133,12 +1322,20 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item == p_item && cache.click_column == i && cache.click_index == j && !p_item->cells[i].buttons[j].disabled) { //being pressed - cache.button_pressed->draw(get_canvas_item(), Rect2(o, s)); + Point2 od = o; + if (rtl) { + od.x = get_size().width - od.x - s.x; + } + cache.button_pressed->draw(get_canvas_item(), Rect2(od, s)); } o.y += (label_h - s.height) / 2; o += cache.button_pressed->get_offset(); + if (rtl) { + o.x = get_size().width - o.x - b->get_width(); + } + b->draw(ci, o, p_item->cells[i].buttons[j].disabled ? Color(1, 1, 1, 0.5) : p_item->cells[i].buttons[j].color); w -= s.width + cache.button_margin; bw += s.width + cache.button_margin; @@ -1152,14 +1349,21 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } if (cache.draw_guides) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(cell_rect.position.x, cell_rect.position.y + cell_rect.size.height), cell_rect.position + cell_rect.size, cache.guide_color, 1); + Rect2 r = cell_rect; + if (rtl) { + r.position.x = get_size().width - r.position.x - r.size.x; + } + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(r.position.x, r.position.y + r.size.height), r.position + r.size, cache.guide_color, 1); } if (i == 0) { if (p_item->cells[0].selected && select_mode == SELECT_ROW) { - Rect2i row_rect = Rect2i(Point2i(cache.bg->get_margin(MARGIN_LEFT), item_rect.position.y), Size2i(get_size().width - cache.bg->get_minimum_size().width, item_rect.size.y)); + Rect2i row_rect = Rect2i(Point2i(cache.bg->get_margin(SIDE_LEFT), item_rect.position.y), Size2i(get_size().width - cache.bg->get_minimum_size().width, item_rect.size.y)); //Rect2 r = Rect2i(row_rect.pos,row_rect.size); - //r.grow(cache.selected->get_margin(MARGIN_LEFT)); + //r.grow(cache.selected->get_margin(SIDE_LEFT)); + if (rtl) { + row_rect.position.x = get_size().width - row_rect.position.x - row_rect.size.x; + } if (has_focus()) { cache.selected_focus->draw(ci, row_rect); } else { @@ -1171,16 +1375,12 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if ((select_mode == SELECT_ROW && selected_item == p_item) || p_item->cells[i].selected) { Rect2i r(cell_rect.position, cell_rect.size); - if (p_item->cells[i].text.size() > 0) { - float icon_width = p_item->cells[i].get_icon_size().width; - if (p_item->get_icon_max_width(i) > 0) { - icon_width = p_item->get_icon_max_width(i); - } - r.position.x += icon_width; - r.size.x -= icon_width; - } p_item->set_meta("__focus_rect", Rect2(r.position, r.size)); + if (rtl) { + r.position.x = get_size().width - r.position.x - r.size.x; + } + if (p_item->cells[i].selected) { if (has_focus()) { cache.selected_focus->draw(ci, r); @@ -1199,6 +1399,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 r.position.x -= cache.hseparation; r.size.x += cache.hseparation; } + if (rtl) { + r.position.x = get_size().width - r.position.x - r.size.x; + } if (p_item->cells[i].custom_bg_outline) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), p_item->cells[i].bg_color); RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y + r.size.y - 1, r.size.x, 1), p_item->cells[i].bg_color); @@ -1212,6 +1415,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (drop_mode_flags && drop_mode_over == p_item) { Rect2 r = cell_rect; bool has_parent = p_item->get_children() != nullptr; + if (rtl) { + r.position.x = get_size().width - r.position.x - r.size.x; + } if (drop_mode_section == -1 || has_parent || drop_mode_section == 0) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), cache.drop_position_color); @@ -1230,12 +1436,21 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 Color col = p_item->cells[i].custom_color ? p_item->cells[i].color : get_theme_color(p_item->cells[i].selected ? "font_color_selected" : "font_color"); Color icon_col = p_item->cells[i].icon_color; + if (p_item->cells[i].dirty) { + const_cast<Tree *>(this)->update_item_cell(p_item, i); + } + + if (rtl) { + item_rect.position.x = get_size().width - item_rect.position.x - item_rect.size.x; + } + Point2i text_pos = item_rect.position; - text_pos.y += Math::floor((item_rect.size.y - font->get_height()) / 2) + font_ascent; + text_pos.y += Math::floor((item_rect.size.y - p_item->cells[i].text_buf->get_size().y) / 2); + int text_width = p_item->cells[i].text_buf->get_size().x; switch (p_item->cells[i].mode) { case TreeItem::CELL_MODE_STRING: { - draw_item_rect(p_item->cells[i], item_rect, col, icon_col); + draw_item_rect(p_item->cells.write[i], item_rect, col, icon_col); } break; case TreeItem::CELL_MODE_CHECK: { Ref<Texture2D> checked = cache.checked; @@ -1256,7 +1471,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 item_rect.size.x -= check_w; item_rect.position.x += check_w; - draw_item_rect(p_item->cells[i], item_rect, col, icon_col); + draw_item_rect(p_item->cells.write[i], item_rect, col, icon_col); } break; case TreeItem::CELL_MODE_RANGE: { @@ -1265,28 +1480,15 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 break; } - int option = (int)p_item->cells[i].val; - - String s = RTR("(Other)"); - Vector<String> strings = p_item->cells[i].text.split(","); - for (int j = 0; j < strings.size(); j++) { - int value = j; - if (!strings[j].get_slicec(':', 1).empty()) { - value = strings[j].get_slicec(':', 1).to_int(); - } - if (option == value) { - s = strings[j].get_slicec(':', 0); - break; - } - } - - if (p_item->cells[i].suffix != String()) { - s += " " + p_item->cells[i].suffix; - } - Ref<Texture2D> downarrow = cache.select_arrow; + int cell_width = item_rect.size.x - downarrow->get_width(); - font->draw(ci, text_pos, s, col, item_rect.size.x - downarrow->get_width()); + p_item->cells.write[i].text_buf->set_width(cell_width); + if (rtl) { + p_item->cells[i].text_buf->draw(ci, text_pos + Vector2(cell_width - text_width, 0), col); + } else { + p_item->cells[i].text_buf->draw(ci, text_pos, col); + } Point2i arrow_pos = item_rect.position; arrow_pos.x += item_rect.size.x - downarrow->get_width(); @@ -1296,14 +1498,14 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } else { Ref<Texture2D> updown = cache.updown; - String valtext = String::num(p_item->cells[i].val, Math::range_step_decimals(p_item->cells[i].step)); + int cell_width = item_rect.size.x - updown->get_width(); - if (p_item->cells[i].suffix != String()) { - valtext += " " + p_item->cells[i].suffix; + if (rtl) { + p_item->cells[i].text_buf->draw(ci, text_pos + Vector2(cell_width - text_width, 0), col); + } else { + p_item->cells[i].text_buf->draw(ci, text_pos, col); } - font->draw(ci, text_pos, valtext, col, item_rect.size.x - updown->get_width()); - if (!p_item->cells[i].editable) { break; } @@ -1341,7 +1543,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } if (!p_item->cells[i].editable) { - draw_item_rect(p_item->cells[i], item_rect, col, icon_col); + draw_item_rect(p_item->cells.write[i], item_rect, col, icon_col); break; } @@ -1369,7 +1571,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 ir.position += cache.custom_button->get_offset(); } - draw_item_rect(p_item->cells[i], ir, col, icon_col); + draw_item_rect(p_item->cells.write[i], ir, col, icon_col); downarrow->draw(ci, arrow_pos); @@ -1383,6 +1585,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } if (select_mode == SELECT_MULTI && selected_item == p_item && selected_col == i) { + if (is_layout_rtl()) { + cell_rect.position.x = get_size().width - cell_rect.position.x - cell_rect.size.x; + } if (has_focus()) { cache.cursor->draw(ci, cell_rect); } else { @@ -1401,7 +1606,13 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 arrow = cache.arrow; } - arrow->draw(ci, p_pos + p_draw_ofs + Point2i(0, (label_h - arrow->get_height()) / 2) - cache.offset); + Point2 apos = p_pos + p_draw_ofs + Point2i(0, (label_h - arrow->get_height()) / 2) - cache.offset; + + if (rtl) { + apos.x = get_size().width - apos.x - arrow->get_width(); + } + + arrow->draw(ci, apos); } } @@ -1437,6 +1648,10 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs; if (root_pos.y + line_width >= 0) { + if (rtl) { + root_pos.x = get_size().width - root_pos.x; + parent_pos.x = get_size().width - parent_pos.x; + } RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x - Math::floor(line_width / 2), root_pos.y), cache.relationship_line_color, line_width); RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y), Point2i(parent_pos.x, prev_ofs), cache.relationship_line_color, line_width); } @@ -1672,7 +1887,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool return -1; } else if (col == 0) { int margin = x_ofs + cache.item_margin; //-cache.hseparation; - //int lm = cache.bg->get_margin(MARGIN_LEFT); + //int lm = cache.bg->get_margin(SIDE_LEFT); col_width -= margin; col_ofs += margin; x -= margin; @@ -1816,7 +2031,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool popup_menu->clear(); for (int i = 0; i < c.text.get_slice_count(","); i++) { String s = c.text.get_slicec(',', i); - popup_menu->add_item(s.get_slicec(':', 0), s.get_slicec(':', 1).empty() ? i : s.get_slicec(':', 1).to_int()); + popup_menu->add_item(s.get_slicec(':', 0), s.get_slicec(':', 1).is_empty() ? i : s.get_slicec(':', 1).to_int()); } popup_menu->set_size(Size2(col_width, 0)); @@ -1976,14 +2191,13 @@ void Tree::_text_editor_enter(String p_text) { case TreeItem::CELL_MODE_RANGE: { c.val = p_text.to_float(); if (c.step > 0) { - c.val = Math::stepify(c.val, c.step); + c.val = Math::snapped(c.val, c.step); } if (c.val < c.min) { c.val = c.min; } else if (c.val > c.max) { c.val = c.max; } - //popup_edited_item->edited_signal.call( popup_edited_item_col ); } break; default: { @@ -2349,8 +2563,13 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } Ref<StyleBox> bg = cache.bg; + bool rtl = is_layout_rtl(); - Point2 pos = mm->get_position() - bg->get_offset(); + Point2 pos = mm->get_position(); + if (rtl) { + pos.x = get_size().width - pos.x; + } + pos -= cache.bg->get_offset(); Cache::ClickType old_hover = cache.hover_type; int old_index = cache.hover_index; @@ -2375,6 +2594,9 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { if (root) { Point2 mpos = mm->get_position(); + if (rtl) { + mpos.x = get_size().width - mpos.x; + } mpos -= cache.bg->get_offset(); mpos.y -= _get_title_button_height(); if (mpos.y >= 0) { @@ -2431,6 +2653,9 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { if (!range_drag_enabled) { Vector2 cpos = mm->get_position(); + if (rtl) { + cpos.x = get_size().width - cpos.x; + } if (cpos.distance_to(pressing_pos) > 2) { range_drag_enabled = true; range_drag_capture_pos = cpos; @@ -2462,9 +2687,15 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { update_cache(); } + bool rtl = is_layout_rtl(); + if (!b->is_pressed()) { if (b->get_button_index() == BUTTON_LEFT) { - Point2 pos = b->get_position() - cache.bg->get_offset(); + Point2 pos = b->get_position(); + if (rtl) { + pos.x = get_size().width - pos.x; + } + pos -= cache.bg->get_offset(); if (show_column_titles) { pos.y -= _get_title_button_height(); @@ -2495,7 +2726,11 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { warp_mouse(range_drag_capture_pos); } else { Rect2 rect = get_selected()->get_meta("__focus_rect"); - if (rect.has_point(Point2(b->get_position().x, b->get_position().y))) { + Point2 mpos = b->get_position(); + if (rtl) { + mpos.x = get_size().width - mpos.x; + } + if (rect.has_point(mpos)) { if (!edit_selected()) { emit_signal("item_double_clicked"); } @@ -2540,7 +2775,11 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { case BUTTON_LEFT: { Ref<StyleBox> bg = cache.bg; - Point2 pos = b->get_position() - bg->get_offset(); + Point2 pos = b->get_position(); + if (rtl) { + pos.x = get_size().width - pos.x; + } + pos -= bg->get_offset(); cache.click_type = Cache::CLICK_NONE; if (show_column_titles) { pos.y -= _get_title_button_height(); @@ -2580,6 +2819,9 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { if (pressing_for_editor) { pressing_pos = b->get_position(); + if (rtl) { + pressing_pos.x = get_size().width - pressing_pos.x; + } } if (b->get_button_index() == BUTTON_RIGHT) { @@ -2643,7 +2885,11 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); double prev_h = h_scroll->get_value(); - h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8); + if (is_layout_rtl()) { + h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * -pan_gesture->get_delta().x / 8); + } else { + h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8); + } if (v_scroll->get_value() != prev_v || h_scroll->get_value() != prev_h) { accept_event(); @@ -2684,7 +2930,7 @@ bool Tree::edit_selected() { popup_menu->clear(); for (int i = 0; i < c.text.get_slice_count(","); i++) { String s2 = c.text.get_slicec(',', i); - popup_menu->add_item(s2.get_slicec(':', 0), s2.get_slicec(':', 1).empty() ? i : s2.get_slicec(':', 1).to_int()); + popup_menu->add_item(s2.get_slicec(':', 0), s2.get_slicec(':', 1).is_empty() ? i : s2.get_slicec(':', 1).to_int()); } popup_menu->set_size(Size2(rect.size.width, 0)); @@ -2759,8 +3005,8 @@ void Tree::update_scrollbars() { Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); - v_scroll->set_begin(Point2(size.width - vmin.width, cache.bg->get_margin(MARGIN_TOP))); - v_scroll->set_end(Point2(size.width, size.height - cache.bg->get_margin(MARGIN_TOP) - cache.bg->get_margin(MARGIN_BOTTOM))); + v_scroll->set_begin(Point2(size.width - vmin.width, cache.bg->get_margin(SIDE_TOP))); + v_scroll->set_end(Point2(size.width, size.height - cache.bg->get_margin(SIDE_TOP) - cache.bg->get_margin(SIDE_BOTTOM))); h_scroll->set_begin(Point2(0, size.height - hmin.height)); h_scroll->set_end(Point2(size.width - vmin.width, size.height)); @@ -2790,7 +3036,13 @@ void Tree::update_scrollbars() { int Tree::_get_title_button_height() const { ERR_FAIL_COND_V(cache.font.is_null() || cache.title_button.is_null(), 0); - return show_column_titles ? cache.font->get_height() + cache.title_button->get_minimum_size().height : 0; + int h = 0; + if (show_column_titles) { + for (int i = 0; i < columns.size(); i++) { + h = MAX(h, columns[i].text_buf->get_size().y + cache.title_button->get_minimum_size().height); + } + } + return h; } void Tree::_notification(int p_what) { @@ -2914,22 +3166,27 @@ void Tree::_notification(int p_what) { if (show_column_titles) { //title buttons - int ofs2 = cache.bg->get_margin(MARGIN_LEFT); + int ofs2 = cache.bg->get_margin(SIDE_LEFT); for (int i = 0; i < columns.size(); i++) { Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? cache.title_button_hover : cache.title_button); Ref<Font> f = cache.tb_font; - Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(MARGIN_TOP), get_column_width(i), tbh); + Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh); + if (is_layout_rtl()) { + tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x; + } sb->draw(ci, tbrect); ofs2 += tbrect.size.width; //text int clip_w = tbrect.size.width - sb->get_minimum_size().width; - f->draw_halign(ci, tbrect.position + Point2i(sb->get_offset().x, (tbrect.size.height - f->get_height()) / 2 + f->get_ascent()), HALIGN_CENTER, clip_w, columns[i].title, cache.title_button_color); + columns.write[i].text_buf->set_width(clip_w); + columns[i].text_buf->draw(ci, tbrect.position + Point2i(sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2, (tbrect.size.height - columns[i].text_buf->get_size().y) / 2), cache.title_button_color); } } } - if (p_what == NOTIFICATION_THEME_CHANGED) { + if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) { update_cache(); + _update_all(); } if (p_what == NOTIFICATION_RESIZED || p_what == NOTIFICATION_TRANSFORM_CHANGED) { @@ -2947,6 +3204,15 @@ void Tree::_notification(int p_what) { } } +void Tree::_update_all() { + for (int i = 0; i < columns.size(); i++) { + update_column(i); + } + if (root) { + update_item_cache(root); + } +} + Size2 Tree::get_minimum_size() const { return Size2(1, 1); } @@ -3022,6 +3288,9 @@ TreeItem *Tree::get_last_item() { void Tree::item_edited(int p_column, TreeItem *p_item, bool p_lmb) { edited_item = p_item; edited_col = p_column; + if (p_item != nullptr && p_column >= 0 && p_column < p_item->cells.size()) { + edited_item->cells.write[p_column].dirty = true; + } if (p_lmb) { emit_signal("item_edited"); } else { @@ -3030,6 +3299,9 @@ void Tree::item_edited(int p_column, TreeItem *p_item, bool p_lmb) { } void Tree::item_changed(int p_column, TreeItem *p_item) { + if (p_item != nullptr && p_column >= 0 && p_column < p_item->cells.size()) { + p_item->cells.write[p_column].dirty = true; + } update(); } @@ -3198,7 +3470,7 @@ int Tree::get_column_width(int p_column) const { Ref<StyleBox> bg = cache.bg; - int expand_area = get_size().width - (bg->get_margin(MARGIN_LEFT) + bg->get_margin(MARGIN_RIGHT)); + int expand_area = get_size().width - (bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT)); if (v_scroll->is_visible_in_tree()) { expand_area -= v_scroll->get_combined_minimum_size().width; @@ -3231,7 +3503,7 @@ void Tree::propagate_set_columns(TreeItem *p_item) { TreeItem *c = p_item->get_children(); while (c) { propagate_set_columns(c); - c = c->get_next(); + c = c->next; } } @@ -3387,7 +3659,11 @@ bool Tree::are_column_titles_visible() const { void Tree::set_column_title(int p_column, const String &p_title) { ERR_FAIL_INDEX(p_column, columns.size()); + if (cache.font.is_null()) { // avoid a strange case that may corrupt stuff + update_cache(); + } columns.write[p_column].title = p_title; + update_column(p_column); update(); } @@ -3396,6 +3672,61 @@ String Tree::get_column_title(int p_column) const { return columns[p_column].title; } +void Tree::set_column_title_direction(int p_column, Control::TextDirection p_text_direction) { + ERR_FAIL_INDEX(p_column, columns.size()); + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (columns[p_column].text_direction != p_text_direction) { + columns.write[p_column].text_direction = p_text_direction; + update_column(p_column); + update(); + } +} + +Control::TextDirection Tree::get_column_title_direction(int p_column) const { + ERR_FAIL_INDEX_V(p_column, columns.size(), TEXT_DIRECTION_INHERITED); + return columns[p_column].text_direction; +} + +void Tree::clear_column_title_opentype_features(int p_column) { + ERR_FAIL_INDEX(p_column, columns.size()); + columns.write[p_column].opentype_features.clear(); + update_column(p_column); + update(); +} + +void Tree::set_column_title_opentype_feature(int p_column, const String &p_name, int p_value) { + ERR_FAIL_INDEX(p_column, columns.size()); + int32_t tag = TS->name_to_tag(p_name); + if (!columns[p_column].opentype_features.has(tag) || (int)columns[p_column].opentype_features[tag] != p_value) { + columns.write[p_column].opentype_features[tag] = p_value; + update_column(p_column); + update(); + } +} + +int Tree::get_column_title_opentype_feature(int p_column, const String &p_name) const { + ERR_FAIL_INDEX_V(p_column, columns.size(), -1); + int32_t tag = TS->name_to_tag(p_name); + if (!columns[p_column].opentype_features.has(tag)) { + return -1; + } + return columns[p_column].opentype_features[tag]; +} + +void Tree::set_column_title_language(int p_column, const String &p_language) { + ERR_FAIL_INDEX(p_column, columns.size()); + if (columns[p_column].language != p_language) { + columns.write[p_column].language = p_language; + update_column(p_column); + update(); + } +} + +String Tree::get_column_title_language(int p_column) const { + ERR_FAIL_INDEX_V(p_column, columns.size(), ""); + return columns[p_column].language; +} + Point2 Tree::get_scroll() const { Point2 ofs; if (h_scroll->is_visible_in_tree()) { @@ -3561,6 +3892,9 @@ TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_ int Tree::get_column_at_position(const Point2 &p_pos) const { if (root) { Point2 pos = p_pos; + if (is_layout_rtl()) { + pos.x = get_size().width - pos.x; + } pos -= cache.bg->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { @@ -3588,6 +3922,9 @@ int Tree::get_column_at_position(const Point2 &p_pos) const { int Tree::get_drop_section_at_position(const Point2 &p_pos) const { if (root) { Point2 pos = p_pos; + if (is_layout_rtl()) { + pos.x = get_size().width - pos.x; + } pos -= cache.bg->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { @@ -3615,6 +3952,9 @@ int Tree::get_drop_section_at_position(const Point2 &p_pos) const { TreeItem *Tree::get_item_at_position(const Point2 &p_pos) const { if (root) { Point2 pos = p_pos; + if (is_layout_rtl()) { + pos.x = get_size().width - pos.x; + } pos -= cache.bg->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { @@ -3735,10 +4075,6 @@ void Tree::set_cursor_can_exit_tree(bool p_enable) { cursor_can_exit_tree = p_enable; } -bool Tree::can_cursor_exit_tree() const { - return cursor_can_exit_tree; -} - void Tree::set_hide_folding(bool p_hide) { hide_folding = p_hide; update(); @@ -3826,6 +4162,17 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_column_title", "column", "title"), &Tree::set_column_title); ClassDB::bind_method(D_METHOD("get_column_title", "column"), &Tree::get_column_title); + + ClassDB::bind_method(D_METHOD("set_column_title_direction", "column", "direction"), &Tree::set_column_title_direction); + ClassDB::bind_method(D_METHOD("get_column_title_direction", "column"), &Tree::get_column_title_direction); + + ClassDB::bind_method(D_METHOD("set_column_title_opentype_feature", "column", "tag", "value"), &Tree::set_column_title_opentype_feature); + ClassDB::bind_method(D_METHOD("get_column_title_opentype_feature", "column", "tag"), &Tree::get_column_title_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_column_title_opentype_features", "column"), &Tree::clear_column_title_opentype_features); + + ClassDB::bind_method(D_METHOD("set_column_title_language", "column", "language"), &Tree::set_column_title_language); + ClassDB::bind_method(D_METHOD("get_column_title_language", "column"), &Tree::get_column_title_language); + ClassDB::bind_method(D_METHOD("get_scroll"), &Tree::get_scroll); ClassDB::bind_method(D_METHOD("set_hide_folding", "hide"), &Tree::set_hide_folding); @@ -3902,7 +4249,7 @@ Tree::Tree() { popup_editor_vb = memnew(VBoxContainer); popup_editor->add_child(popup_editor_vb); popup_editor_vb->add_theme_constant_override("separation", 0); - popup_editor_vb->set_anchors_and_margins_preset(PRESET_WIDE); + popup_editor_vb->set_anchors_and_offsets_preset(PRESET_WIDE); text_editor = memnew(LineEdit); popup_editor_vb->add_child(text_editor); text_editor->set_v_size_flags(SIZE_EXPAND_FILL); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 9554bb4665..dfc02f760d 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,6 +36,7 @@ #include "scene/gui/popup_menu.h" #include "scene/gui/scroll_bar.h" #include "scene/gui/slider.h" +#include "scene/resources/text_line.h" class Tree; @@ -67,6 +68,13 @@ private: Rect2i icon_region; String text; String suffix; + Ref<TextLine> text_buf; + Dictionary opentype_features; + String language; + Control::StructuredTextParser st_parser = Control::STRUCTURED_TEXT_DEFAULT; + Array st_args; + Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; + bool dirty; double min, max, step, val; int icon_max_w; bool expr; @@ -108,6 +116,8 @@ private: Vector<Button> buttons; Cell() { + text_buf.instance(); + dirty = true; custom_draw_obj = ObjectID(); custom_button = false; mode = TreeItem::CELL_MODE_STRING; @@ -182,6 +192,22 @@ public: void set_text(int p_column, String p_text); String get_text(int p_column) const; + void set_text_direction(int p_column, Control::TextDirection p_text_direction); + Control::TextDirection get_text_direction(int p_column) const; + + void set_opentype_feature(int p_column, const String &p_name, int p_value); + int get_opentype_feature(int p_column, const String &p_name) const; + void clear_opentype_features(int p_column); + + void set_structured_text_bidi_override(int p_column, Control::StructuredTextParser p_parser); + Control::StructuredTextParser get_structured_text_bidi_override(int p_column) const; + + void set_structured_text_bidi_override_options(int p_column, Array p_args); + Array get_structured_text_bidi_override_options(int p_column) const; + + void set_language(int p_column, const String &p_language); + String get_language(int p_column) const; + void set_suffix(int p_column, String p_suffix); String get_suffix(int p_column) const; @@ -201,7 +227,6 @@ public: int get_button_count(int p_column) const; String get_button_tooltip(int p_column, int p_idx) const; Ref<Texture2D> get_button(int p_column, int p_idx) const; - int get_button_id(int p_column, int p_idx) const; void erase_button(int p_column, int p_idx); int get_button_by_id(int p_column, int p_id) const; void set_button(int p_column, int p_idx, const Ref<Texture2D> &p_button); @@ -348,7 +373,12 @@ private: int min_width; bool expand; String title; + Ref<TextLine> text_buf; + Dictionary opentype_features; + String language; + Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; ColumnInfo() { + text_buf.instance(); min_width = 1; expand = true; } @@ -374,8 +404,12 @@ private: int compute_item_height(TreeItem *p_item) const; int get_item_height(TreeItem *p_item) const; + void _update_all(); + void update_column(int p_col); + void update_item_cell(TreeItem *p_item, int p_col); + void update_item_cache(TreeItem *p_item); //void draw_item_text(String p_text,const Ref<Texture2D>& p_icon,int p_icon_max_w,bool p_tool,Rect2i p_rect,const Color& p_color); - void draw_item_rect(const TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color); + void draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color); int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item); void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false); int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_doubleclick, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod); @@ -400,6 +434,8 @@ private: struct Cache { Ref<Font> font; Ref<Font> tb_font; + int font_size; + int tb_font_size; Ref<StyleBox> bg; Ref<StyleBox> selected; Ref<StyleBox> selected_focus; @@ -563,6 +599,16 @@ public: void set_column_title(int p_column, const String &p_title); String get_column_title(int p_column) const; + void set_column_title_direction(int p_column, Control::TextDirection p_text_direction); + Control::TextDirection get_column_title_direction(int p_column) const; + + void set_column_title_opentype_feature(int p_column, const String &p_name, int p_value); + int get_column_title_opentype_feature(int p_column, const String &p_name) const; + void clear_column_title_opentype_features(int p_column); + + void set_column_title_language(int p_column, const String &p_language); + String get_column_title_language(int p_column) const; + void set_column_titles_visible(bool p_show); bool are_column_titles_visible() const; @@ -586,7 +632,6 @@ public: void scroll_to_item(TreeItem *p_item); void set_cursor_can_exit_tree(bool p_enable); - bool can_cursor_exit_tree() const; VScrollBar *get_vscroll_bar() { return v_scroll; } diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index e118cb0d8d..84d689369b 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/video_player.h b/scene/gui/video_player.h index 573aec5a2c..089337eed5 100644 --- a/scene/gui/video_player.h +++ b/scene/gui/video_player.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index e6c35352f5..efef49fbf0 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -292,15 +292,10 @@ void CanvasItemMaterial::_bind_methods() { CanvasItemMaterial::CanvasItemMaterial() : element(this) { - blend_mode = BLEND_MODE_MIX; - light_mode = LIGHT_MODE_NORMAL; - particles_animation = false; - set_particles_anim_h_frames(1); set_particles_anim_v_frames(1); set_particles_anim_loop(false); - current_key.key = 0; current_key.invalid_key = 1; _queue_shader_change(); } @@ -364,7 +359,7 @@ void CanvasItem::_propagate_visibility_changed(bool p_visible) { if (p_visible) { update(); //todo optimize } else { - emit_signal(SceneStringNames::get_singleton()->hide); + emit_signal(SceneStringNames::get_singleton()->hidden); } _block(); @@ -742,21 +737,21 @@ void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width); } -void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width) { +void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width, bool p_antialiased) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); Vector<Color> colors; colors.push_back(p_color); - RenderingServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, colors, p_width); + RenderingServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, colors, p_width, p_antialiased); } -void CanvasItem::draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width) { +void CanvasItem::draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width, bool p_antialiased) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - RenderingServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width); + RenderingServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width, p_antialiased); } -void CanvasItem::draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width) { +void CanvasItem::draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width, bool p_antialiased) { Vector<Point2> points; points.resize(p_point_count); const float delta_angle = p_end_angle - p_start_angle; @@ -765,7 +760,7 @@ void CanvasItem::draw_arc(const Vector2 &p_center, float p_radius, float p_start points.set(i, p_center + Vector2(Math::cos(theta), Math::sin(theta)) * p_radius); } - draw_polyline(points, p_color, p_width); + draw_polyline(points, p_color, p_width, p_antialiased); } void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width) { @@ -914,23 +909,24 @@ void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Tex RenderingServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid); } -void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w) { +void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - ERR_FAIL_COND(p_font.is_null()); - p_font->draw(canvas_item, p_pos, p_text, p_modulate, p_clip_w); + p_font->draw_string(canvas_item, p_pos, p_text, p_align, p_width, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags); } -float CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next, const Color &p_modulate) { - ERR_FAIL_COND_V_MSG(!drawing, 0, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); +void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL_COND(p_font.is_null()); + p_font->draw_multiline_string(canvas_item, p_pos, p_text, p_align, p_width, p_max_lines, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags); +} - ERR_FAIL_COND_V(p_char.length() != 1, 0); - ERR_FAIL_COND_V(p_font.is_null(), 0); +float CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const { + ERR_FAIL_COND_V_MSG(!drawing, 0.f, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL_COND_V(p_font.is_null(), 0.f); + ERR_FAIL_COND_V(p_char.length() != 1, 0.f); - if (p_font->has_outline()) { - p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.get_data()[0], Color(1, 1, 1), true); - } - return p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.get_data()[0], p_modulate); + return p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.get_data()[0], p_size, p_modulate, p_outline_size, p_outline_modulate); } void CanvasItem::_notify_transform(CanvasItem *p_node) { @@ -1144,9 +1140,9 @@ void CanvasItem::_bind_methods() { //ClassDB::bind_method(D_METHOD("get_transform"),&CanvasItem::get_transform); ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width"), &CanvasItem::draw_line, DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width"), &CanvasItem::draw_polyline, DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("draw_arc", "center", "radius", "start_angle", "end_angle", "point_count", "color", "width"), &CanvasItem::draw_arc, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(1.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("draw_arc", "center", "radius", "start_angle", "end_angle", "point_count", "color", "width", "antialiased"), &CanvasItem::draw_arc, DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_multiline", "points", "color", "width"), &CanvasItem::draw_multiline, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("draw_multiline_colors", "points", "colors", "width"), &CanvasItem::draw_multiline_colors, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled", "width"), &CanvasItem::draw_rect, DEFVAL(true), DEFVAL(1.0)); @@ -1158,11 +1154,11 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>())); ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture"), &CanvasItem::draw_colored_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>())); - ClassDB::bind_method(D_METHOD("draw_string", "font", "position", "text", "modulate", "clip_w"), &CanvasItem::draw_string, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("draw_char", "font", "position", "char", "next", "modulate"), &CanvasItem::draw_char, DEFVAL(Color(1, 1, 1, 1))); + ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("draw_char", "font", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &CanvasItem::draw_char, DEFVAL(""), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0))); ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "transform", "modulate"), &CanvasItem::draw_mesh, DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture"), &CanvasItem::draw_multimesh); - ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform, DEFVAL(0.0), DEFVAL(Size2(1.0, 1.0))); ClassDB::bind_method(D_METHOD("draw_set_transform_matrix", "xform"), &CanvasItem::draw_set_transform_matrix); ClassDB::bind_method(D_METHOD("get_transform"), &CanvasItem::get_transform); @@ -1226,7 +1222,7 @@ void CanvasItem::_bind_methods() { ADD_SIGNAL(MethodInfo("draw")); ADD_SIGNAL(MethodInfo("visibility_changed")); - ADD_SIGNAL(MethodInfo("hide")); + ADD_SIGNAL(MethodInfo("hidden")); ADD_SIGNAL(MethodInfo("item_rect_changed")); BIND_CONSTANT(NOTIFICATION_TRANSFORM_CHANGED); @@ -1410,30 +1406,7 @@ CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const { CanvasItem::CanvasItem() : xform_change(this) { - window = nullptr; canvas_item = RenderingServer::get_singleton()->canvas_item_create(); - visible = true; - pending_update = false; - modulate = Color(1, 1, 1, 1); - self_modulate = Color(1, 1, 1, 1); - top_level = false; - first_draw = false; - drawing = false; - behind = false; - clip_children = false; - block_transform_notify = false; - canvas_layer = nullptr; - use_parent_material = false; - global_invalid = true; - notify_local_transform = false; - notify_transform = false; - light_mask = 1; - texture_repeat = TEXTURE_REPEAT_PARENT_NODE; - texture_filter = TEXTURE_FILTER_PARENT_NODE; - texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; - texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; - - C = nullptr; } CanvasItem::~CanvasItem() { diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 412ef8079b..64eb3f4e0a 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,7 @@ #include "scene/resources/multimesh.h" #include "scene/resources/shader.h" #include "scene/resources/texture.h" +#include "servers/text_server.h" class CanvasLayer; class Viewport; @@ -72,7 +73,7 @@ private: uint32_t invalid_key : 1; }; - uint32_t key; + uint32_t key = 0; bool operator<(const MaterialKey &p_key) const { return key < p_key.key; @@ -113,10 +114,11 @@ private: _FORCE_INLINE_ void _queue_shader_change(); _FORCE_INLINE_ bool _is_shader_dirty() const; - BlendMode blend_mode; - LightMode light_mode; - bool particles_animation; + BlendMode blend_mode = BLEND_MODE_MIX; + LightMode light_mode = LIGHT_MODE_NORMAL; + bool particles_animation = false; + // Initialized in the constructor. int particles_anim_h_frames; int particles_anim_v_frames; bool particles_anim_loop; @@ -147,7 +149,7 @@ public: static void finish_shaders(); static void flush_changes(); - RID get_shader_rid() const; + virtual RID get_shader_rid() const override; virtual Shader::Mode get_shader_mode() const override; @@ -187,39 +189,38 @@ private: RID canvas_item; String group; - CanvasLayer *canvas_layer; + CanvasLayer *canvas_layer = nullptr; - Color modulate; - Color self_modulate; + Color modulate = Color(1, 1, 1, 1); + Color self_modulate = Color(1, 1, 1, 1); List<CanvasItem *> children_items; - List<CanvasItem *>::Element *C; - - int light_mask; - - Window *window; - bool first_draw; - bool visible; - bool clip_children; - bool pending_update; - bool top_level; - bool drawing; - bool block_transform_notify; - bool behind; - bool use_parent_material; - bool notify_local_transform; - bool notify_transform; - - RS::CanvasItemTextureFilter texture_filter_cache; - RS::CanvasItemTextureRepeat texture_repeat_cache; - - TextureFilter texture_filter; - TextureRepeat texture_repeat; + List<CanvasItem *>::Element *C = nullptr; + + int light_mask = 1; + + Window *window = nullptr; + bool first_draw = false; + bool visible = true; + bool clip_children = false; + bool pending_update = false; + bool top_level = false; + bool drawing = false; + bool block_transform_notify = false; + bool behind = false; + bool use_parent_material = false; + bool notify_local_transform = false; + bool notify_transform = false; + + RS::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; + RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; + TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE; + TextureRepeat texture_repeat = TEXTURE_REPEAT_PARENT_NODE; Ref<Material> material; mutable Transform2D global_transform; - mutable bool global_invalid; + mutable bool global_invalid = true; void _top_level_raise_self(); @@ -331,9 +332,9 @@ public: /* DRAWING API */ void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0); - void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0); - void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0); - void draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width = 1.0); + void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); + void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false); + void draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0); void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0); void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, float p_width = 1.0); @@ -349,8 +350,9 @@ public: void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1)); void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture); - void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1); - float draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", const Color &p_modulate = Color(1, 1, 1)); + void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + float draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; void draw_set_transform(const Point2 &p_offset, float p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0)); void draw_set_transform_matrix(const Transform2D &p_matrix); diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 46cfb968f8..9826d9d4db 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h index 0c68b1ab69..cb10c6299b 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index f484d25dc1..ddcf07c8e9 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/http_request.h b/scene/main/http_request.h index 2e8931120b..6f606296e4 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp index ca8d5a2ca0..1661984e30 100644 --- a/scene/main/instance_placeholder.cpp +++ b/scene/main/instance_placeholder.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/instance_placeholder.h b/scene/main/instance_placeholder.h index ec1f8a9b09..fe20fc4760 100644 --- a/scene/main/instance_placeholder.h +++ b/scene/main/instance_placeholder.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 38baa6c97e..1b6f73efe1 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -797,9 +797,9 @@ bool Node::can_process_notification(int p_what) const { case NOTIFICATION_PHYSICS_PROCESS: return data.physics_process; case NOTIFICATION_PROCESS: - return data.idle_process; + return data.process; case NOTIFICATION_INTERNAL_PROCESS: - return data.idle_process_internal; + return data.process_internal; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: return data.physics_process_internal; } @@ -845,50 +845,50 @@ float Node::get_physics_process_delta_time() const { float Node::get_process_delta_time() const { if (data.tree) { - return data.tree->get_idle_process_time(); + return data.tree->get_process_time(); } else { return 0; } } -void Node::set_process(bool p_idle_process) { - if (data.idle_process == p_idle_process) { +void Node::set_process(bool p_process) { + if (data.process == p_process) { return; } - data.idle_process = p_idle_process; + data.process = p_process; - if (data.idle_process) { - add_to_group("idle_process", false); + if (data.process) { + add_to_group("process", false); } else { - remove_from_group("idle_process"); + remove_from_group("process"); } - _change_notify("idle_process"); + _change_notify("process"); } bool Node::is_processing() const { - return data.idle_process; + return data.process; } -void Node::set_process_internal(bool p_idle_process_internal) { - if (data.idle_process_internal == p_idle_process_internal) { +void Node::set_process_internal(bool p_process_internal) { + if (data.process_internal == p_process_internal) { return; } - data.idle_process_internal = p_idle_process_internal; + data.process_internal = p_process_internal; - if (data.idle_process_internal) { - add_to_group("idle_process_internal", false); + if (data.process_internal) { + add_to_group("process_internal", false); } else { - remove_from_group("idle_process_internal"); + remove_from_group("process_internal"); } - _change_notify("idle_process_internal"); + _change_notify("process_internal"); } bool Node::is_processing_internal() const { - return data.idle_process_internal; + return data.process_internal; } void Node::set_process_priority(int p_priority) { @@ -900,11 +900,11 @@ void Node::set_process_priority(int p_priority) { } if (is_processing()) { - data.tree->make_group_changed("idle_process"); + data.tree->make_group_changed("process"); } if (is_processing_internal()) { - data.tree->make_group_changed("idle_process_internal"); + data.tree->make_group_changed("process_internal"); } if (is_physics_processing()) { @@ -1887,7 +1887,7 @@ void Node::remove_and_skip() { } } - while (!children.empty()) { + while (!children.is_empty()) { Node *c_node = children.front()->get(); data.parent->add_child(c_node); c_node->_propagate_replace_owner(nullptr, new_owner); @@ -2245,7 +2245,7 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const { List<const Node *> process_list; process_list.push_back(this); - while (!process_list.empty()) { + while (!process_list.is_empty()) { const Node *n = process_list.front()->get(); process_list.pop_front(); @@ -2873,6 +2873,7 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_APPLICATION_PAUSED); BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_IN); BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_OUT); + BIND_CONSTANT(NOTIFICATION_TEXT_SERVER_CHANGED); BIND_ENUM_CONSTANT(PAUSE_MODE_INHERIT); BIND_ENUM_CONSTANT(PAUSE_MODE_STOP); @@ -2924,35 +2925,6 @@ String Node::_get_name_num_separator() { } Node::Node() { - data.pos = -1; - data.depth = -1; - data.blocked = 0; - data.parent = nullptr; - data.tree = nullptr; - data.physics_process = false; - data.idle_process = false; - data.process_priority = 0; - data.physics_process_internal = false; - data.idle_process_internal = false; - data.inside_tree = false; - data.ready_notified = false; - - data.owner = nullptr; - data.OW = nullptr; - data.input = false; - data.unhandled_input = false; - data.unhandled_key_input = false; - data.pause_mode = PAUSE_MODE_INHERIT; - data.pause_owner = nullptr; - data.network_master = 1; //server by default - data.path_cache = nullptr; - data.parent_owned = false; - data.in_constructor = true; - data.viewport = nullptr; - data.use_placeholder = false; - data.display_folded = false; - data.ready_first = true; - orphan_node_count++; } diff --git a/scene/main/node.h b/scene/main/node.h index 61740738b0..fc7590c5a4 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -74,9 +74,8 @@ public: private: struct GroupData { - bool persistent; - SceneTree::Group *group; - GroupData() { persistent = false; } + bool persistent = false; + SceneTree::Group *group = nullptr; }; struct NetData { @@ -91,54 +90,54 @@ private: HashMap<NodePath, int> editable_instances; - Node *parent; - Node *owner; - Vector<Node *> children; // list of children - int pos; - int depth; - int blocked; // safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed. + Node *parent = nullptr; + Node *owner = nullptr; + Vector<Node *> children; + int pos = -1; + int depth = -1; + int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed. StringName name; - SceneTree *tree; - bool inside_tree; - bool ready_notified; //this is a small hack, so if a node is added during _ready() to the tree, it correctly gets the _ready() notification - bool ready_first; + SceneTree *tree = nullptr; + bool inside_tree = false; + bool ready_notified = false; // This is a small hack, so if a node is added during _ready() to the tree, it correctly gets the _ready() notification. + bool ready_first = true; #ifdef TOOLS_ENABLED - NodePath import_path; //path used when imported, used by scene editors to keep tracking + NodePath import_path; // Path used when imported, used by scene editors to keep tracking. #endif - Viewport *viewport; + Viewport *viewport = nullptr; Map<StringName, GroupData> grouped; - List<Node *>::Element *OW; // owned element + List<Node *>::Element *OW = nullptr; // Owned element. List<Node *> owned; - PauseMode pause_mode; - Node *pause_owner; + PauseMode pause_mode = PAUSE_MODE_INHERIT; + Node *pause_owner = nullptr; - int network_master; + int network_master = 1; // Server by default. Vector<NetData> rpc_methods; Vector<NetData> rpc_properties; - // variables used to properly sort the node when processing, ignored otherwise - //should move all the stuff below to bits - bool physics_process; - bool idle_process; - int process_priority; + // Variables used to properly sort the node when processing, ignored otherwise. + // TODO: Should move all the stuff below to bits. + bool physics_process = false; + bool process = false; + int process_priority = 0; - bool physics_process_internal; - bool idle_process_internal; + bool physics_process_internal = false; + bool process_internal = false; - bool input; - bool unhandled_input; - bool unhandled_key_input; + bool input = false; + bool unhandled_input = false; + bool unhandled_key_input = false; - bool parent_owned; - bool in_constructor; - bool use_placeholder; + bool parent_owned = false; + bool in_constructor = true; + bool use_placeholder = false; - bool display_folded; + bool display_folded = false; - mutable NodePath *path_cache; + mutable NodePath *path_cache = nullptr; } data; @@ -254,8 +253,8 @@ public: NOTIFICATION_APPLICATION_RESUMED = MainLoop::NOTIFICATION_APPLICATION_RESUMED, NOTIFICATION_APPLICATION_PAUSED = MainLoop::NOTIFICATION_APPLICATION_PAUSED, NOTIFICATION_APPLICATION_FOCUS_IN = MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN, - NOTIFICATION_APPLICATION_FOCUS_OUT = MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT - + NOTIFICATION_APPLICATION_FOCUS_OUT = MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT, + NOTIFICATION_TEXT_SERVER_CHANGED = MainLoop::NOTIFICATION_TEXT_SERVER_CHANGED, }; /* NODE/TREE */ @@ -340,14 +339,14 @@ public: float get_physics_process_delta_time() const; bool is_physics_processing() const; - void set_process(bool p_idle_process); + void set_process(bool p_process); float get_process_delta_time() const; bool is_processing() const; void set_physics_process_internal(bool p_process_internal); bool is_physics_processing_internal() const; - void set_process_internal(bool p_idle_process_internal); + void set_process_internal(bool p_process_internal); bool is_processing_internal() const; void set_process_priority(int p_priority); diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp index c1d4435687..cd9560db61 100644 --- a/scene/main/resource_preloader.cpp +++ b/scene/main/resource_preloader.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/resource_preloader.h b/scene/main/resource_preloader.h index 580dc35a57..1b7ea3fb9f 100644 --- a/scene/main/resource_preloader.h +++ b/scene/main/resource_preloader.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 304e8b9c6d..f18ac3b801 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,7 +42,7 @@ #include "core/string/print_string.h" #include "node.h" #include "scene/debugger/scene_debugger.h" -#include "scene/resources/dynamic_font.h" +#include "scene/resources/font.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" #include "scene/resources/packed_scene.h" @@ -136,7 +136,7 @@ void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) { ERR_FAIL_COND(!E); E->get().nodes.erase(p_node); - if (E->get().nodes.empty()) { + if (E->get().nodes.is_empty()) { group_map.erase(E); } } @@ -183,7 +183,7 @@ void SceneTree::_update_group_order(Group &g, bool p_use_priority) { if (!g.changed) { return; } - if (g.nodes.empty()) { + if (g.nodes.is_empty()) { return; } @@ -206,7 +206,7 @@ void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_grou return; } Group &g = E->get(); - if (g.nodes.empty()) { + if (g.nodes.is_empty()) { return; } @@ -282,7 +282,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr return; } Group &g = E->get(); - if (g.nodes.empty()) { + if (g.nodes.is_empty()) { return; } @@ -333,7 +333,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group return; } Group &g = E->get(); - if (g.nodes.empty()) { + if (g.nodes.is_empty()) { return; } @@ -390,20 +390,20 @@ void SceneTree::set_group(const StringName &p_group, const String &p_name, const set_group_flags(0, p_group, p_name, p_value); } -void SceneTree::init() { +void SceneTree::initialize() { initialized = true; root->_set_tree(this); - MainLoop::init(); + MainLoop::initialize(); } -bool SceneTree::iteration(float p_time) { +bool SceneTree::physics_process(float p_time) { root_lock++; current_frame++; flush_transform_notifications(); - MainLoop::iteration(p_time); + MainLoop::physics_process(p_time); physics_process_time = p_time; emit_signal("physics_frame"); @@ -422,29 +422,25 @@ bool SceneTree::iteration(float p_time) { return _quit; } -bool SceneTree::idle(float p_time) { - //print_line("ram: "+itos(OS::get_singleton()->get_static_memory_usage())+" sram: "+itos(OS::get_singleton()->get_dynamic_memory_usage())); - //print_line("node count: "+itos(get_node_count())); - //print_line("TEXTURE RAM: "+itos(RS::get_singleton()->get_render_info(RS::INFO_TEXTURE_MEM_USED))); - +bool SceneTree::process(float p_time) { root_lock++; - MainLoop::idle(p_time); + MainLoop::process(p_time); - idle_process_time = p_time; + process_time = p_time; if (multiplayer_poll) { multiplayer->poll(); } - emit_signal("idle_frame"); + emit_signal("process_frame"); MessageQueue::get_singleton()->flush(); //small little hack flush_transform_notifications(); - _notify_group_pause("idle_process_internal", Node::NOTIFICATION_INTERNAL_PROCESS); - _notify_group_pause("idle_process", Node::NOTIFICATION_PROCESS); + _notify_group_pause("process_internal", Node::NOTIFICATION_INTERNAL_PROCESS); + _notify_group_pause("process", Node::NOTIFICATION_PROCESS); _flush_ugc(); MessageQueue::get_singleton()->flush(); //small little hack @@ -516,14 +512,14 @@ bool SceneTree::idle(float p_time) { return _quit; } -void SceneTree::finish() { +void SceneTree::finalize() { _flush_delete_queue(); _flush_ugc(); initialized = false; - MainLoop::finish(); + MainLoop::finalize(); if (root) { root->_set_tree(nullptr); @@ -787,7 +783,7 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio return; } Group &g = E->get(); - if (g.nodes.empty()) { + if (g.nodes.is_empty()) { return; } @@ -840,7 +836,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p return; } Group &g = E->get(); - if (g.nodes.empty()) { + if (g.nodes.is_empty()) { return; } @@ -1265,7 +1261,7 @@ void SceneTree::_bind_methods() { ADD_SIGNAL(MethodInfo("node_renamed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("node_configuration_warning_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("idle_frame")); + ADD_SIGNAL(MethodInfo("process_frame")); ADD_SIGNAL(MethodInfo("physics_frame")); ADD_SIGNAL(MethodInfo("files_dropped", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "screen"))); @@ -1303,7 +1299,7 @@ void SceneTree::get_argument_options(const StringName &p_function, int p_idx, Li List<String> directories; directories.push_back(dir_access->get_current_dir()); - while (!directories.empty()) { + while (!directories.is_empty()) { dir_access->change_dir(directories.back()->get()); directories.pop_back(); @@ -1332,14 +1328,6 @@ SceneTree::SceneTree() { if (singleton == nullptr) { singleton = this; } - _quit = false; - accept_quit = true; - quit_on_go_back = true; - initialized = false; -#ifdef DEBUG_ENABLED - debug_collisions_hint = false; - debug_navigation_hint = false; -#endif debug_collisions_color = GLOBAL_DEF("debug/shapes/collision/shape_color", Color(0.0, 0.6, 0.7, 0.5)); debug_collision_contact_color = GLOBAL_DEF("debug/shapes/collision/contact_color", Color(1.0, 0.2, 0.1, 0.8)); debug_navigation_color = GLOBAL_DEF("debug/shapes/navigation/geometry_color", Color(0.1, 1.0, 0.7, 0.4)); @@ -1347,23 +1335,7 @@ SceneTree::SceneTree() { collision_debug_contacts = GLOBAL_DEF("debug/shapes/collision/max_contacts_displayed", 10000); ProjectSettings::get_singleton()->set_custom_property_info("debug/shapes/collision/max_contacts_displayed", PropertyInfo(Variant::INT, "debug/shapes/collision/max_contacts_displayed", PROPERTY_HINT_RANGE, "0,20000,1")); // No negative - tree_version = 1; - physics_process_time = 1; - idle_process_time = 1; - - root = nullptr; - pause = false; - current_frame = 0; - tree_changed_name = "tree_changed"; - node_added_name = "node_added"; - node_removed_name = "node_removed"; - node_renamed_name = "node_renamed"; - ugc_locked = false; - call_lock = 0; - root_lock = 0; - node_count = 0; - - //create with mainloop + // Create with mainloop. root = memnew(Window); root->set_name("root"); @@ -1371,8 +1343,7 @@ SceneTree::SceneTree() { root->set_world_3d(Ref<World3D>(memnew(World3D))); } - // Initialize network state - multiplayer_poll = true; + // Initialize network state. set_multiplayer(Ref<MultiplayerAPI>(memnew(MultiplayerAPI))); //root->set_world_2d( Ref<World2D>( memnew( World2D ))); @@ -1391,6 +1362,10 @@ SceneTree::SceneTree() { const bool use_debanding = GLOBAL_DEF("rendering/quality/screen_filters/use_debanding", false); root->set_use_debanding(use_debanding); + float lod_threshold = GLOBAL_DEF("rendering/quality/mesh_lod/threshold_pixels", 1.0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/mesh_lod/threshold_pixels", PropertyInfo(Variant::FLOAT, "rendering/quality/mesh_lod/threshold_pixels", PROPERTY_HINT_RANGE, "0,1024,0.1")); + root->set_lod_threshold(lod_threshold); + bool snap_2d_transforms = GLOBAL_DEF("rendering/quality/2d/snap_2d_transforms_to_pixel", false); root->set_snap_2d_transforms_to_pixel(snap_2d_transforms); @@ -1405,8 +1380,8 @@ SceneTree::SceneTree() { ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/2d_sdf/oversize", PropertyInfo(Variant::INT, "rendering/quality/2d_sdf/oversize", PROPERTY_HINT_ENUM, "100%,120%,150%,200%")); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/2d_sdf/scale", PropertyInfo(Variant::INT, "rendering/quality/2d_sdf/scale", PROPERTY_HINT_ENUM, "100%,50%,25%")); - { //load default fallback environment - //get possible extensions + { // Load default fallback environment. + // Get possible extensions. List<String> exts; ResourceLoader::get_recognized_extensions_for_type("Environment", &exts); String ext_hint; @@ -1416,9 +1391,9 @@ SceneTree::SceneTree() { } ext_hint += "*." + E->get(); } - //get path + // Get path. String env_path = GLOBAL_DEF("rendering/environment/default_environment", ""); - //setup property + // Setup property. ProjectSettings::get_singleton()->set_custom_property_info("rendering/environment/default_environment", PropertyInfo(Variant::STRING, "rendering/viewport/default_environment", PROPERTY_HINT_FILE, ext_hint)); env_path = env_path.strip_edges(); if (env_path != String()) { @@ -1427,10 +1402,10 @@ SceneTree::SceneTree() { root->get_world_3d()->set_fallback_environment(env); } else { if (Engine::get_singleton()->is_editor_hint()) { - //file was erased, clear the field. + // File was erased, clear the field. ProjectSettings::get_singleton()->set("rendering/environment/default_environment", ""); } else { - //file was erased, notify user. + // File was erased, notify user. ERR_PRINT(RTR("Default Environment as specified in Project Settings (Rendering -> Environment -> Default Environment) could not be loaded.")); } } diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 3e5802ce2e..748908c8a6 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -80,37 +80,36 @@ public: private: struct Group { Vector<Node *> nodes; - //uint64_t last_tree_version; bool changed; Group() { changed = false; }; }; - Window *root; + Window *root = nullptr; - uint64_t tree_version; - float physics_process_time; - float idle_process_time; - bool accept_quit; - bool quit_on_go_back; + uint64_t tree_version = 1; + float physics_process_time = 1.0; + float process_time = 1.0; + bool accept_quit = true; + bool quit_on_go_back = true; #ifdef DEBUG_ENABLED - bool debug_collisions_hint; - bool debug_navigation_hint; + bool debug_collisions_hint = false; + bool debug_navigation_hint = false; #endif - bool pause; - int root_lock; + bool pause = false; + int root_lock = 0; Map<StringName, Group> group_map; - bool _quit; - bool initialized; + bool _quit = false; + bool initialized = false; - StringName tree_changed_name; - StringName node_added_name; - StringName node_removed_name; - StringName node_renamed_name; + StringName tree_changed_name = "tree_changed"; + StringName node_added_name = "node_added"; + StringName node_removed_name = "node_removed"; + StringName node_renamed_name = "node_renamed"; - int64_t current_frame; - int node_count; + int64_t current_frame = 0; + int node_count = 0; #ifdef TOOLS_ENABLED Node *edited_scene_root; @@ -122,14 +121,14 @@ private: bool operator<(const UGCall &p_with) const { return group == p_with.group ? call < p_with.call : group < p_with.group; } }; - //safety for when a node is deleted while a group is being called - int call_lock; - Set<Node *> call_skip; //skip erased nodes + // Safety for when a node is deleted while a group is being called. + int call_lock = 0; + Set<Node *> call_skip; // Skip erased nodes. List<ObjectID> delete_queue; Map<UGCall, Vector<Variant>> unique_group_calls; - bool ugc_locked; + bool ugc_locked = false; void _flush_ugc(); _FORCE_INLINE_ void _update_group_order(Group &g, bool p_use_priority = false); @@ -157,7 +156,7 @@ private: ///network/// Ref<MultiplayerAPI> multiplayer; - bool multiplayer_poll; + bool multiplayer_poll = true; void _network_peer_connected(int p_id); void _network_peer_disconnected(int p_id); @@ -183,7 +182,7 @@ private: Variant _call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error); void _flush_delete_queue(); - //optimization + // Optimization. friend class CanvasItem; friend class Node3D; friend class Viewport; @@ -237,12 +236,12 @@ public: void flush_transform_notifications(); - virtual void init() override; + virtual void initialize() override; - virtual bool iteration(float p_time) override; - virtual bool idle(float p_time) override; + virtual bool physics_process(float p_time) override; + virtual bool process(float p_time) override; - virtual void finish() override; + virtual void finalize() override; void set_auto_accept_quit(bool p_enable); void set_quit_on_go_back(bool p_enable); @@ -250,7 +249,7 @@ public: void quit(int p_exit_code = -1); _FORCE_INLINE_ float get_physics_process_time() const { return physics_process_time; } - _FORCE_INLINE_ float get_idle_process_time() const { return idle_process_time; } + _FORCE_INLINE_ float get_process_time() const { return process_time; } #ifdef TOOLS_ENABLED bool is_node_being_edited(const Node *p_node) const; diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp index 432fb5b4fb..4485cffff5 100644 --- a/scene/main/shader_globals_override.cpp +++ b/scene/main/shader_globals_override.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -264,7 +264,7 @@ String ShaderGlobalsOverride::get_configuration_warning() const { String warning = Node::get_configuration_warning(); if (!active) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene."); diff --git a/scene/main/shader_globals_override.h b/scene/main/shader_globals_override.h index fea1677ad7..9e666a0ec5 100644 --- a/scene/main/shader_globals_override.h +++ b/scene/main/shader_globals_override.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index 1c6037d26e..d7b2292ff5 100644 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/timer.h b/scene/main/timer.h index 61abf04f59..0604a2d990 100644 --- a/scene/main/timer.h +++ b/scene/main/timer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 5be5c1b266..d687d31909 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,6 +35,8 @@ #include "core/debugger/engine_debugger.h" #include "core/input/input.h" #include "core/os/os.h" +#include "core/string/translation.h" + #include "scene/2d/collision_object_2d.h" #include "scene/3d/camera_3d.h" #include "scene/3d/collision_object_3d.h" @@ -285,16 +287,19 @@ void Viewport::_sub_window_update(Window *p_window) { // Draw the title bar text. Ref<Font> title_font = p_window->get_theme_font("title_font"); + int font_size = p_window->get_theme_font_size("title_font_size"); Color title_color = p_window->get_theme_color("title_color"); int title_height = p_window->get_theme_constant("title_height"); - int font_height = title_font->get_height() - title_font->get_descent() * 2; - int x = (r.size.width - title_font->get_string_size(p_window->get_title()).x) / 2; - int y = (-title_height + font_height) / 2; - int close_h_ofs = p_window->get_theme_constant("close_h_ofs"); int close_v_ofs = p_window->get_theme_constant("close_v_ofs"); - title_font->draw(sw.canvas_item, r.position + Point2(x, y), p_window->get_title(), title_color, r.size.width - panel->get_minimum_size().x - close_h_ofs); + TextLine title_text = TextLine(p_window->get_title(), title_font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + title_text.set_width(r.size.width - panel->get_minimum_size().x - close_h_ofs); + title_text.set_direction(p_window->is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + int x = (r.size.width - title_text.get_size().x) / 2; + int y = (-title_height - title_text.get_size().y) / 2; + + title_text.draw(sw.canvas_item, r.position + Point2(x, y), title_color); bool hl = gui.subwindow_focused == sw.window && gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE && gui.subwindow_drag_close_inside; @@ -1500,7 +1505,7 @@ String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Cont } // If we found a tooltip, we stop here. - if (!tooltip.empty()) { + if (!tooltip.is_empty()) { break; } @@ -1533,8 +1538,8 @@ void Viewport::_gui_show_tooltip() { gui.tooltip_control, gui.tooltip_control->get_global_transform().xform_inv(gui.last_mouse_pos), &tooltip_owner); - tooltip_text.strip_edges(); - if (tooltip_text.empty()) { + tooltip_text = tooltip_text.strip_edges(); + if (tooltip_text.is_empty()) { return; // Nothing to show. } @@ -1560,7 +1565,7 @@ void Viewport::_gui_show_tooltip() { base_tooltip = gui.tooltip_label; } - base_tooltip->set_anchors_and_margins_preset(Control::PRESET_WIDE); + base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_WIDE); TooltipPanel *panel = memnew(TooltipPanel); panel->set_transient(false); @@ -1575,7 +1580,8 @@ void Viewport::_gui_show_tooltip() { Point2 tooltip_offset = ProjectSettings::get_singleton()->get("display/mouse_cursor/tooltip_position_offset"); Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_contents_minimum_size()); - Rect2i vr = gui.tooltip_popup->get_parent_visible_window()->get_usable_parent_rect(); + Window *window = gui.tooltip_popup->get_parent_visible_window(); + Rect2i vr = window->get_usable_parent_rect(); if (r.size.x + r.position.x > vr.size.x + vr.position.x) { r.position.x = vr.position.x + vr.size.x - r.size.x; @@ -1589,6 +1595,7 @@ void Viewport::_gui_show_tooltip() { r.position.y = vr.position.y; } + gui.tooltip_popup->set_current_screen(window->get_current_screen()); gui.tooltip_popup->set_position(r.position); gui.tooltip_popup->set_size(r.size); @@ -1804,15 +1811,7 @@ bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_che void Viewport::_gui_input_event(Ref<InputEvent> p_event) { ERR_FAIL_COND(p_event.is_null()); - //? - /* - if (!is_visible()) { - return; //simple and plain - } - */ - Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid()) { gui.key_event_accepted = false; @@ -2000,7 +1999,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } Ref<InputEventMouseMotion> mm = p_event; - if (mm.is_valid()) { gui.key_event_accepted = false; Point2 mpos = mm->get_position(); @@ -2396,19 +2394,19 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } if (!mods && p_event->is_action_pressed("ui_up") && input->is_action_just_pressed("ui_up")) { - next = from->_get_focus_neighbour(MARGIN_TOP); + next = from->_get_focus_neighbor(SIDE_TOP); } if (!mods && p_event->is_action_pressed("ui_left") && input->is_action_just_pressed("ui_left")) { - next = from->_get_focus_neighbour(MARGIN_LEFT); + next = from->_get_focus_neighbor(SIDE_LEFT); } if (!mods && p_event->is_action_pressed("ui_right") && input->is_action_just_pressed("ui_right")) { - next = from->_get_focus_neighbour(MARGIN_RIGHT); + next = from->_get_focus_neighbor(SIDE_RIGHT); } if (!mods && p_event->is_action_pressed("ui_down") && input->is_action_just_pressed("ui_down")) { - next = from->_get_focus_neighbour(MARGIN_BOTTOM); + next = from->_get_focus_neighbor(SIDE_BOTTOM); } if (next) { @@ -3043,7 +3041,10 @@ void Viewport::unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coor ev = p_event; } + // Unhandled Input get_tree()->_call_input_pause(unhandled_input_group, "_unhandled_input", ev, this); + + // Unhandled key Input - used for performance reasons - This is called a lot less then _unhandled_input since it ignores MouseMotion, etc if (!is_input_handled() && Object::cast_to<InputEventKey>(*ev) != nullptr) { get_tree()->_call_input_pause(unhandled_key_input_group, "_unhandled_key_input", ev, this); } @@ -3140,7 +3141,7 @@ String Viewport::get_configuration_warning() const { String warning = Node::get_configuration_warning(); if (size.x == 0 || size.y == 0) { - if (!warning.empty()) { + if (!warning.is_empty()) { warning += "\n\n"; } warning += TTR("Viewport size must be greater than 0 to render anything."); @@ -3193,6 +3194,14 @@ bool Viewport::is_using_debanding() const { return use_debanding; } +void Viewport::set_lod_threshold(float p_pixels) { + lod_threshold = p_pixels; + RS::get_singleton()->viewport_set_lod_threshold(viewport, lod_threshold); +} +float Viewport::get_lod_threshold() const { + return lod_threshold; +} + void Viewport::set_debug_draw(DebugDraw p_debug_draw) { debug_draw = p_debug_draw; RS::get_singleton()->viewport_set_debug_draw(viewport, RS::ViewportDebugDraw(p_debug_draw)); @@ -3506,6 +3515,9 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sdf_scale", "scale"), &Viewport::set_sdf_scale); ClassDB::bind_method(D_METHOD("get_sdf_scale"), &Viewport::get_sdf_scale); + ClassDB::bind_method(D_METHOD("set_lod_threshold", "pixels"), &Viewport::set_lod_threshold); + ClassDB::bind_method(D_METHOD("get_lod_threshold"), &Viewport::get_lod_threshold); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", 0), "set_world_2d", "get_world_2d"); @@ -3517,6 +3529,7 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x,AndroidVR 2x,AndroidVR 4x"), "set_msaa", "get_msaa"); ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled,FXAA"), "set_screen_space_aa", "get_screen_space_aa"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold"); ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw"); ADD_GROUP("Canvas Items", "canvas_item_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,MipmapLinear,MipmapNearest"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter"); @@ -3591,6 +3604,7 @@ void Viewport::_bind_methods() { BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI); BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI_PROBES); BIND_ENUM_CONSTANT(DEBUG_DRAW_GI_BUFFER); + BIND_ENUM_CONSTANT(DEBUG_DRAW_DISABLE_LOD); BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST); BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR); @@ -3653,6 +3667,8 @@ Viewport::Viewport() { set_shadow_atlas_quadrant_subdiv(2, SHADOW_ATLAS_QUADRANT_SUBDIV_16); set_shadow_atlas_quadrant_subdiv(3, SHADOW_ATLAS_QUADRANT_SUBDIV_64); + set_lod_threshold(lod_threshold); + String id = itos(get_instance_id()); input_group = "_vp_input" + id; gui_input_group = "_vp_gui_input" + id; @@ -3825,7 +3841,7 @@ void SubViewport::_bind_methods() { BIND_ENUM_CONSTANT(CLEAR_MODE_ALWAYS); BIND_ENUM_CONSTANT(CLEAR_MODE_NEVER); - BIND_ENUM_CONSTANT(CLEAR_MODE_ONLY_NEXT_FRAME); + BIND_ENUM_CONSTANT(CLEAR_MODE_ONCE); BIND_ENUM_CONSTANT(UPDATE_DISABLED); BIND_ENUM_CONSTANT(UPDATE_ONCE); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 7ce202d27c..f0818a9fed 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -142,6 +142,7 @@ public: DEBUG_DRAW_SDFGI, DEBUG_DRAW_SDFGI_PROBES, DEBUG_DRAW_GI_BUFFER, + DEBUG_DRAW_DISABLE_LOD, }; enum DefaultCanvasItemTextureFilter { @@ -297,6 +298,8 @@ private: MSAA msaa; ScreenSpaceAA screen_space_aa; bool use_debanding = false; + float lod_threshold = 1.0; + Ref<ViewportTexture> default_texture; Set<ViewportTexture *> viewport_textures; @@ -542,6 +545,9 @@ public: void set_use_debanding(bool p_use_debanding); bool is_using_debanding() const; + void set_lod_threshold(float p_pixels); + float get_lod_threshold() const; + Vector2 get_camera_coords(const Vector2 &p_viewport_coords) const; Vector2 get_camera_rect_size() const; @@ -624,7 +630,7 @@ public: enum ClearMode { CLEAR_MODE_ALWAYS, CLEAR_MODE_NEVER, - CLEAR_MODE_ONLY_NEXT_FRAME + CLEAR_MODE_ONCE }; enum UpdateMode { diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 4116d5ce10..0a541a2d07 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,8 +32,9 @@ #include "core/debugger/engine_debugger.h" #include "core/os/keyboard.h" +#include "core/string/translation.h" #include "scene/gui/control.h" -#include "scene/resources/dynamic_font.h" +#include "scene/resources/font.h" #include "scene/scene_string_names.h" void Window::set_title(const String &p_title) { @@ -659,9 +660,8 @@ void Window::_update_viewport_size() { if (!use_font_oversampling) { font_oversampling = 1.0; } - if (DynamicFontAtSize::font_oversampling != font_oversampling) { - DynamicFontAtSize::font_oversampling = font_oversampling; - DynamicFont::update_oversampling(); + if (TS->font_get_oversampling() != font_oversampling) { + TS->font_set_oversampling(font_oversampling); } } @@ -1167,11 +1167,6 @@ Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName return Control::get_icons(theme_owner, theme_owner_window, p_name, type); } -Ref<Shader> Window::get_theme_shader(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::get_shaders(theme_owner, theme_owner_window, p_name, type); -} - Ref<StyleBox> Window::get_theme_stylebox(const StringName &p_name, const StringName &p_type) const { StringName type = p_type ? p_type : get_class_name(); return Control::get_styleboxs(theme_owner, theme_owner_window, p_name, type); @@ -1182,6 +1177,11 @@ Ref<Font> Window::get_theme_font(const StringName &p_name, const StringName &p_t return Control::get_fonts(theme_owner, theme_owner_window, p_name, type); } +int Window::get_theme_font_size(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::get_font_sizes(theme_owner, theme_owner_window, p_name, type); +} + Color Window::get_theme_color(const StringName &p_name, const StringName &p_type) const { StringName type = p_type ? p_type : get_class_name(); return Control::get_colors(theme_owner, theme_owner_window, p_name, type); @@ -1197,11 +1197,6 @@ bool Window::has_theme_icon(const StringName &p_name, const StringName &p_type) return Control::has_icons(theme_owner, theme_owner_window, p_name, type); } -bool Window::has_theme_shader(const StringName &p_name, const StringName &p_type) const { - StringName type = p_type ? p_type : get_class_name(); - return Control::has_shaders(theme_owner, theme_owner_window, p_name, type); -} - bool Window::has_theme_stylebox(const StringName &p_name, const StringName &p_type) const { StringName type = p_type ? p_type : get_class_name(); return Control::has_styleboxs(theme_owner, theme_owner_window, p_name, type); @@ -1212,6 +1207,11 @@ bool Window::has_theme_font(const StringName &p_name, const StringName &p_type) return Control::has_fonts(theme_owner, theme_owner_window, p_name, type); } +bool Window::has_theme_font_size(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::has_font_sizes(theme_owner, theme_owner_window, p_name, type); +} + bool Window::has_theme_color(const StringName &p_name, const StringName &p_type) const { StringName type = p_type ? p_type : get_class_name(); return Control::has_colors(theme_owner, theme_owner_window, p_name, type); @@ -1266,6 +1266,40 @@ bool Window::is_clamped_to_embedder() const { return clamp_to_embedder; } +void Window::set_layout_direction(Window::LayoutDirection p_direction) { + ERR_FAIL_INDEX((int)p_direction, 4); + + layout_dir = p_direction; + propagate_notification(Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED); +} + +Window::LayoutDirection Window::get_layout_direction() const { + return layout_dir; +} + +bool Window::is_layout_rtl() const { + if (layout_dir == LAYOUT_DIRECTION_INHERITED) { + Window *parent = Object::cast_to<Window>(get_parent()); + if (parent) { + return parent->is_layout_rtl(); + } else { + if (GLOBAL_GET("display/window/force_right_to_left_layout_direction")) { + return true; + } + String locale = TranslationServer::get_singleton()->get_tool_locale(); + return TS->is_locale_right_to_left(locale); + } + } else if (layout_dir == LAYOUT_DIRECTION_LOCALE) { + if (GLOBAL_GET("display/window/force_right_to_left_layout_direction")) { + return true; + } + String locale = TranslationServer::get_singleton()->get_tool_locale(); + return TS->is_locale_right_to_left(locale); + } else { + return (layout_dir == LAYOUT_DIRECTION_RTL); + } +} + void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title); ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title); @@ -1344,15 +1378,21 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "type"), &Window::get_theme_icon, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "type"), &Window::get_theme_stylebox, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_theme_font", "name", "type"), &Window::get_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "type"), &Window::get_theme_font_size, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_theme_color", "name", "type"), &Window::get_theme_color, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "type"), &Window::get_theme_constant, DEFVAL("")); ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "type"), &Window::has_theme_icon, DEFVAL("")); ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "type"), &Window::has_theme_stylebox, DEFVAL("")); ClassDB::bind_method(D_METHOD("has_theme_font", "name", "type"), &Window::has_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "type"), &Window::has_theme_font_size, DEFVAL("")); ClassDB::bind_method(D_METHOD("has_theme_color", "name", "type"), &Window::has_theme_color, DEFVAL("")); ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "type"), &Window::has_theme_constant, DEFVAL("")); + ClassDB::bind_method(D_METHOD("set_layout_direction", "direction"), &Window::set_layout_direction); + ClassDB::bind_method(D_METHOD("get_layout_direction"), &Window::get_layout_direction); + ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Window::is_layout_rtl); + ClassDB::bind_method(D_METHOD("popup", "rect"), &Window::popup, DEFVAL(Rect2i())); ClassDB::bind_method(D_METHOD("popup_on_parent", "parent_rect"), &Window::popup_on_parent); ClassDB::bind_method(D_METHOD("popup_centered_ratio", "ratio"), &Window::popup_centered_ratio, DEFVAL(0.8)); @@ -1418,6 +1458,11 @@ void Window::_bind_methods() { BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP_WIDTH); BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP_HEIGHT); BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_EXPAND); + + BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_INHERITED); + BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE); + BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LTR); + BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_RTL); } Window::Window() { diff --git a/scene/main/window.h b/scene/main/window.h index e11cbd8a72..2ffeff03ff 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -69,6 +69,13 @@ public: CONTENT_SCALE_ASPECT_EXPAND, }; + enum LayoutDirection { + LAYOUT_DIRECTION_INHERITED, + LAYOUT_DIRECTION_LOCALE, + LAYOUT_DIRECTION_LTR, + LAYOUT_DIRECTION_RTL + }; + enum { DEFAULT_WINDOW_SIZE = 100, }; @@ -94,6 +101,8 @@ private: bool updating_child_controls = false; bool clamp_to_embedder = false; + LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED; + void _update_child_controls(); Size2i content_scale_size; @@ -149,7 +158,7 @@ public: enum { NOTIFICATION_VISIBILITY_CHANGED = 30, NOTIFICATION_POST_POPUP = 31, - NOTIFICATION_THEME_CHANGED = 32, + NOTIFICATION_THEME_CHANGED = 32 }; void set_title(const String &p_title); @@ -237,19 +246,23 @@ public: void grab_focus(); bool has_focus() const; + void set_layout_direction(LayoutDirection p_direction); + LayoutDirection get_layout_direction() const; + bool is_layout_rtl() const; + Rect2i get_usable_parent_rect() const; Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_type = StringName()) const; - Ref<Shader> get_theme_shader(const StringName &p_name, const StringName &p_type = StringName()) const; Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_type = StringName()) const; Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_type = StringName()) const; + int get_theme_font_size(const StringName &p_name, const StringName &p_type = StringName()) const; Color get_theme_color(const StringName &p_name, const StringName &p_type = StringName()) const; int get_theme_constant(const StringName &p_name, const StringName &p_type = StringName()) const; bool has_theme_icon(const StringName &p_name, const StringName &p_type = StringName()) const; - bool has_theme_shader(const StringName &p_name, const StringName &p_type = StringName()) const; bool has_theme_stylebox(const StringName &p_name, const StringName &p_type = StringName()) const; bool has_theme_font(const StringName &p_name, const StringName &p_type = StringName()) const; + bool has_theme_font_size(const StringName &p_name, const StringName &p_type = StringName()) const; bool has_theme_color(const StringName &p_name, const StringName &p_type = StringName()) const; bool has_theme_constant(const StringName &p_name, const StringName &p_type = StringName()) const; @@ -264,5 +277,6 @@ VARIANT_ENUM_CAST(Window::Mode); VARIANT_ENUM_CAST(Window::Flags); VARIANT_ENUM_CAST(Window::ContentScaleMode); VARIANT_ENUM_CAST(Window::ContentScaleAspect); +VARIANT_ENUM_CAST(Window::LayoutDirection); #endif // WINDOW_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 3ca4e6db3a..b809eea43b 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -118,7 +118,7 @@ #include "scene/gui/tabs.h" #include "scene/gui/text_edit.h" #include "scene/gui/texture_button.h" -#include "scene/gui/texture_progress.h" +#include "scene/gui/texture_progress_bar.h" #include "scene/gui/texture_rect.h" #include "scene/gui/tree.h" #include "scene/gui/video_player.h" @@ -144,7 +144,7 @@ #include "scene/resources/convex_polygon_shape_3d.h" #include "scene/resources/cylinder_shape_3d.h" #include "scene/resources/default_theme/default_theme.h" -#include "scene/resources/dynamic_font.h" +#include "scene/resources/font.h" #include "scene/resources/gradient.h" #include "scene/resources/height_map_shape_3d.h" #include "scene/resources/line_shape_2d.h" @@ -168,6 +168,8 @@ #include "scene/resources/surface_tool.h" #include "scene/resources/syntax_highlighter.h" #include "scene/resources/text_file.h" +#include "scene/resources/text_line.h" +#include "scene/resources/text_paragraph.h" #include "scene/resources/texture.h" #include "scene/resources/tile_set.h" #include "scene/resources/video_stream.h" @@ -230,14 +232,16 @@ static Ref<ResourceFormatSaverText> resource_saver_text; static Ref<ResourceFormatLoaderText> resource_loader_text; -static Ref<ResourceFormatLoaderDynamicFont> resource_loader_dynamic_font; +static Ref<ResourceFormatLoaderFont> resource_loader_font; + +#ifndef DISABLE_DEPRECATED +static Ref<ResourceFormatLoaderCompatFont> resource_loader_compat_font; +#endif /* DISABLE_DEPRECATED */ static Ref<ResourceFormatLoaderStreamTexture2D> resource_loader_stream_texture; static Ref<ResourceFormatLoaderStreamTextureLayered> resource_loader_texture_layered; static Ref<ResourceFormatLoaderStreamTexture3D> resource_loader_texture_3d; -static Ref<ResourceFormatLoaderBMFont> resource_loader_bmfont; - static Ref<ResourceFormatSaverShader> resource_saver_shader; static Ref<ResourceFormatLoaderShader> resource_loader_shader; @@ -248,8 +252,13 @@ void register_scene_types() { Node::init_node_hrcr(); - resource_loader_dynamic_font.instance(); - ResourceLoader::add_resource_format_loader(resource_loader_dynamic_font); + resource_loader_font.instance(); + ResourceLoader::add_resource_format_loader(resource_loader_font); + +#ifndef DISABLE_DEPRECATED + resource_loader_compat_font.instance(); + ResourceLoader::add_resource_format_loader(resource_loader_compat_font); +#endif /* DISABLE_DEPRECATED */ resource_loader_stream_texture.instance(); ResourceLoader::add_resource_format_loader(resource_loader_stream_texture); @@ -272,9 +281,6 @@ void register_scene_types() { resource_loader_shader.instance(); ResourceLoader::add_resource_format_loader(resource_loader_shader, true); - resource_loader_bmfont.instance(); - ResourceLoader::add_resource_format_loader(resource_loader_bmfont, true); - OS::get_singleton()->yield(); //may take time to init ClassDB::register_class<Object>(); @@ -343,7 +349,7 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init - ClassDB::register_class<TextureProgress>(); + ClassDB::register_class<TextureProgressBar>(); ClassDB::register_class<ItemList>(); ClassDB::register_class<LineEdit>(); @@ -673,8 +679,8 @@ void register_scene_types() { #ifndef _3D_DISABLED ClassDB::register_virtual_class<PrimitiveMesh>(); + ClassDB::register_class<BoxMesh>(); ClassDB::register_class<CapsuleMesh>(); - ClassDB::register_class<CubeMesh>(); ClassDB::register_class<CylinderMesh>(); ClassDB::register_class<PlaneMesh>(); ClassDB::register_class<PrismMesh>(); @@ -740,16 +746,13 @@ void register_scene_types() { ClassDB::register_class<StreamTexture2DArray>(); ClassDB::register_class<Animation>(); - ClassDB::register_virtual_class<Font>(); - ClassDB::register_class<BitmapFont>(); + ClassDB::register_class<FontData>(); + ClassDB::register_class<Font>(); ClassDB::register_class<Curve>(); ClassDB::register_class<TextFile>(); - - ClassDB::register_class<DynamicFontData>(); - ClassDB::register_class<DynamicFont>(); - - DynamicFont::initialize_dynamic_fonts(); + ClassDB::register_class<TextLine>(); + ClassDB::register_class<TextParagraph>(); ClassDB::register_virtual_class<StyleBox>(); ClassDB::register_class<StyleBoxEmpty>(); @@ -805,6 +808,9 @@ void register_scene_types() { #ifndef DISABLE_DEPRECATED // Dropped in 4.0, near approximation. ClassDB::add_compatibility_class("AnimationTreePlayer", "AnimationTree"); + ClassDB::add_compatibility_class("BitmapFont", "Font"); + ClassDB::add_compatibility_class("DynamicFont", "Font"); + ClassDB::add_compatibility_class("DynamicFontData", "FontData"); ClassDB::add_compatibility_class("ToolButton", "Button"); // Renamed in 4.0. @@ -841,6 +847,7 @@ void register_scene_types() { ClassDB::add_compatibility_class("CSGShape", "CSGShape3D"); ClassDB::add_compatibility_class("CSGSphere", "CSGSphere3D"); ClassDB::add_compatibility_class("CSGTorus", "CSGTorus3D"); + ClassDB::add_compatibility_class("CubeMesh", "BoxMesh"); ClassDB::add_compatibility_class("CylinderShape", "CylinderShape3D"); ClassDB::add_compatibility_class("DirectionalLight", "DirectionalLight3D"); ClassDB::add_compatibility_class("EditorSpatialGizmo", "EditorNode3DGizmo"); @@ -908,6 +915,7 @@ void register_scene_types() { ClassDB::add_compatibility_class("SpringArm", "SpringArm3D"); ClassDB::add_compatibility_class("Sprite", "Sprite2D"); ClassDB::add_compatibility_class("StaticBody", "StaticBody3D"); + ClassDB::add_compatibility_class("TextureProgress", "TextureProgressBar"); ClassDB::add_compatibility_class("VehicleBody", "VehicleBody3D"); ClassDB::add_compatibility_class("VehicleWheel", "VehicleWheel3D"); ClassDB::add_compatibility_class("ViewportContainer", "SubViewportContainer"); @@ -923,15 +931,15 @@ void register_scene_types() { ClassDB::add_compatibility_class("StreamTexture", "StreamTexture2D"); ClassDB::add_compatibility_class("Light2D", "PointLight2D"); -#endif +#endif /* DISABLE_DEPRECATED */ OS::get_singleton()->yield(); //may take time to init for (int i = 0; i < 20; i++) { - GLOBAL_DEF("layer_names/2d_render/layer_" + itos(i + 1), ""); - GLOBAL_DEF("layer_names/2d_physics/layer_" + itos(i + 1), ""); - GLOBAL_DEF("layer_names/3d_render/layer_" + itos(i + 1), ""); - GLOBAL_DEF("layer_names/3d_physics/layer_" + itos(i + 1), ""); + GLOBAL_DEF(vformat("layer_names/2d_render/layer_%d", i), ""); + GLOBAL_DEF(vformat("layer_names/2d_physics/layer_%d", i), ""); + GLOBAL_DEF(vformat("layer_names/3d_render/layer_%d", i), ""); + GLOBAL_DEF(vformat("layer_names/3d_physics/layer_%d", i), ""); } bool default_theme_hidpi = GLOBAL_DEF("gui/theme/use_hidpi", false); @@ -972,8 +980,13 @@ void unregister_scene_types() { SceneDebugger::deinitialize(); clear_default_theme(); - ResourceLoader::remove_resource_format_loader(resource_loader_dynamic_font); - resource_loader_dynamic_font.unref(); + ResourceLoader::remove_resource_format_loader(resource_loader_font); + resource_loader_font.unref(); + +#ifndef DISABLE_DEPRECATED + ResourceLoader::remove_resource_format_loader(resource_loader_compat_font); + resource_loader_compat_font.unref(); +#endif /* DISABLE_DEPRECATED */ ResourceLoader::remove_resource_format_loader(resource_loader_texture_layered); resource_loader_texture_layered.unref(); @@ -984,8 +997,6 @@ void unregister_scene_types() { ResourceLoader::remove_resource_format_loader(resource_loader_stream_texture); resource_loader_stream_texture.unref(); - DynamicFont::finish_dynamic_fonts(); - ResourceSaver::remove_resource_format_saver(resource_saver_text); resource_saver_text.unref(); @@ -998,9 +1009,6 @@ void unregister_scene_types() { ResourceLoader::remove_resource_format_loader(resource_loader_shader); resource_loader_shader.unref(); - ResourceLoader::remove_resource_format_loader(resource_loader_bmfont); - resource_loader_bmfont.unref(); - //StandardMaterial3D is not initialised when 3D is disabled, so it shouldn't be cleaned up either #ifndef _3D_DISABLED BaseMaterial3D::finish_shaders(); diff --git a/scene/register_scene_types.h b/scene/register_scene_types.h index 603321991e..1ff542eef8 100644 --- a/scene/register_scene_types.h +++ b/scene/register_scene_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/SCsub b/scene/resources/SCsub index 3a86b22835..f4dc7a46fb 100644 --- a/scene/resources/SCsub +++ b/scene/resources/SCsub @@ -2,6 +2,25 @@ Import("env") -env.add_source_files(env.scene_sources, "*.cpp") +# Thirdparty code + +thirdparty_obj = [] + +thirdparty_sources = "#thirdparty/misc/mikktspace.c" + +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.scene_sources += thirdparty_obj + +# Godot source files + +scene_obj = [] + +env.add_source_files(scene_obj, "*.cpp") +env.scene_sources += scene_obj + +# Needed to force rebuilding the scene files when the thirdparty code is updated. +env.Depends(scene_obj, thirdparty_obj) SConscript("default_theme/SCsub") diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index b2aad97d3b..796fe1d106 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,8 +33,6 @@ #include "core/math/geometry_3d.h" -#define ANIM_MIN_LENGTH 0.001 - bool Animation::_set(const StringName &p_name, const Variant &p_value) { String name = p_name; @@ -810,8 +808,8 @@ int Animation::transform_track_insert_key(int p_track, float p_time, const Vecto return ret; } -void Animation::track_remove_key_at_position(int p_track, float p_pos) { - int idx = track_find_key(p_track, p_pos, true); +void Animation::track_remove_key_at_time(int p_track, float p_time) { + int idx = track_find_key(p_track, p_time, true); ERR_FAIL_COND(idx < 0); track_remove_key(p_track, idx); } @@ -2608,7 +2606,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key); ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1)); ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key); - ClassDB::bind_method(D_METHOD("track_remove_key_at_position", "track_idx", "position"), &Animation::track_remove_key_at_position); + ClassDB::bind_method(D_METHOD("track_remove_key_at_time", "track_idx", "time"), &Animation::track_remove_key_at_time); ClassDB::bind_method(D_METHOD("track_set_key_value", "track_idx", "key", "value"), &Animation::track_set_key_value); ClassDB::bind_method(D_METHOD("track_set_key_transition", "track_idx", "key_idx", "transition"), &Animation::track_set_key_transition); ClassDB::bind_method(D_METHOD("track_set_key_time", "track_idx", "key_idx", "time"), &Animation::track_set_key_time); diff --git a/scene/resources/animation.h b/scene/resources/animation.h index c52431f5f6..fa374e6ad2 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,8 @@ #include "core/io/resource.h" +#define ANIM_MIN_LENGTH 0.001 + class Animation : public Resource { GDCLASS(Animation, Resource); RES_BASE_EXTENSION("anim"); @@ -293,7 +295,7 @@ public: void track_set_key_time(int p_track, int p_key_idx, float p_time); int track_find_key(int p_track, float p_time, bool p_exact = false) const; void track_remove_key(int p_track, int p_idx); - void track_remove_key_at_position(int p_track, float p_pos); + void track_remove_key_at_time(int p_track, float p_time); int track_get_key_count(int p_track) const; Variant track_get_key_value(int p_track, int p_key_idx) const; float track_get_key_time(int p_track, int p_key_idx) const; diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 600a859894..8fdb004e65 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h index d91cdef57d..ba475c0888 100644 --- a/scene/resources/audio_stream_sample.h +++ b/scene/resources/audio_stream_sample.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 10f0de8ff8..dfb7a1fdfb 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -43,7 +43,7 @@ void BitMap::create(const Size2 &p_size) { } void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshold) { - ERR_FAIL_COND(p_image.is_null() || p_image->empty()); + ERR_FAIL_COND(p_image.is_null() || p_image->is_empty()); Ref<Image> img = p_image->duplicate(); img->convert(Image::FORMAT_LA8); ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8); @@ -63,7 +63,7 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshol } void BitMap::set_bit_rect(const Rect2 &p_rect, bool p_value) { - Rect2i current = Rect2i(0, 0, width, height).clip(p_rect); + Rect2i current = Rect2i(0, 0, width, height).intersection(p_rect); uint8_t *data = bitmask.ptrw(); for (int i = current.position.x; i < current.position.x + current.size.x; i++) { @@ -482,7 +482,7 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ } Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon) const { - Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); + Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect); print_verbose("BitMap: Rect: " + r); Point2i from; @@ -522,7 +522,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { bool bit_value = p_pixels > 0; p_pixels = Math::abs(p_pixels); - Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); + Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect); Ref<BitMap> copy; copy.instance(); diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index 56ff72c094..7414265cee 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/box_shape_3d.cpp b/scene/resources/box_shape_3d.cpp index e1c8a377c0..fadbf21e43 100644 --- a/scene/resources/box_shape_3d.cpp +++ b/scene/resources/box_shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,8 +34,8 @@ Vector<Vector3> BoxShape3D::get_debug_mesh_lines() const { Vector<Vector3> lines; AABB aabb; - aabb.position = -get_extents(); - aabb.size = aabb.position * -2; + aabb.position = -size / 2; + aabb.size = size; for (int i = 0; i < 12; i++) { Vector3 a, b; @@ -48,33 +48,33 @@ Vector<Vector3> BoxShape3D::get_debug_mesh_lines() const { } real_t BoxShape3D::get_enclosing_radius() const { - return extents.length(); + return size.length() / 2; } void BoxShape3D::_update_shape() { - PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), extents); + PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), size / 2); Shape3D::_update_shape(); } -void BoxShape3D::set_extents(const Vector3 &p_extents) { - extents = p_extents; +void BoxShape3D::set_size(const Vector3 &p_size) { + size = p_size; _update_shape(); notify_change_to_owners(); - _change_notify("extents"); + _change_notify("size"); } -Vector3 BoxShape3D::get_extents() const { - return extents; +Vector3 BoxShape3D::get_size() const { + return size; } void BoxShape3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_extents", "extents"), &BoxShape3D::set_extents); - ClassDB::bind_method(D_METHOD("get_extents"), &BoxShape3D::get_extents); + ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxShape3D::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &BoxShape3D::get_size); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size"); } BoxShape3D::BoxShape3D() : Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_BOX)) { - set_extents(Vector3(1, 1, 1)); + set_size(Vector3(2, 2, 2)); } diff --git a/scene/resources/box_shape_3d.h b/scene/resources/box_shape_3d.h index fe634ce568..fce05d61ed 100644 --- a/scene/resources/box_shape_3d.h +++ b/scene/resources/box_shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,7 +35,7 @@ class BoxShape3D : public Shape3D { GDCLASS(BoxShape3D, Shape3D); - Vector3 extents; + Vector3 size; protected: static void _bind_methods(); @@ -43,8 +43,8 @@ protected: virtual void _update_shape() override; public: - void set_extents(const Vector3 &p_extents); - Vector3 get_extents() const; + void set_size(const Vector3 &p_size); + Vector3 get_size() const; virtual Vector<Vector3> get_debug_mesh_lines() const override; virtual real_t get_enclosing_radius() const override; diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp index 6b6ed51ed0..695b14939f 100644 --- a/scene/resources/camera_effects.cpp +++ b/scene/resources/camera_effects.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/camera_effects.h b/scene/resources/camera_effects.h index 9a26f3d0b2..cf12b6d3c0 100644 --- a/scene/resources/camera_effects.h +++ b/scene/resources/camera_effects.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp index e519970f38..6041f4a8b0 100644 --- a/scene/resources/capsule_shape_2d.cpp +++ b/scene/resources/capsule_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/capsule_shape_2d.h b/scene/resources/capsule_shape_2d.h index 1caa6c68b8..8c8c1b259b 100644 --- a/scene/resources/capsule_shape_2d.h +++ b/scene/resources/capsule_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/capsule_shape_3d.cpp b/scene/resources/capsule_shape_3d.cpp index 5da7f682e5..a0b454a00f 100644 --- a/scene/resources/capsule_shape_3d.cpp +++ b/scene/resources/capsule_shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/capsule_shape_3d.h b/scene/resources/capsule_shape_3d.h index 432ca5654e..b5b8912445 100644 --- a/scene/resources/capsule_shape_3d.h +++ b/scene/resources/capsule_shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp index dc1bf3b185..ba49587d9d 100644 --- a/scene/resources/circle_shape_2d.cpp +++ b/scene/resources/circle_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/circle_shape_2d.h b/scene/resources/circle_shape_2d.h index ac8757e781..a066b773ef 100644 --- a/scene/resources/circle_shape_2d.h +++ b/scene/resources/circle_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/concave_polygon_shape_2d.cpp b/scene/resources/concave_polygon_shape_2d.cpp index eecf8afa8f..3d6df3bb9c 100644 --- a/scene/resources/concave_polygon_shape_2d.cpp +++ b/scene/resources/concave_polygon_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/concave_polygon_shape_2d.h b/scene/resources/concave_polygon_shape_2d.h index df8cc9920f..98ae341e97 100644 --- a/scene/resources/concave_polygon_shape_2d.h +++ b/scene/resources/concave_polygon_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp index 7cbafcbc4d..358c99ad09 100644 --- a/scene/resources/concave_polygon_shape_3d.cpp +++ b/scene/resources/concave_polygon_shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/concave_polygon_shape_3d.h b/scene/resources/concave_polygon_shape_3d.h index c17765b9ef..391459a3d7 100644 --- a/scene/resources/concave_polygon_shape_3d.h +++ b/scene/resources/concave_polygon_shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index 2b7531c630..656e064356 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/convex_polygon_shape_2d.h b/scene/resources/convex_polygon_shape_2d.h index 294157bec5..1813b608bd 100644 --- a/scene/resources/convex_polygon_shape_2d.h +++ b/scene/resources/convex_polygon_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/convex_polygon_shape_3d.cpp b/scene/resources/convex_polygon_shape_3d.cpp index 29549e1114..770d547231 100644 --- a/scene/resources/convex_polygon_shape_3d.cpp +++ b/scene/resources/convex_polygon_shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/convex_polygon_shape_3d.h b/scene/resources/convex_polygon_shape_3d.h index f436d2f5d4..edd127c8f4 100644 --- a/scene/resources/convex_polygon_shape_3d.h +++ b/scene/resources/convex_polygon_shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index de076670cf..123c77a036 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -602,7 +602,7 @@ void Curve2D::remove_point(int p_index) { } void Curve2D::clear_points() { - if (!points.empty()) { + if (!points.is_empty()) { points.clear(); baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); @@ -1087,7 +1087,7 @@ void Curve3D::remove_point(int p_index) { } void Curve3D::clear_points() { - if (!points.empty()) { + if (!points.is_empty()) { points.clear(); baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 6c5f4b7057..f33f3c5abe 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/cylinder_shape_3d.cpp b/scene/resources/cylinder_shape_3d.cpp index bb8c27a60d..3e9cdca872 100644 --- a/scene/resources/cylinder_shape_3d.cpp +++ b/scene/resources/cylinder_shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/cylinder_shape_3d.h b/scene/resources/cylinder_shape_3d.h index e579e1f7cf..93e8345727 100644 --- a/scene/resources/cylinder_shape_3d.h +++ b/scene/resources/cylinder_shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/default_theme/arrow_left.png b/scene/resources/default_theme/arrow_left.png Binary files differnew file mode 100644 index 0000000000..4163059dd3 --- /dev/null +++ b/scene/resources/default_theme/arrow_left.png diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index f65f78b332..dad5622117 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,6 +38,8 @@ #include "font_hidpi.inc" #include "font_lodpi.inc" +#include "servers/text_server.h" + typedef Map<const void *, Ref<ImageTexture>> TexCacheMap; static TexCacheMap *tex_cache; @@ -62,24 +64,35 @@ static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, fl Ref<StyleBoxTexture> style(memnew(StyleBoxTexture)); style->set_texture(texture); - style->set_margin_size(MARGIN_LEFT, p_left * scale); - style->set_margin_size(MARGIN_RIGHT, p_right * scale); - style->set_margin_size(MARGIN_BOTTOM, p_botton * scale); - style->set_margin_size(MARGIN_TOP, p_top * scale); - style->set_default_margin(MARGIN_LEFT, p_margin_left * scale); - style->set_default_margin(MARGIN_RIGHT, p_margin_right * scale); - style->set_default_margin(MARGIN_BOTTOM, p_margin_botton * scale); - style->set_default_margin(MARGIN_TOP, p_margin_top * scale); + style->set_margin_size(SIDE_LEFT, p_left * scale); + style->set_margin_size(SIDE_RIGHT, p_right * scale); + style->set_margin_size(SIDE_BOTTOM, p_botton * scale); + style->set_margin_size(SIDE_TOP, p_top * scale); + style->set_default_margin(SIDE_LEFT, p_margin_left * scale); + style->set_default_margin(SIDE_RIGHT, p_margin_right * scale); + style->set_default_margin(SIDE_BOTTOM, p_margin_botton * scale); + style->set_default_margin(SIDE_TOP, p_margin_top * scale); style->set_draw_center(p_draw_center); return style; } +static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) { + Ref<StyleBoxFlat> style(memnew(StyleBoxFlat)); + style->set_bg_color(p_color); + style->set_default_margin(SIDE_LEFT, p_margin_left * scale); + style->set_default_margin(SIDE_RIGHT, p_margin_right * scale); + style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * scale); + style->set_default_margin(SIDE_TOP, p_margin_top * scale); + + return style; +} + static Ref<StyleBoxTexture> sb_expand(Ref<StyleBoxTexture> p_sbox, float p_left, float p_top, float p_right, float p_botton) { - p_sbox->set_expand_margin_size(MARGIN_LEFT, p_left * scale); - p_sbox->set_expand_margin_size(MARGIN_TOP, p_top * scale); - p_sbox->set_expand_margin_size(MARGIN_RIGHT, p_right * scale); - p_sbox->set_expand_margin_size(MARGIN_BOTTOM, p_botton * scale); + p_sbox->set_expand_margin_size(SIDE_LEFT, p_left * scale); + p_sbox->set_expand_margin_size(SIDE_TOP, p_top * scale); + p_sbox->set_expand_margin_size(SIDE_RIGHT, p_right * scale); + p_sbox->set_expand_margin_size(SIDE_BOTTOM, p_botton * scale); return p_sbox; } @@ -95,47 +108,32 @@ static Ref<Texture2D> make_icon(T p_src) { return texture; } -static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_charcount, const int *p_char_rects, int p_kerning_count, const int *p_kernings, int p_w, int p_h, const unsigned char *p_img) { - Ref<BitmapFont> font(memnew(BitmapFont)); - - Ref<Image> image = memnew(Image(p_img)); - Ref<ImageTexture> tex = memnew(ImageTexture); - tex->create_from_image(image); - - font->add_texture(tex); - - for (int i = 0; i < p_charcount; i++) { - const int *c = &p_char_rects[i * 8]; +static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, bool p_flip_x = false) { + if (!p_flip_y && !p_flip_x) { + return p_texture; + } - int chr = c[0]; - Rect2 frect; - frect.position.x = c[1]; - frect.position.y = c[2]; - frect.size.x = c[3]; - frect.size.y = c[4]; - Point2 align(c[6], c[5]); - int advance = c[7]; + Ref<ImageTexture> texture(memnew(ImageTexture)); + Ref<Image> img = p_texture->get_data(); - font->add_char(chr, 0, frect, align, advance); + if (p_flip_y) { + img->flip_y(); } - - for (int i = 0; i < p_kerning_count; i++) { - font->add_kerning_pair(p_kernings[i * 3 + 0], p_kernings[i * 3 + 1], p_kernings[i * 3 + 2]); + if (p_flip_x) { + img->flip_x(); } - font->set_height(p_height); - font->set_ascent(p_ascent); - - return font; + texture->create_from_image(img); + return texture; } static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_botton = -1) { Ref<StyleBox> style(memnew(StyleBoxEmpty)); - style->set_default_margin(MARGIN_LEFT, p_margin_left * scale); - style->set_default_margin(MARGIN_RIGHT, p_margin_right * scale); - style->set_default_margin(MARGIN_BOTTOM, p_margin_botton * scale); - style->set_default_margin(MARGIN_TOP, p_margin_top * scale); + style->set_default_margin(SIDE_LEFT, p_margin_left * scale); + style->set_default_margin(SIDE_RIGHT, p_margin_right * scale); + style->set_default_margin(SIDE_BOTTOM, p_margin_botton * scale); + style->set_default_margin(SIDE_TOP, p_margin_top * scale); return style; } @@ -164,7 +162,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<StyleBoxTexture> focus = make_stylebox(focus_png, 5, 5, 5, 5); for (int i = 0; i < 4; i++) { - focus->set_expand_margin_size(Margin(i), 1 * scale); + focus->set_expand_margin_size(Side(i), 1 * scale); } // Button @@ -182,11 +180,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("focus", "Button", sb_button_focus); theme->set_font("font", "Button", Ref<Font>()); + theme->set_font_size("font_size", "Button", -1); + theme->set_constant("outline_size", "Button", 0 * scale); theme->set_color("font_color", "Button", control_font_color); theme->set_color("font_color_pressed", "Button", control_font_color_pressed); theme->set_color("font_color_hover", "Button", control_font_color_hover); theme->set_color("font_color_disabled", "Button", control_font_color_disabled); + theme->set_color("font_outline_modulate", "Button", Color(1, 1, 1)); theme->set_constant("hseparation", "Button", 2 * scale); @@ -195,6 +196,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("focus", "LinkButton", focus); theme->set_font("font", "LinkButton", Ref<Font>()); + theme->set_font_size("font_size", "LinkButton", -1); theme->set_color("font_color", "LinkButton", control_font_color); theme->set_color("font_color_pressed", "LinkButton", control_font_color_pressed); @@ -211,6 +213,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus); theme->set_font("font", "ColorPickerButton", Ref<Font>()); + theme->set_font_size("font_size", "ColorPickerButton", -1); theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1)); theme->set_color("font_color_pressed", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1)); @@ -221,21 +224,33 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // OptionButton + Ref<StyleBox> sb_optbutton_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2); + theme->set_stylebox("focus", "OptionButton", sb_optbutton_focus); + Ref<StyleBox> sb_optbutton_normal = sb_expand(make_stylebox(option_button_normal_png, 4, 4, 21, 4, 6, 3, 9, 3), 2, 2, 2, 2); Ref<StyleBox> sb_optbutton_pressed = sb_expand(make_stylebox(option_button_pressed_png, 4, 4, 21, 4, 6, 3, 9, 3), 2, 2, 2, 2); Ref<StyleBox> sb_optbutton_hover = sb_expand(make_stylebox(option_button_hover_png, 4, 4, 21, 4, 6, 2, 9, 2), 2, 2, 2, 2); Ref<StyleBox> sb_optbutton_disabled = sb_expand(make_stylebox(option_button_disabled_png, 4, 4, 21, 4, 6, 2, 9, 2), 2, 2, 2, 2); - Ref<StyleBox> sb_optbutton_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2); theme->set_stylebox("normal", "OptionButton", sb_optbutton_normal); theme->set_stylebox("pressed", "OptionButton", sb_optbutton_pressed); theme->set_stylebox("hover", "OptionButton", sb_optbutton_hover); theme->set_stylebox("disabled", "OptionButton", sb_optbutton_disabled); - theme->set_stylebox("focus", "OptionButton", sb_optbutton_focus); + + Ref<StyleBox> sb_optbutton_normal_mirrored = sb_expand(make_stylebox(option_button_normal_mirrored_png, 21, 4, 4, 4, 9, 3, 6, 3), 2, 2, 2, 2); + Ref<StyleBox> sb_optbutton_pressed_mirrored = sb_expand(make_stylebox(option_button_pressed_mirrored_png, 21, 4, 4, 4, 9, 3, 6, 3), 2, 2, 2, 2); + Ref<StyleBox> sb_optbutton_hover_mirrored = sb_expand(make_stylebox(option_button_hover_mirrored_png, 21, 4, 4, 4, 9, 2, 6, 2), 2, 2, 2, 2); + Ref<StyleBox> sb_optbutton_disabled_mirrored = sb_expand(make_stylebox(option_button_disabled_mirrored_png, 21, 4, 4, 4, 9, 2, 6, 2), 2, 2, 2, 2); + + theme->set_stylebox("normal_mirrored", "OptionButton", sb_optbutton_normal_mirrored); + theme->set_stylebox("pressed_mirrored", "OptionButton", sb_optbutton_pressed_mirrored); + theme->set_stylebox("hover_mirrored", "OptionButton", sb_optbutton_hover_mirrored); + theme->set_stylebox("disabled_mirrored", "OptionButton", sb_optbutton_disabled_mirrored); theme->set_icon("arrow", "OptionButton", make_icon(option_arrow_png)); theme->set_font("font", "OptionButton", Ref<Font>()); + theme->set_font_size("font_size", "OptionButton", -1); theme->set_color("font_color", "OptionButton", control_font_color); theme->set_color("font_color_pressed", "OptionButton", control_font_color_pressed); @@ -254,6 +269,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("focus", "MenuButton", sb_button_focus); theme->set_font("font", "MenuButton", Ref<Font>()); + theme->set_font_size("font_size", "MenuButton", -1); theme->set_color("font_color", "MenuButton", control_font_color); theme->set_color("font_color_pressed", "MenuButton", control_font_color_pressed); @@ -265,15 +281,15 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // CheckBox Ref<StyleBox> cbx_empty = memnew(StyleBoxEmpty); - cbx_empty->set_default_margin(MARGIN_LEFT, 4 * scale); - cbx_empty->set_default_margin(MARGIN_RIGHT, 4 * scale); - cbx_empty->set_default_margin(MARGIN_TOP, 4 * scale); - cbx_empty->set_default_margin(MARGIN_BOTTOM, 4 * scale); + cbx_empty->set_default_margin(SIDE_LEFT, 4 * scale); + cbx_empty->set_default_margin(SIDE_RIGHT, 4 * scale); + cbx_empty->set_default_margin(SIDE_TOP, 4 * scale); + cbx_empty->set_default_margin(SIDE_BOTTOM, 4 * scale); Ref<StyleBox> cbx_focus = focus; - cbx_focus->set_default_margin(MARGIN_LEFT, 4 * scale); - cbx_focus->set_default_margin(MARGIN_RIGHT, 4 * scale); - cbx_focus->set_default_margin(MARGIN_TOP, 4 * scale); - cbx_focus->set_default_margin(MARGIN_BOTTOM, 4 * scale); + cbx_focus->set_default_margin(SIDE_LEFT, 4 * scale); + cbx_focus->set_default_margin(SIDE_RIGHT, 4 * scale); + cbx_focus->set_default_margin(SIDE_TOP, 4 * scale); + cbx_focus->set_default_margin(SIDE_BOTTOM, 4 * scale); theme->set_stylebox("normal", "CheckBox", cbx_empty); theme->set_stylebox("pressed", "CheckBox", cbx_empty); @@ -288,6 +304,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("radio_unchecked", "CheckBox", make_icon(radio_unchecked_png)); theme->set_font("font", "CheckBox", Ref<Font>()); + theme->set_font_size("font_size", "CheckBox", -1); theme->set_color("font_color", "CheckBox", control_font_color); theme->set_color("font_color_pressed", "CheckBox", control_font_color_pressed); @@ -301,10 +318,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // CheckButton Ref<StyleBox> cb_empty = memnew(StyleBoxEmpty); - cb_empty->set_default_margin(MARGIN_LEFT, 6 * scale); - cb_empty->set_default_margin(MARGIN_RIGHT, 6 * scale); - cb_empty->set_default_margin(MARGIN_TOP, 4 * scale); - cb_empty->set_default_margin(MARGIN_BOTTOM, 4 * scale); + cb_empty->set_default_margin(SIDE_LEFT, 6 * scale); + cb_empty->set_default_margin(SIDE_RIGHT, 6 * scale); + cb_empty->set_default_margin(SIDE_TOP, 4 * scale); + cb_empty->set_default_margin(SIDE_BOTTOM, 4 * scale); theme->set_stylebox("normal", "CheckButton", cb_empty); theme->set_stylebox("pressed", "CheckButton", cb_empty); @@ -318,7 +335,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("off", "CheckButton", make_icon(toggle_off_png)); theme->set_icon("off_disabled", "CheckButton", make_icon(toggle_off_disabled_png)); + theme->set_icon("on_mirrored", "CheckButton", make_icon(toggle_on_mirrored_png)); + theme->set_icon("on_disabled_mirrored", "CheckButton", make_icon(toggle_on_disabled_mirrored_png)); + theme->set_icon("off_mirrored", "CheckButton", make_icon(toggle_off_mirrored_png)); + theme->set_icon("off_disabled_mirrored", "CheckButton", make_icon(toggle_off_disabled_mirrored_png)); + theme->set_font("font", "CheckButton", Ref<Font>()); + theme->set_font_size("font_size", "CheckButton", -1); theme->set_color("font_color", "CheckButton", control_font_color); theme->set_color("font_color_pressed", "CheckButton", control_font_color_pressed); @@ -333,6 +356,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("normal", "Label", memnew(StyleBoxEmpty)); theme->set_font("font", "Label", Ref<Font>()); + theme->set_font_size("font_size", "Label", -1); theme->set_color("font_color", "Label", Color(1, 1, 1)); theme->set_color("font_color_shadow", "Label", Color(0, 0, 0, 0)); @@ -340,7 +364,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("shadow_offset_x", "Label", 1 * scale); theme->set_constant("shadow_offset_y", "Label", 1 * scale); - theme->set_constant("shadow_as_outline", "Label", 0 * scale); + theme->set_constant("outline_size", "Label", 0 * scale); + theme->set_constant("shadow_outline_size", "Label", 1 * scale); theme->set_constant("line_spacing", "Label", 3 * scale); // LineEdit @@ -350,6 +375,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("read_only", "LineEdit", make_stylebox(line_edit_disabled_png, 6, 6, 6, 6)); theme->set_font("font", "LineEdit", Ref<Font>()); + theme->set_font_size("font_size", "LineEdit", -1); theme->set_color("font_color", "LineEdit", control_font_color); theme->set_color("font_color_selected", "LineEdit", Color(0, 0, 0)); @@ -369,6 +395,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("fg", "ProgressBar", make_stylebox(progress_fill_png, 6, 6, 6, 6, 2, 1, 2, 1)); theme->set_font("font", "ProgressBar", Ref<Font>()); + theme->set_font_size("font_size", "ProgressBar", -1); theme->set_color("font_color", "ProgressBar", control_font_color_hover); theme->set_color("font_color_shadow", "ProgressBar", Color(0, 0, 0)); @@ -384,6 +411,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("space", "TextEdit", make_icon(space_png)); theme->set_font("font", "TextEdit", Ref<Font>()); + theme->set_font_size("font_size", "TextEdit", -1); theme->set_color("background_color", "TextEdit", Color(0, 0, 0, 0)); theme->set_color("completion_background_color", "TextEdit", Color(0.17, 0.16, 0.2)); @@ -423,6 +451,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("folded", "CodeEdit", make_icon(arrow_right_png)); theme->set_font("font", "CodeEdit", Ref<Font>()); + theme->set_font_size("font_size", "CodeEdit", -1); theme->set_color("background_color", "CodeEdit", Color(0, 0, 0, 0)); theme->set_color("completion_background_color", "CodeEdit", Color(0.17, 0.16, 0.2)); @@ -539,7 +568,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<StyleBoxTexture> selected = make_stylebox(selection_png, 6, 6, 6, 6); for (int i = 0; i < 4; i++) { - selected->set_expand_margin_size(Margin(i), 2 * scale); + selected->set_expand_margin_size(Side(i), 2 * scale); } theme->set_stylebox("panel", "PopupPanel", style_pp); @@ -563,13 +592,16 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("radio_checked", "PopupMenu", make_icon(radio_checked_png)); theme->set_icon("radio_unchecked", "PopupMenu", make_icon(radio_unchecked_png)); theme->set_icon("submenu", "PopupMenu", make_icon(submenu_png)); + theme->set_icon("submenu_mirrored", "PopupMenu", make_icon(submenu_mirrored_png)); theme->set_font("font", "PopupMenu", Ref<Font>()); + theme->set_font_size("font_size", "PopupMenu", -1); theme->set_color("font_color", "PopupMenu", control_font_color); theme->set_color("font_color_accel", "PopupMenu", Color(0.7, 0.7, 0.7, 0.8)); theme->set_color("font_color_disabled", "PopupMenu", Color(0.4, 0.4, 0.4, 0.8)); theme->set_color("font_color_hover", "PopupMenu", control_font_color); + theme->set_color("font_color_separator", "PopupMenu", control_font_color); theme->set_constant("hseparation", "PopupMenu", 4 * scale); theme->set_constant("vseparation", "PopupMenu", 4 * scale); @@ -585,8 +617,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<StyleBoxTexture> graph_bpoint = make_stylebox(graph_node_breakpoint_png, 6, 24, 6, 5, 16, 24, 16, 6); Ref<StyleBoxTexture> graph_position = make_stylebox(graph_node_position_png, 6, 24, 6, 5, 16, 24, 16, 6); - //graphsb->set_expand_margin_size(MARGIN_LEFT,10); - //graphsb->set_expand_margin_size(MARGIN_RIGHT,10); + //graphsb->set_expand_margin_size(SIDE_LEFT,10); + //graphsb->set_expand_margin_size(SIDE_RIGHT,10); theme->set_stylebox("frame", "GraphNode", graphsb); theme->set_stylebox("selectedframe", "GraphNode", graphsbselected); theme->set_stylebox("defaultframe", "GraphNode", graphsbdefault); @@ -632,9 +664,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("select_arrow", "Tree", make_icon(dropdown_png)); theme->set_icon("arrow", "Tree", make_icon(arrow_down_png)); theme->set_icon("arrow_collapsed", "Tree", make_icon(arrow_right_png)); + theme->set_icon("arrow_collapsed_mirrored", "Tree", make_icon(arrow_left_png)); theme->set_font("title_button_font", "Tree", Ref<Font>()); theme->set_font("font", "Tree", Ref<Font>()); + theme->set_font_size("font_size", "Tree", -1); theme->set_color("title_button_color", "Tree", control_font_color); theme->set_color("font_color", "Tree", control_font_color_low); @@ -663,7 +697,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("vseparation", "ItemList", 2); theme->set_constant("icon_margin", "ItemList", 4); theme->set_constant("line_separation", "ItemList", 2 * scale); + theme->set_font("font", "ItemList", Ref<Font>()); + theme->set_font_size("font_size", "ItemList", -1); + theme->set_color("font_color", "ItemList", control_font_color_lower); theme->set_color("font_color_selected", "ItemList", control_font_color_pressed); theme->set_color("guide_color", "ItemList", Color(0, 0, 0, 0.1)); @@ -676,8 +713,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<StyleBoxTexture> tc_sb = sb_expand(make_stylebox(tab_container_bg_png, 4, 4, 4, 4, 4, 4, 4, 4), 3, 3, 3, 3); - tc_sb->set_expand_margin_size(MARGIN_TOP, 2 * scale); - tc_sb->set_default_margin(MARGIN_TOP, 8 * scale); + tc_sb->set_expand_margin_size(SIDE_TOP, 2 * scale); + tc_sb->set_default_margin(SIDE_TOP, 8 * scale); theme->set_stylebox("tab_fg", "TabContainer", sb_expand(make_stylebox(tab_current_png, 4, 4, 4, 1, 16, 4, 16, 4), 2, 2, 2, 2)); theme->set_stylebox("tab_bg", "TabContainer", sb_expand(make_stylebox(tab_behind_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3)); @@ -692,6 +729,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("menu_highlight", "TabContainer", make_icon(tab_menu_hl_png)); theme->set_font("font", "TabContainer", Ref<Font>()); + theme->set_font_size("font_size", "TabContainer", -1); theme->set_color("font_color_fg", "TabContainer", control_font_color_hover); theme->set_color("font_color_bg", "TabContainer", control_font_color_low); @@ -716,6 +754,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("close", "Tabs", make_icon(tab_close_png)); theme->set_font("font", "Tabs", Ref<Font>()); + theme->set_font_size("font_size", "Tabs", -1); theme->set_color("font_color_fg", "Tabs", control_font_color_hover); theme->set_color("font_color_bg", "Tabs", control_font_color_low); @@ -769,12 +808,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<StyleBoxTexture> style_tt = make_stylebox(tooltip_bg_png, 4, 4, 4, 4); for (int i = 0; i < 4; i++) { - style_tt->set_expand_margin_size((Margin)i, 4 * scale); + style_tt->set_expand_margin_size((Side)i, 4 * scale); } theme->set_stylebox("panel", "TooltipPanel", style_tt); theme->set_font("font", "TooltipLabel", Ref<Font>()); + theme->set_font_size("font_size", "TooltipLabel", -1); theme->set_color("font_color", "TooltipLabel", Color(0, 0, 0)); theme->set_color("font_color_shadow", "TooltipLabel", Color(0, 0, 0, 0.1)); @@ -793,6 +833,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_font("bold_italics_font", "RichTextLabel", Ref<Font>()); theme->set_font("mono_font", "RichTextLabel", Ref<Font>()); + theme->set_font_size("normal_font_size", "RichTextLabel", -1); + theme->set_font_size("bold_font_size", "RichTextLabel", -1); + theme->set_font_size("italics_font_size", "RichTextLabel", -1); + theme->set_font_size("bold_italics_font_size", "RichTextLabel", -1); + theme->set_font_size("mono_font_size", "RichTextLabel", -1); + theme->set_color("default_color", "RichTextLabel", Color(1, 1, 1)); theme->set_color("font_color_selected", "RichTextLabel", font_color_selection); theme->set_color("selection_color", "RichTextLabel", Color(0.1, 0.1, 1, 0.8)); @@ -807,6 +853,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale); theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale); + theme->set_color("table_odd_row_bg", "RichTextLabel", Color(0, 0, 0, 0)); + theme->set_color("table_even_row_bg", "RichTextLabel", Color(0, 0, 0, 0)); + theme->set_color("table_border", "RichTextLabel", Color(0, 0, 0, 0)); // Containers theme->set_stylebox("bg", "VSplitContainer", make_stylebox(vsplit_bg_png, 1, 1, 1, 1)); @@ -835,6 +884,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("reset", "GraphEdit", make_icon(icon_zoom_reset_png)); theme->set_icon("more", "GraphEdit", make_icon(icon_zoom_more_png)); theme->set_icon("snap", "GraphEdit", make_icon(icon_snap_grid_png)); + theme->set_icon("minimap", "GraphEdit", make_icon(icon_grid_minimap_png)); theme->set_stylebox("bg", "GraphEdit", make_stylebox(tree_bg_png, 4, 4, 4, 5)); theme->set_color("grid_minor", "GraphEdit", Color(1, 1, 1, 0.05)); theme->set_color("grid_major", "GraphEdit", Color(1, 1, 1, 0.2)); @@ -848,6 +898,19 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale); theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale); + theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0)); + Ref<StyleBoxFlat> style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0); + style_minimap_camera->set_border_color(Color(0.65, 0.65, 0.65, 0.45)); + style_minimap_camera->set_border_width_all(1); + theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera); + Ref<StyleBoxFlat> style_minimap_node = make_flat_stylebox(Color(1, 1, 1), 0, 0, 0, 0); + style_minimap_node->set_corner_radius_all(2); + theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node); + + Ref<Texture2D> resizer_icon = make_icon(window_resizer_png); + theme->set_icon("resizer", "GraphEditMinimap", flip_icon(resizer_icon, true, true)); + theme->set_color("resizer_color", "GraphEditMinimap", Color(1, 1, 1, 0.85)); + // Theme default_icon = make_icon(error_icon_png); @@ -863,12 +926,47 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { Ref<StyleBox> default_style; Ref<Texture2D> default_icon; Ref<Font> default_font; + int default_font_size = 16; if (p_font.is_valid()) { default_font = p_font; } else if (p_hidpi) { - default_font = make_font(_hidpi_font_height, _hidpi_font_ascent, _hidpi_font_charcount, &_hidpi_font_charrects[0][0], _hidpi_font_kerning_pair_count, &_hidpi_font_kerning_pairs[0][0], _hidpi_font_img_width, _hidpi_font_img_height, _hidpi_font_img_data); + TextServer::BitmapFontData data; + data.height = _hidpi_font_height; + data.ascent = _hidpi_font_ascent; + data.charcount = _hidpi_font_charcount; + data.char_rects = &_hidpi_font_charrects[0][0]; + data.kerning_count = _hidpi_font_kerning_pair_count; + data.kernings = &_hidpi_font_kerning_pairs[0][0]; + data.w = _hidpi_font_img_width; + data.h = _hidpi_font_img_height; + data.img = _hidpi_font_img_data; + + Ref<FontData> font_data; + font_data.instance(); + font_data->load_memory((const uint8_t *)&data, sizeof(data), "fnt"); + default_font_size = font_data->get_base_size(); + + default_font.instance(); + default_font->add_data(font_data); } else { - default_font = make_font(_lodpi_font_height, _lodpi_font_ascent, _lodpi_font_charcount, &_lodpi_font_charrects[0][0], _lodpi_font_kerning_pair_count, &_lodpi_font_kerning_pairs[0][0], _lodpi_font_img_width, _lodpi_font_img_height, _lodpi_font_img_data); + TextServer::BitmapFontData data; + data.height = _lodpi_font_height; + data.ascent = _lodpi_font_ascent; + data.charcount = _lodpi_font_charcount; + data.char_rects = &_lodpi_font_charrects[0][0]; + data.kerning_count = _lodpi_font_kerning_pair_count; + data.kernings = &_lodpi_font_kerning_pairs[0][0]; + data.w = _lodpi_font_img_width; + data.h = _lodpi_font_img_height; + data.img = _lodpi_font_img_data; + + Ref<FontData> font_data; + font_data.instance(); + font_data->load_memory((const uint8_t *)&data, sizeof(data), "fnt"); + default_font_size = font_data->get_base_size(); + + default_font.instance(); + default_font->add_data(font_data); } Ref<Font> large_font = default_font; fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0); @@ -877,6 +975,7 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { Theme::set_default_icon(default_icon); Theme::set_default_style(default_style); Theme::set_default_font(default_font); + Theme::set_default_font_size(default_font_size); } void clear_default_theme() { diff --git a/scene/resources/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h index 46f89a9b50..a7b2bec5a4 100644 --- a/scene/resources/default_theme/default_theme.h +++ b/scene/resources/default_theme/default_theme.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/default_theme/font_lodpi.inc b/scene/resources/default_theme/font_lodpi.inc index 959e2c1d7b..d2f5851224 100644 --- a/scene/resources/default_theme/font_lodpi.inc +++ b/scene/resources/default_theme/font_lodpi.inc @@ -8,7 +8,7 @@ static const int _lodpi_font_charrects[191][8]={ {224,85,180,5,11,0,1,7}, {192,32,16,11,13,-2,-1,9}, {96,2,216,3,2,0,3,8}, -{160,1734439808,0,0,0,11,0,4}, +{160,0,0,0,0,11,0,4}, {32,0,0,0,0,11,0,4}, {33,65,234,2,10,1,1,4}, {225,112,169,5,11,0,1,7}, diff --git a/scene/resources/default_theme/icon_grid_minimap.png b/scene/resources/default_theme/icon_grid_minimap.png Binary files differnew file mode 100644 index 0000000000..00a6179d5e --- /dev/null +++ b/scene/resources/default_theme/icon_grid_minimap.png diff --git a/scene/resources/default_theme/option_button_disabled_mirrored.png b/scene/resources/default_theme/option_button_disabled_mirrored.png Binary files differnew file mode 100644 index 0000000000..9d149a35ca --- /dev/null +++ b/scene/resources/default_theme/option_button_disabled_mirrored.png diff --git a/scene/resources/default_theme/option_button_hover_mirrored.png b/scene/resources/default_theme/option_button_hover_mirrored.png Binary files differnew file mode 100644 index 0000000000..d49c165645 --- /dev/null +++ b/scene/resources/default_theme/option_button_hover_mirrored.png diff --git a/scene/resources/default_theme/option_button_normal_mirrored.png b/scene/resources/default_theme/option_button_normal_mirrored.png Binary files differnew file mode 100644 index 0000000000..feec848f33 --- /dev/null +++ b/scene/resources/default_theme/option_button_normal_mirrored.png diff --git a/scene/resources/default_theme/option_button_pressed_mirrored.png b/scene/resources/default_theme/option_button_pressed_mirrored.png Binary files differnew file mode 100644 index 0000000000..94cabb18d6 --- /dev/null +++ b/scene/resources/default_theme/option_button_pressed_mirrored.png diff --git a/scene/resources/default_theme/submenu_mirrored.png b/scene/resources/default_theme/submenu_mirrored.png Binary files differnew file mode 100644 index 0000000000..1142b9ba9f --- /dev/null +++ b/scene/resources/default_theme/submenu_mirrored.png diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h index a15efb593a..b905c9db69 100644 --- a/scene/resources/default_theme/theme_data.h +++ b/scene/resources/default_theme/theme_data.h @@ -6,6 +6,10 @@ static const unsigned char arrow_down_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x34, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x32, 0x78, 0xf0, 0x1f, 0x15, 0x52, 0x20, 0xf1, 0x30, 0xee, 0xc1, 0x17, 0xb8, 0xf0, 0xb7, 0x87, 0x69, 0x48, 0xb6, 0xdc, 0xd7, 0xb8, 0x7f, 0x9, 0x2c, 0x7c, 0xfd, 0xb1, 0x2e, 0x9a, 0x3, 0x5e, 0x70, 0x3f, 0x9c, 0xff, 0x70, 0xfe, 0xb, 0x6e, 0x6, 0xea, 0x3, 0x0, 0xfb, 0x81, 0x48, 0xb8, 0x4d, 0xe4, 0x75, 0xd9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char arrow_left_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0xa0, 0x0, 0xdc, 0x4f, 0x78, 0xf0, 0xff, 0xc1, 0xff, 0x7, 0xff, 0x21, 0x3c, 0x46, 0x98, 0xf0, 0x43, 0xed, 0xff, 0x27, 0x19, 0xb8, 0x19, 0x18, 0x18, 0x18, 0x14, 0x18, 0x19, 0x18, 0x18, 0x18, 0x98, 0x20, 0xc2, 0x2f, 0xb8, 0xff, 0xaf, 0x82, 0x8, 0xc3, 0x0, 0x54, 0xe2, 0xe7, 0x14, 0x6, 0x2d, 0x54, 0x83, 0x99, 0x70, 0xd9, 0x8, 0x95, 0x60, 0xcf, 0x61, 0xb8, 0x86, 0x55, 0x42, 0xe2, 0x2b, 0x63, 0x18, 0xc3, 0x57, 0xac, 0x46, 0xc9, 0x5f, 0xfd, 0x9f, 0x43, 0x89, 0x67, 0x19, 0x18, 0x0, 0xf4, 0x30, 0x14, 0x49, 0xef, 0xe6, 0x74, 0x60, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char arrow_right_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x17, 0x3c, 0xf8, 0xf, 0x82, 0xf7, 0x13, 0x70, 0x48, 0x3c, 0xf8, 0xf2, 0x50, 0x1b, 0x43, 0x2, 0xa, 0xaf, 0xbe, 0xe0, 0xc6, 0x2e, 0xf1, 0xff, 0xe1, 0x7c, 0x12, 0x24, 0x10, 0x46, 0x11, 0xb6, 0x1c, 0xe1, 0x5c, 0xa, 0x0, 0x0, 0xe0, 0x14, 0x48, 0xb1, 0x3d, 0x1b, 0x7a, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -162,6 +166,10 @@ static const unsigned char icon_folder_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x6, 0x78, 0x70, 0xf4, 0xc1, 0x7f, 0x24, 0x78, 0x18, 0x53, 0xc1, 0x7f, 0x54, 0x48, 0x50, 0xc1, 0x43, 0x1b, 0xbc, 0xa, 0x50, 0xad, 0x23, 0xa4, 0xe0, 0xff, 0x70, 0x52, 0x70, 0x18, 0x97, 0xf4, 0xfd, 0x43, 0xd4, 0x88, 0x4a, 0x0, 0x5a, 0xcb, 0x18, 0xab, 0x5e, 0xd9, 0x1a, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char icon_grid_minimap_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x2, 0xd, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x75, 0x93, 0x31, 0x68, 0x14, 0x51, 0x10, 0x86, 0xbf, 0xd9, 0xd, 0xbb, 0xde, 0x76, 0x82, 0x21, 0xf8, 0xe0, 0xbc, 0x5d, 0x8b, 0x80, 0x69, 0x6c, 0xd2, 0x5a, 0x6a, 0x91, 0xc3, 0xd2, 0x46, 0x22, 0x8, 0x9, 0x89, 0x70, 0x85, 0x10, 0x41, 0xd, 0x24, 0x45, 0xb0, 0xb, 0x68, 0x11, 0x14, 0x24, 0x10, 0x22, 0x62, 0x21, 0x41, 0xe, 0x4b, 0x21, 0xa4, 0xb7, 0x49, 0x17, 0xb1, 0x8, 0xb9, 0xdd, 0xc7, 0x86, 0x33, 0x21, 0xe1, 0x3a, 0x8f, 0x64, 0x61, 0x6f, 0x2c, 0xbc, 0x3b, 0x36, 0xb9, 0xdc, 0xc0, 0x2b, 0xde, 0xcc, 0xfc, 0xf3, 0xff, 0xfc, 0xcc, 0x48, 0xa3, 0xd1, 0x78, 0x20, 0x22, 0x13, 0xbe, 0xef, 0xaf, 0xdf, 0xac, 0xd7, 0x1f, 0xe1, 0x38, 0xd3, 0xa8, 0x2a, 0xf0, 0x45, 0x6a, 0xb5, 0xcf, 0x5c, 0x11, 0xcd, 0x66, 0x33, 0x38, 0x3f, 0x3f, 0x9f, 0x13, 0x91, 0x7d, 0xb1, 0xd6, 0x6e, 0xaa, 0xea, 0xd3, 0xe0, 0xe8, 0xe8, 0xde, 0xe8, 0xee, 0xee, 0x37, 0xc0, 0xe9, 0xf6, 0x75, 0xf0, 0xfd, 0x9, 0x99, 0x9d, 0x6d, 0x15, 0x81, 0x59, 0x96, 0x3d, 0x3, 0x5e, 0x2, 0x63, 0x22, 0xf2, 0x69, 0xa4, 0x57, 0x1c, 0xdd, 0xdb, 0xfb, 0x5b, 0x0, 0x3, 0x38, 0x67, 0x41, 0x30, 0x11, 0xc7, 0xf1, 0x13, 0x0, 0x11, 0x71, 0xb2, 0x2c, 0x7b, 0xd8, 0xad, 0xad, 0x2, 0x6f, 0xb9, 0x0, 0x38, 0x3c, 0xfc, 0x5, 0x9c, 0xf6, 0xff, 0x22, 0x27, 0x27, 0xe3, 0xe3, 0x7f, 0xa, 0x3, 0x67, 0x45, 0xe4, 0xbb, 0xe7, 0x79, 0xb7, 0xc3, 0x30, 0x7c, 0xd7, 0x67, 0xe9, 0xe3, 0x67, 0x66, 0x5c, 0x60, 0x1, 0x50, 0x40, 0x51, 0x7d, 0x71, 0x6b, 0x72, 0xf2, 0x20, 0x8a, 0xa2, 0xf9, 0x28, 0x8a, 0xe6, 0x1, 0x3a, 0x9d, 0xce, 0x4f, 0x63, 0x4c, 0x3b, 0x4d, 0xd3, 0xd2, 0xc0, 0x80, 0x3c, 0xcf, 0xf, 0x92, 0xa9, 0xa9, 0x31, 0x60, 0x5, 0x58, 0x91, 0x5a, 0xed, 0xc7, 0x15, 0xfe, 0x95, 0xac, 0xb5, 0xcf, 0xf3, 0x3c, 0x3f, 0xe8, 0x25, 0x46, 0xa, 0xc5, 0xd, 0x11, 0x59, 0xb3, 0xd5, 0xea, 0x1b, 0xa0, 0x95, 0x54, 0xab, 0x5b, 0x97, 0xd1, 0x22, 0xb2, 0xa6, 0xaa, 0x6d, 0x60, 0xd, 0x58, 0xba, 0xa0, 0x20, 0xc, 0xc3, 0x65, 0xd7, 0x75, 0x23, 0xe0, 0x2e, 0xb0, 0x1, 0x5c, 0xbf, 0xf4, 0x0, 0xbe, 0xba, 0xae, 0x1b, 0x85, 0x61, 0xb8, 0x3c, 0xa0, 0x20, 0x4d, 0xd3, 0x52, 0xb9, 0x5c, 0x6e, 0xc5, 0x71, 0xbc, 0x23, 0x22, 0xd3, 0x61, 0x18, 0xde, 0x2f, 0xb2, 0x27, 0x49, 0xa2, 0xaa, 0xba, 0x53, 0x2e, 0x97, 0x5b, 0x69, 0x9a, 0x96, 0xf2, 0x3c, 0x1f, 0xf0, 0xc0, 0x5a, 0x6b, 0x5f, 0x1, 0x25, 0x86, 0x84, 0xe3, 0x38, 0x9e, 0xb5, 0x76, 0x2e, 0xcf, 0xf3, 0xfd, 0x1, 0x5, 0x22, 0xb2, 0xa1, 0xaa, 0x4b, 0x22, 0x72, 0xad, 0xcb, 0x38, 0xe0, 0x81, 0xaa, 0x7e, 0x0, 0xce, 0x44, 0xe4, 0xbd, 0xaa, 0xbe, 0xbe, 0xa0, 0xa0, 0x52, 0xa9, 0x2c, 0x7a, 0x9e, 0x17, 0x1, 0x3d, 0xe0, 0x55, 0x1e, 0x6c, 0x79, 0x9e, 0x17, 0x55, 0x2a, 0x95, 0xc5, 0x1, 0x5, 0xcd, 0x66, 0x33, 0x30, 0xc6, 0x9c, 0xc6, 0x71, 0xbc, 0x2d, 0x22, 0x8f, 0x87, 0x78, 0xb0, 0x6d, 0x8c, 0x39, 0xed, 0xae, 0x74, 0xdf, 0x83, 0x3a, 0x70, 0x9c, 0x65, 0x59, 0x23, 0x49, 0x92, 0x5, 0x11, 0x9, 0x86, 0x79, 0x20, 0x22, 0x41, 0x92, 0x24, 0xb, 0x59, 0x96, 0x35, 0x80, 0x63, 0xa0, 0x2e, 0x3d, 0xf6, 0xc2, 0x91, 0xdc, 0x0, 0x5c, 0x55, 0x5d, 0xbf, 0x4, 0x9e, 0x3, 0x72, 0xfe, 0xaf, 0xfb, 0xaa, 0xe7, 0x79, 0x1f, 0x8d, 0x31, 0x6d, 0x29, 0x36, 0xf5, 0xce, 0x14, 0xb8, 0x33, 0x44, 0xc4, 0x6f, 0xdf, 0xf7, 0xd7, 0x8d, 0x31, 0xed, 0x5e, 0xe2, 0x1f, 0xb, 0x5c, 0xe2, 0xcb, 0xd, 0x9b, 0x69, 0xcb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char icon_parent_folder_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x68, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x33, 0xb8, 0x27, 0xfe, 0xe0, 0xfc, 0x83, 0x73, 0xf7, 0xc4, 0x71, 0x48, 0xdf, 0x11, 0x7b, 0x78, 0xe9, 0xc1, 0x3f, 0x20, 0xbc, 0xfe, 0x40, 0x12, 0x8f, 0x34, 0x4c, 0x9, 0xa6, 0xe1, 0x57, 0x80, 0x12, 0x17, 0x81, 0xf8, 0x2f, 0x58, 0xe1, 0x15, 0x34, 0x8b, 0x1e, 0x9c, 0x5, 0xa, 0x5e, 0xb8, 0x23, 0x6, 0x52, 0x70, 0x5b, 0x14, 0xac, 0xf0, 0xc, 0xaa, 0x82, 0x7d, 0xf, 0x8e, 0xde, 0x14, 0xf9, 0xcf, 0x8, 0x52, 0xc0, 0xc0, 0x70, 0x5b, 0xf4, 0xe1, 0xc9, 0x7, 0x47, 0xb1, 0xb8, 0x3, 0xaa, 0x0, 0xa, 0x48, 0x52, 0x80, 0xb0, 0xea, 0xc8, 0xc3, 0x83, 0xc, 0x83, 0xe, 0x0, 0x0, 0xb8, 0x27, 0x55, 0x4c, 0xbe, 0xc0, 0xd2, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -214,18 +222,34 @@ static const unsigned char option_button_disabled_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x2f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x3f, 0x3f, 0x5a, 0x5a, 0x5a, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x59, 0x59, 0x59, 0x2a, 0x2a, 0x30, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x27, 0x35, 0x35, 0x35, 0x4a, 0x4a, 0x4a, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x56, 0x56, 0x56, 0x62, 0x62, 0x62, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x48, 0x48, 0x48, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, 0x54, 0x54, 0x54, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x49, 0x49, 0x49, 0x3a, 0x3a, 0x3a, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x39, 0x39, 0x39, 0x47, 0x47, 0x47, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x46, 0x46, 0x46, 0xd3, 0xa7, 0xd4, 0x88, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xec, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0x8e, 0x5, 0x4e, 0x0, 0x31, 0x10, 0x45, 0xff, 0xef, 0x56, 0xc2, 0xe2, 0xee, 0x4e, 0x3c, 0xc8, 0xd, 0xb8, 0x38, 0xa7, 0xc0, 0xdd, 0xdd, 0x5d, 0xbb, 0x94, 0x4c, 0xd2, 0xc1, 0xdf, 0x4a, 0x47, 0x5e, 0x27, 0xc3, 0xc, 0x80, 0x64, 0x24, 0xb8, 0xc7, 0x4f, 0x2c, 0x6b, 0xd5, 0x90, 0xff, 0xdd, 0x23, 0x7e, 0xc1, 0xa2, 0xb6, 0x64, 0xad, 0x26, 0x82, 0xc6, 0xef, 0x45, 0xbc, 0xe3, 0x1, 0x2c, 0x69, 0x9b, 0xc8, 0x7f, 0x5, 0x36, 0x1e, 0x41, 0x84, 0xd6, 0x4, 0x25, 0x90, 0xb7, 0x39, 0x6c, 0x4c, 0x2f, 0xbe, 0x68, 0xdf, 0x13, 0xa1, 0x9e, 0xc8, 0xa4, 0xb7, 0xe7, 0x47, 0x93, 0x63, 0x1b, 0xf9, 0xdc, 0x8, 0x88, 0x60, 0xbf, 0x4, 0xff, 0xd2, 0x56, 0x93, 0xe3, 0xb7, 0xb2, 0xf6, 0x2c, 0x36, 0x1, 0x16, 0xf4, 0x5f, 0x42, 0xc4, 0x17, 0x8f, 0xb5, 0xc0, 0xa5, 0x8, 0x30, 0x5f, 0xc2, 0x5d, 0xcf, 0xc9, 0xd, 0x74, 0x87, 0x8b, 0x5e, 0x56, 0x22, 0x24, 0xf7, 0x6d, 0xc2, 0xd1, 0x80, 0x26, 0x27, 0x5d, 0x5b, 0x67, 0x7d, 0x2f, 0x80, 0x4d, 0xc9, 0x7f, 0x9, 0x63, 0xa7, 0x61, 0x23, 0xc7, 0x74, 0x3d, 0xf, 0xae, 0x41, 0x84, 0x6f, 0x13, 0xe6, 0x26, 0xfa, 0x36, 0x46, 0x73, 0xbc, 0xba, 0x3b, 0xd6, 0xca, 0x8, 0xd0, 0xd6, 0x36, 0x4e, 0x35, 0xeb, 0xad, 0xd7, 0xb0, 0x60, 0x72, 0xdc, 0xda, 0x9c, 0x2, 0x67, 0x76, 0x64, 0x82, 0x99, 0x9b, 0xb6, 0x80, 0xb0, 0x7e, 0x8d, 0xf6, 0x5a, 0xdd, 0xe1, 0xb5, 0xba, 0xba, 0xb1, 0x0, 0xcd, 0xc7, 0x50, 0x23, 0xeb, 0xfb, 0x7f, 0xb4, 0xc8, 0x22, 0x18, 0xdd, 0x0, 0xd5, 0xec, 0x4e, 0x53, 0xc6, 0x18, 0x44, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char option_button_disabled_mirrored_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x2f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x5a, 0x5a, 0x5a, 0x3e, 0x3e, 0x3e, 0x2a, 0x2a, 0x30, 0x59, 0x59, 0x59, 0x22, 0x22, 0x27, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x4a, 0x4a, 0x4a, 0x36, 0x36, 0x36, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x62, 0x62, 0x62, 0x56, 0x56, 0x56, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x54, 0x54, 0x54, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x48, 0x48, 0x48, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x3a, 0x3a, 0x3a, 0x49, 0x49, 0x49, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x47, 0x47, 0x47, 0x39, 0x39, 0x39, 0x46, 0x46, 0x46, 0xda, 0x9d, 0x96, 0x5c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0xf, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0xef, 0x50, 0x45, 0x4b, 0x6d, 0xe, 0x95, 0xa6, 0xa5, 0x40, 0xab, 0x15, 0x5, 0xdc, 0xc0, 0x8a, 0xa, 0xb8, 0xa1, 0xef, 0xff, 0xc, 0x86, 0x90, 0x30, 0xc, 0x17, 0xfa, 0x5d, 0x26, 0xff, 0x4c, 0x4e, 0x12, 0x41, 0x98, 0x32, 0x33, 0x1b, 0x98, 0x9b, 0x17, 0x83, 0x84, 0x18, 0xa, 0x47, 0xa2, 0xb1, 0xe9, 0x7d, 0x21, 0x16, 0x58, 0x58, 0x5c, 0x5a, 0x96, 0x24, 0x29, 0x9e, 0x48, 0xa6, 0xd2, 0x2b, 0x51, 0xb2, 0xb4, 0xba, 0x96, 0xc9, 0xca, 0x87, 0x47, 0x8c, 0x9c, 0xc9, 0x1d, 0x9f, 0x30, 0xeb, 0x1b, 0xe9, 0x8, 0x9, 0x36, 0x15, 0x25, 0xaf, 0x9e, 0x6a, 0x8c, 0x8a, 0xa0, 0xa0, 0x8f, 0x9c, 0x15, 0xb7, 0x52, 0x61, 0x12, 0xa8, 0x6, 0x56, 0x4d, 0x2b, 0xcb, 0x98, 0xb8, 0xc4, 0x3, 0x5d, 0x2f, 0x24, 0x43, 0xc3, 0xc0, 0x6, 0xcd, 0x2a, 0xcb, 0x4c, 0xe, 0x4a, 0x95, 0x2a, 0x57, 0x49, 0x88, 0x24, 0x70, 0xc, 0x70, 0xcf, 0xcb, 0x17, 0x8c, 0x5, 0x8e, 0x77, 0xc9, 0x79, 0xf1, 0x20, 0x9, 0x80, 0x4, 0xd6, 0x64, 0x50, 0xbb, 0xe2, 0x6a, 0xd2, 0x30, 0xc0, 0xd7, 0xf5, 0x89, 0x19, 0xb4, 0x46, 0xbe, 0x79, 0xc3, 0x35, 0x69, 0x80, 0x6e, 0x15, 0xb8, 0x33, 0xef, 0x99, 0x7, 0x84, 0x5a, 0x6d, 0xae, 0x45, 0x8f, 0xb0, 0x1f, 0xed, 0x86, 0x3f, 0xbe, 0xa6, 0x6f, 0x3f, 0x75, 0x9e, 0xb9, 0xe, 0x1d, 0xd2, 0x78, 0x79, 0xed, 0x62, 0xf0, 0x19, 0xdc, 0xeb, 0x17, 0xdf, 0xb8, 0x77, 0x7a, 0xcd, 0xed, 0x8f, 0xcf, 0x5e, 0xb7, 0x8e, 0x19, 0xe5, 0xab, 0x3f, 0xf8, 0x66, 0xda, 0x3b, 0xf4, 0xa1, 0x76, 0xf7, 0x90, 0xe3, 0x8e, 0x67, 0x70, 0x1, 0xbc, 0x9f, 0x91, 0xc1, 0xfe, 0x1, 0x7d, 0xea, 0x7f, 0x3f, 0xeb, 0xef, 0xef, 0xfe, 0x5, 0xd6, 0xe3, 0x56, 0x89, 0xd8, 0x62, 0xb6, 0x83, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char option_button_hover_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0x4b, 0x5f, 0x5a, 0x6c, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x5f, 0x5a, 0x6b, 0x2a, 0x2a, 0x30, 0x56, 0x53, 0x64, 0x22, 0x22, 0x27, 0x3e, 0x3b, 0x46, 0x57, 0x53, 0x63, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x5b, 0x57, 0x68, 0x5a, 0x56, 0x67, 0x67, 0x63, 0x76, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x4d, 0x4a, 0x57, 0x49, 0x46, 0x52, 0x48, 0x45, 0x51, 0x5a, 0x56, 0x65, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x5b, 0x57, 0x66, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x43, 0x40, 0x4c, 0x54, 0x50, 0x5f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x47, 0x43, 0x51, 0x43, 0x3f, 0x4d, 0x42, 0x3f, 0x4c, 0x53, 0x4f, 0x5f, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x53, 0x50, 0x5f, 0x53, 0x4f, 0x5e, 0x5f, 0x5a, 0x6c, 0xd3, 0x26, 0x54, 0x35, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe5, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x85, 0x91, 0x43, 0x62, 0xc5, 0x60, 0x18, 0x45, 0xef, 0x8d, 0x51, 0xdb, 0xee, 0x46, 0xca, 0x6d, 0x77, 0x58, 0xce, 0x6b, 0xdb, 0x7c, 0x8a, 0xad, 0xea, 0x44, 0x1f, 0x4e, 0x92, 0x1f, 0x4c, 0x0, 0xe0, 0x9, 0x61, 0xf0, 0x89, 0x32, 0x12, 0xcd, 0xd4, 0x8, 0xef, 0x1f, 0x35, 0x54, 0xa0, 0x68, 0x1a, 0x34, 0x89, 0x8, 0x86, 0xa4, 0xd, 0x57, 0xb4, 0xdf, 0x79, 0x5, 0x89, 0x94, 0xba, 0xf9, 0xb3, 0xc0, 0xce, 0xeb, 0xf0, 0x17, 0x1c, 0xab, 0x11, 0x9, 0x1a, 0xf9, 0x96, 0x84, 0x5d, 0x5e, 0x43, 0x15, 0x7, 0x2e, 0x42, 0x41, 0x51, 0xd3, 0xbe, 0x67, 0xd5, 0x6b, 0x42, 0x3a, 0x38, 0x9b, 0xf5, 0x2e, 0x20, 0xfa, 0x5, 0x33, 0x41, 0x69, 0xf4, 0xeb, 0x49, 0x6c, 0x19, 0xe6, 0xbd, 0xdd, 0xd, 0x48, 0xa0, 0x92, 0xb, 0x36, 0x72, 0x6a, 0x26, 0xf0, 0x14, 0xa, 0x10, 0x72, 0xe1, 0x7d, 0xf4, 0xf6, 0x35, 0x1b, 0xc3, 0xe3, 0x18, 0x9d, 0x50, 0xf0, 0xe4, 0xc2, 0x17, 0xae, 0x27, 0xd3, 0xe4, 0x6e, 0xe8, 0xf8, 0x7e, 0xbc, 0x1, 0x48, 0x9e, 0x57, 0xf8, 0xc5, 0xfc, 0xbd, 0x7a, 0x98, 0xc4, 0x94, 0x47, 0xbf, 0xe4, 0xce, 0x48, 0x8, 0x6e, 0x69, 0x91, 0x63, 0x47, 0x73, 0x49, 0xbc, 0x77, 0x3e, 0xd7, 0x4b, 0x3b, 0x12, 0x36, 0x16, 0xb3, 0x85, 0x6a, 0x68, 0xce, 0x39, 0x62, 0xc6, 0xba, 0x3d, 0x8d, 0xe7, 0x91, 0x20, 0xac, 0xad, 0xa6, 0xc2, 0xe, 0x6, 0xcc, 0x74, 0xc, 0x2d, 0xe7, 0xf9, 0x55, 0x2, 0x28, 0x94, 0x37, 0xab, 0xee, 0xa1, 0xcc, 0xbf, 0xdb, 0xed, 0x3, 0x70, 0xe6, 0x4f, 0x4a, 0xc3, 0xed, 0xed, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char option_button_hover_mirrored_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x5f, 0x5a, 0x6c, 0x42, 0x40, 0x4b, 0x2a, 0x2a, 0x30, 0x5f, 0x5a, 0x6b, 0x22, 0x22, 0x27, 0x56, 0x53, 0x64, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x57, 0x53, 0x63, 0x3e, 0x3c, 0x47, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x67, 0x63, 0x76, 0x5a, 0x56, 0x67, 0x5b, 0x57, 0x68, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x5a, 0x56, 0x65, 0x48, 0x45, 0x51, 0x49, 0x46, 0x52, 0x4d, 0x4a, 0x57, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x5b, 0x57, 0x66, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x54, 0x50, 0x5f, 0x43, 0x40, 0x4c, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x53, 0x4f, 0x5f, 0x42, 0x3f, 0x4c, 0x43, 0x3f, 0x4d, 0x47, 0x43, 0x51, 0x5f, 0x5a, 0x6c, 0x53, 0x4f, 0x5e, 0x53, 0x50, 0x5f, 0x68, 0x6, 0xa3, 0x65, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x15, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0x8b, 0xb, 0x54, 0x51, 0x4b, 0x6d, 0x8f, 0x60, 0xd3, 0x62, 0xc1, 0x56, 0x11, 0x5, 0xdc, 0xd0, 0x5a, 0x14, 0x70, 0x5f, 0xdf, 0xff, 0x1, 0xc, 0x69, 0xc2, 0x30, 0x5c, 0xe8, 0x77, 0x99, 0xfc, 0x33, 0x39, 0x49, 0x38, 0x6e, 0xc8, 0xc8, 0xa8, 0x6f, 0x6c, 0x9c, 0xf7, 0x63, 0x7c, 0x20, 0x18, 0xa, 0x47, 0x86, 0xf7, 0xb9, 0x88, 0x6f, 0x62, 0x72, 0x6a, 0x5a, 0x10, 0x84, 0x68, 0x2c, 0x9e, 0x48, 0xce, 0x84, 0xf1, 0xd2, 0xec, 0x5c, 0x3a, 0x23, 0x6e, 0x6c, 0x52, 0x62, 0x3a, 0xbb, 0xb5, 0xed, 0xd9, 0x99, 0x5f, 0x48, 0x86, 0x70, 0xb0, 0x28, 0x49, 0x39, 0x79, 0x57, 0xa1, 0x64, 0x15, 0xf6, 0xf2, 0x9e, 0xc2, 0xfe, 0x52, 0x22, 0x88, 0x3, 0x59, 0x43, 0xb2, 0x6e, 0x64, 0x28, 0x1d, 0x15, 0x59, 0x90, 0x2f, 0x1c, 0xc4, 0x3, 0xbd, 0xc0, 0x4, 0xc5, 0x28, 0x89, 0x54, 0x16, 0x8a, 0xe5, 0x43, 0xa6, 0x1c, 0xe3, 0x71, 0x60, 0x69, 0x60, 0x1f, 0x95, 0x8e, 0x29, 0x3, 0xac, 0xca, 0x9, 0x53, 0x89, 0xfa, 0x71, 0x0, 0x38, 0x30, 0x6, 0x83, 0xea, 0x29, 0x53, 0x15, 0x7a, 0x1, 0x3a, 0xab, 0xd, 0xcc, 0xa0, 0xd4, 0x73, 0x8d, 0x73, 0xa6, 0x41, 0x2, 0xf5, 0x42, 0x82, 0x4b, 0xfd, 0x8a, 0xba, 0x56, 0xd5, 0xe6, 0xd, 0xd3, 0x24, 0x47, 0x98, 0xb7, 0x66, 0xdd, 0xe9, 0x5f, 0xd3, 0x31, 0xef, 0xdc, 0x16, 0xe3, 0x92, 0x21, 0xb5, 0xfb, 0x87, 0x36, 0x2, 0x87, 0x42, 0x9d, 0xee, 0xe3, 0x13, 0xd5, 0x72, 0xc9, 0x35, 0x97, 0x9f, 0x5f, 0x3a, 0xed, 0x1a, 0xa2, 0xa4, 0xd7, 0xee, 0xdb, 0xbb, 0xe7, 0xe3, 0x33, 0x45, 0x1e, 0x6a, 0x65, 0x55, 0xb5, 0xec, 0xfe, 0xc, 0x36, 0xc0, 0xd7, 0xb7, 0xe7, 0x67, 0x6d, 0x9d, 0x3c, 0xf5, 0xbf, 0x9f, 0xf5, 0xf7, 0x77, 0xff, 0x2, 0xa7, 0xc5, 0x58, 0xc8, 0x6e, 0x59, 0x8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char option_button_normal_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x56, 0x52, 0x60, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x52, 0x22, 0x22, 0x27, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x5d, 0x5a, 0x6a, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x46, 0x42, 0x4f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x4e, 0x4b, 0x58, 0x8, 0xd9, 0x10, 0xcb, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xd1, 0x55, 0x5a, 0xc4, 0x30, 0x14, 0x5, 0xe0, 0x73, 0xea, 0x82, 0xbb, 0x3b, 0x6c, 0x80, 0x45, 0xb0, 0x70, 0x5e, 0xb1, 0x37, 0xdc, 0x75, 0xdc, 0x3d, 0x43, 0x2a, 0x5f, 0x3a, 0x7e, 0x6a, 0xc9, 0xbd, 0x7f, 0x9d, 0x2a, 0x0, 0xa4, 0x16, 0xd, 0xaa, 0x18, 0x8e, 0x41, 0x3f, 0x11, 0xd1, 0xbe, 0x52, 0xc7, 0x48, 0xa8, 0xfb, 0x5e, 0x68, 0xd4, 0x24, 0x96, 0x6a, 0xfc, 0xaf, 0x8b, 0x32, 0xbf, 0x61, 0x90, 0xc6, 0x3c, 0x27, 0x3, 0xce, 0xfe, 0x20, 0x2, 0x4b, 0xd2, 0x45, 0x1f, 0x14, 0xd5, 0x78, 0x5e, 0x36, 0x1c, 0x7d, 0xe5, 0x33, 0x2, 0xb3, 0x84, 0x8a, 0xec, 0xd4, 0xeb, 0x9a, 0x1a, 0x1b, 0x82, 0xf5, 0x79, 0x20, 0x2, 0x46, 0x1f, 0x58, 0x8d, 0x95, 0xe4, 0x6a, 0x1d, 0xcf, 0x4f, 0x89, 0x85, 0x10, 0x80, 0x56, 0x1f, 0x8, 0xf4, 0x53, 0xf7, 0x81, 0x6c, 0x4, 0xa0, 0xf5, 0x41, 0x69, 0xeb, 0xb7, 0xd0, 0x7b, 0x86, 0xcc, 0x36, 0xbb, 0x11, 0x90, 0x66, 0x1f, 0x88, 0xef, 0xbd, 0x64, 0x92, 0x5a, 0x7b, 0x4e, 0xed, 0x34, 0x42, 0x20, 0xa5, 0xd5, 0x7, 0x27, 0x69, 0xfb, 0x51, 0x8d, 0x69, 0x6e, 0xd5, 0xcc, 0xb9, 0x18, 0xf4, 0xaf, 0x40, 0x6e, 0x3f, 0x1d, 0xab, 0xf1, 0xdd, 0xc7, 0xf1, 0x12, 0x45, 0xc, 0xce, 0x4f, 0x17, 0x12, 0xd0, 0xb6, 0xc4, 0x87, 0x1a, 0x6f, 0x2f, 0x48, 0x8b, 0xef, 0x31, 0xd0, 0x6e, 0xce, 0xa8, 0xc0, 0x25, 0x56, 0x7d, 0x5, 0x52, 0xed, 0x6e, 0xae, 0x68, 0x0, 0xd4, 0x86, 0x7f, 0x56, 0x43, 0x62, 0x38, 0xc, 0x46, 0x28, 0xba, 0x1, 0x7a, 0xad, 0x4f, 0x59, 0x90, 0xab, 0xbf, 0xa4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char option_button_normal_mirrored_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x56, 0x53, 0x61, 0x3c, 0x3a, 0x45, 0x2a, 0x2a, 0x30, 0x56, 0x52, 0x60, 0x22, 0x22, 0x27, 0x47, 0x44, 0x52, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x47, 0x44, 0x50, 0x33, 0x31, 0x3a, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x5d, 0x5a, 0x6a, 0x51, 0x4f, 0x5d, 0x52, 0x50, 0x5d, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x51, 0x4e, 0x5b, 0x41, 0x3e, 0x49, 0x42, 0x3e, 0x4a, 0x46, 0x42, 0x4e, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x50, 0x4e, 0x5a, 0x40, 0x3e, 0x48, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x47, 0x43, 0x50, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x44, 0x41, 0x4e, 0x36, 0x34, 0x3e, 0x4e, 0x4b, 0x58, 0x44, 0x41, 0x4c, 0x44, 0x42, 0x4d, 0x7d, 0x2e, 0xcf, 0xc5, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x13, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0x8b, 0xb, 0x54, 0x51, 0x4b, 0x6d, 0xf, 0x68, 0xd3, 0xa2, 0x5, 0x5b, 0x40, 0x14, 0x70, 0x3, 0x2b, 0x2a, 0xe0, 0xbe, 0xbd, 0xff, 0x3, 0x18, 0xd2, 0x84, 0x61, 0xb8, 0xd0, 0xef, 0x32, 0xf9, 0x67, 0x72, 0x92, 0x70, 0xdc, 0x88, 0xb1, 0x71, 0xcf, 0xc4, 0x24, 0xef, 0xc5, 0x78, 0x9f, 0x3f, 0x10, 0xc, 0x8d, 0xee, 0x73, 0x21, 0xcf, 0xd4, 0xf4, 0xcc, 0xac, 0x20, 0x8, 0xe1, 0x48, 0x34, 0x16, 0x9f, 0xb, 0xe2, 0xa5, 0xf9, 0x85, 0x64, 0x4a, 0xdc, 0xda, 0xa6, 0xc4, 0x64, 0x7a, 0x67, 0xd7, 0xb5, 0xb7, 0xb8, 0x14, 0xf, 0xe0, 0x60, 0x59, 0x92, 0x32, 0xf2, 0xbe, 0x42, 0xc9, 0x2a, 0x64, 0x73, 0xae, 0x83, 0xc3, 0x95, 0x98, 0x1f, 0x7, 0xb2, 0x86, 0x64, 0xdd, 0x48, 0x51, 0x3a, 0xca, 0x1f, 0x1d, 0x53, 0xb9, 0x6c, 0xd4, 0xd7, 0xf, 0x4c, 0x50, 0x8c, 0x82, 0x48, 0xa5, 0x21, 0x5f, 0x3c, 0x61, 0x8a, 0x11, 0x1e, 0x7, 0x96, 0x6, 0x76, 0xa9, 0x50, 0xa6, 0xc, 0xb0, 0x2a, 0xa7, 0x4c, 0x25, 0xec, 0xc5, 0x1, 0xe0, 0xc0, 0x18, 0xe, 0xaa, 0x67, 0x4c, 0x55, 0xe8, 0x7, 0xe8, 0xbc, 0x36, 0x34, 0x83, 0x52, 0xcf, 0x34, 0x2e, 0x98, 0x6, 0x9, 0xd4, 0x4b, 0x9, 0xae, 0xf4, 0x6b, 0xea, 0x46, 0x55, 0x9b, 0x2d, 0xa6, 0x49, 0x8e, 0x30, 0x6f, 0xcd, 0xba, 0x33, 0xb8, 0xa6, 0x63, 0xde, 0xb5, 0xef, 0x99, 0x36, 0x19, 0x52, 0x7b, 0x78, 0xec, 0x20, 0x70, 0x28, 0xd4, 0xed, 0x3d, 0x3d, 0x33, 0x2f, 0xe4, 0x9a, 0xab, 0xaf, 0x6f, 0xdd, 0x4e, 0xd, 0x51, 0xd2, 0x7b, 0xef, 0xe3, 0x93, 0x6a, 0x25, 0xc8, 0x43, 0xad, 0xad, 0xab, 0x96, 0x3d, 0x98, 0xc1, 0x6, 0xf8, 0xfa, 0x76, 0xfd, 0x6c, 0x6c, 0x92, 0xa7, 0xfe, 0xf7, 0xb3, 0xfe, 0xfe, 0xee, 0x5f, 0xa1, 0x5f, 0x59, 0xbd, 0x75, 0x41, 0x2b, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char option_button_pressed_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x2f, 0x37, 0x46, 0x43, 0x4f, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x44, 0x50, 0x2a, 0x2a, 0x30, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x27, 0x3d, 0x3a, 0x45, 0x56, 0x52, 0x60, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x43, 0x40, 0x4c, 0x42, 0x40, 0x4b, 0x4c, 0x49, 0x56, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x3a, 0x38, 0x41, 0x36, 0x34, 0x3d, 0x44, 0x41, 0x4c, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x44, 0x42, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x50, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x3f, 0x3d, 0x47, 0x4f, 0x4c, 0x59, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x45, 0x42, 0x4d, 0x41, 0x3e, 0x49, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x52, 0x4e, 0x5c, 0x51, 0x4e, 0x5b, 0x5d, 0x59, 0x69, 0x10, 0x9d, 0xe0, 0x3c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xcf, 0x3, 0x62, 0x4, 0x51, 0x10, 0x4, 0xd0, 0xaa, 0x31, 0x62, 0xdb, 0xb8, 0x49, 0x2e, 0x9e, 0x3b, 0xc4, 0xb6, 0x9d, 0xc5, 0x58, 0x1f, 0xc1, 0xd6, 0xe8, 0x77, 0xf7, 0x1b, 0x59, 0x6c, 0x2, 0x20, 0x37, 0xaa, 0xc5, 0x17, 0x7e, 0xc7, 0x62, 0x28, 0x45, 0x75, 0xfe, 0x6c, 0xe1, 0x4f, 0x68, 0x86, 0x41, 0x69, 0x44, 0x51, 0x4b, 0xb1, 0xce, 0xcc, 0xe4, 0x83, 0xd7, 0xb0, 0x48, 0x6b, 0x98, 0xe8, 0x9, 0x38, 0x70, 0x8b, 0xa, 0xcc, 0x12, 0x1a, 0xf0, 0x4d, 0xac, 0x87, 0xf3, 0x96, 0x6f, 0x8e, 0x5f, 0x56, 0xc0, 0x53, 0x20, 0x8f, 0xbf, 0xdb, 0x86, 0x58, 0x5b, 0x9, 0xbf, 0x47, 0x80, 0xa, 0x58, 0x1a, 0x38, 0xad, 0x9, 0x5f, 0xac, 0xe3, 0x20, 0xbc, 0x4b, 0x46, 0x4b, 0x0, 0x3a, 0x1a, 0x24, 0xd0, 0x69, 0x85, 0xc0, 0x63, 0x5, 0x60, 0x68, 0xf0, 0x36, 0x7f, 0xf3, 0xaa, 0xbe, 0xe1, 0x61, 0x81, 0x69, 0x5, 0x72, 0x5b, 0x83, 0xe4, 0x6a, 0x59, 0x16, 0xf7, 0x53, 0x47, 0x77, 0x8b, 0xad, 0x12, 0xe4, 0xb9, 0xa3, 0xc1, 0xe6, 0x83, 0x7b, 0x20, 0xd6, 0xb4, 0xe7, 0xbf, 0xed, 0xe1, 0x1a, 0xd8, 0xfa, 0xdf, 0xb9, 0x70, 0xb8, 0x21, 0xd6, 0xbb, 0x17, 0x1b, 0xe3, 0x4c, 0x6a, 0xb0, 0xbd, 0x25, 0x5, 0x3b, 0x5e, 0x7c, 0x21, 0xc0, 0xc2, 0x68, 0xee, 0xf1, 0xbc, 0x6, 0x46, 0xb1, 0xbd, 0x5e, 0x30, 0x5, 0x27, 0x19, 0x24, 0xb8, 0x61, 0x6e, 0xf8, 0xf5, 0xf7, 0xcd, 0x47, 0x16, 0xa0, 0x18, 0x13, 0x6a, 0x64, 0x7d, 0xff, 0x8f, 0x1e, 0x59, 0x84, 0xa2, 0x1b, 0x0, 0xe5, 0xe0, 0x4e, 0x46, 0x1d, 0x98, 0x92, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char option_button_pressed_mirrored_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x46, 0x43, 0x4f, 0x31, 0x2f, 0x38, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x50, 0x22, 0x22, 0x27, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x56, 0x52, 0x60, 0x3c, 0x3a, 0x45, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x4c, 0x49, 0x56, 0x42, 0x40, 0x4b, 0x43, 0x40, 0x4c, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x44, 0x41, 0x4c, 0x36, 0x34, 0x3d, 0x3a, 0x38, 0x41, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x44, 0x41, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4e, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x3f, 0x3d, 0x48, 0x50, 0x4d, 0x5a, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x50, 0x4e, 0x5a, 0x40, 0x3e, 0x48, 0x41, 0x3e, 0x49, 0x45, 0x42, 0x4d, 0x5d, 0x59, 0x69, 0x51, 0x4e, 0x5b, 0x52, 0x4e, 0x5c, 0x49, 0x7e, 0x80, 0x9, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x14, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x40, 0x3, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x40, 0xc0, 0xc6, 0xc1, 0xc9, 0xc5, 0xcd, 0x83, 0x2e, 0xcf, 0xc0, 0xc3, 0xcc, 0xcb, 0xc7, 0x2f, 0x20, 0x28, 0x28, 0x28, 0x24, 0x2c, 0x22, 0x2a, 0x26, 0xce, 0xd, 0x14, 0x92, 0x90, 0x54, 0x51, 0x55, 0x53, 0xd7, 0x80, 0x2, 0x35, 0x15, 0x4d, 0x2d, 0x6d, 0x8, 0xd0, 0x91, 0x92, 0x16, 0xe3, 0x2, 0x2a, 0x90, 0xd1, 0xd5, 0xd5, 0xd3, 0x37, 0x30, 0x84, 0x2, 0x7d, 0x23, 0x63, 0x13, 0x53, 0x28, 0x30, 0x93, 0x15, 0xe5, 0x4, 0x2a, 0xd0, 0x37, 0xb7, 0xd0, 0xb7, 0xb4, 0x52, 0x85, 0x2, 0x4b, 0xb, 0x6b, 0x1b, 0x5b, 0x18, 0xb0, 0x13, 0xe1, 0x0, 0x29, 0xb0, 0x37, 0x36, 0xb4, 0x72, 0x50, 0x83, 0x2, 0x4d, 0x63, 0x6b, 0x47, 0x27, 0x18, 0x70, 0x14, 0x66, 0x3, 0x2a, 0x70, 0x36, 0x37, 0x76, 0x71, 0x75, 0x70, 0x83, 0x2, 0x2b, 0x63, 0x67, 0x77, 0xf, 0x18, 0x70, 0x17, 0x62, 0x7, 0x2a, 0x30, 0x6, 0x2a, 0xb0, 0x42, 0x56, 0xe0, 0xe9, 0x5, 0x3, 0x9e, 0x82, 0x20, 0x5, 0x16, 0xde, 0x3e, 0x48, 0x6e, 0x30, 0xf4, 0xd5, 0xf3, 0xf3, 0x87, 0x1, 0x3f, 0xb0, 0x2, 0xa3, 0x0, 0x5d, 0xe3, 0x40, 0xcb, 0x20, 0x28, 0x8, 0x36, 0x32, 0xa, 0x9, 0x85, 0x81, 0x10, 0xb0, 0x15, 0xf6, 0x61, 0xf6, 0xbe, 0xe1, 0x70, 0x6f, 0x86, 0xdb, 0x47, 0x44, 0x46, 0xc1, 0x40, 0x24, 0xd8, 0x91, 0xe6, 0xd1, 0x31, 0xb1, 0x16, 0xc6, 0xe1, 0x50, 0x60, 0x11, 0x17, 0x9f, 0x90, 0x8, 0x5, 0x49, 0xc9, 0x60, 0x6f, 0xca, 0xa5, 0xa4, 0xc6, 0xc5, 0xfa, 0x58, 0x40, 0x81, 0x6e, 0x5a, 0x7c, 0x7a, 0x6, 0x4, 0x64, 0x66, 0xc9, 0x83, 0x3, 0x4a, 0x41, 0xd1, 0xc8, 0xd9, 0x5, 0xee, 0x6, 0x17, 0x63, 0xe3, 0xec, 0x1c, 0x8, 0xc8, 0x55, 0x52, 0x6, 0x7, 0x35, 0xc1, 0xc8, 0xc2, 0x1f, 0xdd, 0x0, 0xa5, 0xe, 0x59, 0xe5, 0x7f, 0xe9, 0xa4, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char overbright_indicator_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x1, 0x85, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x0, 0x0, 0x78, 0x9c, 0x7d, 0x91, 0x3d, 0x48, 0xc3, 0x40, 0x1c, 0xc5, 0x5f, 0x53, 0xa5, 0x2a, 0x2d, 0xe, 0x16, 0x11, 0x75, 0xc8, 0x50, 0x9d, 0x2c, 0x8a, 0x8a, 0x38, 0x6a, 0x15, 0x8a, 0x50, 0x21, 0xd4, 0xa, 0xad, 0x3a, 0x98, 0x5c, 0xfa, 0x21, 0x34, 0x69, 0x48, 0x52, 0x5c, 0x1c, 0x5, 0xd7, 0x82, 0x83, 0x1f, 0x8b, 0x55, 0x7, 0x17, 0x67, 0x5d, 0x1d, 0x5c, 0x5, 0x41, 0xf0, 0x3, 0xc4, 0xc5, 0xd5, 0x49, 0xd1, 0x45, 0x4a, 0xfc, 0x5f, 0x5a, 0x68, 0x11, 0xe3, 0xc1, 0x71, 0x3f, 0xde, 0xdd, 0x7b, 0xdc, 0xbd, 0x3, 0x84, 0x6a, 0x91, 0x69, 0x56, 0xdb, 0x18, 0xa0, 0xe9, 0xb6, 0x99, 0x8c, 0xc7, 0xc4, 0x74, 0x66, 0x45, 0xc, 0xbc, 0xa2, 0x13, 0x3, 0x8, 0xa1, 0x17, 0xa3, 0x32, 0xb3, 0x8c, 0x59, 0x49, 0x4a, 0xc0, 0x73, 0x7c, 0xdd, 0xc3, 0xc7, 0xd7, 0xbb, 0x28, 0xcf, 0xf2, 0x3e, 0xf7, 0xe7, 0x8, 0xa9, 0x59, 0x8b, 0x1, 0x3e, 0x91, 0x78, 0x86, 0x19, 0xa6, 0x4d, 0xbc, 0x4e, 0x3c, 0xb5, 0x69, 0x1b, 0x9c, 0xf7, 0x89, 0xc3, 0xac, 0x20, 0xab, 0xc4, 0xe7, 0xc4, 0x23, 0x26, 0x5d, 0x90, 0xf8, 0x91, 0xeb, 0x4a, 0x9d, 0xdf, 0x38, 0xe7, 0x5d, 0x16, 0x78, 0x66, 0xd8, 0x4c, 0x25, 0xe7, 0x88, 0xc3, 0xc4, 0x62, 0xbe, 0x85, 0x95, 0x16, 0x66, 0x5, 0x53, 0x23, 0x9e, 0x24, 0x8e, 0xa8, 0x9a, 0x4e, 0xf9, 0x42, 0xba, 0xce, 0x2a, 0xe7, 0x2d, 0xce, 0x5a, 0xb1, 0xcc, 0x1a, 0xf7, 0xe4, 0x2f, 0xc, 0x66, 0xf5, 0xe5, 0x25, 0xae, 0xd3, 0x1c, 0x44, 0x1c, 0xb, 0x58, 0x84, 0x4, 0x11, 0xa, 0xca, 0xd8, 0x40, 0x11, 0x36, 0xa2, 0xb4, 0xea, 0xa4, 0x58, 0x48, 0xd2, 0x7e, 0xcc, 0xc3, 0xdf, 0xef, 0xfa, 0x25, 0x72, 0x29, 0xe4, 0xda, 0x0, 0x23, 0xc7, 0x3c, 0x4a, 0xd0, 0x20, 0xbb, 0x7e, 0xf0, 0x3f, 0xf8, 0xdd, 0xad, 0x95, 0x9b, 0x18, 0xaf, 0x27, 0x5, 0x63, 0x40, 0xfb, 0x8b, 0xe3, 0x7c, 0xc, 0x1, 0x81, 0x5d, 0xa0, 0x56, 0x71, 0x9c, 0xef, 0x63, 0xc7, 0xa9, 0x9d, 0x0, 0xfe, 0x67, 0xe0, 0x4a, 0x6f, 0xfa, 0x4b, 0x55, 0x60, 0xfa, 0x93, 0xf4, 0x4a, 0x53, 0x8b, 0x1c, 0x1, 0xdd, 0xdb, 0xc0, 0xc5, 0x75, 0x53, 0x53, 0xf6, 0x80, 0xcb, 0x1d, 0xa0, 0xef, 0xc9, 0x90, 0x4d, 0xd9, 0x95, 0xfc, 0x34, 0x85, 0x5c, 0xe, 0x78, 0x3f, 0xa3, 0x6f, 0xca, 0x0, 0x3d, 0xb7, 0x40, 0xd7, 0x6a, 0xbd, 0xb7, 0xc6, 0x3e, 0x4e, 0x1f, 0x80, 0x14, 0x75, 0x95, 0xb8, 0x1, 0xe, 0xe, 0x81, 0xe1, 0x3c, 0x65, 0xaf, 0x79, 0xbc, 0xbb, 0xa3, 0xb5, 0xb7, 0x7f, 0xcf, 0x34, 0xfa, 0xfb, 0x1, 0x8e, 0x80, 0x72, 0xb2, 0xed, 0x78, 0xfa, 0x7b, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x15, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0x63, 0x63, 0x66, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4c, 0x39, 0x3a, 0xe, 0x0, 0x0, 0x0, 0x6, 0x74, 0x52, 0x4e, 0x53, 0xff, 0xff, 0xff, 0x7f, 0x0, 0x80, 0x2c, 0x16, 0xc1, 0x6d, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x6, 0x61, 0x66, 0xb8, 0x7d, 0x0, 0x0, 0x0, 0x32, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x0, 0x1, 0x46, 0x65, 0x17, 0x17, 0x30, 0x43, 0xc8, 0x4, 0x50, 0x88, 0x1c, 0x52, 0x1, 0x0, 0x2, 0x40, 0x14, 0xbb, 0x70, 0x8b, 0x40, 0xff, 0x2c, 0x18, 0xbe, 0xc6, 0xed, 0x8d, 0x42, 0xa1, 0x50, 0x28, 0x14, 0xa, 0x85, 0xbd, 0xb0, 0x13, 0xfc, 0x71, 0x1, 0xca, 0xf, 0x19, 0x62, 0x24, 0xd6, 0x8, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -314,6 +338,10 @@ static const unsigned char submenu_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x78, 0xc0, 0xf0, 0xe0, 0x3f, 0x8, 0xde, 0x4f, 0x60, 0x0, 0x3, 0xb8, 0xc0, 0x83, 0x2f, 0xf, 0xb5, 0xe1, 0x2, 0x50, 0x78, 0xf5, 0x5, 0x37, 0xaa, 0xc0, 0xff, 0x87, 0xf3, 0x31, 0x4, 0x30, 0xb5, 0x60, 0x1a, 0x8a, 0x61, 0x2d, 0x0, 0xa6, 0x55, 0x4f, 0xb1, 0x91, 0xd6, 0xa7, 0xae, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char submenu_mirrored_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x55, 0xcb, 0xa1, 0x11, 0x83, 0x40, 0x14, 0x45, 0xd1, 0xb3, 0x6b, 0x10, 0x34, 0x10, 0x47, 0x34, 0xf4, 0x13, 0x9d, 0x2, 0x28, 0x87, 0x2a, 0xe8, 0x84, 0x2, 0x82, 0x65, 0x71, 0x69, 0x0, 0x11, 0xf5, 0x11, 0x4c, 0x66, 0xd8, 0xe7, 0xee, 0x99, 0x79, 0x80, 0xed, 0x5d, 0xa2, 0x44, 0x9, 0x12, 0xec, 0x43, 0x2c, 0x5a, 0x78, 0xa6, 0xcc, 0xb7, 0x8d, 0xf9, 0x4a, 0xc8, 0xfc, 0x26, 0x3d, 0x37, 0xa8, 0x97, 0x69, 0x46, 0x6b, 0x5, 0x8f, 0x23, 0xbd, 0x1c, 0xd5, 0xa5, 0xfb, 0xc4, 0xf8, 0x87, 0x13, 0xd2, 0x2f, 0x14, 0x49, 0x6f, 0xb1, 0x11, 0xe1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char tab_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x19, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xc0, 0x2, 0xfe, 0x47, 0xfe, 0x17, 0x1, 0xc2, 0x48, 0xd2, 0x84, 0x10, 0x2, 0x84, 0xb9, 0x98, 0x0, 0x0, 0xbf, 0x67, 0x1d, 0x5, 0x89, 0x9b, 0x48, 0x90, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -354,6 +382,14 @@ static const unsigned char toggle_off_disabled_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0xfc, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x24, 0x24, 0x28, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x19, 0x1c, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0x4e, 0x4e, 0x52, 0x1a, 0x1a, 0x1d, 0x32, 0x32, 0x37, 0x2c, 0x26, 0x2c, 0x26, 0x25, 0x2a, 0x27, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x2f, 0x26, 0x2d, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x15, 0x18, 0x20, 0x20, 0x25, 0x20, 0x20, 0x24, 0x5b, 0x5b, 0x5f, 0x84, 0x84, 0x87, 0x77, 0x77, 0x7a, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x69, 0x69, 0x6c, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x27, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1e, 0x0, 0x0, 0x0, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71, 0xb, 0x1b, 0xbb, 0x0, 0x0, 0x0, 0x54, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x98, 0xe5, 0xfa, 0xfe, 0xff, 0xff, 0x8, 0x17, 0x35, 0x86, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x7, 0x3a, 0xb4, 0xff, 0xff, 0xff, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xb, 0x28, 0x8a, 0xff, 0x8b, 0xf6, 0x45, 0x5, 0x9b, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x37, 0xf, 0xff, 0xfb, 0x4c, 0xfe, 0x4e, 0x4f, 0x50, 0xfb, 0x9c, 0xf6, 0x8c, 0x3b, 0xbb, 0x3c, 0x87, 0xf3, 0x53, 0x14, 0xd4, 0x6d, 0x6c, 0xf9, 0x0, 0x0, 0x2, 0x3, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x55, 0x85, 0x9a, 0xe2, 0x30, 0x18, 0xbc, 0x7a, 0x8b, 0xbb, 0x7b, 0x2, 0xbb, 0x4d, 0x36, 0xb8, 0x3b, 0xeb, 0xae, 0xef, 0xff, 0x2e, 0x47, 0x48, 0x3f, 0xa0, 0x7a, 0xae, 0x83, 0x56, 0xfe, 0xe9, 0xfc, 0xfe, 0xe9, 0x6f, 0x2, 0xc7, 0xb, 0x82, 0xf8, 0x45, 0x8, 0x2, 0xcf, 0x39, 0x9a, 0xf3, 0xa2, 0x24, 0x2b, 0xaa, 0xe6, 0xfb, 0x2, 0x34, 0x55, 0x91, 0x25, 0x91, 0xb7, 0x3f, 0x5d, 0xf4, 0xab, 0x81, 0x60, 0x28, 0x1c, 0x89, 0xc6, 0x3c, 0x11, 0x8d, 0x84, 0x43, 0xc1, 0x80, 0xea, 0x17, 0x2d, 0x2a, 0xf8, 0x78, 0x22, 0x99, 0x4a, 0x67, 0xb2, 0xb9, 0x7c, 0xe1, 0xb, 0xc8, 0xe7, 0xb2, 0x99, 0x74, 0x2a, 0x99, 0x88, 0xf3, 0x26, 0xfb, 0x62, 0xa9, 0x5c, 0xa9, 0xd6, 0x0, 0x80, 0x10, 0xb2, 0xfb, 0x20, 0x5, 0x80, 0x75, 0xe8, 0x48, 0x52, 0xad, 0x94, 0x4b, 0xc5, 0x23, 0x6, 0xae, 0xa1, 0x9d, 0x9c, 0xd6, 0x80, 0x8e, 0xf0, 0x1e, 0x48, 0xdf, 0xb1, 0xd4, 0x81, 0x8b, 0x8e, 0xd3, 0x13, 0xad, 0xc1, 0x1d, 0xfc, 0x57, 0x82, 0x67, 0x35, 0x48, 0x30, 0x6a, 0xb6, 0x76, 0x97, 0x5b, 0x3a, 0x41, 0x98, 0xb4, 0x77, 0x4a, 0xdc, 0x3c, 0x39, 0xb, 0x2a, 0xfb, 0x38, 0xf0, 0x9d, 0x6e, 0xaf, 0x6, 0x11, 0xea, 0x1f, 0xdf, 0x41, 0x10, 0x6a, 0x7b, 0xc6, 0x62, 0xd0, 0xed, 0xf0, 0x6, 0x81, 0x58, 0xa, 0xd, 0x1, 0xa1, 0xa2, 0x8f, 0xa1, 0x23, 0xd2, 0xf2, 0x62, 0x18, 0x8e, 0x4a, 0x63, 0x26, 0x81, 0x93, 0x2, 0x13, 0xa0, 0xe3, 0xbe, 0xf5, 0xe, 0x82, 0x3d, 0x25, 0x14, 0x26, 0x1, 0x89, 0x11, 0xf0, 0x72, 0x70, 0xba, 0x15, 0x60, 0xbf, 0x3, 0x11, 0xe3, 0xcf, 0x6c, 0xbe, 0x58, 0xa2, 0x42, 0x7f, 0xb1, 0xc5, 0xee, 0x8b, 0x9d, 0x5f, 0xad, 0x37, 0xcc, 0x7, 0x41, 0x9, 0x65, 0x21, 0xbd, 0xd9, 0x8a, 0x3d, 0xe9, 0xf9, 0x7c, 0x46, 0x16, 0xb3, 0x3e, 0x35, 0xa4, 0x5f, 0x6, 0x2e, 0x46, 0x8a, 0xc0, 0x8, 0xd4, 0xcb, 0x2b, 0x88, 0x75, 0x3b, 0x81, 0x8e, 0xd, 0x1, 0xd4, 0x68, 0x8e, 0xfa, 0xe7, 0x94, 0xe0, 0x7c, 0x4f, 0x90, 0xbf, 0x56, 0x45, 0x46, 0x50, 0xba, 0xa9, 0x41, 0xec, 0x10, 0xb0, 0x96, 0x41, 0xd0, 0x3f, 0xdf, 0x7e, 0xe1, 0x79, 0xff, 0xfc, 0xfc, 0x9c, 0x6c, 0xbf, 0xf6, 0x14, 0xb7, 0x25, 0x83, 0x40, 0xd, 0xd7, 0x3c, 0x15, 0x90, 0x9d, 0x2, 0xec, 0xae, 0x40, 0x9, 0xdd, 0x1, 0xef, 0x18, 0xa0, 0x2, 0x59, 0xda, 0x5c, 0xd8, 0xc7, 0x80, 0x97, 0xd7, 0x5f, 0xc8, 0x42, 0x7f, 0x79, 0xbe, 0x9c, 0x17, 0x2c, 0x4, 0xfb, 0x2c, 0xd0, 0x3a, 0xb8, 0xff, 0x42, 0x1d, 0x10, 0x5a, 0x95, 0x33, 0x44, 0xd8, 0x97, 0x81, 0xfb, 0xa4, 0xc4, 0xed, 0x2b, 0xf1, 0x1, 0xfe, 0x40, 0x25, 0xd2, 0x5e, 0x18, 0x7c, 0x7b, 0x2f, 0x3c, 0xd2, 0x5e, 0xf8, 0x72, 0x37, 0x16, 0xbe, 0xdc, 0x8d, 0x6c, 0x1e, 0x3c, 0x3d, 0xd7, 0x40, 0xdb, 0x32, 0xf, 0xbc, 0xec, 0x9f, 0x5f, 0xf6, 0xf3, 0x80, 0x4d, 0x24, 0x2d, 0xf8, 0xfa, 0x6, 0xea, 0x80, 0xd, 0x24, 0x68, 0x0, 0x6c, 0x5f, 0x8e, 0xf6, 0xd5, 0xd7, 0xa0, 0x56, 0x34, 0xcf, 0xb4, 0x86, 0x92, 0xc, 0xa5, 0x33, 0x17, 0xf9, 0xc2, 0x17, 0x91, 0xbf, 0xc8, 0xa4, 0x43, 0x49, 0xa5, 0xc1, 0x5b, 0xa7, 0x72, 0x47, 0xd, 0xac, 0x47, 0xd7, 0xef, 0xb1, 0x2f, 0xe0, 0xfd, 0x7a, 0xb4, 0xe, 0xa8, 0x1d, 0x91, 0xb3, 0x6f, 0x95, 0xb1, 0xb4, 0xf9, 0x28, 0x7d, 0x79, 0x2f, 0x94, 0x3e, 0x36, 0xd2, 0x98, 0xe7, 0x5c, 0x36, 0x93, 0xf8, 0x15, 0xa0, 0x9b, 0xe9, 0xff, 0xc2, 0x67, 0x14, 0xf4, 0xa5, 0xb3, 0x35, 0x5e, 0x63, 0x97, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char toggle_off_disabled_mirrored_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x9c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x1d, 0x1d, 0x21, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x2e, 0x2e, 0x2e, 0x46, 0x46, 0x46, 0x57, 0x57, 0x57, 0x60, 0x60, 0x60, 0x62, 0x62, 0x62, 0x5e, 0x5e, 0x5e, 0x4a, 0x4a, 0x4a, 0x12, 0x12, 0x15, 0x25, 0x27, 0x2d, 0x42, 0x42, 0x42, 0x59, 0x59, 0x59, 0x32, 0x32, 0x32, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x39, 0x39, 0x39, 0x49, 0x49, 0x49, 0x5a, 0x5a, 0x5a, 0x48, 0x48, 0x48, 0x54, 0x54, 0x54, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x1e, 0x1e, 0x22, 0x25, 0x26, 0x2b, 0x3f, 0x3f, 0x3f, 0xe, 0xe, 0x10, 0x2c, 0x2c, 0x2c, 0x58, 0x58, 0x58, 0x5d, 0x5d, 0x5f, 0x80, 0x80, 0x80, 0x79, 0x79, 0x79, 0x40, 0x40, 0x44, 0x32, 0x32, 0x37, 0x41, 0x41, 0x41, 0x1a, 0x1a, 0x1d, 0x6a, 0x6a, 0x6d, 0x52, 0x52, 0x52, 0x3a, 0x3a, 0x3a, 0x5b, 0x5b, 0x5b, 0x2f, 0x2f, 0x2f, 0x50, 0x50, 0x51, 0x13, 0x13, 0x15, 0x2b, 0xcd, 0x4, 0x96, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x1, 0x29, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x94, 0xdb, 0x76, 0x83, 0x20, 0x10, 0x45, 0x8d, 0xe8, 0x24, 0x98, 0xda, 0x1a, 0x93, 0xda, 0x5b, 0xac, 0xb9, 0x50, 0x14, 0x52, 0x72, 0xfd, 0xff, 0x7f, 0xab, 0x68, 0x49, 0x28, 0x5a, 0x35, 0xf5, 0xa5, 0xf, 0x39, 0xf, 0xb3, 0x96, 0xe3, 0xda, 0x7, 0x98, 0x81, 0xb1, 0xac, 0x9b, 0xfe, 0x97, 0x6, 0x36, 0x72, 0x5c, 0x68, 0x91, 0xeb, 0x20, 0x7b, 0x50, 0xcf, 0xf, 0x5b, 0xe1, 0xb3, 0xc9, 0xb0, 0x6, 0x1f, 0xe1, 0x46, 0xc6, 0x1b, 0xdf, 0xf9, 0xf7, 0xa5, 0x1e, 0x2, 0xf, 0xf0, 0xc8, 0xe4, 0x27, 0x8d, 0xcb, 0x87, 0xd3, 0xd9, 0xf8, 0x31, 0x7a, 0x92, 0x7a, 0xf6, 0x5e, 0x5e, 0xdf, 0xa6, 0xa1, 0x3b, 0x31, 0xc, 0x1a, 0xd7, 0xf, 0xe7, 0xf1, 0xbb, 0xfe, 0x9d, 0xc4, 0xf3, 0x10, 0xff, 0xe4, 0x17, 0x4d, 0xfc, 0x72, 0x15, 0x7b, 0xc6, 0x81, 0xe2, 0xd5, 0x72, 0xa1, 0xf3, 0xeb, 0xc6, 0xf3, 0x93, 0x8f, 0xc4, 0x4c, 0x25, 0x33, 0x2, 0x6b, 0xcd, 0xc0, 0x56, 0x3f, 0x10, 0x4d, 0x33, 0x6, 0x24, 0xcd, 0x55, 0x4, 0x2e, 0x93, 0x9b, 0xa0, 0x6a, 0x1a, 0x6c, 0xe0, 0x53, 0x33, 0x40, 0x2a, 0x2f, 0x28, 0xe2, 0x29, 0x22, 0x12, 0x24, 0x25, 0x9d, 0x6b, 0xbb, 0xab, 0x1a, 0xec, 0xb6, 0x80, 0x34, 0x3, 0x47, 0x6d, 0x40, 0x42, 0x94, 0x11, 0x21, 0xd, 0x84, 0x32, 0xd8, 0x1f, 0xaa, 0x6, 0x87, 0x3d, 0xe8, 0x65, 0x54, 0x3d, 0x24, 0x22, 0xf, 0x47, 0x4a, 0x84, 0x10, 0xbc, 0x8, 0x45, 0xd6, 0x8f, 0xaa, 0x6, 0x91, 0xf, 0x50, 0xd3, 0x44, 0x5e, 0xec, 0xe0, 0x78, 0xfd, 0xe, 0x2e, 0x35, 0x60, 0xc0, 0x33, 0xf3, 0x8, 0x1d, 0x6a, 0x70, 0xee, 0x2, 0xc9, 0x44, 0x46, 0xc1, 0x30, 0xe8, 0xd0, 0x85, 0xcb, 0x3d, 0xe0, 0x4c, 0xd6, 0x92, 0xf1, 0xef, 0xd0, 0xf5, 0x1e, 0xf4, 0xbe, 0x89, 0xfd, 0xdf, 0x42, 0xff, 0xd7, 0x68, 0x9d, 0xae, 0x9b, 0x7, 0xa7, 0xba, 0x89, 0x4, 0x9d, 0x55, 0x37, 0x91, 0xca, 0x99, 0x88, 0xdb, 0x61, 0xfc, 0xeb, 0x4c, 0xbc, 0xe9, 0xaf, 0xfa, 0x2, 0xdc, 0x1a, 0x30, 0x60, 0x4e, 0xef, 0xb8, 0xbb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + +static const unsigned char toggle_off_mirrored_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0xaa, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x24, 0x24, 0x28, 0x20, 0x20, 0x25, 0x14, 0x14, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x27, 0x10, 0x10, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x19, 0x19, 0x1c, 0x12, 0x12, 0x15, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x11, 0x11, 0x14, 0x1e, 0x1e, 0x22, 0x23, 0x23, 0x27, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x25, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x24, 0x24, 0x27, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x1a, 0x1a, 0x1e, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x26, 0x11, 0x11, 0x13, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x24, 0x24, 0x28, 0x2d, 0x26, 0x2c, 0x51, 0x2c, 0x39, 0x6c, 0x31, 0x42, 0x71, 0x32, 0x44, 0x6e, 0x31, 0x43, 0x63, 0x2f, 0x3f, 0x4d, 0x2b, 0x37, 0x27, 0x25, 0x2a, 0x47, 0x2a, 0x35, 0x68, 0x30, 0x40, 0x52, 0x2c, 0x39, 0x3c, 0x28, 0x31, 0x2e, 0x25, 0x2c, 0x26, 0x25, 0x2a, 0x32, 0x26, 0x2e, 0x4d, 0x2b, 0x38, 0x66, 0x30, 0x40, 0x50, 0x2c, 0x38, 0x5e, 0x2e, 0x3d, 0x38, 0x27, 0x30, 0x35, 0x27, 0x2f, 0x5f, 0x2e, 0x3d, 0x44, 0x2a, 0x34, 0x5f, 0x2f, 0x3e, 0x2f, 0x25, 0x2c, 0x43, 0x2a, 0x34, 0x2c, 0x26, 0x2c, 0x66, 0x2f, 0x40, 0x37, 0x27, 0x30, 0x36, 0x27, 0x30, 0x64, 0x2f, 0x3f, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x32, 0x32, 0x37, 0x46, 0x2a, 0x35, 0x53, 0x2c, 0x39, 0xc9, 0xc9, 0xca, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x5d, 0x2e, 0x3d, 0x3e, 0x29, 0x32, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x69, 0x30, 0x41, 0x2f, 0x26, 0x2d, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x4e, 0x4e, 0x52, 0x48, 0x2b, 0x36, 0x2c, 0x26, 0x2b, 0x25, 0x25, 0x27, 0xea, 0xac, 0x78, 0x5d, 0x0, 0x0, 0x0, 0x51, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xfa, 0xe5, 0x98, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xf3, 0x86, 0x7, 0x3a, 0x96, 0xf9, 0xb4, 0x9a, 0xb9, 0xb, 0x28, 0x76, 0xfb, 0x8a, 0xde, 0xf6, 0x82, 0x9b, 0xc6, 0xe6, 0x4c, 0xfe, 0x4f, 0xe9, 0xfb, 0x37, 0x83, 0x9c, 0xf6, 0x77, 0x8b, 0x3b, 0x9c, 0xbb, 0x74, 0xda, 0xf3, 0x87, 0xfb, 0x45, 0x4e, 0x53, 0x5, 0xf, 0x14, 0xc7, 0x22, 0x44, 0x61, 0x0, 0x0, 0x2, 0x45, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xd5, 0x55, 0xe7, 0x5f, 0xd3, 0x40, 0x18, 0x26, 0x3b, 0x91, 0x24, 0x65, 0x95, 0xd, 0xd, 0x8e, 0x22, 0x6a, 0x71, 0x2b, 0x82, 0x75, 0xe1, 0x66, 0xa6, 0x97, 0xa0, 0x56, 0x45, 0xa4, 0x2a, 0x2e, 0xdc, 0x76, 0x25, 0x18, 0x84, 0x6, 0x9c, 0xff, 0xb3, 0xb9, 0x26, 0x17, 0xda, 0xa4, 0x49, 0xd4, 0x4f, 0xfa, 0x7c, 0xb8, 0xdf, 0xe5, 0xcd, 0xef, 0x79, 0xee, 0xbd, 0xf7, 0xde, 0xd1, 0xd4, 0xf4, 0xf, 0x1, 0xc3, 0x9, 0x82, 0x8c, 0x4, 0x41, 0xe0, 0x58, 0x43, 0x3a, 0x4e, 0x52, 0x34, 0xc3, 0x72, 0xbb, 0x22, 0xc0, 0xb1, 0xc, 0x4d, 0x91, 0xb8, 0xff, 0x74, 0xb2, 0x99, 0xe5, 0x5, 0x31, 0xd6, 0xd2, 0xda, 0x16, 0x8a, 0xd6, 0x96, 0x98, 0x28, 0xf0, 0x6c, 0x33, 0xe9, 0xf1, 0x2, 0x6f, 0xef, 0x88, 0x77, 0x76, 0x75, 0xf7, 0xf4, 0xf6, 0xc9, 0x11, 0xc8, 0xf4, 0xf5, 0xf, 0xc, 0x76, 0xc6, 0x3b, 0xda, 0xeb, 0x9c, 0xc0, 0x13, 0xd2, 0xd0, 0xee, 0x3d, 0x99, 0x0, 0xe, 0x50, 0x20, 0xec, 0xbd, 0xb5, 0x1, 0x40, 0xdd, 0xbb, 0x6f, 0x48, 0x4a, 0xd4, 0x28, 0x60, 0x49, 0x6e, 0x78, 0xff, 0x48, 0xe0, 0xa1, 0x90, 0xbb, 0x70, 0xeb, 0xf6, 0x1d, 0x1b, 0xd9, 0xbb, 0xf7, 0x16, 0x80, 0x7a, 0x60, 0x98, 0x4b, 0x62, 0x3b, 0xf7, 0x67, 0x84, 0x83, 0xa1, 0xbe, 0xdf, 0x5f, 0x7c, 0xb0, 0xf4, 0x70, 0x39, 0x57, 0xdd, 0xe7, 0x1e, 0x3d, 0x7e, 0xb2, 0xa8, 0xa8, 0x87, 0x4, 0xc6, 0x8d, 0x3, 0x9e, 0x1a, 0x3d, 0x7c, 0x24, 0x94, 0xbf, 0xf2, 0xf4, 0x59, 0xed, 0xf7, 0xf3, 0x17, 0x2b, 0x8a, 0x7a, 0x74, 0x34, 0x85, 0x2e, 0x41, 0x4a, 0xe2, 0xb1, 0x30, 0x7e, 0xee, 0xe5, 0xea, 0xab, 0x7a, 0xcb, 0xf2, 0xea, 0x6b, 0x70, 0x5c, 0x94, 0x48, 0xe7, 0x6, 0x14, 0x7f, 0x22, 0x34, 0xf0, 0x6f, 0xde, 0xbe, 0xf3, 0x9a, 0xde, 0x7f, 0xf8, 0x8, 0x4e, 0xf2, 0x94, 0x73, 0x3, 0x5a, 0x38, 0x85, 0x7e, 0xe4, 0xb, 0xc5, 0x52, 0x59, 0xd6, 0x8a, 0x16, 0xaa, 0x8b, 0xe, 0x8d, 0x6b, 0x9f, 0xfc, 0xa2, 0x4b, 0x6b, 0xe0, 0xb4, 0x40, 0xdb, 0x2, 0x4, 0x23, 0xf6, 0x20, 0xbb, 0xb1, 0x9e, 0xff, 0x5c, 0xcc, 0x6b, 0x90, 0xa8, 0xd9, 0x6c, 0xb, 0x1b, 0x9b, 0x7e, 0x81, 0xcd, 0xd, 0xa5, 0x5f, 0x64, 0x1c, 0x1, 0x76, 0xac, 0x17, 0x39, 0x0, 0x49, 0xeb, 0x15, 0xcd, 0x84, 0x2, 0x26, 0x12, 0xd8, 0xda, 0xf6, 0xb, 0x6c, 0x6f, 0x29, 0x67, 0xc6, 0x58, 0x47, 0x40, 0x1a, 0x47, 0x6f, 0xa8, 0x99, 0xd6, 0xf2, 0xa5, 0xa0, 0x19, 0x86, 0xa1, 0x57, 0x97, 0xaa, 0x35, 0x9b, 0x6b, 0x10, 0xd8, 0xac, 0xa2, 0x8e, 0x4b, 0xc8, 0x83, 0x18, 0x4a, 0x22, 0x1d, 0x7a, 0x50, 0xf8, 0xaa, 0x41, 0xa6, 0x66, 0x44, 0x78, 0xa0, 0xc6, 0x58, 0x37, 0x6, 0x13, 0xc8, 0x6e, 0x56, 0x64, 0xbd, 0x54, 0xf6, 0x8, 0x34, 0x8e, 0x1, 0x38, 0x8b, 0x62, 0x80, 0xd3, 0x69, 0xf7, 0x15, 0xb4, 0x92, 0x59, 0x2a, 0xc8, 0x1e, 0x81, 0xa0, 0x57, 0x48, 0xd3, 0x6e, 0x1e, 0x9c, 0x73, 0x7f, 0xe8, 0x95, 0x6f, 0x56, 0x2c, 0xcb, 0xba, 0xb3, 0x84, 0xe5, 0xc1, 0x79, 0x94, 0x7, 0x7f, 0x97, 0x89, 0xca, 0x5, 0x37, 0x13, 0x61, 0x2d, 0x5c, 0xfc, 0xf3, 0x5a, 0xb8, 0xb4, 0x53, 0xb, 0xbf, 0x51, 0x8d, 0xdf, 0x43, 0xab, 0x11, 0xf6, 0x83, 0xc9, 0xcb, 0xa1, 0x3e, 0xd4, 0xf7, 0x83, 0x1f, 0x40, 0xbd, 0x32, 0x59, 0xd3, 0xf, 0x60, 0x47, 0xe2, 0x84, 0xab, 0xd7, 0x82, 0xc8, 0x76, 0x47, 0x72, 0x9a, 0x92, 0xd5, 0x91, 0x7e, 0x82, 0xeb, 0x37, 0x4, 0x2e, 0x51, 0xdf, 0xd3, 0x92, 0x4c, 0x5c, 0xec, 0xea, 0x9e, 0x18, 0x91, 0x23, 0x91, 0xb9, 0x39, 0x30, 0x28, 0xc6, 0x99, 0xa4, 0xa7, 0x31, 0x63, 0x64, 0x8a, 0xe5, 0xd3, 0x53, 0xd3, 0x33, 0x6d, 0x11, 0x98, 0x99, 0x9e, 0x4a, 0xf3, 0x6c, 0x8a, 0xf4, 0xcd, 0x6, 0xc, 0x9f, 0xa5, 0xe6, 0xe6, 0xa5, 0xe8, 0xb9, 0x20, 0xcd, 0xcf, 0x51, 0xb3, 0x8d, 0x67, 0x8b, 0x35, 0x99, 0xa2, 0x7, 0x93, 0x35, 0x9a, 0x2, 0x26, 0xd3, 0xff, 0x8b, 0x5f, 0x3d, 0xdc, 0x7c, 0xb4, 0x8c, 0xb2, 0xd8, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char toggle_on_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x74, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x12, 0x12, 0x15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x1a, 0x1a, 0x1e, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x2c, 0x36, 0x27, 0x49, 0x65, 0x29, 0x5d, 0x85, 0x2a, 0x66, 0x95, 0x2a, 0x68, 0x99, 0x29, 0x64, 0x92, 0x28, 0x4c, 0x6b, 0x25, 0x27, 0x2d, 0x27, 0x43, 0x5c, 0x29, 0x5f, 0x89, 0x27, 0x49, 0x66, 0x25, 0x30, 0x3e, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x25, 0x2d, 0x38, 0x25, 0x3a, 0x4c, 0x27, 0x4d, 0x6b, 0x29, 0x60, 0x8c, 0x27, 0x44, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x59, 0x7f, 0x25, 0x34, 0x43, 0x25, 0x35, 0x45, 0x28, 0x58, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x40, 0x57, 0x27, 0x41, 0x57, 0x25, 0x2a, 0x33, 0x29, 0x5d, 0x87, 0x25, 0x34, 0x44, 0x25, 0x2b, 0x34, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x27, 0x43, 0x5b, 0x27, 0x4d, 0x6c, 0x27, 0x4e, 0x6d, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x28, 0x56, 0x7b, 0x26, 0x3b, 0x4e, 0x26, 0x3a, 0x4e, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0xe4, 0xe4, 0xe5, 0x27, 0x44, 0x5d, 0xdd, 0xc9, 0xf2, 0x7e, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0x8, 0x17, 0x35, 0x73, 0xd9, 0x7, 0x3a, 0x96, 0xf9, 0x9a, 0xb, 0x28, 0x76, 0xfb, 0x77, 0xde, 0x45, 0x5, 0x82, 0xc6, 0xc6, 0x37, 0xf, 0xe9, 0x4c, 0x4e, 0x4f, 0x50, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x37, 0x21, 0x5a, 0x6c, 0x0, 0x0, 0x2, 0x4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x95, 0x63, 0x83, 0xdc, 0x60, 0x10, 0xc7, 0x1b, 0x3c, 0xc1, 0xda, 0x3e, 0xdb, 0x36, 0xe7, 0x6c, 0xdb, 0xa, 0xcf, 0xc6, 0x7e, 0xf8, 0x2a, 0x6d, 0xb8, 0xac, 0x7b, 0xbf, 0xf7, 0xf3, 0x1f, 0xcf, 0x7c, 0xf8, 0x97, 0xc0, 0x70, 0x82, 0x20, 0xb3, 0x42, 0x10, 0x38, 0x96, 0xd2, 0x1c, 0x27, 0x11, 0x45, 0x33, 0xac, 0x2d, 0xb, 0x2c, 0x43, 0x53, 0x88, 0xc4, 0xad, 0xde, 0x49, 0x3b, 0xe3, 0x70, 0xba, 0xdc, 0x1e, 0xaf, 0x2f, 0x23, 0x5e, 0x8f, 0xdb, 0xe5, 0x74, 0x30, 0x76, 0xd2, 0x14, 0x5, 0xee, 0xf, 0x4, 0x43, 0xe1, 0x48, 0x34, 0x16, 0x87, 0x2c, 0xc4, 0x63, 0xd1, 0x48, 0x38, 0x14, 0xc, 0xf8, 0x71, 0x83, 0x7d, 0xa2, 0xa0, 0xb0, 0xa8, 0x78, 0x4, 0x72, 0x64, 0xa4, 0xb8, 0xa8, 0xb0, 0x20, 0xa1, 0x53, 0xc0, 0x4a, 0xd8, 0xd2, 0xb2, 0x72, 0xc8, 0xc4, 0xe8, 0xd8, 0xf8, 0xc4, 0xa4, 0xc2, 0xd4, 0xf4, 0x28, 0x94, 0x97, 0x95, 0xb2, 0x25, 0x98, 0x96, 0x3f, 0xed, 0xac, 0xc8, 0x18, 0xfb, 0xcc, 0xec, 0xdc, 0xfc, 0xc2, 0xe2, 0xd2, 0x17, 0x96, 0x57, 0x56, 0xd7, 0xd6, 0x37, 0x66, 0xe2, 0x15, 0x4e, 0x5a, 0xad, 0x3, 0x5e, 0x59, 0x55, 0x5d, 0x93, 0xd1, 0x7e, 0x73, 0x6b, 0x1b, 0x74, 0xec, 0xec, 0x6e, 0xce, 0xd4, 0xd4, 0x56, 0x55, 0x7e, 0x4f, 0x82, 0x2c, 0x70, 0xd5, 0x41, 0x6, 0xf6, 0xf6, 0xb7, 0x56, 0xc0, 0xc0, 0xca, 0xd6, 0xc1, 0x5e, 0x5d, 0x7d, 0x41, 0x83, 0x12, 0x2, 0x86, 0x1c, 0x8d, 0x90, 0x89, 0xc3, 0xa3, 0x63, 0x30, 0xb1, 0x33, 0x77, 0x2, 0x8d, 0xe, 0xa4, 0x8, 0xe0, 0x94, 0xb3, 0x9, 0x54, 0x4e, 0xcf, 0x38, 0x5e, 0x0, 0x91, 0x93, 0x40, 0x94, 0x41, 0xe1, 0xfc, 0x2, 0x2c, 0x5c, 0x9e, 0x43, 0x73, 0x4b, 0xab, 0x92, 0x3, 0x41, 0xbb, 0xa2, 0xa0, 0x22, 0x5f, 0x9d, 0x5e, 0x73, 0xa7, 0x22, 0x27, 0x6b, 0x2, 0x37, 0xb7, 0x60, 0xe1, 0xee, 0x6, 0xda, 0xea, 0x69, 0x42, 0x11, 0x60, 0xda, 0x63, 0x5a, 0x0, 0xdc, 0x3d, 0xc0, 0xd5, 0x83, 0xf8, 0xc8, 0x8b, 0xaa, 0xc0, 0xd3, 0x33, 0x58, 0x78, 0x7e, 0x82, 0xf2, 0xe, 0x86, 0x54, 0x4, 0xa, 0x3a, 0xb5, 0x1e, 0x8a, 0x8f, 0x0, 0xf0, 0x72, 0x26, 0xca, 0x2f, 0x8f, 0xaa, 0xc0, 0xc4, 0x22, 0x58, 0x58, 0x9c, 0x0, 0xe8, 0x2a, 0xf8, 0x26, 0xc0, 0xb8, 0xb5, 0x21, 0xba, 0xff, 0x12, 0xc1, 0xd9, 0xeb, 0x67, 0xe3, 0xb7, 0xb3, 0x9c, 0x23, 0xa0, 0x5d, 0x6d, 0xa0, 0xf2, 0xf8, 0x0, 0xf7, 0xbc, 0xf0, 0x59, 0x40, 0xe0, 0x72, 0xad, 0x1, 0x4e, 0xb5, 0xe8, 0xba, 0x20, 0xf2, 0x8f, 0xfc, 0xd9, 0xd7, 0x2, 0x3e, 0xe6, 0xda, 0x5, 0xc, 0x39, 0xba, 0x41, 0xe3, 0xfe, 0x41, 0x2, 0x38, 0x15, 0x0, 0x24, 0x21, 0xf3, 0x1c, 0x74, 0x7, 0x11, 0xf6, 0xd3, 0x93, 0xa8, 0xee, 0x42, 0x6d, 0xfe, 0xbb, 0xd0, 0xa3, 0xed, 0x42, 0xe, 0xdb, 0xb8, 0x61, 0xdc, 0xc6, 0xa4, 0xba, 0x8d, 0xea, 0x3d, 0xe8, 0xed, 0xab, 0xc9, 0xe7, 0x1e, 0xd4, 0xf4, 0xf5, 0xab, 0xf7, 0x40, 0xb9, 0x48, 0xac, 0x73, 0x60, 0x10, 0x72, 0x66, 0x70, 0xc0, 0xc9, 0x26, 0x8c, 0x37, 0xad, 0x84, 0xe, 0xba, 0xc2, 0x91, 0xb6, 0x72, 0xc8, 0x4a, 0x79, 0x5b, 0x24, 0xec, 0xa, 0xd2, 0x25, 0xb8, 0xf9, 0x2a, 0x57, 0x32, 0x8e, 0x96, 0xfa, 0x8e, 0x21, 0x5f, 0x16, 0x86, 0x3a, 0xea, 0x5b, 0x1c, 0x4c, 0x25, 0x89, 0x59, 0xbf, 0x4a, 0x3, 0x6a, 0x1d, 0x2e, 0xc8, 0xfe, 0x17, 0xa, 0x86, 0x5b, 0x51, 0x3, 0x8e, 0xa5, 0xf9, 0x4c, 0x64, 0xe, 0x68, 0x9f, 0xe9, 0xbd, 0xf0, 0x9, 0xb7, 0x71, 0x36, 0xc6, 0x9b, 0x3d, 0x7f, 0x21, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -362,6 +398,14 @@ static const unsigned char toggle_on_disabled_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x53, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x2e, 0x2e, 0x2e, 0x46, 0x46, 0x46, 0x57, 0x57, 0x57, 0x60, 0x60, 0x60, 0x62, 0x62, 0x62, 0x5e, 0x5e, 0x5e, 0x4a, 0x4a, 0x4a, 0x12, 0x12, 0x15, 0x25, 0x27, 0x2d, 0x42, 0x42, 0x42, 0x59, 0x59, 0x59, 0x32, 0x32, 0x32, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x39, 0x39, 0x39, 0x49, 0x49, 0x49, 0x5a, 0x5a, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x48, 0x48, 0x48, 0x54, 0x54, 0x54, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x25, 0x26, 0x2b, 0x3f, 0x3f, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x2c, 0x2c, 0x2c, 0x58, 0x58, 0x58, 0x1a, 0x1a, 0x1e, 0x40, 0x40, 0x44, 0x56, 0x56, 0x58, 0x80, 0x80, 0x80, 0x79, 0x79, 0x79, 0x3c, 0x3c, 0x3d, 0x2e, 0x2e, 0x30, 0x27, 0x27, 0x29, 0x64, 0x64, 0x66, 0x41, 0x41, 0x41, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0x5d, 0x5f, 0x34, 0x34, 0x36, 0x52, 0x52, 0x52, 0x3a, 0x3a, 0x3a, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x32, 0x32, 0x37, 0x42, 0x42, 0x44, 0x6a, 0x6a, 0x6d, 0x5b, 0x5b, 0x5b, 0x2f, 0x2f, 0x2f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x49, 0x4a, 0x0, 0x0, 0x0, 0x50, 0x50, 0x51, 0x70, 0x70, 0x74, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbd, 0xb, 0x85, 0x35, 0x0, 0x0, 0x0, 0x71, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xff, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xff, 0x7, 0x3a, 0x96, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb, 0x28, 0x76, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x77, 0xde, 0xff, 0xff, 0x45, 0x5, 0x82, 0xff, 0xff, 0xc6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x37, 0xf, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x4c, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4e, 0x4f, 0xff, 0x50, 0xff, 0xff, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x49, 0x96, 0x6e, 0xf, 0x0, 0x0, 0x1, 0xfa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x18, 0x5e, 0x0, 0xd0, 0x5c, 0x39, 0x28, 0x49, 0x12, 0xc0, 0x60, 0xb8, 0xda, 0xdd, 0xcb, 0x31, 0x33, 0xb6, 0x6d, 0xdb, 0x5e, 0xdb, 0xd6, 0xfb, 0x17, 0x4e, 0x7d, 0x37, 0xe6, 0xf9, 0x2b, 0x23, 0x7f, 0x9c, 0x20, 0x28, 0x86, 0xe1, 0xb, 0xc1, 0x30, 0x14, 0x99, 0x6a, 0x8e, 0xe2, 0x4, 0x49, 0xd1, 0xcc, 0xda, 0x2, 0x18, 0x9a, 0x22, 0x9, 0x1c, 0x9d, 0xf4, 0x8e, 0xaf, 0xd3, 0x1b, 0x9b, 0x5b, 0xdb, 0x1c, 0x2e, 0x6f, 0x2e, 0x5c, 0xce, 0xf6, 0xd6, 0xe6, 0x6, 0xbd, 0x8e, 0x8f, 0x45, 0x81, 0xf2, 0x5, 0x42, 0x91, 0x58, 0x22, 0x95, 0xc9, 0x61, 0x1, 0x72, 0x99, 0x54, 0x22, 0x16, 0x9, 0x5, 0x7c, 0x74, 0xc4, 0x5e, 0xa1, 0x54, 0xa9, 0x35, 0x5a, 0x58, 0x12, 0xad, 0x46, 0xad, 0x52, 0x2a, 0x86, 0x14, 0x10, 0x1d, 0xa3, 0x37, 0x18, 0x61, 0x1e, 0x26, 0xb3, 0xc5, 0x6a, 0x63, 0xb1, 0x3b, 0x4c, 0x60, 0x34, 0xe8, 0x19, 0x1d, 0x32, 0xc8, 0x9f, 0xda, 0x74, 0xce, 0x8d, 0xdd, 0xe5, 0xf6, 0x98, 0xbd, 0x3e, 0xff, 0x57, 0x2, 0xa6, 0x60, 0x28, 0xec, 0x76, 0xc9, 0x9d, 0x9b, 0x54, 0xbf, 0xe, 0x68, 0x24, 0x1a, 0x8b, 0xcf, 0xb5, 0x4f, 0x24, 0x53, 0x30, 0x44, 0x3a, 0x99, 0x70, 0xc5, 0x33, 0xd1, 0xc8, 0x8f, 0x24, 0x70, 0xe5, 0x56, 0x16, 0xe6, 0x90, 0xcb, 0x27, 0x4d, 0x63, 0x9, 0x25, 0xf3, 0xb9, 0x6c, 0x41, 0x59, 0x64, 0x43, 0x40, 0x88, 0x8d, 0x12, 0xcc, 0xa3, 0x5c, 0x49, 0xc3, 0x18, 0x69, 0x4f, 0x19, 0x4a, 0x1b, 0x4, 0x2b, 0x80, 0x92, 0x9b, 0x55, 0xe8, 0x53, 0xab, 0x37, 0x9a, 0x2d, 0x68, 0x37, 0x3a, 0xd0, 0xee, 0x2, 0x4b, 0xcf, 0x1, 0x13, 0x38, 0x7a, 0xb0, 0xb3, 0xbb, 0xc7, 0xe6, 0x80, 0x51, 0x5b, 0x52, 0xe8, 0xd3, 0xdd, 0xaf, 0x1d, 0x34, 0x6a, 0xed, 0x46, 0x77, 0x20, 0x70, 0x78, 0x4, 0x13, 0x1c, 0x1d, 0xc2, 0x71, 0x81, 0xc2, 0x58, 0x1, 0xfa, 0x44, 0x36, 0x8, 0xa0, 0x71, 0xa, 0xb0, 0x7f, 0xd6, 0x3e, 0x6f, 0xb6, 0xfb, 0x2, 0x17, 0x97, 0x30, 0xc1, 0xe5, 0x5, 0x18, 0xaf, 0x68, 0x9c, 0x15, 0x50, 0x5e, 0xf, 0x7a, 0xd8, 0x3e, 0x7, 0x80, 0x9b, 0x7a, 0xbb, 0x7b, 0x73, 0xde, 0x17, 0xb0, 0xfa, 0x60, 0x2, 0x9f, 0x15, 0xe0, 0x56, 0xf9, 0x5d, 0x80, 0xde, 0x1e, 0xc, 0xd1, 0xe9, 0xd7, 0x8, 0xea, 0x77, 0xed, 0x2e, 0xdc, 0xd7, 0x97, 0x8e, 0x80, 0xda, 0x3a, 0x86, 0x3e, 0xe7, 0x67, 0x70, 0xda, 0x6c, 0xb5, 0xbb, 0xd0, 0x6a, 0x2c, 0x5b, 0x3, 0x94, 0xdc, 0xad, 0x42, 0x9f, 0x76, 0xf3, 0xbc, 0x59, 0x87, 0xaf, 0xe1, 0x9f, 0x2f, 0xd5, 0x5, 0x76, 0xe, 0x1e, 0x60, 0xc0, 0xe9, 0x59, 0x7, 0xa0, 0xd6, 0x2, 0xe8, 0xb4, 0xe6, 0xcf, 0xc1, 0x83, 0x90, 0x40, 0x7e, 0x79, 0x12, 0xfb, 0xbb, 0x90, 0x59, 0x7d, 0x17, 0x1e, 0xd9, 0x5d, 0xf8, 0xd5, 0x6d, 0xec, 0xdf, 0x83, 0xa7, 0xe7, 0xf8, 0x2a, 0xf7, 0x20, 0xfe, 0xfc, 0xc2, 0xde, 0x83, 0xfe, 0x45, 0x62, 0x36, 0x5f, 0xdf, 0x60, 0x69, 0xde, 0x5e, 0x37, 0x19, 0xc5, 0xe8, 0x4d, 0xd3, 0x51, 0xc2, 0x2d, 0xb1, 0xe4, 0xd8, 0x8, 0xb, 0x31, 0x1e, 0x4b, 0xc4, 0x5b, 0x42, 0x4a, 0x87, 0x8e, 0x5f, 0xe5, 0x8, 0xbd, 0xb1, 0x5b, 0xb8, 0x7a, 0xe7, 0x2d, 0xe0, 0xfd, 0xaa, 0xb0, 0xbb, 0x41, 0x47, 0x70, 0x64, 0xf2, 0xab, 0x14, 0x89, 0xbd, 0xf, 0xe5, 0xe2, 0xbf, 0xa0, 0xfc, 0xd8, 0x23, 0x8a, 0x28, 0x32, 0xe3, 0x33, 0xe1, 0x4b, 0xc0, 0x7e, 0xa6, 0xff, 0x87, 0xcf, 0xb, 0x94, 0xb9, 0x37, 0x3c, 0xc6, 0xd8, 0xcd, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char toggle_on_disabled_mirrored_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x69, 0x50, 0x4c, 0x54, 0x45, 0x93, 0x7f, 0x2b, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x19, 0x19, 0x1c, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0x4e, 0x4e, 0x52, 0x1a, 0x1a, 0x1d, 0x32, 0x32, 0x37, 0x2c, 0x26, 0x2c, 0x26, 0x25, 0x2a, 0x27, 0x25, 0x2a, 0x11, 0x11, 0x14, 0x2f, 0x26, 0x2d, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x15, 0x15, 0x18, 0x5b, 0x5b, 0x5f, 0x84, 0x84, 0x87, 0x77, 0x77, 0x7a, 0x69, 0x69, 0x6c, 0x20, 0x20, 0x24, 0x24, 0x24, 0x27, 0x23, 0x23, 0x28, 0x1a, 0x1a, 0x1e, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0xd7, 0x77, 0xc6, 0x92, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x1, 0x35, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x94, 0xd1, 0x72, 0x83, 0x20, 0x10, 0x45, 0x4d, 0x90, 0xb8, 0x18, 0x96, 0x6c, 0xb5, 0x24, 0x41, 0x68, 0x9b, 0xf4, 0xff, 0x3f, 0xb2, 0x84, 0xb4, 0x8a, 0xa, 0x3a, 0x9d, 0x74, 0xa6, 0x2f, 0xb9, 0xbe, 0x38, 0xca, 0x3d, 0x5c, 0x16, 0xd8, 0xa2, 0x78, 0xea, 0x2f, 0xb5, 0xd9, 0xb2, 0xb2, 0xe4, 0x2b, 0x2a, 0x4b, 0xb6, 0xdd, 0x24, 0xed, 0xbb, 0x8a, 0x1, 0x8, 0x21, 0xee, 0xe3, 0xc4, 0x4d, 0x20, 0x6a, 0x91, 0x84, 0x54, 0xbb, 0xb9, 0x7f, 0xcf, 0x40, 0xa2, 0xea, 0x85, 0x32, 0x50, 0x6a, 0xc8, 0xe4, 0xd8, 0x4f, 0xfd, 0x7, 0x26, 0x48, 0xe1, 0x4b, 0x13, 0x7e, 0x37, 0x92, 0x50, 0x51, 0x1b, 0x92, 0xe4, 0x56, 0x72, 0x18, 0xfb, 0x5f, 0x99, 0x40, 0xd4, 0xf1, 0x8, 0x42, 0x6c, 0x17, 0x6b, 0x71, 0x1c, 0x1, 0x4e, 0x40, 0x21, 0x74, 0x24, 0x89, 0xd4, 0x2c, 0x11, 0x4e, 0xb1, 0xff, 0xc, 0x52, 0xe9, 0xe9, 0x8, 0x52, 0x8b, 0x11, 0xf8, 0x39, 0x2, 0x6c, 0x7d, 0x80, 0xf9, 0x8, 0xa4, 0xe1, 0xd5, 0x74, 0x16, 0xb9, 0xee, 0x5a, 0xae, 0xdd, 0xcf, 0xb7, 0xb7, 0x8, 0xe0, 0x2b, 0x40, 0x73, 0x40, 0x4, 0x75, 0x6, 0xa9, 0x43, 0xdd, 0xb9, 0x8, 0xc0, 0x46, 0x0, 0x25, 0xe7, 0x0, 0xa9, 0xfa, 0x0, 0x9d, 0xe7, 0x1b, 0xd4, 0xce, 0xea, 0x1, 0x50, 0x8e, 0x1, 0x89, 0x82, 0x35, 0x3d, 0x20, 0xb8, 0x94, 0xd1, 0x4e, 0xb9, 0x1, 0xc0, 0x7f, 0x91, 0x80, 0x42, 0x2, 0xe5, 0xcd, 0xd6, 0x24, 0x13, 0xbc, 0xc3, 0x5a, 0xd, 0x90, 0x93, 0xf5, 0x4b, 0xf0, 0x8b, 0x49, 0xd6, 0x60, 0x75, 0x17, 0xb4, 0x75, 0xd6, 0x84, 0x95, 0xb8, 0xe4, 0x2e, 0xac, 0x9f, 0x3, 0xba, 0x9d, 0x4b, 0xf4, 0xb3, 0xb4, 0xfd, 0x4c, 0xf1, 0x39, 0x28, 0x3e, 0xc4, 0x63, 0x27, 0xb1, 0x38, 0x3e, 0x7a, 0x17, 0xb2, 0xb7, 0x31, 0xeb, 0x9f, 0xdc, 0xc6, 0xa2, 0xb8, 0x30, 0x68, 0xa7, 0xfd, 0x60, 0xc1, 0x7f, 0x99, 0x77, 0x94, 0xeb, 0x27, 0xd4, 0x70, 0x6f, 0x48, 0xe2, 0x5b, 0xe0, 0x9f, 0xa4, 0xbf, 0xba, 0x66, 0x7b, 0x22, 0x5f, 0x55, 0xb6, 0x27, 0x3e, 0xf5, 0x8f, 0xfa, 0x2, 0xa0, 0x14, 0x20, 0xeb, 0xde, 0xb1, 0x8c, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + +static const unsigned char toggle_on_mirrored_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x9b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x24, 0x24, 0x28, 0x20, 0x20, 0x25, 0x14, 0x14, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x27, 0x10, 0x10, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x19, 0x19, 0x1c, 0x12, 0x12, 0x15, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0x1e, 0x22, 0x23, 0x23, 0x27, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1d, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0xb, 0xb, 0xd, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x1a, 0x1a, 0x1e, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x11, 0x11, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x25, 0x2c, 0x36, 0x28, 0x4c, 0x6b, 0x29, 0x64, 0x92, 0x2a, 0x68, 0x99, 0x2a, 0x66, 0x95, 0x29, 0x5d, 0x85, 0x27, 0x49, 0x65, 0x25, 0x25, 0x28, 0x25, 0x27, 0x2d, 0x27, 0x44, 0x5c, 0x29, 0x60, 0x8c, 0x27, 0x4d, 0x6b, 0x25, 0x3a, 0x4c, 0x25, 0x2d, 0x38, 0x25, 0x26, 0x2c, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2d, 0x25, 0x30, 0x3e, 0x27, 0x49, 0x66, 0x29, 0x5f, 0x89, 0x27, 0x43, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x58, 0x7f, 0x25, 0x35, 0x45, 0x25, 0x34, 0x43, 0x28, 0x59, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x41, 0x57, 0x27, 0x40, 0x57, 0x25, 0x2b, 0x34, 0x25, 0x34, 0x44, 0x29, 0x5d, 0x87, 0x25, 0x2a, 0x33, 0x27, 0x43, 0x5b, 0x27, 0x4e, 0x6d, 0x27, 0x4d, 0x6c, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x28, 0x56, 0x7b, 0x26, 0x3a, 0x4e, 0x26, 0x3b, 0x4e, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x27, 0x44, 0x5d, 0xa6, 0xa2, 0x25, 0x5b, 0x0, 0x0, 0x0, 0x4c, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xfa, 0xe5, 0x98, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xf3, 0x86, 0x7, 0x3a, 0x96, 0xf9, 0xb4, 0x9a, 0xb9, 0xb, 0x28, 0x77, 0xfb, 0x8b, 0x5, 0x45, 0xde, 0xf6, 0x82, 0x9b, 0xf, 0x37, 0xc6, 0xe6, 0xe9, 0xfb, 0x4e, 0x50, 0x83, 0x9c, 0x78, 0x8c, 0x3c, 0x9c, 0xbb, 0x74, 0xda, 0x87, 0x53, 0x14, 0xd0, 0x92, 0x4e, 0x2c, 0x0, 0x0, 0x2, 0x35, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0x63, 0x60, 0x18, 0x44, 0x80, 0x91, 0x89, 0x99, 0x99, 0x85, 0x20, 0x60, 0x66, 0x66, 0x62, 0xc4, 0xaa, 0x9d, 0x89, 0x85, 0x95, 0x8d, 0x9d, 0x83, 0x93, 0x8b, 0x0, 0xe0, 0xe4, 0x60, 0x67, 0x63, 0x65, 0x61, 0xc2, 0xb4, 0x9d, 0x85, 0x9b, 0x83, 0x87, 0x97, 0x8f, 0x5f, 0x40, 0x50, 0x8, 0x2f, 0x10, 0x14, 0xe0, 0xe7, 0xe3, 0xe5, 0xe1, 0xe0, 0x66, 0x41, 0x73, 0x5, 0x93, 0xb0, 0x88, 0xa8, 0x98, 0xb8, 0x84, 0xa4, 0x94, 0xb4, 0xf, 0x1, 0xe0, 0x2b, 0x2d, 0x23, 0x2b, 0x27, 0x26, 0x2a, 0x22, 0x8c, 0xe2, 0x8, 0x26, 0x79, 0x5, 0x45, 0x25, 0x65, 0x5f, 0xc, 0xd5, 0x7e, 0xfe, 0x7e, 0x58, 0xd, 0x51, 0x51, 0x55, 0x54, 0x90, 0x47, 0x32, 0x81, 0x51, 0x8d, 0x53, 0x5d, 0x43, 0xd3, 0x27, 0x20, 0x30, 0x28, 0x18, 0x2, 0x42, 0x42, 0xc3, 0x2, 0x20, 0x6, 0x84, 0xe3, 0x70, 0x87, 0x96, 0x3a, 0xa7, 0x1a, 0x23, 0xc2, 0xff, 0xec, 0xbc, 0xda, 0xd2, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x20, 0x90, 0x90, 0x98, 0x94, 0x9c, 0x12, 0x1, 0x36, 0x2, 0x97, 0x4f, 0x74, 0x78, 0xd9, 0xe1, 0xe1, 0xc0, 0xa4, 0xab, 0xa7, 0x6f, 0x10, 0x91, 0x9a, 0x96, 0x8e, 0xac, 0x22, 0x23, 0x33, 0x35, 0x2, 0x6f, 0x58, 0x18, 0xea, 0xe9, 0x42, 0x3d, 0xc1, 0x68, 0xa4, 0x60, 0x6c, 0x92, 0x95, 0x9d, 0x19, 0x8b, 0xaa, 0x22, 0x36, 0x33, 0x27, 0xb, 0x9f, 0x9, 0xa6, 0xc6, 0xa, 0x46, 0x10, 0x27, 0x30, 0xb1, 0xf2, 0x98, 0xf9, 0xe4, 0x26, 0xa7, 0xa3, 0xab, 0xc8, 0xcb, 0x2f, 0xc0, 0x1b, 0x1f, 0xe6, 0x3c, 0xac, 0x10, 0x3, 0x58, 0x2c, 0x2c, 0xad, 0x7c, 0xa, 0x8b, 0x30, 0x55, 0x14, 0x17, 0xc2, 0x99, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x3e, 0x95, 0x65, 0x55, 0x3e, 0x95, 0xd5, 0x30, 0x31, 0x6b, 0x4b, 0xb, 0x88, 0x1f, 0x84, 0xd9, 0x8d, 0x6d, 0x7c, 0x6a, 0x6a, 0x31, 0xd, 0xa8, 0xab, 0x81, 0x33, 0xab, 0xeb, 0x4b, 0x1a, 0xca, 0x4a, 0x2a, 0xcb, 0xaa, 0x91, 0xc, 0xb0, 0x35, 0x66, 0x67, 0x6, 0x1b, 0xa0, 0xc6, 0x61, 0xa7, 0xe9, 0xd3, 0xd8, 0x84, 0x69, 0x40, 0x53, 0x23, 0xdc, 0x1, 0x65, 0xcd, 0x3e, 0x3e, 0xf5, 0x2d, 0x95, 0xad, 0xe5, 0x95, 0x8, 0x3, 0x7c, 0xed, 0x38, 0x58, 0x20, 0x6, 0x28, 0xd8, 0xfb, 0xf8, 0x84, 0x24, 0x60, 0x1a, 0x90, 0x10, 0x2, 0x63, 0x55, 0xb6, 0x2, 0x89, 0xb6, 0xd2, 0xca, 0xea, 0xb6, 0x56, 0x84, 0x1, 0x3e, 0xf6, 0xa, 0x2c, 0xc4, 0xba, 0xa0, 0x19, 0xe4, 0x82, 0xd2, 0x76, 0xa0, 0xe6, 0xf2, 0x52, 0x4c, 0x17, 0x10, 0x13, 0x6, 0xad, 0x2d, 0x3e, 0xcd, 0xe5, 0x15, 0x40, 0x3, 0x2a, 0xca, 0x30, 0xc3, 0x80, 0x98, 0x58, 0xa8, 0x2c, 0x6f, 0x2d, 0x2f, 0x5, 0x7, 0x60, 0x2b, 0x66, 0x2c, 0x30, 0xb1, 0x8a, 0x3a, 0x10, 0x4c, 0x7, 0xcd, 0x2d, 0x55, 0xc0, 0xb0, 0xac, 0xf0, 0xf1, 0xa9, 0xaa, 0x80, 0x9, 0x39, 0x8a, 0x42, 0xd3, 0x1, 0xc5, 0x29, 0x11, 0x94, 0x17, 0x9c, 0x48, 0xcf, 0xb, 0xce, 0xf0, 0xbc, 0x0, 0xcb, 0x8d, 0x1d, 0xa8, 0xb9, 0x31, 0x12, 0xbf, 0x7e, 0xe4, 0xdc, 0x8, 0x2a, 0xf, 0x5c, 0x5c, 0xd, 0xb0, 0x94, 0x7, 0xb8, 0xf5, 0xbb, 0xb9, 0x20, 0x95, 0x7, 0xa0, 0x12, 0x89, 0x93, 0xd7, 0xdd, 0x3, 0x53, 0x9d, 0x5f, 0x38, 0xf6, 0xf2, 0x40, 0xc5, 0x93, 0x97, 0x53, 0x1e, 0xb5, 0x4c, 0x53, 0x63, 0x17, 0xe5, 0x13, 0x97, 0xb0, 0xd1, 0xf4, 0x21, 0x8, 0x7c, 0x6d, 0x65, 0xe5, 0xf8, 0x44, 0xd9, 0xd5, 0xd0, 0xa, 0x66, 0x46, 0x16, 0x5d, 0xe, 0x1e, 0x4b, 0x63, 0x3b, 0x2f, 0x21, 0x2, 0xc0, 0xcb, 0xce, 0xd8, 0x92, 0x87, 0x43, 0x97, 0x5, 0xa3, 0x6e, 0x60, 0x64, 0x32, 0x62, 0xb5, 0xf0, 0x56, 0x20, 0x5c, 0x2f, 0x28, 0x78, 0x5b, 0xb0, 0x1a, 0x61, 0xaf, 0x5b, 0x80, 0x35, 0x13, 0xe1, 0x8a, 0x9, 0x58, 0x35, 0xe1, 0xa8, 0x99, 0x86, 0x2e, 0x0, 0x0, 0x69, 0x2c, 0x6b, 0xc2, 0xf1, 0x2f, 0x53, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char tooltip_bg_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0xdd, 0xdd, 0xdd, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0xbc, 0x3, 0x4f, 0xe9, 0x0, 0x0, 0x0, 0xd, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0xf5, 0xfe, 0xcc, 0xff, 0xb7, 0x4a, 0xbe, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x54, 0x76, 0x1, 0x2, 0x23, 0x1, 0x6, 0xd1, 0xf4, 0xe, 0x20, 0x28, 0xb, 0x64, 0xd0, 0x5c, 0x7d, 0x17, 0x8, 0x76, 0x4d, 0x62, 0x70, 0x7f, 0x7f, 0x6, 0x8, 0xfe, 0x95, 0x30, 0x78, 0xdc, 0x1, 0x31, 0xce, 0xb6, 0x50, 0xc8, 0x80, 0x1b, 0x8, 0xb7, 0x2, 0x6e, 0x29, 0xdc, 0x19, 0x0, 0xcf, 0x24, 0x4d, 0xb3, 0xd0, 0x4d, 0xb9, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -426,4 +470,8 @@ static const unsigned char window_resizer_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0xbc, 0xe0, 0x45, 0x3f, 0x1, 0xe9, 0xec, 0xfe, 0x81, 0x94, 0x86, 0xb1, 0x70, 0x48, 0x23, 0x58, 0x84, 0xa4, 0x7, 0x15, 0x0, 0x0, 0xed, 0x9f, 0x18, 0xe8, 0xcd, 0x91, 0xd8, 0xe, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char window_resizer_mirrored_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x27, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x18, 0x44, 0xe0, 0x45, 0x3f, 0x76, 0x71, 0x26, 0x18, 0xa3, 0x19, 0xa7, 0x12, 0x38, 0xc8, 0xee, 0xa7, 0xb1, 0x12, 0x98, 0x4, 0x4e, 0x25, 0x8, 0x9, 0x4a, 0x94, 0xc, 0x10, 0x0, 0x0, 0x9d, 0x84, 0x18, 0x73, 0x33, 0x1c, 0x96, 0xd6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + // shaders block diff --git a/scene/resources/default_theme/toggle_off_disabled_mirrored.png b/scene/resources/default_theme/toggle_off_disabled_mirrored.png Binary files differnew file mode 100644 index 0000000000..799b63c098 --- /dev/null +++ b/scene/resources/default_theme/toggle_off_disabled_mirrored.png diff --git a/scene/resources/default_theme/toggle_off_mirrored.png b/scene/resources/default_theme/toggle_off_mirrored.png Binary files differnew file mode 100644 index 0000000000..3487605d58 --- /dev/null +++ b/scene/resources/default_theme/toggle_off_mirrored.png diff --git a/scene/resources/default_theme/toggle_on_disabled_mirrored.png b/scene/resources/default_theme/toggle_on_disabled_mirrored.png Binary files differnew file mode 100644 index 0000000000..0758babd4f --- /dev/null +++ b/scene/resources/default_theme/toggle_on_disabled_mirrored.png diff --git a/scene/resources/default_theme/toggle_on_mirrored.png b/scene/resources/default_theme/toggle_on_mirrored.png Binary files differnew file mode 100644 index 0000000000..3fd953c8e2 --- /dev/null +++ b/scene/resources/default_theme/toggle_on_mirrored.png diff --git a/scene/resources/default_theme/window_resizer_mirrored.png b/scene/resources/default_theme/window_resizer_mirrored.png Binary files differnew file mode 100644 index 0000000000..bbce5f1406 --- /dev/null +++ b/scene/resources/default_theme/window_resizer_mirrored.png diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp deleted file mode 100644 index d76d364737..0000000000 --- a/scene/resources/dynamic_font.cpp +++ /dev/null @@ -1,1156 +0,0 @@ -/*************************************************************************/ -/* dynamic_font.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "modules/modules_enabled.gen.h" -#ifdef MODULE_FREETYPE_ENABLED - -#include "dynamic_font.h" - -#include "core/os/file_access.h" -#include "core/os/os.h" - -#include FT_STROKER_H - -#define __STDC_LIMIT_MACROS -#include <stdint.h> - -bool DynamicFontData::CacheID::operator<(CacheID right) const { - return key < right.key; -} - -Ref<DynamicFontAtSize> DynamicFontData::_get_dynamic_font_at_size(CacheID p_cache_id) { - if (size_cache.has(p_cache_id)) { - return Ref<DynamicFontAtSize>(size_cache[p_cache_id]); - } - - Ref<DynamicFontAtSize> dfas; - - dfas.instance(); - - dfas->font = Ref<DynamicFontData>(this); - - size_cache[p_cache_id] = dfas.ptr(); - dfas->id = p_cache_id; - dfas->_load(); - - return dfas; -} - -void DynamicFontData::set_font_ptr(const uint8_t *p_font_mem, int p_font_mem_size) { - font_mem = p_font_mem; - font_mem_size = p_font_mem_size; -} - -void DynamicFontData::set_font_path(const String &p_path) { - font_path = p_path; -} - -String DynamicFontData::get_font_path() const { - return font_path; -} - -void DynamicFontData::set_force_autohinter(bool p_force) { - force_autohinter = p_force; -} - -void DynamicFontData::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &DynamicFontData::set_antialiased); - ClassDB::bind_method(D_METHOD("is_antialiased"), &DynamicFontData::is_antialiased); - ClassDB::bind_method(D_METHOD("set_font_path", "path"), &DynamicFontData::set_font_path); - ClassDB::bind_method(D_METHOD("get_font_path"), &DynamicFontData::get_font_path); - ClassDB::bind_method(D_METHOD("set_hinting", "mode"), &DynamicFontData::set_hinting); - ClassDB::bind_method(D_METHOD("get_hinting"), &DynamicFontData::get_hinting); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "is_antialiased"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting"); - - BIND_ENUM_CONSTANT(HINTING_NONE); - BIND_ENUM_CONSTANT(HINTING_LIGHT); - BIND_ENUM_CONSTANT(HINTING_NORMAL); - - ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_path", PROPERTY_HINT_FILE, "*.ttf,*.otf"), "set_font_path", "get_font_path"); -} - -DynamicFontData::DynamicFontData() { - antialiased = true; - force_autohinter = false; - hinting = DynamicFontData::HINTING_NORMAL; - font_mem = nullptr; - font_mem_size = 0; -} - -DynamicFontData::~DynamicFontData() { -} - -//////////////////// -HashMap<String, Vector<uint8_t>> DynamicFontAtSize::_fontdata; - -Error DynamicFontAtSize::_load() { - int error = FT_Init_FreeType(&library); - - ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType."); - - // FT_OPEN_STREAM is extremely slow only on Android. - if (OS::get_singleton()->get_name() == "Android" && font->font_mem == nullptr && font->font_path != String()) { - // cache font only once for each font->font_path - if (_fontdata.has(font->font_path)) { - font->set_font_ptr(_fontdata[font->font_path].ptr(), _fontdata[font->font_path].size()); - - } else { - FileAccess *f = FileAccess::open(font->font_path, FileAccess::READ); - if (!f) { - FT_Done_FreeType(library); - ERR_FAIL_V_MSG(ERR_CANT_OPEN, "Cannot open font file '" + font->font_path + "'."); - } - - size_t len = f->get_len(); - _fontdata[font->font_path] = Vector<uint8_t>(); - Vector<uint8_t> &fontdata = _fontdata[font->font_path]; - fontdata.resize(len); - f->get_buffer(fontdata.ptrw(), len); - font->set_font_ptr(fontdata.ptr(), len); - f->close(); - } - } - - if (font->font_mem == nullptr && font->font_path != String()) { - FileAccess *f = FileAccess::open(font->font_path, FileAccess::READ); - if (!f) { - FT_Done_FreeType(library); - ERR_FAIL_V_MSG(ERR_CANT_OPEN, "Cannot open font file '" + font->font_path + "'."); - } - - memset(&stream, 0, sizeof(FT_StreamRec)); - stream.base = nullptr; - stream.size = f->get_len(); - stream.pos = 0; - stream.descriptor.pointer = f; - stream.read = _ft_stream_io; - stream.close = _ft_stream_close; - - FT_Open_Args fargs; - memset(&fargs, 0, sizeof(FT_Open_Args)); - fargs.flags = FT_OPEN_STREAM; - fargs.stream = &stream; - error = FT_Open_Face(library, &fargs, 0, &face); - } else if (font->font_mem) { - memset(&stream, 0, sizeof(FT_StreamRec)); - stream.base = (unsigned char *)font->font_mem; - stream.size = font->font_mem_size; - stream.pos = 0; - - FT_Open_Args fargs; - memset(&fargs, 0, sizeof(FT_Open_Args)); - fargs.memory_base = (unsigned char *)font->font_mem; - fargs.memory_size = font->font_mem_size; - fargs.flags = FT_OPEN_MEMORY; - fargs.stream = &stream; - error = FT_Open_Face(library, &fargs, 0, &face); - - } else { - FT_Done_FreeType(library); - ERR_FAIL_V_MSG(ERR_UNCONFIGURED, "DynamicFont uninitialized."); - } - - //error = FT_New_Face( library, src_path.utf8().get_data(),0,&face ); - - if (error == FT_Err_Unknown_File_Format) { - FT_Done_FreeType(library); - ERR_FAIL_V_MSG(ERR_FILE_CANT_OPEN, "Unknown font format."); - - } else if (error) { - FT_Done_FreeType(library); - ERR_FAIL_V_MSG(ERR_FILE_CANT_OPEN, "Error loading font."); - } - - if (FT_HAS_COLOR(face) && face->num_fixed_sizes > 0) { - int best_match = 0; - int diff = ABS(id.size - ((int64_t)face->available_sizes[0].width)); - scale_color_font = float(id.size * oversampling) / face->available_sizes[0].width; - for (int i = 1; i < face->num_fixed_sizes; i++) { - int ndiff = ABS(id.size - ((int64_t)face->available_sizes[i].width)); - if (ndiff < diff) { - best_match = i; - diff = ndiff; - scale_color_font = float(id.size * oversampling) / face->available_sizes[i].width; - } - } - FT_Select_Size(face, best_match); - } else { - FT_Set_Pixel_Sizes(face, 0, id.size * oversampling); - } - - ascent = (face->size->metrics.ascender / 64.0) / oversampling * scale_color_font; - descent = (-face->size->metrics.descender / 64.0) / oversampling * scale_color_font; - underline_position = -face->underline_position / 64.0 / oversampling * scale_color_font; - underline_thickness = face->underline_thickness / 64.0 / oversampling * scale_color_font; - linegap = 0; - - valid = true; - return OK; -} - -float DynamicFontAtSize::font_oversampling = 1.0; - -float DynamicFontAtSize::get_height() const { - return ascent + descent; -} - -float DynamicFontAtSize::get_ascent() const { - return ascent; -} - -float DynamicFontAtSize::get_descent() const { - return descent; -} - -float DynamicFontAtSize::get_underline_position() const { - return underline_position; -} - -float DynamicFontAtSize::get_underline_thickness() const { - return underline_thickness; -} - -const Pair<const DynamicFontAtSize::Character *, DynamicFontAtSize *> DynamicFontAtSize::_find_char_with_font(char32_t p_char, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const { - const Character *chr = char_map.getptr(p_char); - ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(nullptr, nullptr))); - - if (!chr->found) { - //not found, try in fallbacks - for (int i = 0; i < p_fallbacks.size(); i++) { - DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr()); - if (!fb->valid) { - continue; - } - - fb->_update_char(p_char); - const Character *fallback_chr = fb->char_map.getptr(p_char); - ERR_CONTINUE(!fallback_chr); - - if (!fallback_chr->found) { - continue; - } - - return Pair<const Character *, DynamicFontAtSize *>(fallback_chr, fb); - } - - //not found, try 0xFFFD to display 'not found'. - const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD); - chr = char_map.getptr(0xFFFD); - ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(nullptr, nullptr))); - } - - return Pair<const Character *, DynamicFontAtSize *>(chr, const_cast<DynamicFontAtSize *>(this)); -} - -Size2 DynamicFontAtSize::get_char_size(char32_t p_char, char32_t p_next, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const { - if (!valid) { - return Size2(1, 1); - } - const_cast<DynamicFontAtSize *>(this)->_update_char(p_char); - - Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks); - const Character *ch = char_pair_with_font.first; - ERR_FAIL_COND_V(!ch, Size2()); - - Size2 ret(0, get_height()); - - if (ch->found) { - ret.x = ch->advance; - } - - return ret; -} - -String DynamicFontAtSize::get_available_chars() const { - String chars; - - FT_UInt gindex; - FT_ULong charcode = FT_Get_First_Char(face, &gindex); - while (gindex != 0) { - if (charcode != 0) { - chars += char32_t(charcode); - } - charcode = FT_Get_Next_Char(face, charcode, &gindex); - } - - return chars; -} - -float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks, bool p_advance_only, bool p_outline) const { - if (!valid) { - return 0; - } - - const_cast<DynamicFontAtSize *>(this)->_update_char(p_char); - - Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks); - const Character *ch = char_pair_with_font.first; - DynamicFontAtSize *font = char_pair_with_font.second; - - ERR_FAIL_COND_V(!ch, 0.0); - - float advance = 0.0; - - // use normal character size if there's no outline character - if (p_outline && !ch->found) { - FT_GlyphSlot slot = face->glyph; - int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT); - if (!error) { - error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); - if (!error) { - Character character = Character::not_found(); - character = const_cast<DynamicFontAtSize *>(this)->_bitmap_to_character(slot->bitmap, slot->bitmap_top, slot->bitmap_left, slot->advance.x / 64.0); - advance = character.advance; - } - } - } - - if (ch->found) { - ERR_FAIL_COND_V(ch->texture_idx < -1 || ch->texture_idx >= font->textures.size(), 0); - - if (!p_advance_only && ch->texture_idx != -1) { - Point2 cpos = p_pos; - cpos.x += ch->h_align; - cpos.y -= font->get_ascent(); - cpos.y += ch->v_align; - Color modulate = p_modulate; - if (FT_HAS_COLOR(face)) { - modulate.r = modulate.g = modulate.b = 1.0; - } - RID texture = font->textures[ch->texture_idx].texture->get_rid(); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, ch->rect.size), texture, ch->rect_uv, modulate, false, false); - } - - advance = ch->advance; - } - - return advance; -} - -unsigned long DynamicFontAtSize::_ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) { - FileAccess *f = (FileAccess *)stream->descriptor.pointer; - - if (f->get_position() != offset) { - f->seek(offset); - } - - if (count == 0) { - return 0; - } - - return f->get_buffer(buffer, count); -} - -void DynamicFontAtSize::_ft_stream_close(FT_Stream stream) { - FileAccess *f = (FileAccess *)stream->descriptor.pointer; - f->close(); - memdelete(f); -} - -DynamicFontAtSize::Character DynamicFontAtSize::Character::not_found() { - Character ch; - ch.texture_idx = -1; - ch.advance = 0; - ch.h_align = 0; - ch.v_align = 0; - ch.found = false; - return ch; -} - -DynamicFontAtSize::TexturePosition DynamicFontAtSize::_find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height) { - TexturePosition ret; - ret.index = -1; - ret.x = 0; - ret.y = 0; - - int mw = p_width; - int mh = p_height; - - for (int i = 0; i < textures.size(); i++) { - const CharTexture &ct = textures[i]; - - if (ct.texture->get_format() != p_image_format) { - continue; - } - - if (mw > ct.texture_size || mh > ct.texture_size) { //too big for this texture - continue; - } - - ret.y = 0x7FFFFFFF; - ret.x = 0; - - for (int j = 0; j < ct.texture_size - mw; j++) { - int max_y = 0; - - for (int k = j; k < j + mw; k++) { - int y = ct.offsets[k]; - if (y > max_y) { - max_y = y; - } - } - - if (max_y < ret.y) { - ret.y = max_y; - ret.x = j; - } - } - - if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_size) { - continue; //fail, could not fit it here - } - - ret.index = i; - break; - } - - if (ret.index == -1) { - //could not find texture to fit, create one - ret.x = 0; - ret.y = 0; - - int texsize = MAX(id.size * oversampling * 8, 256); - if (mw > texsize) { - texsize = mw; //special case, adapt to it? - } - if (mh > texsize) { - texsize = mh; //special case, adapt to it? - } - - texsize = next_power_of_2(texsize); - - texsize = MIN(texsize, 4096); - - CharTexture tex; - tex.texture_size = texsize; - tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha - - { - //zero texture - uint8_t *w = tex.imgdata.ptrw(); - ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret); - - // Initialize the texture to all-white pixels to prevent artifacts when the - // font is displayed at a non-default scale with filtering enabled. - if (p_color_size == 2) { - for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { - w[i + 0] = 255; - w[i + 1] = 0; - } - } else { - for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { - w[i + 0] = 255; - w[i + 1] = 255; - w[i + 2] = 255; - w[i + 3] = 0; - } - } - } - tex.offsets.resize(texsize); - for (int i = 0; i < texsize; i++) { //zero offsets - tex.offsets.write[i] = 0; - } - - textures.push_back(tex); - ret.index = textures.size() - 1; - } - - return ret; -} - -DynamicFontAtSize::Character DynamicFontAtSize::_bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance) { - int w = bitmap.width; - int h = bitmap.rows; - - int mw = w + rect_margin * 2; - int mh = h + rect_margin * 2; - - ERR_FAIL_COND_V(mw > 4096, Character::not_found()); - ERR_FAIL_COND_V(mh > 4096, Character::not_found()); - - int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2; - Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8; - - TexturePosition tex_pos = _find_texture_pos_for_glyph(color_size, require_format, mw, mh); - ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found()); - - //fit character in char texture - - CharTexture &tex = textures.write[tex_pos.index]; - - { - uint8_t *wr = tex.imgdata.ptrw(); - - for (int i = 0; i < h; i++) { - for (int j = 0; j < w; j++) { - int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size; - ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found()); - switch (bitmap.pixel_mode) { - case FT_PIXEL_MODE_MONO: { - int byte = i * bitmap.pitch + (j >> 3); - int bit = 1 << (7 - (j % 8)); - wr[ofs + 0] = 255; //grayscale as 1 - wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0; - } break; - case FT_PIXEL_MODE_GRAY: - wr[ofs + 0] = 255; //grayscale as 1 - wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j]; - break; - case FT_PIXEL_MODE_BGRA: { - int ofs_color = i * bitmap.pitch + (j << 2); - wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; - wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; - wr[ofs + 3] = bitmap.buffer[ofs_color + 3]; - } break; - // TODO: FT_PIXEL_MODE_LCD - default: - ERR_FAIL_V_MSG(Character::not_found(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + "."); - break; - } - } - } - } - - //blit to image and texture - { - Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata)); - - if (tex.texture.is_null()) { - tex.texture.instance(); - tex.texture->create_from_image(img); - } else { - tex.texture->update(img); //update - } - } - - // update height array - - for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { - tex.offsets.write[k] = tex_pos.y + mh; - } - - Character chr; - chr.h_align = xofs * scale_color_font / oversampling; - chr.v_align = ascent - (yofs * scale_color_font / oversampling); // + ascent - descent; - chr.advance = advance * scale_color_font / oversampling; - chr.texture_idx = tex_pos.index; - chr.found = true; - - chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h); - chr.rect = chr.rect_uv; - chr.rect.position /= oversampling; - chr.rect.size = chr.rect.size * scale_color_font / oversampling; - return chr; -} - -DynamicFontAtSize::Character DynamicFontAtSize::_make_outline_char(char32_t p_char) { - Character ret = Character::not_found(); - - if (FT_Load_Char(face, p_char, FT_LOAD_NO_BITMAP | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)) != 0) { - return ret; - } - - FT_Stroker stroker; - if (FT_Stroker_New(library, &stroker) != 0) { - return ret; - } - - FT_Stroker_Set(stroker, (int)(id.outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0); - FT_Glyph glyph; - FT_BitmapGlyph glyph_bitmap; - - if (FT_Get_Glyph(face->glyph, &glyph) != 0) { - goto cleanup_stroker; - } - if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) { - goto cleanup_glyph; - } - if (FT_Glyph_To_Bitmap(&glyph, font->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) { - goto cleanup_glyph; - } - - glyph_bitmap = (FT_BitmapGlyph)glyph; - ret = _bitmap_to_character(glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, glyph->advance.x / 65536.0); - -cleanup_glyph: - FT_Done_Glyph(glyph); -cleanup_stroker: - FT_Stroker_Done(stroker); - return ret; -} - -void DynamicFontAtSize::_update_char(char32_t p_char) { - if (char_map.has(p_char)) { - return; - } - - _THREAD_SAFE_METHOD_ - - Character character = Character::not_found(); - - FT_GlyphSlot slot = face->glyph; - - if (FT_Get_Char_Index(face, p_char) == 0) { - char_map[p_char] = character; - return; - } - - int ft_hinting; - - switch (font->hinting) { - case DynamicFontData::HINTING_NONE: - ft_hinting = FT_LOAD_NO_HINTING; - break; - case DynamicFontData::HINTING_LIGHT: - ft_hinting = FT_LOAD_TARGET_LIGHT; - break; - default: - ft_hinting = FT_LOAD_TARGET_NORMAL; - break; - } - - int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting); - if (error) { - char_map[p_char] = character; - return; - } - - if (id.outline_size > 0) { - character = _make_outline_char(p_char); - } else { - error = FT_Render_Glyph(face->glyph, font->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); - if (!error) { - character = _bitmap_to_character(slot->bitmap, slot->bitmap_top, slot->bitmap_left, slot->advance.x / 64.0); - } - } - - char_map[p_char] = character; -} - -void DynamicFontAtSize::update_oversampling() { - if (oversampling == font_oversampling || !valid) { - return; - } - - FT_Done_FreeType(library); - textures.clear(); - char_map.clear(); - oversampling = font_oversampling; - valid = false; - _load(); -} - -DynamicFontAtSize::DynamicFontAtSize() { - valid = false; - rect_margin = 1; - ascent = 1; - descent = 1; - linegap = 1; - oversampling = font_oversampling; - scale_color_font = 1; -} - -DynamicFontAtSize::~DynamicFontAtSize() { - if (valid) { - FT_Done_FreeType(library); - } - font->size_cache.erase(id); - font.unref(); -} - -///////////////////////// - -void DynamicFont::_reload_cache() { - ERR_FAIL_COND(cache_id.size < 1); - if (!data.is_valid()) { - data_at_size.unref(); - outline_data_at_size.unref(); - fallbacks.resize(0); - fallback_data_at_size.resize(0); - fallback_outline_data_at_size.resize(0); - return; - } - - data_at_size = data->_get_dynamic_font_at_size(cache_id); - if (outline_cache_id.outline_size > 0) { - outline_data_at_size = data->_get_dynamic_font_at_size(outline_cache_id); - fallback_outline_data_at_size.resize(fallback_data_at_size.size()); - } else { - outline_data_at_size.unref(); - fallback_outline_data_at_size.resize(0); - } - - for (int i = 0; i < fallbacks.size(); i++) { - fallback_data_at_size.write[i] = fallbacks.write[i]->_get_dynamic_font_at_size(cache_id); - if (outline_cache_id.outline_size > 0) { - fallback_outline_data_at_size.write[i] = fallbacks.write[i]->_get_dynamic_font_at_size(outline_cache_id); - } - } - - emit_changed(); - _change_notify(); -} - -void DynamicFont::set_font_data(const Ref<DynamicFontData> &p_data) { - data = p_data; - _reload_cache(); - - emit_changed(); - _change_notify(); -} - -Ref<DynamicFontData> DynamicFont::get_font_data() const { - return data; -} - -void DynamicFont::set_size(int p_size) { - if (cache_id.size == p_size) { - return; - } - cache_id.size = p_size; - outline_cache_id.size = p_size; - _reload_cache(); -} - -int DynamicFont::get_size() const { - return cache_id.size; -} - -void DynamicFont::set_outline_size(int p_size) { - if (outline_cache_id.outline_size == p_size) { - return; - } - ERR_FAIL_COND(p_size < 0 || p_size > UINT8_MAX); - outline_cache_id.outline_size = p_size; - _reload_cache(); -} - -int DynamicFont::get_outline_size() const { - return outline_cache_id.outline_size; -} - -void DynamicFont::set_outline_color(Color p_color) { - if (p_color != outline_color) { - outline_color = p_color; - emit_changed(); - _change_notify(); - } -} - -Color DynamicFont::get_outline_color() const { - return outline_color; -} - -bool DynamicFontData::is_antialiased() const { - return antialiased; -} - -void DynamicFontData::set_antialiased(bool p_antialiased) { - if (antialiased == p_antialiased) { - return; - } - antialiased = p_antialiased; -} - -DynamicFontData::Hinting DynamicFontData::get_hinting() const { - return hinting; -} - -void DynamicFontData::set_hinting(Hinting p_hinting) { - if (hinting == p_hinting) { - return; - } - hinting = p_hinting; -} - -int DynamicFont::get_spacing(int p_type) const { - if (p_type == SPACING_TOP) { - return spacing_top; - } else if (p_type == SPACING_BOTTOM) { - return spacing_bottom; - } else if (p_type == SPACING_CHAR) { - return spacing_char; - } else if (p_type == SPACING_SPACE) { - return spacing_space; - } - - return 0; -} - -void DynamicFont::set_spacing(int p_type, int p_value) { - if (p_type == SPACING_TOP) { - spacing_top = p_value; - } else if (p_type == SPACING_BOTTOM) { - spacing_bottom = p_value; - } else if (p_type == SPACING_CHAR) { - spacing_char = p_value; - } else if (p_type == SPACING_SPACE) { - spacing_space = p_value; - } - - emit_changed(); - _change_notify(); -} - -float DynamicFont::get_height() const { - if (!data_at_size.is_valid()) { - return 1; - } - - return data_at_size->get_height() + spacing_top + spacing_bottom; -} - -float DynamicFont::get_ascent() const { - if (!data_at_size.is_valid()) { - return 1; - } - - return data_at_size->get_ascent() + spacing_top; -} - -float DynamicFont::get_descent() const { - if (!data_at_size.is_valid()) { - return 1; - } - - return data_at_size->get_descent() + spacing_bottom; -} - -float DynamicFont::get_underline_position() const { - if (!data_at_size.is_valid()) { - return 2; - } - - return data_at_size->get_underline_position(); -} - -float DynamicFont::get_underline_thickness() const { - if (!data_at_size.is_valid()) { - return 1; - } - - return data_at_size->get_underline_thickness(); -} - -Size2 DynamicFont::get_char_size(char32_t p_char, char32_t p_next) const { - if (!data_at_size.is_valid()) { - return Size2(1, 1); - } - - Size2 ret = data_at_size->get_char_size(p_char, p_next, fallback_data_at_size); - if (p_char == ' ') { - ret.width += spacing_space + spacing_char; - } else if (p_next) { - ret.width += spacing_char; - } - - return ret; -} - -String DynamicFont::get_available_chars() const { - if (!data_at_size.is_valid()) { - return ""; - } - - String chars = data_at_size->get_available_chars(); - - for (int i = 0; i < fallback_data_at_size.size(); i++) { - String fallback_chars = fallback_data_at_size[i]->get_available_chars(); - for (int j = 0; j < fallback_chars.length(); j++) { - if (chars.find_char(fallback_chars[j]) == -1) { - chars += fallback_chars[j]; - } - } - } - - return chars; -} - -bool DynamicFont::is_distance_field_hint() const { - return false; -} - -bool DynamicFont::has_outline() const { - return outline_cache_id.outline_size > 0; -} - -float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, bool p_outline) const { - const Ref<DynamicFontAtSize> &font_at_size = p_outline && outline_cache_id.outline_size > 0 ? outline_data_at_size : data_at_size; - - if (!font_at_size.is_valid()) { - return 0; - } - - const Vector<Ref<DynamicFontAtSize>> &fallbacks = p_outline && outline_cache_id.outline_size > 0 ? fallback_outline_data_at_size : fallback_data_at_size; - Color color = p_outline && outline_cache_id.outline_size > 0 ? p_modulate * outline_color : p_modulate; - - // If requested outline draw, but no outline is present, simply return advance without drawing anything - bool advance_only = p_outline && outline_cache_id.outline_size == 0; - return font_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, color, fallbacks, advance_only, p_outline) + spacing_char; -} - -void DynamicFont::set_fallback(int p_idx, const Ref<DynamicFontData> &p_data) { - ERR_FAIL_COND(p_data.is_null()); - ERR_FAIL_INDEX(p_idx, fallbacks.size()); - fallbacks.write[p_idx] = p_data; - fallback_data_at_size.write[p_idx] = fallbacks.write[p_idx]->_get_dynamic_font_at_size(cache_id); -} - -void DynamicFont::add_fallback(const Ref<DynamicFontData> &p_data) { - ERR_FAIL_COND(p_data.is_null()); - fallbacks.push_back(p_data); - fallback_data_at_size.push_back(fallbacks.write[fallbacks.size() - 1]->_get_dynamic_font_at_size(cache_id)); //const.. - if (outline_cache_id.outline_size > 0) { - fallback_outline_data_at_size.push_back(fallbacks.write[fallbacks.size() - 1]->_get_dynamic_font_at_size(outline_cache_id)); - } - - _change_notify(); - emit_changed(); - _change_notify(); -} - -int DynamicFont::get_fallback_count() const { - return fallbacks.size(); -} - -Ref<DynamicFontData> DynamicFont::get_fallback(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, fallbacks.size(), Ref<DynamicFontData>()); - - return fallbacks[p_idx]; -} - -void DynamicFont::remove_fallback(int p_idx) { - ERR_FAIL_INDEX(p_idx, fallbacks.size()); - fallbacks.remove(p_idx); - fallback_data_at_size.remove(p_idx); - emit_changed(); - _change_notify(); -} - -bool DynamicFont::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; - if (str.begins_with("fallback/")) { - int idx = str.get_slicec('/', 1).to_int(); - Ref<DynamicFontData> fd = p_value; - - if (fd.is_valid()) { - if (idx == fallbacks.size()) { - add_fallback(fd); - return true; - } else if (idx >= 0 && idx < fallbacks.size()) { - set_fallback(idx, fd); - return true; - } else { - return false; - } - } else if (idx >= 0 && idx < fallbacks.size()) { - remove_fallback(idx); - return true; - } - } - - return false; -} - -bool DynamicFont::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("fallback/")) { - int idx = str.get_slicec('/', 1).to_int(); - - if (idx == fallbacks.size()) { - r_ret = Ref<DynamicFontData>(); - return true; - } else if (idx >= 0 && idx < fallbacks.size()) { - r_ret = get_fallback(idx); - return true; - } - } - - return false; -} - -void DynamicFont::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < fallbacks.size(); i++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "fallback/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "DynamicFontData")); - } - - p_list->push_back(PropertyInfo(Variant::OBJECT, "fallback/" + itos(fallbacks.size()), PROPERTY_HINT_RESOURCE_TYPE, "DynamicFontData")); -} - -void DynamicFont::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_font_data", "data"), &DynamicFont::set_font_data); - ClassDB::bind_method(D_METHOD("get_font_data"), &DynamicFont::get_font_data); - - ClassDB::bind_method(D_METHOD("get_available_chars"), &DynamicFont::get_available_chars); - - ClassDB::bind_method(D_METHOD("set_size", "data"), &DynamicFont::set_size); - ClassDB::bind_method(D_METHOD("get_size"), &DynamicFont::get_size); - - ClassDB::bind_method(D_METHOD("set_outline_size", "size"), &DynamicFont::set_outline_size); - ClassDB::bind_method(D_METHOD("get_outline_size"), &DynamicFont::get_outline_size); - - ClassDB::bind_method(D_METHOD("set_outline_color", "color"), &DynamicFont::set_outline_color); - ClassDB::bind_method(D_METHOD("get_outline_color"), &DynamicFont::get_outline_color); - - ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &DynamicFont::set_spacing); - ClassDB::bind_method(D_METHOD("get_spacing", "type"), &DynamicFont::get_spacing); - - ClassDB::bind_method(D_METHOD("add_fallback", "data"), &DynamicFont::add_fallback); - ClassDB::bind_method(D_METHOD("set_fallback", "idx", "data"), &DynamicFont::set_fallback); - ClassDB::bind_method(D_METHOD("get_fallback", "idx"), &DynamicFont::get_fallback); - ClassDB::bind_method(D_METHOD("remove_fallback", "idx"), &DynamicFont::remove_fallback); - ClassDB::bind_method(D_METHOD("get_fallback_count"), &DynamicFont::get_fallback_count); - - ADD_GROUP("Settings", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "size", PROPERTY_HINT_RANGE, "1,1024,1"), "set_size", "get_size"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,1024,1"), "set_outline_size", "get_outline_size"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color"); - ADD_GROUP("Extra Spacing", "extra_spacing"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_top"), "set_spacing", "get_spacing", SPACING_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_bottom"), "set_spacing", "get_spacing", SPACING_BOTTOM); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_char"), "set_spacing", "get_spacing", SPACING_CHAR); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_space"), "set_spacing", "get_spacing", SPACING_SPACE); - ADD_GROUP("Font", ""); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font_data", PROPERTY_HINT_RESOURCE_TYPE, "DynamicFontData"), "set_font_data", "get_font_data"); - - BIND_ENUM_CONSTANT(SPACING_TOP); - BIND_ENUM_CONSTANT(SPACING_BOTTOM); - BIND_ENUM_CONSTANT(SPACING_CHAR); - BIND_ENUM_CONSTANT(SPACING_SPACE); -} - -Mutex DynamicFont::dynamic_font_mutex; - -SelfList<DynamicFont>::List *DynamicFont::dynamic_fonts = nullptr; - -DynamicFont::DynamicFont() : - font_list(this) { - valid = false; - cache_id.size = 16; - outline_cache_id.size = 16; - spacing_top = 0; - spacing_bottom = 0; - spacing_char = 0; - spacing_space = 0; - outline_color = Color(1, 1, 1); - - MutexLock lock(dynamic_font_mutex); - dynamic_fonts->add(&font_list); -} - -DynamicFont::~DynamicFont() { - MutexLock lock(dynamic_font_mutex); - dynamic_fonts->remove(&font_list); -} - -void DynamicFont::initialize_dynamic_fonts() { - dynamic_fonts = memnew(SelfList<DynamicFont>::List()); -} - -void DynamicFont::finish_dynamic_fonts() { - memdelete(dynamic_fonts); - dynamic_fonts = nullptr; -} - -void DynamicFont::update_oversampling() { - Vector<Ref<DynamicFont>> changed; - { - MutexLock lock(dynamic_font_mutex); - - SelfList<DynamicFont> *E = dynamic_fonts->first(); - while (E) { - if (E->self()->data_at_size.is_valid()) { - E->self()->data_at_size->update_oversampling(); - - if (E->self()->outline_data_at_size.is_valid()) { - E->self()->outline_data_at_size->update_oversampling(); - } - - for (int i = 0; i < E->self()->fallback_data_at_size.size(); i++) { - if (E->self()->fallback_data_at_size[i].is_valid()) { - E->self()->fallback_data_at_size.write[i]->update_oversampling(); - - if (E->self()->has_outline() && E->self()->fallback_outline_data_at_size[i].is_valid()) { - E->self()->fallback_outline_data_at_size.write[i]->update_oversampling(); - } - } - } - - changed.push_back(Ref<DynamicFont>(E->self())); - } - - E = E->next(); - } - } - - for (int i = 0; i < changed.size(); i++) { - changed.write[i]->emit_changed(); - } -} - -///////////////////////// - -RES ResourceFormatLoaderDynamicFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { - if (r_error) { - *r_error = ERR_FILE_CANT_OPEN; - } - - Ref<DynamicFontData> dfont; - dfont.instance(); - dfont->set_font_path(p_path); - - if (r_error) { - *r_error = OK; - } - - return dfont; -} - -void ResourceFormatLoaderDynamicFont::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("ttf"); - p_extensions->push_back("otf"); -} - -bool ResourceFormatLoaderDynamicFont::handles_type(const String &p_type) const { - return (p_type == "DynamicFontData"); -} - -String ResourceFormatLoaderDynamicFont::get_resource_type(const String &p_path) const { - String el = p_path.get_extension().to_lower(); - if (el == "ttf" || el == "otf") { - return "DynamicFontData"; - } - return ""; -} - -#endif diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h deleted file mode 100644 index 73ba42f8a7..0000000000 --- a/scene/resources/dynamic_font.h +++ /dev/null @@ -1,316 +0,0 @@ -/*************************************************************************/ -/* dynamic_font.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef DYNAMIC_FONT_H -#define DYNAMIC_FONT_H - -#include "modules/modules_enabled.gen.h" -#ifdef MODULE_FREETYPE_ENABLED - -#include "core/io/resource_loader.h" -#include "core/os/mutex.h" -#include "core/os/thread_safe.h" -#include "core/templates/pair.h" -#include "scene/resources/font.h" - -#include <ft2build.h> -#include FT_FREETYPE_H - -class DynamicFontAtSize; -class DynamicFont; - -class DynamicFontData : public Resource { - GDCLASS(DynamicFontData, Resource); - -public: - struct CacheID { - union { - struct { - uint32_t size : 16; - uint32_t outline_size : 8; - uint32_t unused : 8; - }; - uint32_t key; - }; - bool operator<(CacheID right) const; - CacheID() { - key = 0; - } - }; - - enum Hinting { - HINTING_NONE, - HINTING_LIGHT, - HINTING_NORMAL - }; - - bool is_antialiased() const; - void set_antialiased(bool p_antialiased); - Hinting get_hinting() const; - void set_hinting(Hinting p_hinting); - -private: - const uint8_t *font_mem; - int font_mem_size; - bool antialiased; - bool force_autohinter; - Hinting hinting; - - String font_path; - Map<CacheID, DynamicFontAtSize *> size_cache; - - friend class DynamicFontAtSize; - - friend class DynamicFont; - - Ref<DynamicFontAtSize> _get_dynamic_font_at_size(CacheID p_cache_id); - -protected: - static void _bind_methods(); - -public: - void set_font_ptr(const uint8_t *p_font_mem, int p_font_mem_size); - void set_font_path(const String &p_path); - String get_font_path() const; - void set_force_autohinter(bool p_force); - - DynamicFontData(); - ~DynamicFontData(); -}; - -VARIANT_ENUM_CAST(DynamicFontData::Hinting); - -class DynamicFontAtSize : public Reference { - GDCLASS(DynamicFontAtSize, Reference); - - _THREAD_SAFE_CLASS_ - - FT_Library library; /* handle to library */ - FT_Face face; /* handle to face object */ - FT_StreamRec stream; - - float ascent; - float descent; - float linegap; - float rect_margin; - float oversampling; - float scale_color_font; - float underline_position; - float underline_thickness; - - bool valid; - - struct CharTexture { - Vector<uint8_t> imgdata; - int texture_size; - Vector<int> offsets; - Ref<ImageTexture> texture; - }; - - Vector<CharTexture> textures; - - struct Character { - bool found; - int texture_idx; - Rect2 rect; - Rect2 rect_uv; - float v_align; - float h_align; - float advance; - - Character() { - texture_idx = 0; - v_align = 0; - } - - static Character not_found(); - }; - - struct TexturePosition { - int index; - int x; - int y; - }; - - const Pair<const Character *, DynamicFontAtSize *> _find_char_with_font(char32_t p_char, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const; - Character _make_outline_char(char32_t p_char); - TexturePosition _find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height); - Character _bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance); - - static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count); - static void _ft_stream_close(FT_Stream stream); - - HashMap<char32_t, Character> char_map; - - _FORCE_INLINE_ void _update_char(char32_t p_char); - - friend class DynamicFontData; - Ref<DynamicFontData> font; - DynamicFontData::CacheID id; - - static HashMap<String, Vector<uint8_t>> _fontdata; - Error _load(); - -public: - static float font_oversampling; - - float get_height() const; - - float get_ascent() const; - float get_descent() const; - float get_underline_position() const; - float get_underline_thickness() const; - - Size2 get_char_size(char32_t p_char, char32_t p_next, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const; - String get_available_chars() const; - - float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks, bool p_advance_only = false, bool p_outline = false) const; - - void set_texture_flags(uint32_t p_flags); - void update_oversampling(); - - DynamicFontAtSize(); - ~DynamicFontAtSize(); -}; - -/////////////// - -class DynamicFont : public Font { - GDCLASS(DynamicFont, Font); - -public: - enum SpacingType { - SPACING_TOP, - SPACING_BOTTOM, - SPACING_CHAR, - SPACING_SPACE - }; - -private: - Ref<DynamicFontData> data; - Ref<DynamicFontAtSize> data_at_size; - Ref<DynamicFontAtSize> outline_data_at_size; - - Vector<Ref<DynamicFontData>> fallbacks; - Vector<Ref<DynamicFontAtSize>> fallback_data_at_size; - Vector<Ref<DynamicFontAtSize>> fallback_outline_data_at_size; - - DynamicFontData::CacheID cache_id; - DynamicFontData::CacheID outline_cache_id; - - bool valid; - int spacing_top; - int spacing_bottom; - int spacing_char; - int spacing_space; - - Color outline_color; - -protected: - void _reload_cache(); - - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; - - static void _bind_methods(); - -public: - void set_font_data(const Ref<DynamicFontData> &p_data); - Ref<DynamicFontData> get_font_data() const; - - void set_size(int p_size); - int get_size() const; - - void set_outline_size(int p_size); - int get_outline_size() const; - - void set_outline_color(Color p_color); - Color get_outline_color() const; - - bool get_use_mipmaps() const; - void set_use_mipmaps(bool p_enable); - - bool get_use_filter() const; - void set_use_filter(bool p_enable); - - int get_spacing(int p_type) const; - void set_spacing(int p_type, int p_value); - - void add_fallback(const Ref<DynamicFontData> &p_data); - void set_fallback(int p_idx, const Ref<DynamicFontData> &p_data); - int get_fallback_count() const; - Ref<DynamicFontData> get_fallback(int p_idx) const; - void remove_fallback(int p_idx); - - virtual float get_height() const override; - - virtual float get_ascent() const override; - virtual float get_descent() const override; - virtual float get_underline_position() const override; - virtual float get_underline_thickness() const override; - - virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const override; - String get_available_chars() const; - - virtual bool is_distance_field_hint() const override; - - virtual bool has_outline() const override; - - virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const override; - - SelfList<DynamicFont> font_list; - - static Mutex dynamic_font_mutex; - static SelfList<DynamicFont>::List *dynamic_fonts; - - static void initialize_dynamic_fonts(); - static void finish_dynamic_fonts(); - static void update_oversampling(); - - DynamicFont(); - ~DynamicFont(); -}; - -VARIANT_ENUM_CAST(DynamicFont::SpacingType); - -///////////// - -class ResourceFormatLoaderDynamicFont : public ResourceFormatLoader { -public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; -}; - -#endif - -#endif diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index bacdb6be54..b7b5f72fcd 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,7 @@ #include "environment.h" #include "core/config/project_settings.h" +#include "core/core_string_names.h" #include "servers/rendering_server.h" #include "texture.h" @@ -367,49 +368,58 @@ float Environment::get_ssao_intensity() const { return ssao_intensity; } -void Environment::set_ssao_bias(float p_bias) { - ssao_bias = p_bias; +void Environment::set_ssao_power(float p_power) { + ssao_power = p_power; _update_ssao(); } -float Environment::get_ssao_bias() const { - return ssao_bias; +float Environment::get_ssao_power() const { + return ssao_power; } -void Environment::set_ssao_direct_light_affect(float p_direct_light_affect) { - ssao_direct_light_affect = p_direct_light_affect; +void Environment::set_ssao_detail(float p_detail) { + ssao_detail = p_detail; _update_ssao(); } -float Environment::get_ssao_direct_light_affect() const { - return ssao_direct_light_affect; +float Environment::get_ssao_detail() const { + return ssao_detail; } -void Environment::set_ssao_ao_channel_affect(float p_ao_channel_affect) { - ssao_ao_channel_affect = p_ao_channel_affect; +void Environment::set_ssao_horizon(float p_horizon) { + ssao_horizon = p_horizon; _update_ssao(); } -float Environment::get_ssao_ao_channel_affect() const { - return ssao_ao_channel_affect; +float Environment::get_ssao_horizon() const { + return ssao_horizon; } -void Environment::set_ssao_blur(SSAOBlur p_blur) { - ssao_blur = p_blur; +void Environment::set_ssao_sharpness(float p_sharpness) { + ssao_sharpness = p_sharpness; _update_ssao(); } -Environment::SSAOBlur Environment::get_ssao_blur() const { - return ssao_blur; +float Environment::get_ssao_sharpness() const { + return ssao_sharpness; } -void Environment::set_ssao_edge_sharpness(float p_edge_sharpness) { - ssao_edge_sharpness = p_edge_sharpness; +void Environment::set_ssao_direct_light_affect(float p_direct_light_affect) { + ssao_direct_light_affect = p_direct_light_affect; _update_ssao(); } -float Environment::get_ssao_edge_sharpness() const { - return ssao_edge_sharpness; +float Environment::get_ssao_direct_light_affect() const { + return ssao_direct_light_affect; +} + +void Environment::set_ssao_ao_channel_affect(float p_ao_channel_affect) { + ssao_ao_channel_affect = p_ao_channel_affect; + _update_ssao(); +} + +float Environment::get_ssao_ao_channel_affect() const { + return ssao_ao_channel_affect; } void Environment::_update_ssao() { @@ -418,11 +428,12 @@ void Environment::_update_ssao() { ssao_enabled, ssao_radius, ssao_intensity, - ssao_bias, + ssao_power, + ssao_detail, + ssao_horizon, + ssao_sharpness, ssao_direct_light_affect, - ssao_ao_channel_affect, - RS::EnvironmentSSAOBlur(ssao_blur), - ssao_edge_sharpness); + ssao_ao_channel_affect); } // SDFGI @@ -590,7 +601,7 @@ void Environment::set_glow_level(int p_level, float p_intensity) { } float Environment::get_glow_level(int p_level) const { - ERR_FAIL_INDEX_V(p_level, RS::MAX_GLOW_LEVELS, false); + ERR_FAIL_INDEX_V(p_level, RS::MAX_GLOW_LEVELS, 0.0); return glow_levels[p_level]; } @@ -891,23 +902,38 @@ float Environment::get_adjustment_saturation() const { return adjustment_saturation; } -void Environment::set_adjustment_color_correction(const Ref<Texture2D> &p_ramp) { - adjustment_color_correction = p_ramp; +void Environment::set_adjustment_color_correction(Ref<Texture> p_color_correction) { + adjustment_color_correction = p_color_correction; + Ref<GradientTexture> grad_tex = p_color_correction; + if (grad_tex.is_valid()) { + if (!grad_tex->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment))) { + grad_tex->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment)); + } + } + Ref<Texture2D> adjustment_texture_2d = adjustment_color_correction; + if (adjustment_texture_2d.is_valid()) { + use_1d_color_correction = true; + } else { + use_1d_color_correction = false; + } _update_adjustment(); } -Ref<Texture2D> Environment::get_adjustment_color_correction() const { +Ref<Texture> Environment::get_adjustment_color_correction() const { return adjustment_color_correction; } void Environment::_update_adjustment() { + RID color_correction = adjustment_color_correction.is_valid() ? adjustment_color_correction->get_rid() : RID(); + RS::get_singleton()->environment_set_adjustment( environment, adjustment_enabled, adjustment_brightness, adjustment_contrast, adjustment_saturation, - adjustment_color_correction.is_valid() ? adjustment_color_correction->get_rid() : RID()); + use_1d_color_correction, + color_correction); } // Private methods, constructor and destructor @@ -1134,26 +1160,29 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_ssao_radius"), &Environment::get_ssao_radius); ClassDB::bind_method(D_METHOD("set_ssao_intensity", "intensity"), &Environment::set_ssao_intensity); ClassDB::bind_method(D_METHOD("get_ssao_intensity"), &Environment::get_ssao_intensity); - ClassDB::bind_method(D_METHOD("set_ssao_bias", "bias"), &Environment::set_ssao_bias); - ClassDB::bind_method(D_METHOD("get_ssao_bias"), &Environment::get_ssao_bias); + ClassDB::bind_method(D_METHOD("set_ssao_power", "power"), &Environment::set_ssao_power); + ClassDB::bind_method(D_METHOD("get_ssao_power"), &Environment::get_ssao_power); + ClassDB::bind_method(D_METHOD("set_ssao_detail", "detail"), &Environment::set_ssao_detail); + ClassDB::bind_method(D_METHOD("get_ssao_detail"), &Environment::get_ssao_detail); + ClassDB::bind_method(D_METHOD("set_ssao_horizon", "horizon"), &Environment::set_ssao_horizon); + ClassDB::bind_method(D_METHOD("get_ssao_horizon"), &Environment::get_ssao_horizon); + ClassDB::bind_method(D_METHOD("set_ssao_sharpness", "sharpness"), &Environment::set_ssao_sharpness); + ClassDB::bind_method(D_METHOD("get_ssao_sharpness"), &Environment::get_ssao_sharpness); ClassDB::bind_method(D_METHOD("set_ssao_direct_light_affect", "amount"), &Environment::set_ssao_direct_light_affect); ClassDB::bind_method(D_METHOD("get_ssao_direct_light_affect"), &Environment::get_ssao_direct_light_affect); ClassDB::bind_method(D_METHOD("set_ssao_ao_channel_affect", "amount"), &Environment::set_ssao_ao_channel_affect); ClassDB::bind_method(D_METHOD("get_ssao_ao_channel_affect"), &Environment::get_ssao_ao_channel_affect); - ClassDB::bind_method(D_METHOD("set_ssao_blur", "mode"), &Environment::set_ssao_blur); - ClassDB::bind_method(D_METHOD("get_ssao_blur"), &Environment::get_ssao_blur); - ClassDB::bind_method(D_METHOD("set_ssao_edge_sharpness", "edge_sharpness"), &Environment::set_ssao_edge_sharpness); - ClassDB::bind_method(D_METHOD("get_ssao_edge_sharpness"), &Environment::get_ssao_edge_sharpness); ADD_GROUP("SSAO", "ssao_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssao_enabled"), "set_ssao_enabled", "is_ssao_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_radius", PROPERTY_HINT_RANGE, "0.1,128,0.01"), "set_ssao_radius", "get_ssao_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_intensity", PROPERTY_HINT_RANGE, "0.0,128,0.01"), "set_ssao_intensity", "get_ssao_intensity"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_bias", PROPERTY_HINT_RANGE, "0.001,8,0.001"), "set_ssao_bias", "get_ssao_bias"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_radius", PROPERTY_HINT_RANGE, "0.01,16,0.01,or_greater"), "set_ssao_radius", "get_ssao_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_intensity", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_ssao_intensity", "get_ssao_intensity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_power", PROPERTY_HINT_EXP_EASING), "set_ssao_power", "get_ssao_power"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_detail", PROPERTY_HINT_RANGE, "0,5,0.01"), "set_ssao_detail", "get_ssao_detail"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_horizon", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssao_horizon", "get_ssao_horizon"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_sharpness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssao_sharpness", "get_ssao_sharpness"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_light_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_direct_light_affect", "get_ssao_direct_light_affect"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_ao_channel_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_ao_channel_affect", "get_ssao_ao_channel_affect"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "ssao_blur", PROPERTY_HINT_ENUM, "Disabled,1x1,2x2,3x3"), "set_ssao_blur", "get_ssao_blur"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_edge_sharpness", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_ssao_edge_sharpness", "get_ssao_edge_sharpness"); // SDFGI @@ -1319,7 +1348,7 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_brightness", PROPERTY_HINT_RANGE, "0.01,8,0.01"), "set_adjustment_brightness", "get_adjustment_brightness"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_contrast", PROPERTY_HINT_RANGE, "0.01,8,0.01"), "set_adjustment_contrast", "get_adjustment_contrast"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "adjustment_saturation", PROPERTY_HINT_RANGE, "0.01,8,0.01"), "set_adjustment_saturation", "get_adjustment_saturation"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "adjustment_color_correction", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_adjustment_color_correction", "get_adjustment_color_correction"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "adjustment_color_correction", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D,Texture3D"), "set_adjustment_color_correction", "get_adjustment_color_correction"); // Constants @@ -1351,11 +1380,6 @@ void Environment::_bind_methods() { BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_REPLACE); BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_MIX); - BIND_ENUM_CONSTANT(SSAO_BLUR_DISABLED); - BIND_ENUM_CONSTANT(SSAO_BLUR_1x1); - BIND_ENUM_CONSTANT(SSAO_BLUR_2x2); - BIND_ENUM_CONSTANT(SSAO_BLUR_3x3); - BIND_ENUM_CONSTANT(SDFGI_CASCADES_4); BIND_ENUM_CONSTANT(SDFGI_CASCADES_6); BIND_ENUM_CONSTANT(SDFGI_CASCADES_8); diff --git a/scene/resources/environment.h b/scene/resources/environment.h index 23c7f5180c..1676a28b39 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -70,13 +70,6 @@ public: TONE_MAPPER_ACES, }; - enum SSAOBlur { - SSAO_BLUR_DISABLED, - SSAO_BLUR_1x1, - SSAO_BLUR_2x2, - SSAO_BLUR_3x3, - }; - enum SDFGICascades { SDFGI_CASCADES_4, SDFGI_CASCADES_6, @@ -148,12 +141,13 @@ private: // SSAO bool ssao_enabled = false; float ssao_radius = 1.0; - float ssao_intensity = 1.0; - float ssao_bias = 0.01; + float ssao_intensity = 2.0; + float ssao_power = 1.5; + float ssao_detail = 0.5; + float ssao_horizon = 0.06; + float ssao_sharpness = 0.98; float ssao_direct_light_affect = 0.0; float ssao_ao_channel_affect = 0.0; - SSAOBlur ssao_blur = SSAO_BLUR_3x3; - float ssao_edge_sharpness = 4.0; void _update_ssao(); // SDFGI @@ -211,7 +205,8 @@ private: float adjustment_brightness = 1.0; float adjustment_contrast = 1.0; float adjustment_saturation = 1.0; - Ref<Texture2D> adjustment_color_correction; + bool use_1d_color_correction = true; + Ref<Texture> adjustment_color_correction; void _update_adjustment(); protected: @@ -294,16 +289,18 @@ public: float get_ssao_radius() const; void set_ssao_intensity(float p_intensity); float get_ssao_intensity() const; - void set_ssao_bias(float p_bias); - float get_ssao_bias() const; + void set_ssao_power(float p_power); + float get_ssao_power() const; + void set_ssao_detail(float p_detail); + float get_ssao_detail() const; + void set_ssao_horizon(float p_horizon); + float get_ssao_horizon() const; + void set_ssao_sharpness(float p_sharpness); + float get_ssao_sharpness() const; void set_ssao_direct_light_affect(float p_direct_light_affect); float get_ssao_direct_light_affect() const; void set_ssao_ao_channel_affect(float p_ao_channel_affect); float get_ssao_ao_channel_affect() const; - void set_ssao_blur(SSAOBlur p_blur); - SSAOBlur get_ssao_blur() const; - void set_ssao_edge_sharpness(float p_edge_sharpness); - float get_ssao_edge_sharpness() const; // SDFGI void set_sdfgi_enabled(bool p_enabled); @@ -402,8 +399,8 @@ public: float get_adjustment_contrast() const; void set_adjustment_saturation(float p_saturation); float get_adjustment_saturation() const; - void set_adjustment_color_correction(const Ref<Texture2D> &p_ramp); - Ref<Texture2D> get_adjustment_color_correction() const; + void set_adjustment_color_correction(Ref<Texture> p_color_correction); + Ref<Texture> get_adjustment_color_correction() const; Environment(); ~Environment(); @@ -413,7 +410,6 @@ VARIANT_ENUM_CAST(Environment::BGMode) VARIANT_ENUM_CAST(Environment::AmbientSource) VARIANT_ENUM_CAST(Environment::ReflectionSource) VARIANT_ENUM_CAST(Environment::ToneMapper) -VARIANT_ENUM_CAST(Environment::SSAOBlur) VARIANT_ENUM_CAST(Environment::SDFGICascades) VARIANT_ENUM_CAST(Environment::SDFGIYScale) VARIANT_ENUM_CAST(Environment::GlowBlendMode) diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 7bda889e46..1c06d7b519 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,639 +31,1017 @@ #include "font.h" #include "core/io/resource_loader.h" -#include "core/os/file_access.h" +#include "core/string/translation.h" +#include "core/templates/hashfuncs.h" +#include "scene/resources/text_line.h" +#include "scene/resources/text_paragraph.h" -void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate, const Color &p_outline_modulate) const { - float length = get_string_size(p_text).width; - if (length >= p_width) { - draw(p_canvas_item, p_pos, p_text, p_modulate, p_width, p_outline_modulate); - return; - } +void FontData::_bind_methods() { + ClassDB::bind_method(D_METHOD("load_resource", "filename", "base_size"), &FontData::load_resource, DEFVAL(16)); + ClassDB::bind_method(D_METHOD("load_memory", "data", "type", "base_size"), &FontData::_load_memory, DEFVAL(16)); - float ofs = 0.f; - switch (p_align) { - case HALIGN_LEFT: { - ofs = 0; - } break; - case HALIGN_CENTER: { - ofs = Math::floor((p_width - length) / 2.0); - } break; - case HALIGN_RIGHT: { - ofs = p_width - length; - } break; - default: { - ERR_PRINT("Unknown halignment type"); - } break; - } - draw(p_canvas_item, p_pos + Point2(ofs, 0), p_text, p_modulate, p_width, p_outline_modulate); -} + ClassDB::bind_method(D_METHOD("set_data_path", "path"), &FontData::set_data_path); + ClassDB::bind_method(D_METHOD("get_data_path"), &FontData::get_data_path); -void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w, const Color &p_outline_modulate) const { - Vector2 ofs; + ClassDB::bind_method(D_METHOD("get_height", "size"), &FontData::get_height); + ClassDB::bind_method(D_METHOD("get_ascent", "size"), &FontData::get_ascent); + ClassDB::bind_method(D_METHOD("get_descent", "size"), &FontData::get_descent); - int chars_drawn = 0; - bool with_outline = has_outline(); - for (int i = 0; i < p_text.length(); i++) { - int width = get_char_size(p_text[i]).width; + ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &FontData::get_underline_position); + ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &FontData::get_underline_thickness); - if (p_clip_w >= 0 && (ofs.x + width) > p_clip_w) { - break; //clip - } + ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased); + ClassDB::bind_method(D_METHOD("get_antialiased"), &FontData::get_antialiased); - ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], with_outline ? p_outline_modulate : p_modulate, with_outline); - ++chars_drawn; - } + ClassDB::bind_method(D_METHOD("get_variation_list"), &FontData::get_variation_list); + + ClassDB::bind_method(D_METHOD("set_variation", "tag", "value"), &FontData::set_variation); + ClassDB::bind_method(D_METHOD("get_variation", "tag"), &FontData::get_variation); + + ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting); + ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting); + + ClassDB::bind_method(D_METHOD("set_force_autohinter", "enabled"), &FontData::set_force_autohinter); + ClassDB::bind_method(D_METHOD("get_force_autohinter"), &FontData::get_force_autohinter); + + ClassDB::bind_method(D_METHOD("set_distance_field_hint", "distance_field"), &FontData::set_distance_field_hint); + ClassDB::bind_method(D_METHOD("get_distance_field_hint"), &FontData::get_distance_field_hint); + + ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char); + ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars); + + ClassDB::bind_method(D_METHOD("get_glyph_advance", "index", "size"), &FontData::get_glyph_advance); + ClassDB::bind_method(D_METHOD("get_glyph_kerning", "index_a", "index_b", "size"), &FontData::get_glyph_kerning); + + ClassDB::bind_method(D_METHOD("get_base_size"), &FontData::get_base_size); + + ClassDB::bind_method(D_METHOD("has_outline"), &FontData::has_outline); + + ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &FontData::is_language_supported); + ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontData::set_language_support_override); + ClassDB::bind_method(D_METHOD("get_language_support_override", "language"), &FontData::get_language_support_override); + ClassDB::bind_method(D_METHOD("remove_language_support_override", "language"), &FontData::remove_language_support_override); + ClassDB::bind_method(D_METHOD("get_language_support_overrides"), &FontData::get_language_support_overrides); + + ClassDB::bind_method(D_METHOD("is_script_supported", "script"), &FontData::is_script_supported); + ClassDB::bind_method(D_METHOD("set_script_support_override", "script", "supported"), &FontData::set_script_support_override); + ClassDB::bind_method(D_METHOD("get_script_support_override", "script"), &FontData::get_script_support_override); + ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontData::remove_script_support_override); + ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontData::get_script_support_overrides); + + ClassDB::bind_method(D_METHOD("get_glyph_index", "char", "variation_selector"), &FontData::get_glyph_index, DEFVAL(0x0000)); + ClassDB::bind_method(D_METHOD("draw_glyph", "canvas", "size", "pos", "index", "color"), &FontData::draw_glyph, DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("draw_glyph_outline", "canvas", "size", "outline_size", "pos", "index", "color"), &FontData::draw_glyph_outline, DEFVAL(Color(1, 1, 1))); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "data_path", PROPERTY_HINT_FILE, "*.ttf,*.otf,*.woff,*.fnt,*.font"), "set_data_path", "get_data_path"); - if (has_outline()) { - ofs = Vector2(0, 0); - for (int i = 0; i < chars_drawn; i++) { - ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], p_modulate, false); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "get_antialiased"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "get_force_autohinter"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_field_hint"), "set_distance_field_hint", "get_distance_field_hint"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting"); +} + +bool FontData::_set(const StringName &p_name, const Variant &p_value) { + String str = p_name; + if (str.begins_with("language_support_override/")) { + String lang = str.get_slicec('/', 1); + if (lang == "_new") { + return false; } + set_language_support_override(lang, p_value); + return true; + } + if (str.begins_with("script_support_override/")) { + String scr = str.get_slicec('/', 1); + if (scr == "_new") { + return false; + } + set_script_support_override(scr, p_value); + return true; + } + if (str.begins_with("variation/")) { + String name = str.get_slicec('/', 1); + set_variation(name, p_value); + return true; } -} -void Font::update_changes() { - emit_changed(); + return false; } -void Font::_bind_methods() { - ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "string", "modulate", "clip_w", "outline_modulate"), &Font::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(-1), DEFVAL(Color(1, 1, 1))); - ClassDB::bind_method(D_METHOD("get_ascent"), &Font::get_ascent); - ClassDB::bind_method(D_METHOD("get_descent"), &Font::get_descent); - ClassDB::bind_method(D_METHOD("get_height"), &Font::get_height); - ClassDB::bind_method(D_METHOD("is_distance_field_hint"), &Font::is_distance_field_hint); - ClassDB::bind_method(D_METHOD("get_char_size", "char", "next"), &Font::get_char_size, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("get_string_size", "string"), &Font::get_string_size); - ClassDB::bind_method(D_METHOD("get_wordwrap_string_size", "string", "width"), &Font::get_wordwrap_string_size); - ClassDB::bind_method(D_METHOD("has_outline"), &Font::has_outline); - ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "position", "char", "next", "modulate", "outline"), &Font::draw_char, DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes); -} +bool FontData::_get(const StringName &p_name, Variant &r_ret) const { + String str = p_name; + if (str.begins_with("language_support_override/")) { + String lang = str.get_slicec('/', 1); + if (lang == "_new") { + return true; + } + r_ret = get_language_support_override(lang); + return true; + } + if (str.begins_with("script_support_override/")) { + String scr = str.get_slicec('/', 1); + if (scr == "_new") { + return true; + } + r_ret = get_script_support_override(scr); + return true; + } + if (str.begins_with("variation/")) { + String name = str.get_slicec('/', 1); -Font::Font() { + r_ret = get_variation(name); + return true; + } + + return false; } -///////////////////////////////////////////////////////////////// +void FontData::_get_property_list(List<PropertyInfo> *p_list) const { + Vector<String> lang_over = get_language_support_overrides(); + for (int i = 0; i < lang_over.size(); i++) { + p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE)); + } + p_list->push_back(PropertyInfo(Variant::NIL, "language_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); -void BitmapFont::_set_chars(const Vector<int> &p_chars) { - int len = p_chars.size(); - //char 1 charsize 1 texture, 4 rect, 2 align, advance 1 - ERR_FAIL_COND(len % 9); - if (!len) { - return; //none to do + Vector<String> scr_over = get_script_support_overrides(); + for (int i = 0; i < scr_over.size(); i++) { + p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE)); } - int chars = len / 9; + p_list->push_back(PropertyInfo(Variant::NIL, "script_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - const int *r = p_chars.ptr(); - for (int i = 0; i < chars; i++) { - const int *data = &r[i * 9]; - add_char(data[0], data[1], Rect2(data[2], data[3], data[4], data[5]), Size2(data[6], data[7]), data[8]); + Dictionary variations = get_variation_list(); + for (const Variant *ftr = variations.next(nullptr); ftr != nullptr; ftr = variations.next(ftr)) { + Vector3i v = variations[*ftr]; + p_list->push_back(PropertyInfo(Variant::FLOAT, "variation/" + TS->tag_to_name(*ftr), PROPERTY_HINT_RANGE, itos(v.x) + "," + itos(v.y), PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE)); } } -Vector<int> BitmapFont::_get_chars() const { - Vector<int> chars; +RID FontData::get_rid() const { + return rid; +} - const char32_t *key = nullptr; +void FontData::load_resource(const String &p_filename, int p_base_size) { + if (rid != RID()) { + TS->free(rid); + } + rid = TS->create_font_resource(p_filename, p_base_size); + path = p_filename; + base_size = TS->font_get_base_size(rid); + emit_changed(); +} - while ((key = char_map.next(key))) { - const Character *c = char_map.getptr(*key); - ERR_FAIL_COND_V(!c, Vector<int>()); - chars.push_back(*key); - chars.push_back(c->texture_idx); - chars.push_back(c->rect.position.x); - chars.push_back(c->rect.position.y); +void FontData::_load_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size) { + if (rid != RID()) { + TS->free(rid); + } + rid = TS->create_font_memory(p_data.ptr(), p_data.size(), p_type, p_base_size); + path = TTR("(Memory: " + p_type.to_upper() + " @ 0x" + String::num_int64((uint64_t)p_data.ptr(), 16, true) + ")"); + base_size = TS->font_get_base_size(rid); + emit_changed(); +} - chars.push_back(c->rect.size.x); - chars.push_back(c->rect.size.y); - chars.push_back(c->h_align); - chars.push_back(c->v_align); - chars.push_back(c->advance); +void FontData::load_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) { + if (rid != RID()) { + TS->free(rid); } + rid = TS->create_font_memory(p_data, p_size, p_type, p_base_size); + path = TTR("(Memory: " + p_type.to_upper() + " @ 0x" + String::num_int64((uint64_t)p_data, 16, true) + ")"); + base_size = TS->font_get_base_size(rid); + emit_changed(); +} - return chars; +void FontData::set_data_path(const String &p_path) { + load_resource(p_path, base_size); } -void BitmapFont::_set_kernings(const Vector<int> &p_kernings) { - int len = p_kernings.size(); - ERR_FAIL_COND(len % 3); - if (!len) { - return; - } - const int *r = p_kernings.ptr(); +String FontData::get_data_path() const { + return path; +} - for (int i = 0; i < len / 3; i++) { - const int *data = &r[i * 3]; - add_kerning_pair(data[0], data[1], data[2]); +float FontData::get_height(int p_size) const { + if (rid == RID()) { + return 0.f; // Do not raise errors in getters, to prevent editor from spamming errors on incomplete (without data_path set) fonts. } + return TS->font_get_height(rid, (p_size < 0) ? base_size : p_size); } -Vector<int> BitmapFont::_get_kernings() const { - Vector<int> kernings; - - for (Map<KerningPairKey, int>::Element *E = kerning_map.front(); E; E = E->next()) { - kernings.push_back(E->key().A); - kernings.push_back(E->key().B); - kernings.push_back(E->get()); +float FontData::get_ascent(int p_size) const { + if (rid == RID()) { + return 0.f; } + return TS->font_get_ascent(rid, (p_size < 0) ? base_size : p_size); +} - return kernings; +float FontData::get_descent(int p_size) const { + if (rid == RID()) { + return 0.f; + } + return TS->font_get_descent(rid, (p_size < 0) ? base_size : p_size); } -void BitmapFont::_set_textures(const Vector<Variant> &p_textures) { - textures.clear(); - for (int i = 0; i < p_textures.size(); i++) { - Ref<Texture2D> tex = p_textures[i]; - ERR_CONTINUE(!tex.is_valid()); - add_texture(tex); +float FontData::get_underline_position(int p_size) const { + if (rid == RID()) { + return 0.f; } + return TS->font_get_underline_position(rid, (p_size < 0) ? base_size : p_size); } -Vector<Variant> BitmapFont::_get_textures() const { - Vector<Variant> rtextures; - for (int i = 0; i < textures.size(); i++) { - rtextures.push_back(textures[i]); +Dictionary FontData::get_feature_list() const { + if (rid == RID()) { + return Dictionary(); } - return rtextures; + return TS->font_get_feature_list(rid); } -Error BitmapFont::create_from_fnt(const String &p_file) { - //fnt format used by angelcode bmfont - //http://www.angelcode.com/products/bmfont/ +float FontData::get_underline_thickness(int p_size) const { + if (rid == RID()) { + return 0.f; + } + return TS->font_get_underline_thickness(rid, (p_size < 0) ? base_size : p_size); +} - FileAccess *f = FileAccess::open(p_file, FileAccess::READ); +Dictionary FontData::get_variation_list() const { + if (rid == RID()) { + return Dictionary(); + } + return TS->font_get_variation_list(rid); +} - ERR_FAIL_COND_V_MSG(!f, ERR_FILE_NOT_FOUND, "Can't open font: " + p_file + "."); +void FontData::set_variation(const String &p_name, double p_value) { + ERR_FAIL_COND(rid == RID()); + TS->font_set_variation(rid, p_name, p_value); + emit_changed(); +} - clear(); +double FontData::get_variation(const String &p_name) const { + if (rid == RID()) { + return 0; + } + return TS->font_get_variation(rid, p_name); +} - while (true) { - String line = f->get_line(); +void FontData::set_antialiased(bool p_antialiased) { + ERR_FAIL_COND(rid == RID()); + TS->font_set_antialiased(rid, p_antialiased); + emit_changed(); +} - int delimiter = line.find(" "); - String type = line.substr(0, delimiter); - int pos = delimiter + 1; - Map<String, String> keys; +bool FontData::get_antialiased() const { + if (rid == RID()) { + return false; + } + return TS->font_get_antialiased(rid); +} - while (pos < line.size() && line[pos] == ' ') { - pos++; - } +void FontData::set_distance_field_hint(bool p_distance_field) { + ERR_FAIL_COND(rid == RID()); + TS->font_set_distance_field_hint(rid, p_distance_field); + emit_changed(); +} - while (pos < line.size()) { - int eq = line.find("=", pos); - if (eq == -1) { - break; - } - String key = line.substr(pos, eq - pos); - int end = -1; - String value; - if (line[eq + 1] == '"') { - end = line.find("\"", eq + 2); - if (end == -1) { - break; - } - value = line.substr(eq + 2, end - 1 - eq - 1); - pos = end + 1; - } else { - end = line.find(" ", eq + 1); - if (end == -1) { - end = line.size(); - } +bool FontData::get_distance_field_hint() const { + if (rid == RID()) { + return false; + } + return TS->font_get_distance_field_hint(rid); +} - value = line.substr(eq + 1, end - eq); +void FontData::set_hinting(TextServer::Hinting p_hinting) { + ERR_FAIL_COND(rid == RID()); + TS->font_set_hinting(rid, p_hinting); + emit_changed(); +} - pos = end; - } +TextServer::Hinting FontData::get_hinting() const { + if (rid == RID()) { + return TextServer::HINTING_NONE; + } + return TS->font_get_hinting(rid); +} - while (pos < line.size() && line[pos] == ' ') { - pos++; - } +void FontData::set_force_autohinter(bool p_enabeld) { + ERR_FAIL_COND(rid == RID()); + TS->font_set_force_autohinter(rid, p_enabeld); + emit_changed(); +} - keys[key] = value; - } +bool FontData::get_force_autohinter() const { + if (rid == RID()) { + return false; + } + return TS->font_get_force_autohinter(rid); +} - if (type == "info") { - if (keys.has("face")) { - set_name(keys["face"]); - } - /* - if (keys.has("size")) - font->set_height(keys["size"].to_int()); - */ - - } else if (type == "common") { - if (keys.has("lineHeight")) { - set_height(keys["lineHeight"].to_int()); - } - if (keys.has("base")) { - set_ascent(keys["base"].to_int()); - } +bool FontData::has_char(char32_t p_char) const { + if (rid == RID()) { + return false; + } + return TS->font_has_char(rid, p_char); +} - } else if (type == "page") { - if (keys.has("file")) { - String base_dir = p_file.get_base_dir(); - String file = base_dir.plus_file(keys["file"]); - Ref<Texture2D> tex = ResourceLoader::load(file); - if (tex.is_null()) { - ERR_PRINT("Can't load font texture!"); - } else { - add_texture(tex); - } - } - } else if (type == "char") { - char32_t idx = 0; - if (keys.has("id")) { - idx = keys["id"].to_int(); - } +String FontData::get_supported_chars() const { + ERR_FAIL_COND_V(rid == RID(), String()); + return TS->font_get_supported_chars(rid); +} - Rect2 rect; +Vector2 FontData::get_glyph_advance(uint32_t p_index, int p_size) const { + ERR_FAIL_COND_V(rid == RID(), Vector2()); + return TS->font_get_glyph_advance(rid, p_index, (p_size < 0) ? base_size : p_size); +} - if (keys.has("x")) { - rect.position.x = keys["x"].to_int(); - } - if (keys.has("y")) { - rect.position.y = keys["y"].to_int(); - } - if (keys.has("width")) { - rect.size.width = keys["width"].to_int(); - } - if (keys.has("height")) { - rect.size.height = keys["height"].to_int(); - } +Vector2 FontData::get_glyph_kerning(uint32_t p_index_a, uint32_t p_index_b, int p_size) const { + ERR_FAIL_COND_V(rid == RID(), Vector2()); + return TS->font_get_glyph_kerning(rid, p_index_a, p_index_b, (p_size < 0) ? base_size : p_size); +} - Point2 ofs; +bool FontData::has_outline() const { + if (rid == RID()) { + return false; + } + return TS->font_has_outline(rid); +} - if (keys.has("xoffset")) { - ofs.x = keys["xoffset"].to_int(); - } - if (keys.has("yoffset")) { - ofs.y = keys["yoffset"].to_int(); - } +float FontData::get_base_size() const { + return base_size; +} - int texture = 0; - if (keys.has("page")) { - texture = keys["page"].to_int(); - } - int advance = -1; - if (keys.has("xadvance")) { - advance = keys["xadvance"].to_int(); - } +bool FontData::is_language_supported(const String &p_language) const { + if (rid == RID()) { + return false; + } + return TS->font_is_language_supported(rid, p_language); +} - add_char(idx, texture, rect, ofs, advance); +void FontData::set_language_support_override(const String &p_language, bool p_supported) { + ERR_FAIL_COND(rid == RID()); + TS->font_set_language_support_override(rid, p_language, p_supported); + emit_changed(); +} - } else if (type == "kerning") { - char32_t first = 0, second = 0; - int k = 0; +bool FontData::get_language_support_override(const String &p_language) const { + if (rid == RID()) { + return false; + } + return TS->font_get_language_support_override(rid, p_language); +} - if (keys.has("first")) { - first = keys["first"].to_int(); - } - if (keys.has("second")) { - second = keys["second"].to_int(); - } - if (keys.has("amount")) { - k = keys["amount"].to_int(); - } +void FontData::remove_language_support_override(const String &p_language) { + ERR_FAIL_COND(rid == RID()); + TS->font_remove_language_support_override(rid, p_language); + emit_changed(); +} - add_kerning_pair(first, second, -k); - } +Vector<String> FontData::get_language_support_overrides() const { + if (rid == RID()) { + return Vector<String>(); + } + return TS->font_get_language_support_overrides(rid); +} - if (f->eof_reached()) { - break; - } +bool FontData::is_script_supported(const String &p_script) const { + if (rid == RID()) { + return false; } + return TS->font_is_script_supported(rid, p_script); +} - memdelete(f); +void FontData::set_script_support_override(const String &p_script, bool p_supported) { + ERR_FAIL_COND(rid == RID()); + TS->font_set_script_support_override(rid, p_script, p_supported); + emit_changed(); +} - return OK; +bool FontData::get_script_support_override(const String &p_script) const { + if (rid == RID()) { + return false; + } + return TS->font_get_script_support_override(rid, p_script); } -void BitmapFont::set_height(float p_height) { - height = p_height; +void FontData::remove_script_support_override(const String &p_script) { + ERR_FAIL_COND(rid == RID()); + TS->font_remove_script_support_override(rid, p_script); + emit_changed(); } -float BitmapFont::get_height() const { - return height; +Vector<String> FontData::get_script_support_overrides() const { + if (rid == RID()) { + return Vector<String>(); + } + return TS->font_get_script_support_overrides(rid); } -void BitmapFont::set_ascent(float p_ascent) { - ascent = p_ascent; +uint32_t FontData::get_glyph_index(char32_t p_char, char32_t p_variation_selector) const { + ERR_FAIL_COND_V(rid == RID(), 0); + return TS->font_get_glyph_index(rid, p_char, p_variation_selector); } -float BitmapFont::get_ascent() const { - return ascent; +Vector2 FontData::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { + ERR_FAIL_COND_V(rid == RID(), Vector2()); + return TS->font_draw_glyph(rid, p_canvas, (p_size <= 0) ? base_size : p_size, p_pos, p_index, p_color); } -float BitmapFont::get_descent() const { - return height - ascent; +Vector2 FontData::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { + ERR_FAIL_COND_V(rid == RID(), Vector2()); + return TS->font_draw_glyph_outline(rid, p_canvas, (p_size <= 0) ? base_size : p_size, p_outline_size, p_pos, p_index, p_color); } -float BitmapFont::get_underline_position() const { - return 2; +FontData::FontData() {} + +FontData::FontData(const String &p_filename, int p_base_size) { + load_resource(p_filename, p_base_size); } -float BitmapFont::get_underline_thickness() const { - return 1; +FontData::FontData(const PackedByteArray &p_data, const String &p_type, int p_base_size) { + _load_memory(p_data, p_type, p_base_size); } -void BitmapFont::add_texture(const Ref<Texture2D> &p_texture) { - ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object."); - textures.push_back(p_texture); +FontData::~FontData() { + if (rid != RID()) { + TS->free(rid); + } } -int BitmapFont::get_texture_count() const { - return textures.size(); -}; +/*************************************************************************/ -Ref<Texture2D> BitmapFont::get_texture(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, textures.size(), Ref<Texture2D>()); - return textures[p_idx]; -}; +void Font::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_data", "data"), &Font::add_data); + ClassDB::bind_method(D_METHOD("set_data", "idx", "data"), &Font::set_data); + ClassDB::bind_method(D_METHOD("get_data_count"), &Font::get_data_count); + ClassDB::bind_method(D_METHOD("get_data", "idx"), &Font::get_data); + ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data); -int BitmapFont::get_character_count() const { - return char_map.size(); -}; + ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(-1)); -Vector<char32_t> BitmapFont::get_char_keys() const { - Vector<char32_t> chars; - chars.resize(char_map.size()); - const char32_t *ct = nullptr; - int count = 0; - while ((ct = char_map.next(ct))) { - chars.write[count++] = *ct; - }; + ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(-1)); - return chars; -}; + ClassDB::bind_method(D_METHOD("get_spacing", "type"), &Font::get_spacing); + ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &Font::set_spacing); -BitmapFont::Character BitmapFont::get_character(char32_t p_char) const { - if (!char_map.has(p_char)) { - ERR_FAIL_V(Character()); - }; + ClassDB::bind_method(D_METHOD("get_string_size", "text", "size"), &Font::get_string_size, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND)); - return char_map[p_char]; -}; + ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); -void BitmapFont::add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { - if (p_advance < 0) { - p_advance = p_rect.size.width; - } + ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char); + ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars); + + ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0))); + + ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes); - Character c; - c.rect = p_rect; - c.texture_idx = p_texture_idx; - c.v_align = p_align.y; - c.advance = p_advance; - c.h_align = p_align.x; + ADD_GROUP("Extra Spacing", "extra_spacing"); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_top"), "set_spacing", "get_spacing", SPACING_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_bottom"), "set_spacing", "get_spacing", SPACING_BOTTOM); - char_map[p_char] = c; + BIND_ENUM_CONSTANT(SPACING_TOP); + BIND_ENUM_CONSTANT(SPACING_BOTTOM); } -void BitmapFont::add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { - KerningPairKey kpk; - kpk.A = p_A; - kpk.B = p_B; +void Font::_data_changed() { + cache.clear(); + cache_wrap.clear(); - if (p_kerning == 0 && kerning_map.has(kpk)) { - kerning_map.erase(kpk); - } else { - kerning_map[kpk] = p_kerning; + emit_changed(); + _change_notify(); +} + +bool Font::_set(const StringName &p_name, const Variant &p_value) { + String str = p_name; +#ifndef DISABLE_DEPRECATED + if (str == "font_data") { // Compatibility, DynamicFont main data + Ref<FontData> fd = p_value; + if (fd.is_valid()) { + add_data(fd); + return true; + } + return false; + } else if (str.begins_with("fallback/")) { // Compatibility, DynamicFont fallback data + Ref<FontData> fd = p_value; + if (fd.is_valid()) { + add_data(fd); + return true; + } + return false; + } else if (str == "fallback") { // Compatibility, BitmapFont fallback + Ref<Font> f = p_value; + if (f.is_valid()) { + for (int i = 0; i < f->get_data_count(); i++) { + add_data(f->get_data(i)); + } + return true; + } + return false; } +#endif /* DISABLE_DEPRECATED */ + if (str.begins_with("data/")) { + int idx = str.get_slicec('/', 1).to_int(); + Ref<FontData> fd = p_value; + + if (fd.is_valid()) { + if (idx == data.size()) { + add_data(fd); + return true; + } else if (idx >= 0 && idx < data.size()) { + set_data(idx, fd); + return true; + } else { + return false; + } + } else if (idx >= 0 && idx < data.size()) { + remove_data(idx); + return true; + } + } + + return false; } -Vector<BitmapFont::KerningPairKey> BitmapFont::get_kerning_pair_keys() const { - Vector<BitmapFont::KerningPairKey> ret; - ret.resize(kerning_map.size()); - int i = 0; +bool Font::_get(const StringName &p_name, Variant &r_ret) const { + String str = p_name; + if (str.begins_with("data/")) { + int idx = str.get_slicec('/', 1).to_int(); + + if (idx == data.size()) { + r_ret = Ref<FontData>(); + return true; + } else if (idx >= 0 && idx < data.size()) { + r_ret = get_data(idx); + return true; + } + } - for (Map<KerningPairKey, int>::Element *E = kerning_map.front(); E; E = E->next()) { - ret.write[i++] = E->key(); + return false; +} + +void Font::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < data.size(); i++) { + p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "FontData")); } - return ret; + p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(data.size()), PROPERTY_HINT_RESOURCE_TYPE, "FontData")); } -int BitmapFont::get_kerning_pair(char32_t p_A, char32_t p_B) const { - KerningPairKey kpk; - kpk.A = p_A; - kpk.B = p_B; +void Font::add_data(const Ref<FontData> &p_data) { + ERR_FAIL_COND(p_data.is_null()); + data.push_back(p_data); - const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk); - if (E) { - return E->get(); + if (data[data.size() - 1].is_valid()) { + data.write[data.size() - 1]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); } - return 0; + cache.clear(); + cache_wrap.clear(); + + emit_changed(); + _change_notify(); } -void BitmapFont::set_distance_field_hint(bool p_distance_field) { - distance_field_hint = p_distance_field; +void Font::set_data(int p_idx, const Ref<FontData> &p_data) { + ERR_FAIL_COND(p_data.is_null()); + ERR_FAIL_INDEX(p_idx, data.size()); + + if (data[p_idx].is_valid()) { + data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed)); + } + + data.write[p_idx] = p_data; + + if (data[p_idx].is_valid()) { + data.write[p_idx]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); + } + + cache.clear(); + cache_wrap.clear(); + emit_changed(); + _change_notify(); } -bool BitmapFont::is_distance_field_hint() const { - return distance_field_hint; +int Font::get_data_count() const { + return data.size(); } -void BitmapFont::clear() { - height = 1; - ascent = 0; - char_map.clear(); - textures.clear(); - kerning_map.clear(); - distance_field_hint = false; +Ref<FontData> Font::get_data(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, data.size(), Ref<FontData>()); + return data[p_idx]; } -Size2 Font::get_string_size(const String &p_string) const { - float w = 0; +void Font::remove_data(int p_idx) { + ERR_FAIL_INDEX(p_idx, data.size()); + + if (data[p_idx].is_valid()) { + data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed)); + } + + data.remove(p_idx); + + cache.clear(); + cache_wrap.clear(); + + emit_changed(); + _change_notify(); +} + +Dictionary Font::get_feature_list() const { + Dictionary out; + for (int i = 0; i < data.size(); i++) { + Dictionary data_ftrs = data[i]->get_feature_list(); + for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) { + out[*ftr] = data_ftrs[*ftr]; + } + } + return out; +} - int l = p_string.length(); - if (l == 0) { - return Size2(0, get_height()); +float Font::get_height(int p_size) const { + float ret = 0.f; + for (int i = 0; i < data.size(); i++) { + ret = MAX(ret, data[i]->get_height(p_size)); } - const char32_t *sptr = &p_string[0]; + return ret + spacing_top + spacing_bottom; +} - for (int i = 0; i < l; i++) { - w += get_char_size(sptr[i], sptr[i + 1]).width; +float Font::get_ascent(int p_size) const { + float ret = 0.f; + for (int i = 0; i < data.size(); i++) { + ret = MAX(ret, data[i]->get_ascent(p_size)); } + return ret + spacing_top; +} - return Size2(w, get_height()); +float Font::get_descent(int p_size) const { + float ret = 0.f; + for (int i = 0; i < data.size(); i++) { + ret = MAX(ret, data[i]->get_descent(p_size)); + } + return ret + spacing_bottom; } -Size2 Font::get_wordwrap_string_size(const String &p_string, float p_width) const { - ERR_FAIL_COND_V(p_width <= 0, Vector2(0, get_height())); +float Font::get_underline_position(int p_size) const { + float ret = 0.f; + for (int i = 0; i < data.size(); i++) { + ret = MAX(ret, data[i]->get_underline_position(p_size)); + } + return ret; +} - int l = p_string.length(); - if (l == 0) { - return Size2(p_width, get_height()); +float Font::get_underline_thickness(int p_size) const { + float ret = 0.f; + for (int i = 0; i < data.size(); i++) { + ret = MAX(ret, data[i]->get_underline_thickness(p_size)); } + return ret; +} - float line_w = 0; - float h = 0; - float space_w = get_char_size(' ').width; - Vector<String> lines = p_string.split("\n"); - for (int i = 0; i < lines.size(); i++) { - h += get_height(); - String t = lines[i]; - line_w = 0; - Vector<String> words = t.split(" "); - for (int j = 0; j < words.size(); j++) { - line_w += get_string_size(words[j]).x; - if (line_w > p_width) { - h += get_height(); - line_w = get_string_size(words[j]).x; - } else { - line_w += space_w; - } - } +int Font::get_spacing(int p_type) const { + if (p_type == SPACING_TOP) { + return spacing_top; + } else if (p_type == SPACING_BOTTOM) { + return spacing_bottom; } - return Size2(p_width, h); + return 0; } -void BitmapFont::set_fallback(const Ref<BitmapFont> &p_fallback) { - for (Ref<BitmapFont> fallback_child = p_fallback; fallback_child != nullptr; fallback_child = fallback_child->get_fallback()) { - ERR_FAIL_COND_MSG(fallback_child == this, "Can't set as fallback one of its parents to prevent crashes due to recursive loop."); +void Font::set_spacing(int p_type, int p_value) { + if (p_type == SPACING_TOP) { + spacing_top = p_value; + } else if (p_type == SPACING_BOTTOM) { + spacing_bottom = p_value; } - fallback = p_fallback; + emit_changed(); + _change_notify(); } -Ref<BitmapFont> BitmapFont::get_fallback() const { - return fallback; +// Drawing string and string sizes, cached. + +Size2 Font::get_string_size(const String &p_text, int p_size) const { + ERR_FAIL_COND_V(data.is_empty(), Size2()); + + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_size, hash); + + Ref<TextLine> buffer; + if (cache.has(hash)) { + buffer = cache.get(hash); + } else { + buffer.instance(); + int size = p_size <= 0 ? data[0]->get_base_size() : p_size; + buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + cache.insert(hash, buffer); + } + if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { + return buffer->get_size() + Vector2(0, spacing_top + spacing_bottom); + } else { + return buffer->get_size() + Vector2(spacing_top + spacing_bottom, 0); + } } -float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, bool p_outline) const { - const Character *c = char_map.getptr(p_char); +Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p_size, uint8_t p_flags) const { + ERR_FAIL_COND_V(data.is_empty(), Size2()); + + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_size, hash); - if (!c) { - if (fallback.is_valid()) { - return fallback->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, p_outline); + uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); + wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); + + Ref<TextParagraph> lines_buffer; + if (cache_wrap.has(wrp_hash)) { + lines_buffer = cache_wrap.get(wrp_hash); + } else { + lines_buffer.instance(); + int size = p_size <= 0 ? data[0]->get_base_size() : p_size; + lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + lines_buffer->set_width(p_width); + lines_buffer->set_flags(p_flags); + cache_wrap.insert(wrp_hash, lines_buffer); + } + + Size2 ret; + for (int i = 0; i < lines_buffer->get_line_count(); i++) { + Size2 line_size = lines_buffer->get_line_size(i); + if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { + ret.x = MAX(ret.x, line_size.x); + ret.y += line_size.y + spacing_top + spacing_bottom; + } else { + ret.y = MAX(ret.y, line_size.y); + ret.x += line_size.x + spacing_top + spacing_bottom; } - return 0; + } + return ret; +} + +void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_size, hash); + + Ref<TextLine> buffer; + if (cache.has(hash)) { + buffer = cache.get(hash); + } else { + buffer.instance(); + int size = p_size <= 0 ? data[0]->get_base_size() : p_size; + buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + cache.insert(hash, buffer); } - ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0); - if (!p_outline && c->texture_idx != -1) { - Point2 cpos = p_pos; - cpos.x += c->h_align; - cpos.y -= ascent; - cpos.y += c->v_align; - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx]->get_rid(), c->rect, p_modulate, false, false); + Vector2 ofs = p_pos; + if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y += spacing_top - buffer->get_line_ascent(); + } else { + ofs.x += spacing_top - buffer->get_line_ascent(); } - return get_char_size(p_char, p_next).width; + buffer->set_width(p_width); + buffer->set_align(p_align); + + if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { + buffer->draw_outline(p_canvas_item, ofs, p_outline_size, p_outline_modulate); + } + buffer->draw(p_canvas_item, ofs, p_modulate); } -Size2 BitmapFont::get_char_size(char32_t p_char, char32_t p_next) const { - const Character *c = char_map.getptr(p_char); +void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_size, hash); - if (!c) { - if (fallback.is_valid()) { - return fallback->get_char_size(p_char, p_next); - } - return Size2(); + uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); + wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); + + Ref<TextParagraph> lines_buffer; + if (cache_wrap.has(wrp_hash)) { + lines_buffer = cache_wrap.get(wrp_hash); + } else { + lines_buffer.instance(); + int size = p_size <= 0 ? data[0]->get_base_size() : p_size; + lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + lines_buffer->set_width(p_width); + lines_buffer->set_flags(p_flags); + cache_wrap.insert(wrp_hash, lines_buffer); } - Size2 ret(c->advance, c->rect.size.y); + lines_buffer->set_align(p_align); + + Vector2 lofs = p_pos; + for (int i = 0; i < lines_buffer->get_line_count(); i++) { + if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { + lofs.y += spacing_top; + if (i == 0) { + lofs.y -= lines_buffer->get_line_ascent(0); + } + } else { + lofs.x += spacing_top; + if (i == 0) { + lofs.x -= lines_buffer->get_line_ascent(0); + } + } + if (p_width > 0) { + lines_buffer->set_align(p_align); + } + + if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { + lines_buffer->draw_line_outline(p_canvas_item, lofs, i, p_outline_size, p_outline_modulate); + } + lines_buffer->draw_line(p_canvas_item, lofs, i, p_modulate); - if (p_next) { - KerningPairKey kpk; - kpk.A = p_char; - kpk.B = p_next; + Size2 line_size = lines_buffer->get_line_size(i); + if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { + lofs.y += line_size.y + spacing_bottom; + } else { + lofs.x += line_size.x + spacing_bottom; + } - const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk); - if (E) { - ret.width -= E->get(); + if ((p_max_lines > 0) && (i >= p_max_lines)) { + return; } } +} - return ret; +bool Font::has_char(char32_t p_char) const { + for (int i = 0; i < data.size(); i++) { + if (data[i]->has_char(p_char)) { + return true; + } + } + return false; } -void BitmapFont::_bind_methods() { - ClassDB::bind_method(D_METHOD("create_from_fnt", "path"), &BitmapFont::create_from_fnt); - ClassDB::bind_method(D_METHOD("set_height", "px"), &BitmapFont::set_height); +String Font::get_supported_chars() const { + String chars; + for (int i = 0; i < data.size(); i++) { + String data_chars = data[i]->get_supported_chars(); + for (int j = 0; j < data_chars.length(); j++) { + if (chars.find_char(data_chars[j]) == -1) { + chars += data_chars[j]; + } + } + } + return chars; +} - ClassDB::bind_method(D_METHOD("set_ascent", "px"), &BitmapFont::set_ascent); +Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const { + for (int i = 0; i < data.size(); i++) { + if (data[i]->has_char(p_char)) { + int size = p_size <= 0 ? data[i]->get_base_size() : p_size; + uint32_t glyph_a = data[i]->get_glyph_index(p_char); + Size2 ret = Size2(data[i]->get_glyph_advance(glyph_a, size).x, data[i]->get_height(size)); + if ((p_next != 0) && data[i]->has_char(p_next)) { + uint32_t glyph_b = data[i]->get_glyph_index(p_next); + ret.x -= data[i]->get_glyph_kerning(glyph_a, glyph_b, size).x; + } + return ret; + } + } + return Size2(); +} - ClassDB::bind_method(D_METHOD("add_kerning_pair", "char_a", "char_b", "kerning"), &BitmapFont::add_kerning_pair); - ClassDB::bind_method(D_METHOD("get_kerning_pair", "char_a", "char_b"), &BitmapFont::get_kerning_pair); +float Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const { + for (int i = 0; i < data.size(); i++) { + if (data[i]->has_char(p_char)) { + int size = p_size <= 0 ? data[i]->get_base_size() : p_size; + uint32_t glyph_a = data[i]->get_glyph_index(p_char); + float ret = data[i]->get_glyph_advance(glyph_a, size).x; + if ((p_next != 0) && data[i]->has_char(p_next)) { + uint32_t glyph_b = data[i]->get_glyph_index(p_next); + ret -= data[i]->get_glyph_kerning(glyph_a, glyph_b, size).x; + } + if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { + data[i]->draw_glyph_outline(p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate); + } + data[i]->draw_glyph(p_canvas_item, size, p_pos, glyph_a, p_modulate); + return ret; + } + } + return 0; +} - ClassDB::bind_method(D_METHOD("add_texture", "texture"), &BitmapFont::add_texture); - ClassDB::bind_method(D_METHOD("add_char", "character", "texture", "rect", "align", "advance"), &BitmapFont::add_char, DEFVAL(Point2()), DEFVAL(-1)); +Vector<RID> Font::get_rids() const { + Vector<RID> ret; + for (int i = 0; i < data.size(); i++) { + RID rid = data[i]->get_rid(); + if (rid != RID()) { + ret.push_back(rid); + } + } + return ret; +} - ClassDB::bind_method(D_METHOD("get_texture_count"), &BitmapFont::get_texture_count); - ClassDB::bind_method(D_METHOD("get_texture", "idx"), &BitmapFont::get_texture); +void Font::update_changes() { + emit_changed(); +} - ClassDB::bind_method(D_METHOD("set_distance_field_hint", "enable"), &BitmapFont::set_distance_field_hint); +Font::Font() { + cache.set_capacity(128); + cache_wrap.set_capacity(32); +} - ClassDB::bind_method(D_METHOD("clear"), &BitmapFont::clear); +Font::~Font() { + cache.clear(); + cache_wrap.clear(); +} - ClassDB::bind_method(D_METHOD("_set_chars"), &BitmapFont::_set_chars); - ClassDB::bind_method(D_METHOD("_get_chars"), &BitmapFont::_get_chars); +/*************************************************************************/ - ClassDB::bind_method(D_METHOD("_set_kernings"), &BitmapFont::_set_kernings); - ClassDB::bind_method(D_METHOD("_get_kernings"), &BitmapFont::_get_kernings); +RES ResourceFormatLoaderFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { + if (r_error) { + *r_error = ERR_FILE_CANT_OPEN; + } - ClassDB::bind_method(D_METHOD("_set_textures"), &BitmapFont::_set_textures); - ClassDB::bind_method(D_METHOD("_get_textures"), &BitmapFont::_get_textures); + Ref<FontData> dfont; + dfont.instance(); + dfont->load_resource(p_path); - ClassDB::bind_method(D_METHOD("set_fallback", "fallback"), &BitmapFont::set_fallback); - ClassDB::bind_method(D_METHOD("get_fallback"), &BitmapFont::get_fallback); + if (r_error) { + *r_error = OK; + } + + return dfont; +} - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_textures", "_get_textures"); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "chars", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_chars", "_get_chars"); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "kernings", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_kernings", "_get_kernings"); +void ResourceFormatLoaderFont::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { +#ifndef DISABLE_DEPRECATED + if (p_type == "DynamicFontData") { + p_extensions->push_back("ttf"); + p_extensions->push_back("otf"); + p_extensions->push_back("woff"); + return; + } + if (p_type == "BitmapFont") { // BitmapFont (*.font, *fnt) is handled by ResourceFormatLoaderCompatFont + return; + } +#endif /* DISABLE_DEPRECATED */ + if (p_type == "" || handles_type(p_type)) { + get_recognized_extensions(p_extensions); + } +} - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "1,1024,1"), "set_height", "get_height"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ascent", PROPERTY_HINT_RANGE, "0,1024,1"), "set_ascent", "get_ascent"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_field"), "set_distance_field_hint", "is_distance_field_hint"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback", PROPERTY_HINT_RESOURCE_TYPE, "BitmapFont"), "set_fallback", "get_fallback"); +void ResourceFormatLoaderFont::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("ttf"); + p_extensions->push_back("otf"); + p_extensions->push_back("woff"); + p_extensions->push_back("font"); + p_extensions->push_back("fnt"); } -BitmapFont::BitmapFont() { - clear(); +bool ResourceFormatLoaderFont::handles_type(const String &p_type) const { + return (p_type == "FontData"); } -BitmapFont::~BitmapFont() { - clear(); +String ResourceFormatLoaderFont::get_resource_type(const String &p_path) const { + String el = p_path.get_extension().to_lower(); + if (el == "ttf" || el == "otf" || el == "woff" || el == "font" || el == "fnt") { + return "FontData"; + } + return ""; } -//////////// +#ifndef DISABLE_DEPRECATED -RES ResourceFormatLoaderBMFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { +RES ResourceFormatLoaderCompatFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { if (r_error) { *r_error = ERR_FILE_CANT_OPEN; } - Ref<BitmapFont> font; - font.instance(); + Ref<FontData> dfont; + dfont.instance(); + dfont->load_resource(p_path); - Error err = font->create_from_fnt(p_path); + Ref<Font> font; + font.instance(); + font->add_data(dfont); - if (err) { - if (r_error) { - *r_error = err; - } - return RES(); + if (r_error) { + *r_error = OK; } return font; } -void ResourceFormatLoaderBMFont::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("fnt"); +void ResourceFormatLoaderCompatFont::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { + if (p_type == "BitmapFont") { + p_extensions->push_back("font"); + p_extensions->push_back("fnt"); + } } -bool ResourceFormatLoaderBMFont::handles_type(const String &p_type) const { - return (p_type == "BitmapFont"); +void ResourceFormatLoaderCompatFont::get_recognized_extensions(List<String> *p_extensions) const { } -String ResourceFormatLoaderBMFont::get_resource_type(const String &p_path) const { - String el = p_path.get_extension().to_lower(); - if (el == "fnt") { - return "BitmapFont"; - } +bool ResourceFormatLoaderCompatFont::handles_type(const String &p_type) const { + return (p_type == "Font"); +} + +String ResourceFormatLoaderCompatFont::get_resource_type(const String &p_path) const { return ""; } + +#endif /* DISABLE_DEPRECATED */ diff --git a/scene/resources/font.h b/scene/resources/font.h index d8c350bb41..086226c082 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,178 +32,193 @@ #define FONT_H #include "core/io/resource.h" +#include "core/templates/lru.h" #include "core/templates/map.h" #include "scene/resources/texture.h" +#include "servers/text_server.h" -class Font : public Resource { - GDCLASS(Font, Resource); +/*************************************************************************/ + +class FontData : public Resource { + GDCLASS(FontData, Resource); + + RID rid; + int base_size = 16; + String path; protected: static void _bind_methods(); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + public: - virtual float get_height() const = 0; + virtual RID get_rid() const override; - virtual float get_ascent() const = 0; - virtual float get_descent() const = 0; - virtual float get_underline_position() const = 0; - virtual float get_underline_thickness() const = 0; + void load_resource(const String &p_filename, int p_base_size = 16); + void load_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16); + void _load_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size = 16); - virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const = 0; - Size2 get_string_size(const String &p_string) const; - Size2 get_wordwrap_string_size(const String &p_string, float p_width) const; + void set_data_path(const String &p_path); + String get_data_path() const; - virtual bool is_distance_field_hint() const = 0; + float get_height(int p_size) const; + float get_ascent(int p_size) const; + float get_descent(int p_size) const; - void draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1, const Color &p_outline_modulate = Color(1, 1, 1)) const; - void draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate = Color(1, 1, 1), const Color &p_outline_modulate = Color(1, 1, 1)) const; + Dictionary get_feature_list() const; + Dictionary get_variation_list() const; - virtual bool has_outline() const { return false; } - virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const = 0; + void set_variation(const String &p_name, double p_value); + double get_variation(const String &p_name) const; - void update_changes(); - Font(); -}; + float get_underline_position(int p_size) const; + float get_underline_thickness(int p_size) const; -// Helper class to that draws outlines immediately and draws characters in its destructor. -class FontDrawer { - const Ref<Font> &font; - Color outline_color; - bool has_outline; - - struct PendingDraw { - RID canvas_item; - Point2 pos; - char32_t chr; - char32_t next; - Color modulate; - }; + void set_antialiased(bool p_antialiased); + bool get_antialiased() const; - Vector<PendingDraw> pending_draws; + void set_distance_field_hint(bool p_distance_field); + bool get_distance_field_hint() const; -public: - FontDrawer(const Ref<Font> &p_font, const Color &p_outline_color) : - font(p_font), - outline_color(p_outline_color) { - has_outline = p_font->has_outline(); - } - - float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1)) { - if (has_outline) { - PendingDraw draw = { p_canvas_item, p_pos, p_char, p_next, p_modulate }; - pending_draws.push_back(draw); - } - return font->draw_char(p_canvas_item, p_pos, p_char, p_next, has_outline ? outline_color : p_modulate, has_outline); - } - - ~FontDrawer() { - for (int i = 0; i < pending_draws.size(); ++i) { - const PendingDraw &draw = pending_draws[i]; - font->draw_char(draw.canvas_item, draw.pos, draw.chr, draw.next, draw.modulate, false); - } - } -}; + void set_force_autohinter(bool p_enabeld); + bool get_force_autohinter() const; -class BitmapFont : public Font { - GDCLASS(BitmapFont, Font); - RES_BASE_EXTENSION("font"); + void set_hinting(TextServer::Hinting p_hinting); + TextServer::Hinting get_hinting() const; - Vector<Ref<Texture2D>> textures; + bool has_char(char32_t p_char) const; + String get_supported_chars() const; -public: - struct Character { - int texture_idx; - Rect2 rect; - float v_align; - float h_align; - float advance; - - Character() { - texture_idx = 0; - v_align = 0; - } - }; + Vector2 get_glyph_advance(uint32_t p_index, int p_size) const; + Vector2 get_glyph_kerning(uint32_t p_index_a, uint32_t p_index_b, int p_size) const; + + bool has_outline() const; + float get_base_size() const; + + bool is_language_supported(const String &p_language) const; + void set_language_support_override(const String &p_language, bool p_supported); + bool get_language_support_override(const String &p_language) const; + void remove_language_support_override(const String &p_language); + Vector<String> get_language_support_overrides() const; + + bool is_script_supported(const String &p_script) const; + void set_script_support_override(const String &p_script, bool p_supported); + bool get_script_support_override(const String &p_script) const; + void remove_script_support_override(const String &p_script); + Vector<String> get_script_support_overrides() const; - struct KerningPairKey { - union { - struct { - uint32_t A, B; - }; + uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector = 0x0000) const; - uint64_t pair; - }; + Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const; + Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const; + + FontData(); + FontData(const String &p_filename, int p_base_size); + FontData(const PackedByteArray &p_data, const String &p_type, int p_base_size); + + ~FontData(); +}; + +/*************************************************************************/ - _FORCE_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; } +class TextLine; +class TextParagraph; + +class Font : public Resource { + GDCLASS(Font, Resource); + +public: + enum SpacingType { + SPACING_TOP, + SPACING_BOTTOM }; private: - HashMap<char32_t, Character> char_map; - Map<KerningPairKey, int> kerning_map; - - float height; - float ascent; - bool distance_field_hint; + int spacing_top = 0; + int spacing_bottom = 0; - void _set_chars(const Vector<int> &p_chars); - Vector<int> _get_chars() const; - void _set_kernings(const Vector<int> &p_kernings); - Vector<int> _get_kernings() const; - void _set_textures(const Vector<Variant> &p_textures); - Vector<Variant> _get_textures() const; + mutable LRUCache<uint64_t, Ref<TextLine>> cache; + mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap; - Ref<BitmapFont> fallback; + Vector<Ref<FontData>> data; protected: static void _bind_methods(); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + + void _data_changed(); + public: - Error create_from_fnt(const String &p_file); + Dictionary get_feature_list() const; - void set_height(float p_height); - float get_height() const override; + // Font data control. + void add_data(const Ref<FontData> &p_data); + void set_data(int p_idx, const Ref<FontData> &p_data); + int get_data_count() const; + Ref<FontData> get_data(int p_idx) const; + void remove_data(int p_idx); - void set_ascent(float p_ascent); - float get_ascent() const override; - float get_descent() const override; - float get_underline_position() const override; - float get_underline_thickness() const override; + float get_height(int p_size = -1) const; + float get_ascent(int p_size = -1) const; + float get_descent(int p_size = -1) const; - void add_texture(const Ref<Texture2D> &p_texture); - void add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance = -1); + float get_underline_position(int p_size = -1) const; + float get_underline_thickness(int p_size = -1) const; - int get_character_count() const; - Vector<char32_t> get_char_keys() const; - Character get_character(char32_t p_char) const; + int get_spacing(int p_type) const; + void set_spacing(int p_type, int p_value); - int get_texture_count() const; - Ref<Texture2D> get_texture(int p_idx) const; + // Drawing string. + Size2 get_string_size(const String &p_text, int p_size = -1) const; + Size2 get_multiline_string_size(const String &p_text, float p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const; - void add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning); - int get_kerning_pair(char32_t p_A, char32_t p_B) const; - Vector<KerningPairKey> get_kerning_pair_keys() const; + void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const override; + // Helper functions. + bool has_char(char32_t p_char) const; + String get_supported_chars() const; - void set_fallback(const Ref<BitmapFont> &p_fallback); - Ref<BitmapFont> get_fallback() const; + Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const; + float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; - void clear(); + Vector<RID> get_rids() const; - void set_distance_field_hint(bool p_distance_field); - bool is_distance_field_hint() const override; + void update_changes(); + + Font(); + ~Font(); +}; + +VARIANT_ENUM_CAST(Font::SpacingType); - float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const override; +/*************************************************************************/ - BitmapFont(); - ~BitmapFont(); +class ResourceFormatLoaderFont : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false); + virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; }; -class ResourceFormatLoaderBMFont : public ResourceFormatLoader { +#ifndef DISABLE_DEPRECATED + +class ResourceFormatLoaderCompatFont : public ResourceFormatLoader { public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false); + virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; }; -#endif +#endif /* DISABLE_DEPRECATED */ + +#endif /* FONT_H */ diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp index 6b41b97e45..b87235518f 100644 --- a/scene/resources/gradient.cpp +++ b/scene/resources/gradient.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -47,7 +47,7 @@ Gradient::~Gradient() { void Gradient::_bind_methods() { ClassDB::bind_method(D_METHOD("add_point", "offset", "color"), &Gradient::add_point); - ClassDB::bind_method(D_METHOD("remove_point", "offset"), &Gradient::remove_point); + ClassDB::bind_method(D_METHOD("remove_point", "point"), &Gradient::remove_point); ClassDB::bind_method(D_METHOD("set_offset", "point", "offset"), &Gradient::set_offset); ClassDB::bind_method(D_METHOD("get_offset", "point"), &Gradient::get_offset); diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h index e839909770..344a3fd283 100644 --- a/scene/resources/gradient.h +++ b/scene/resources/gradient.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -82,7 +82,7 @@ public: Vector<Color> get_colors() const; _FORCE_INLINE_ Color get_color_at_offset(float p_offset) { - if (points.empty()) { + if (points.is_empty()) { return Color(0, 0, 0, 1); } diff --git a/scene/resources/height_map_shape_3d.cpp b/scene/resources/height_map_shape_3d.cpp index 2ae47bcf3c..a32cc1c4df 100644 --- a/scene/resources/height_map_shape_3d.cpp +++ b/scene/resources/height_map_shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/height_map_shape_3d.h b/scene/resources/height_map_shape_3d.h index 9ee8b49689..eda405fc06 100644 --- a/scene/resources/height_map_shape_3d.h +++ b/scene/resources/height_map_shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/line_shape_2d.cpp b/scene/resources/line_shape_2d.cpp index 58653c5f4a..2b08c21c34 100644 --- a/scene/resources/line_shape_2d.cpp +++ b/scene/resources/line_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,7 +36,7 @@ bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { Vector2 point = get_distance() * get_normal(); - Vector2 l[2][2] = { { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }, { point, point + get_normal() * 30 } }; + Vector2 l[2][2] = { { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }, { point, point + get_normal() * 30 } }; for (int i = 0; i < 2; i++) { Vector2 closest = Geometry2D::get_closest_point_to_segment(p_point, l[i]); @@ -77,7 +77,7 @@ real_t LineShape2D::get_distance() const { void LineShape2D::draw(const RID &p_to_rid, const Color &p_color) { Vector2 point = get_distance() * get_normal(); - Vector2 l1[2] = { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }; + Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }; RS::get_singleton()->canvas_item_add_line(p_to_rid, l1[0], l1[1], p_color, 3); Vector2 l2[2] = { point, point + get_normal() * 30 }; RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, 3); @@ -86,7 +86,7 @@ void LineShape2D::draw(const RID &p_to_rid, const Color &p_color) { Rect2 LineShape2D::get_rect() const { Vector2 point = get_distance() * get_normal(); - Vector2 l1[2] = { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }; + Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }; Vector2 l2[2] = { point, point + get_normal() * 30 }; Rect2 rect; rect.position = l1[0]; diff --git a/scene/resources/line_shape_2d.h b/scene/resources/line_shape_2d.h index 7e67a8f67c..043be1eebe 100644 --- a/scene/resources/line_shape_2d.h +++ b/scene/resources/line_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 35c967ce27..445c0d9677 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,6 +36,7 @@ #include "editor/editor_settings.h" #endif +#include "scene/main/scene_tree.h" #include "scene/scene_string_names.h" void Material::set_next_pass(const Ref<Material> &p_pass) { @@ -80,6 +81,14 @@ void Material::_validate_property(PropertyInfo &property) const { } } +void Material::inspect_native_shader_code() { + SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop()); + RID shader = get_shader_rid(); + if (st && shader.is_valid()) { + st->call_group("_native_shader_source_visualizer", "_inspect_shader", shader); + } +} + void Material::_bind_methods() { ClassDB::bind_method(D_METHOD("set_next_pass", "next_pass"), &Material::set_next_pass); ClassDB::bind_method(D_METHOD("get_next_pass"), &Material::get_next_pass); @@ -87,6 +96,9 @@ void Material::_bind_methods() { ClassDB::bind_method(D_METHOD("set_render_priority", "priority"), &Material::set_render_priority); ClassDB::bind_method(D_METHOD("get_render_priority"), &Material::get_render_priority); + ClassDB::bind_method(D_METHOD("inspect_native_shader_code"), &Material::inspect_native_shader_code); + ClassDB::set_method_flags(get_class_static(), _scs_create("inspect_native_shader_code"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); + ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RENDER_PRIORITY_MIN) + "," + itos(RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "next_pass", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_next_pass", "get_next_pass"); @@ -260,6 +272,13 @@ Shader::Mode ShaderMaterial::get_shader_mode() const { return Shader::MODE_SPATIAL; } } +RID ShaderMaterial::get_shader_rid() const { + if (shader.is_valid()) { + return shader->get_rid(); + } else { + return RID(); + } +} ShaderMaterial::ShaderMaterial() { } @@ -969,11 +988,11 @@ void BaseMaterial3D::_update_shader() { if (features[FEATURE_NORMAL_MAPPING]) { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tNORMALMAP = triplanar_texture(texture_normal,uv1_power_normal,uv1_triplanar_pos).rgb;\n"; + code += "\tNORMAL_MAP = triplanar_texture(texture_normal,uv1_power_normal,uv1_triplanar_pos).rgb;\n"; } else { - code += "\tNORMALMAP = texture(texture_normal,base_uv).rgb;\n"; + code += "\tNORMAL_MAP = texture(texture_normal,base_uv).rgb;\n"; } - code += "\tNORMALMAP_DEPTH = normal_scale;\n"; + code += "\tNORMAL_MAP_DEPTH = normal_scale;\n"; } if (features[FEATURE_EMISSION]) { @@ -1000,7 +1019,7 @@ void BaseMaterial3D::_update_shader() { if (features[FEATURE_REFRACTION]) { if (features[FEATURE_NORMAL_MAPPING]) { - code += "\tvec3 ref_normal = normalize( mix(NORMAL,TANGENT * NORMALMAP.x + BINORMAL * NORMALMAP.y + NORMAL * NORMALMAP.z,NORMALMAP_DEPTH) );\n"; + code += "\tvec3 ref_normal = normalize( mix(NORMAL,TANGENT * NORMAL_MAP.x + BINORMAL * NORMAL_MAP.y + NORMAL * NORMAL_MAP.z,NORMAL_MAP_DEPTH) );\n"; } else { code += "\tvec3 ref_normal = NORMAL;\n"; } @@ -1198,8 +1217,8 @@ void BaseMaterial3D::_update_shader() { break; // Internal value, skip. } - code += "\tvec3 detail_norm = mix(NORMALMAP,detail_norm_tex.rgb,detail_tex.a);\n"; - code += "\tNORMALMAP = mix(NORMALMAP,detail_norm,detail_mask_tex.r);\n"; + code += "\tvec3 detail_norm = mix(NORMAL_MAP,detail_norm_tex.rgb,detail_tex.a);\n"; + code += "\tNORMAL_MAP = mix(NORMAL_MAP,detail_norm,detail_mask_tex.r);\n"; code += "\tALBEDO.rgb = mix(ALBEDO.rgb,detail,detail_mask_tex.r);\n"; } @@ -2417,10 +2436,10 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Height", "heightmap_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "heightmap_enabled"), "set_feature", "get_feature", FEATURE_HEIGHT_MAPPING); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "heightmap_scale", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_heightmap_scale", "get_heightmap_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "heightmap_scale", PROPERTY_HINT_RANGE, "-16,16,0.001"), "set_heightmap_scale", "get_heightmap_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "heightmap_deep_parallax"), "set_heightmap_deep_parallax", "is_heightmap_deep_parallax_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "heightmap_min_layers", PROPERTY_HINT_RANGE, "1,32,1"), "set_heightmap_deep_parallax_min_layers", "get_heightmap_deep_parallax_min_layers"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "heightmap_max_layers", PROPERTY_HINT_RANGE, "1,32,1"), "set_heightmap_deep_parallax_max_layers", "get_heightmap_deep_parallax_max_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "heightmap_min_layers", PROPERTY_HINT_RANGE, "1,64,1"), "set_heightmap_deep_parallax_min_layers", "get_heightmap_deep_parallax_min_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "heightmap_max_layers", PROPERTY_HINT_RANGE, "1,64,1"), "set_heightmap_deep_parallax_max_layers", "get_heightmap_deep_parallax_max_layers"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "heightmap_flip_tangent"), "set_heightmap_deep_parallax_flip_tangent", "get_heightmap_deep_parallax_flip_tangent"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "heightmap_flip_binormal"), "set_heightmap_deep_parallax_flip_binormal", "get_heightmap_deep_parallax_flip_binormal"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "heightmap_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_HEIGHTMAP); diff --git a/scene/resources/material.h b/scene/resources/material.h index caf28eea18..0048b43c8b 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -47,6 +47,8 @@ class Material : public Resource { Ref<Material> next_pass; int render_priority; + void inspect_native_shader_code(); + protected: _FORCE_INLINE_ RID _get_material() const { return material; } static void _bind_methods(); @@ -66,6 +68,7 @@ public: int get_render_priority() const; virtual RID get_rid() const override; + virtual RID get_shader_rid() const = 0; virtual Shader::Mode get_shader_mode() const = 0; Material(); @@ -100,6 +103,8 @@ public: virtual Shader::Mode get_shader_mode() const override; + virtual RID get_shader_rid() const override; + ShaderMaterial(); ~ShaderMaterial(); }; @@ -736,7 +741,7 @@ public: static RID get_material_rid_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false); - RID get_shader_rid() const; + virtual RID get_shader_rid() const override; virtual Shader::Mode get_shader_mode() const override; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 97c0c7a81d..32fede1e5f 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -74,7 +74,7 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { } Array a = surface_get_arrays(i); - ERR_FAIL_COND_V(a.empty(), Ref<TriangleMesh>()); + ERR_FAIL_COND_V(a.is_empty(), Ref<TriangleMesh>()); int vc = surface_get_array_len(i); Vector<Vector3> vertices = a[ARRAY_VERTEX]; @@ -157,7 +157,7 @@ void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) { bool Mesh::surface_is_softbody_friendly(int p_idx) const { const uint32_t surface_format = surface_get_format(p_idx); - return (surface_format & Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE && (!(surface_format & Mesh::ARRAY_COMPRESS_NORMAL))); + return (surface_format & Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE); } Vector<Face3> Mesh::get_faces() const { @@ -226,7 +226,7 @@ Ref<Shape3D> Mesh::create_convex_shape() const { for (int i = 0; i < get_surface_count(); i++) { Array a = surface_get_arrays(i); - ERR_FAIL_COND_V(a.empty(), Ref<ConvexPolygonShape3D>()); + ERR_FAIL_COND_V(a.is_empty(), Ref<ConvexPolygonShape3D>()); Vector<Vector3> v = a[ARRAY_VERTEX]; vertices.append_array(v); } @@ -266,7 +266,7 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { } Array a = surface_get_arrays(i); - ERR_FAIL_COND_V(a.empty(), Ref<ArrayMesh>()); + ERR_FAIL_COND_V(a.is_empty(), Ref<ArrayMesh>()); if (i == 0) { arrays = a; @@ -480,8 +480,30 @@ void Mesh::_bind_methods() { BIND_ENUM_CONSTANT(PRIMITIVE_TRIANGLES); BIND_ENUM_CONSTANT(PRIMITIVE_TRIANGLE_STRIP); - BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED); - BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE); + BIND_ENUM_CONSTANT(ARRAY_VERTEX); + BIND_ENUM_CONSTANT(ARRAY_NORMAL); + BIND_ENUM_CONSTANT(ARRAY_TANGENT); + BIND_ENUM_CONSTANT(ARRAY_COLOR); + BIND_ENUM_CONSTANT(ARRAY_TEX_UV); + BIND_ENUM_CONSTANT(ARRAY_TEX_UV2); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM0); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM1); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM2); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM3); + BIND_ENUM_CONSTANT(ARRAY_BONES); + BIND_ENUM_CONSTANT(ARRAY_WEIGHTS); + BIND_ENUM_CONSTANT(ARRAY_INDEX); + BIND_ENUM_CONSTANT(ARRAY_MAX); + + BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGBA8_UNORM); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGBA8_SNORM); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RG_HALF); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGBA_HALF); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM_R_FLOAT); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RG_FLOAT); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGB_FLOAT); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM_RGBA_FLOAT); + BIND_ENUM_CONSTANT(ARRAY_CUSTOM_MAX); BIND_ENUM_CONSTANT(ARRAY_FORMAT_VERTEX); BIND_ENUM_CONSTANT(ARRAY_FORMAT_NORMAL); @@ -489,31 +511,31 @@ void Mesh::_bind_methods() { BIND_ENUM_CONSTANT(ARRAY_FORMAT_COLOR); BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV); BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV2); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM3); BIND_ENUM_CONSTANT(ARRAY_FORMAT_BONES); BIND_ENUM_CONSTANT(ARRAY_FORMAT_WEIGHTS); BIND_ENUM_CONSTANT(ARRAY_FORMAT_INDEX); - BIND_ENUM_CONSTANT(ARRAY_COMPRESS_NORMAL); - BIND_ENUM_CONSTANT(ARRAY_COMPRESS_TANGENT); - BIND_ENUM_CONSTANT(ARRAY_COMPRESS_COLOR); - BIND_ENUM_CONSTANT(ARRAY_COMPRESS_TEX_UV); - BIND_ENUM_CONSTANT(ARRAY_COMPRESS_TEX_UV2); - BIND_ENUM_CONSTANT(ARRAY_COMPRESS_INDEX); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_BLEND_SHAPE_MASK); - BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_2D_VERTICES); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_BASE); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0_SHIFT); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1_SHIFT); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2_SHIFT); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM3_SHIFT); - BIND_ENUM_CONSTANT(ARRAY_COMPRESS_DEFAULT); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_MASK); + BIND_ENUM_CONSTANT(ARRAY_COMPRESS_FLAGS_BASE); - BIND_ENUM_CONSTANT(ARRAY_VERTEX); - BIND_ENUM_CONSTANT(ARRAY_NORMAL); - BIND_ENUM_CONSTANT(ARRAY_TANGENT); - BIND_ENUM_CONSTANT(ARRAY_COLOR); - BIND_ENUM_CONSTANT(ARRAY_TEX_UV); - BIND_ENUM_CONSTANT(ARRAY_TEX_UV2); - BIND_ENUM_CONSTANT(ARRAY_BONES); - BIND_ENUM_CONSTANT(ARRAY_WEIGHTS); - BIND_ENUM_CONSTANT(ARRAY_INDEX); - BIND_ENUM_CONSTANT(ARRAY_MAX); + BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_2D_VERTICES); + BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_DYNAMIC_UPDATE); + BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_8_BONE_WEIGHTS); + + BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED); + BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE); } void Mesh::clear_cache() const { @@ -560,11 +582,50 @@ Vector<Ref<Shape3D>> Mesh::convex_decompose() const { Mesh::Mesh() { } +#if 0 static Vector<uint8_t> _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_format, uint32_t p_elements) { - bool vertex_16bit = p_format & ((1 << (Mesh::ARRAY_VERTEX + Mesh::ARRAY_COMPRESS_BASE))); - bool has_bones = (p_format & Mesh::ARRAY_FORMAT_BONES); - bool bone_8 = has_bones && !(p_format & (Mesh::ARRAY_COMPRESS_INDEX << 2)); - bool weight_32 = has_bones && !(p_format & (Mesh::ARRAY_COMPRESS_TEX_UV2 << 2)); + enum ArrayType { + OLD_ARRAY_VERTEX = 0, + OLD_ARRAY_NORMAL = 1, + OLD_ARRAY_TANGENT = 2, + OLD_ARRAY_COLOR = 3, + OLD_ARRAY_TEX_UV = 4, + OLD_ARRAY_TEX_UV2 = 5, + OLD_ARRAY_BONES = 6, + OLD_ARRAY_WEIGHTS = 7, + OLD_ARRAY_INDEX = 8, + OLD_ARRAY_MAX = 9 + }; + + enum ArrayFormat { + /* OLD_ARRAY FORMAT FLAGS */ + OLD_ARRAY_FORMAT_VERTEX = 1 << OLD_ARRAY_VERTEX, // mandatory + OLD_ARRAY_FORMAT_NORMAL = 1 << OLD_ARRAY_NORMAL, + OLD_ARRAY_FORMAT_TANGENT = 1 << OLD_ARRAY_TANGENT, + OLD_ARRAY_FORMAT_COLOR = 1 << OLD_ARRAY_COLOR, + OLD_ARRAY_FORMAT_TEX_UV = 1 << OLD_ARRAY_TEX_UV, + OLD_ARRAY_FORMAT_TEX_UV2 = 1 << OLD_ARRAY_TEX_UV2, + OLD_ARRAY_FORMAT_BONES = 1 << OLD_ARRAY_BONES, + OLD_ARRAY_FORMAT_WEIGHTS = 1 << OLD_ARRAY_WEIGHTS, + OLD_ARRAY_FORMAT_INDEX = 1 << OLD_ARRAY_INDEX, + + OLD_ARRAY_COMPRESS_BASE = (OLD_ARRAY_INDEX + 1), + OLD_ARRAY_COMPRESS_NORMAL = 1 << (OLD_ARRAY_NORMAL + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_TANGENT = 1 << (OLD_ARRAY_TANGENT + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_COLOR = 1 << (OLD_ARRAY_COLOR + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_TEX_UV = 1 << (OLD_ARRAY_TEX_UV + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_TEX_UV2 = 1 << (OLD_ARRAY_TEX_UV2 + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_INDEX = 1 << (OLD_ARRAY_INDEX + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_DEFAULT = OLD_ARRAY_COMPRESS_NORMAL | OLD_ARRAY_COMPRESS_TANGENT | OLD_ARRAY_COMPRESS_COLOR | OLD_ARRAY_COMPRESS_TEX_UV | OLD_ARRAY_COMPRESS_TEX_UV2, + + OLD_ARRAY_FLAG_USE_2D_VERTICES = OLD_ARRAY_COMPRESS_INDEX << 1, + OLD_ARRAY_FLAG_USE_DYNAMIC_UPDATE = OLD_ARRAY_COMPRESS_INDEX << 3, + }; + + bool vertex_16bit = p_format & ((1 << (OLD_ARRAY_VERTEX + OLD_ARRAY_COMPRESS_BASE))); + bool has_bones = (p_format & OLD_ARRAY_FORMAT_BONES); + bool bone_8 = has_bones && !(p_format & (OLD_ARRAY_COMPRESS_INDEX << 2)); + bool weight_32 = has_bones && !(p_format & (OLD_ARRAY_COMPRESS_TEX_UV2 << 2)); print_line("convert vertex16: " + itos(vertex_16bit) + " convert bone 8 " + itos(bone_8) + " convert weight 32 " + itos(weight_32)); @@ -572,7 +633,7 @@ static Vector<uint8_t> _fix_array_compatibility(const Vector<uint8_t> &p_src, ui return p_src; } - bool vertex_2d = (p_format & (Mesh::ARRAY_COMPRESS_INDEX << 1)); + bool vertex_2d = (p_format & (OLD_ARRAY_COMPRESS_INDEX << 1)); uint32_t src_stride = p_src.size() / p_elements; uint32_t dst_stride = src_stride + (vertex_16bit ? 4 : 0) + (bone_8 ? 4 : 0) - (weight_32 ? 8 : 0); @@ -671,25 +732,11 @@ static Vector<uint8_t> _fix_array_compatibility(const Vector<uint8_t> &p_src, ui return ret; } +#endif bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { String sname = p_name; - if (p_name == "blend_shape/names") { - Vector<String> sk = p_value; - int sz = sk.size(); - const String *r = sk.ptr(); - for (int i = 0; i < sz; i++) { - add_blend_shape(r[i]); - } - return true; - } - - if (p_name == "blend_shape/mode") { - set_blend_shape_mode(BlendShapeMode(int(p_value))); - return true; - } - if (sname.begins_with("surface_")) { int sl = sname.find("/"); if (sl == -1) { @@ -727,6 +774,7 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { add_surface_from_arrays(PrimitiveType(int(d["primitive"])), d["arrays"], d["morph_arrays"]); } else if (d.has("array_data")) { +#if 0 //print_line("array data (old style"); //older format (3.x) Vector<uint8_t> array_data = d["array_data"]; @@ -793,6 +841,7 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { } add_surface(format, PrimitiveType(primitive), array_data, vertex_count, array_index_data, index_count, aabb, blend_shapes, bone_aabb); +#endif } else { ERR_FAIL_V(false); } @@ -811,6 +860,28 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { return false; } +void ArrayMesh::_set_blend_shape_names(const PackedStringArray &p_names) { + ERR_FAIL_COND(surfaces.size() > 0); + + blend_shapes.resize(p_names.size()); + for (int i = 0; i < p_names.size(); i++) { + blend_shapes.write[i] = p_names[i]; + } + + if (mesh.is_valid()) { + RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size()); + } +} + +PackedStringArray ArrayMesh::_get_blend_shape_names() const { + PackedStringArray sarr; + sarr.resize(blend_shapes.size()); + for (int i = 0; i < blend_shapes.size(); i++) { + sarr.write[i] = blend_shapes[i]; + } + return sarr; +} + Array ArrayMesh::_get_surfaces() const { if (mesh.is_null()) { return Array(); @@ -824,6 +895,12 @@ Array ArrayMesh::_get_surfaces() const { data["primitive"] = surface.primitive; data["vertex_data"] = surface.vertex_data; data["vertex_count"] = surface.vertex_count; + if (surface.skin_data.size()) { + data["skin_data"] = surface.skin_data; + } + if (surface.attribute_data.size()) { + data["attribute_data"] = surface.attribute_data; + } data["aabb"] = surface.aabb; if (surface.index_count) { data["index_data"] = surface.index_data; @@ -848,9 +925,8 @@ Array ArrayMesh::_get_surfaces() const { data["bone_aabbs"] = bone_aabbs; } - Array blend_shapes; - for (int j = 0; j < surface.blend_shapes.size(); j++) { - blend_shapes.push_back(surface.blend_shapes[j]); + if (surface.blend_shape_data.size()) { + data["blend_shapes"] = surface.blend_shape_data; } if (surfaces[i].material.is_valid()) { @@ -875,6 +951,7 @@ void ArrayMesh::_create_if_empty() const { if (!mesh.is_valid()) { mesh = RS::get_singleton()->mesh_create(); RS::get_singleton()->mesh_set_blend_shape_mode(mesh, (RS::BlendShapeMode)blend_shape_mode); + RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size()); } } @@ -896,6 +973,12 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) { surface.primitive = RS::PrimitiveType(int(d["primitive"])); surface.vertex_data = d["vertex_data"]; surface.vertex_count = d["vertex_count"]; + if (d.has("attribute_data")) { + surface.attribute_data = d["attribute_data"]; + } + if (d.has("skin_data")) { + surface.skin_data = d["skin_data"]; + } surface.aabb = d["aabb"]; if (d.has("index_data")) { @@ -923,10 +1006,7 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) { } if (d.has("blend_shapes")) { - Array blend_shapes; - for (int j = 0; j < blend_shapes.size(); j++) { - surface.blend_shapes.push_back(blend_shapes[j]); - } + surface.blend_shape_data = d["blend_shapes"]; } Ref<Material> material; @@ -946,15 +1026,7 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) { if (d.has("2d")) { _2d = d["2d"]; } - /* - print_line("format: " + itos(surface.format)); - print_line("aabb: " + surface.aabb); - print_line("array size: " + itos(surface.vertex_data.size())); - print_line("vertex count: " + itos(surface.vertex_count)); - print_line("index size: " + itos(surface.index_data.size())); - print_line("index count: " + itos(surface.index_count)); - print_line("primitive: " + itos(surface.primitive)); -*/ + surface_data.push_back(surface); surface_materials.push_back(material); surface_names.push_back(name); @@ -970,7 +1042,7 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) { } else { // if mesh does not exist (first time this is loaded, most likely), // we can create it with a single call, which is a lot more efficient and thread friendly - mesh = RS::get_singleton()->mesh_create_from_surfaces(surface_data); + mesh = RS::get_singleton()->mesh_create_from_surfaces(surface_data, blend_shapes.size()); RS::get_singleton()->mesh_set_blend_shape_mode(mesh, (RS::BlendShapeMode)blend_shape_mode); } @@ -982,7 +1054,6 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) { s.aabb = surface_data[i].aabb; if (i == 0) { aabb = s.aabb; - blend_shapes.resize(surface_data[i].blend_shapes.size()); } else { aabb.merge_with(s.aabb); } @@ -1006,18 +1077,7 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const { } String sname = p_name; - - if (p_name == "blend_shape/names") { - Vector<String> sk; - for (int i = 0; i < blend_shapes.size(); i++) { - sk.push_back(blend_shapes[i]); - } - r_ret = sk; - return true; - } else if (p_name == "blend_shape/mode") { - r_ret = get_blend_shape_mode(); - return true; - } else if (sname.begins_with("surface_")) { + if (sname.begins_with("surface_")) { int sl = sname.find("/"); if (sl == -1) { return false; @@ -1040,11 +1100,6 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const { return; } - if (blend_shapes.size()) { - p_list->push_back(PropertyInfo(Variant::PACKED_STRING_ARRAY, "blend_shape/names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::INT, "blend_shape/mode", PROPERTY_HINT_ENUM, "Normalized,Relative")); - } - for (int i = 0; i < surfaces.size(); i++) { p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i + 1) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); if (surfaces[i].is_2d) { @@ -1070,7 +1125,7 @@ void ArrayMesh::_recompute_aabb() { #ifndef _MSC_VER #warning need to add binding to add_surface using future MeshSurfaceData object #endif -void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<Vector<uint8_t>> &p_blend_shapes, const Vector<AABB> &p_bone_aabb, const Vector<RS::SurfaceData::LOD> &p_lods) { +void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods) { _create_if_empty(); Surface s; @@ -1090,10 +1145,12 @@ void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const sd.aabb = p_aabb; sd.vertex_count = p_vertex_count; sd.vertex_data = p_array; + sd.attribute_data = p_attribute_array; + sd.skin_data = p_skin_array; sd.index_count = p_index_count; sd.index_data = p_index_array; - sd.blend_shapes = p_blend_shapes; - sd.bone_aabbs = p_bone_aabb; + sd.blend_shape_data = p_blend_shape_data; + sd.bone_aabbs = p_bone_aabbs; sd.lods = p_lods; RenderingServer::get_singleton()->mesh_add_surface(mesh, sd); @@ -1111,15 +1168,17 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array & Error err = RS::get_singleton()->mesh_create_surface_data_from_arrays(&surface, (RenderingServer::PrimitiveType)p_primitive, p_arrays, p_blend_shapes, p_lods, p_flags); ERR_FAIL_COND(err != OK); - /* print_line("format: " + itos(surface.format)); + /* Debug code. + print_line("format: " + itos(surface.format)); print_line("aabb: " + surface.aabb); print_line("array size: " + itos(surface.vertex_data.size())); print_line("vertex count: " + itos(surface.vertex_count)); print_line("index size: " + itos(surface.index_data.size())); print_line("index count: " + itos(surface.index_count)); print_line("primitive: " + itos(surface.primitive)); -*/ - add_surface(surface.format, PrimitiveType(surface.primitive), surface.vertex_data, surface.vertex_count, surface.index_data, surface.index_count, surface.aabb, surface.blend_shapes, surface.bone_aabbs, surface.lods); + */ + + add_surface(surface.format, PrimitiveType(surface.primitive), surface.vertex_data, surface.attribute_data, surface.skin_data, surface.vertex_count, surface.index_data, surface.index_count, surface.aabb, surface.blend_shape_data, surface.bone_aabbs, surface.lods); } Array ArrayMesh::surface_get_arrays(int p_surface) const { @@ -1155,7 +1214,10 @@ void ArrayMesh::add_blend_shape(const StringName &p_name) { } blend_shapes.push_back(name); - //RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size()); + + if (mesh.is_valid()) { + RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size()); + } } int ArrayMesh::get_blend_shape_count() const { @@ -1171,6 +1233,10 @@ void ArrayMesh::clear_blend_shapes() { ERR_FAIL_COND_MSG(surfaces.size(), "Can't set shape key count if surfaces are already created."); blend_shapes.clear(); + + if (mesh.is_valid()) { + RS::get_singleton()->mesh_set_blend_shape_count(mesh, 0); + } } void ArrayMesh::set_blend_shape_mode(BlendShapeMode p_mode) { @@ -1284,7 +1350,7 @@ AABB ArrayMesh::get_custom_aabb() const { return custom_aabb; } -void ArrayMesh::regen_normalmaps() { +void ArrayMesh::regen_normal_maps() { if (surfaces.size() == 0) { return; } @@ -1308,7 +1374,7 @@ bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_v struct ArrayMeshLightmapSurface { Ref<Material> material; - Vector<SurfaceTool::Vertex> vertices; + LocalVector<SurfaceTool::Vertex> vertices; Mesh::PrimitiveType primitive; uint32_t format; }; @@ -1349,7 +1415,7 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach Array arrays = surface_get_arrays(i); s.material = surface_get_material(i); - s.vertices = SurfaceTool::create_vertex_array_from_triangle_arrays(arrays); + SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices); Vector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX]; int vc = rvertices.size(); @@ -1452,29 +1518,29 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second]; if (lightmap_surfaces[surface].format & ARRAY_FORMAT_COLOR) { - surfaces_tools.write[surface]->add_color(v.color); + surfaces_tools.write[surface]->set_color(v.color); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TEX_UV) { - surfaces_tools.write[surface]->add_uv(v.uv); + surfaces_tools.write[surface]->set_uv(v.uv); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_NORMAL) { - surfaces_tools.write[surface]->add_normal(v.normal); + surfaces_tools.write[surface]->set_normal(v.normal); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TANGENT) { Plane t; t.normal = v.tangent; t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1; - surfaces_tools.write[surface]->add_tangent(t); + surfaces_tools.write[surface]->set_tangent(t); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_BONES) { - surfaces_tools.write[surface]->add_bones(v.bones); + surfaces_tools.write[surface]->set_bones(v.bones); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) { - surfaces_tools.write[surface]->add_weights(v.weights); + surfaces_tools.write[surface]->set_weights(v.weights); } Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]); - surfaces_tools.write[surface]->add_uv2(uv2); + surfaces_tools.write[surface]->set_uv2(uv2); surfaces_tools.write[surface]->add_vertex(v.vertex); } @@ -1507,7 +1573,7 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ArrayMesh::set_blend_shape_mode); ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ArrayMesh::get_blend_shape_mode); - ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(ARRAY_COMPRESS_DEFAULT)); + ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0)); ClassDB::bind_method(D_METHOD("clear_surfaces"), &ArrayMesh::clear_surfaces); ClassDB::bind_method(D_METHOD("surface_update_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_region); ClassDB::bind_method(D_METHOD("surface_get_array_len", "surf_idx"), &ArrayMesh::surface_get_array_len); @@ -1520,8 +1586,8 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("create_trimesh_shape"), &ArrayMesh::create_trimesh_shape); ClassDB::bind_method(D_METHOD("create_convex_shape"), &ArrayMesh::create_convex_shape); ClassDB::bind_method(D_METHOD("create_outline", "margin"), &ArrayMesh::create_outline); - ClassDB::bind_method(D_METHOD("regen_normalmaps"), &ArrayMesh::regen_normalmaps); - ClassDB::set_method_flags(get_class_static(), _scs_create("regen_normalmaps"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); + ClassDB::bind_method(D_METHOD("regen_normal_maps"), &ArrayMesh::regen_normal_maps); + ClassDB::set_method_flags(get_class_static(), _scs_create("regen_normal_maps"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ClassDB::bind_method(D_METHOD("lightmap_unwrap", "transform", "texel_size"), &ArrayMesh::lightmap_unwrap); ClassDB::set_method_flags(get_class_static(), _scs_create("lightmap_unwrap"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ClassDB::bind_method(D_METHOD("get_faces"), &ArrayMesh::get_faces); @@ -1530,36 +1596,16 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &ArrayMesh::set_custom_aabb); ClassDB::bind_method(D_METHOD("get_custom_aabb"), &ArrayMesh::get_custom_aabb); + ClassDB::bind_method(D_METHOD("_set_blend_shape_names", "blend_shape_names"), &ArrayMesh::_set_blend_shape_names); + ClassDB::bind_method(D_METHOD("_get_blend_shape_names"), &ArrayMesh::_get_blend_shape_names); + ClassDB::bind_method(D_METHOD("_set_surfaces", "surfaces"), &ArrayMesh::_set_surfaces); ClassDB::bind_method(D_METHOD("_get_surfaces"), &ArrayMesh::_get_surfaces); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces"); ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_shape_mode", PROPERTY_HINT_ENUM, "Normalized,Relative"), "set_blend_shape_mode", "get_blend_shape_mode"); ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb"); - - BIND_CONSTANT(NO_INDEX_ARRAY); - BIND_CONSTANT(ARRAY_WEIGHTS_SIZE); - - BIND_ENUM_CONSTANT(ARRAY_VERTEX); - BIND_ENUM_CONSTANT(ARRAY_NORMAL); - BIND_ENUM_CONSTANT(ARRAY_TANGENT); - BIND_ENUM_CONSTANT(ARRAY_COLOR); - BIND_ENUM_CONSTANT(ARRAY_TEX_UV); - BIND_ENUM_CONSTANT(ARRAY_TEX_UV2); - BIND_ENUM_CONSTANT(ARRAY_BONES); - BIND_ENUM_CONSTANT(ARRAY_WEIGHTS); - BIND_ENUM_CONSTANT(ARRAY_INDEX); - BIND_ENUM_CONSTANT(ARRAY_MAX); - - BIND_ENUM_CONSTANT(ARRAY_FORMAT_VERTEX); - BIND_ENUM_CONSTANT(ARRAY_FORMAT_NORMAL); - BIND_ENUM_CONSTANT(ARRAY_FORMAT_TANGENT); - BIND_ENUM_CONSTANT(ARRAY_FORMAT_COLOR); - BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV); - BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV2); - BIND_ENUM_CONSTANT(ARRAY_FORMAT_BONES); - BIND_ENUM_CONSTANT(ARRAY_FORMAT_WEIGHTS); - BIND_ENUM_CONSTANT(ARRAY_FORMAT_INDEX); } void ArrayMesh::reload_from_file() { diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 642ae7e1b0..2f25ecd60b 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -53,7 +53,10 @@ public: NO_INDEX_ARRAY = RenderingServer::NO_INDEX_ARRAY, ARRAY_WEIGHTS_SIZE = RenderingServer::ARRAY_WEIGHTS_SIZE }; - + enum BlendShapeMode { + BLEND_SHAPE_MODE_NORMALIZED = RS::BLEND_SHAPE_MODE_NORMALIZED, + BLEND_SHAPE_MODE_RELATIVE = RS::BLEND_SHAPE_MODE_RELATIVE, + }; enum ArrayType { ARRAY_VERTEX = RenderingServer::ARRAY_VERTEX, ARRAY_NORMAL = RenderingServer::ARRAY_NORMAL, @@ -61,6 +64,10 @@ public: ARRAY_COLOR = RenderingServer::ARRAY_COLOR, ARRAY_TEX_UV = RenderingServer::ARRAY_TEX_UV, ARRAY_TEX_UV2 = RenderingServer::ARRAY_TEX_UV2, + ARRAY_CUSTOM0 = RenderingServer::ARRAY_CUSTOM0, + ARRAY_CUSTOM1 = RenderingServer::ARRAY_CUSTOM1, + ARRAY_CUSTOM2 = RenderingServer::ARRAY_CUSTOM2, + ARRAY_CUSTOM3 = RenderingServer::ARRAY_CUSTOM3, ARRAY_BONES = RenderingServer::ARRAY_BONES, ARRAY_WEIGHTS = RenderingServer::ARRAY_WEIGHTS, ARRAY_INDEX = RenderingServer::ARRAY_INDEX, @@ -68,30 +75,47 @@ public: }; + enum ArrayCustomFormat { + ARRAY_CUSTOM_RGBA8_UNORM, + ARRAY_CUSTOM_RGBA8_SNORM, + ARRAY_CUSTOM_RG_HALF, + ARRAY_CUSTOM_RGBA_HALF, + ARRAY_CUSTOM_R_FLOAT, + ARRAY_CUSTOM_RG_FLOAT, + ARRAY_CUSTOM_RGB_FLOAT, + ARRAY_CUSTOM_RGBA_FLOAT, + ARRAY_CUSTOM_MAX + }; + enum ArrayFormat { - /* ARRAY FORMAT FLAGS */ - ARRAY_FORMAT_VERTEX = 1 << ARRAY_VERTEX, // mandatory - ARRAY_FORMAT_NORMAL = 1 << ARRAY_NORMAL, - ARRAY_FORMAT_TANGENT = 1 << ARRAY_TANGENT, - ARRAY_FORMAT_COLOR = 1 << ARRAY_COLOR, - ARRAY_FORMAT_TEX_UV = 1 << ARRAY_TEX_UV, - ARRAY_FORMAT_TEX_UV2 = 1 << ARRAY_TEX_UV2, - ARRAY_FORMAT_BONES = 1 << ARRAY_BONES, - ARRAY_FORMAT_WEIGHTS = 1 << ARRAY_WEIGHTS, - ARRAY_FORMAT_INDEX = 1 << ARRAY_INDEX, - - ARRAY_COMPRESS_BASE = (ARRAY_INDEX + 1), - ARRAY_COMPRESS_NORMAL = 1 << (ARRAY_NORMAL + ARRAY_COMPRESS_BASE), - ARRAY_COMPRESS_TANGENT = 1 << (ARRAY_TANGENT + ARRAY_COMPRESS_BASE), - ARRAY_COMPRESS_COLOR = 1 << (ARRAY_COLOR + ARRAY_COMPRESS_BASE), - ARRAY_COMPRESS_TEX_UV = 1 << (ARRAY_TEX_UV + ARRAY_COMPRESS_BASE), - ARRAY_COMPRESS_TEX_UV2 = 1 << (ARRAY_TEX_UV2 + ARRAY_COMPRESS_BASE), - ARRAY_COMPRESS_INDEX = 1 << (ARRAY_INDEX + ARRAY_COMPRESS_BASE), - - ARRAY_FLAG_USE_2D_VERTICES = ARRAY_COMPRESS_INDEX << 1, - ARRAY_FLAG_USE_DYNAMIC_UPDATE = ARRAY_COMPRESS_INDEX << 3, - - ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2 + ARRAY_FORMAT_VERTEX = RS::ARRAY_FORMAT_VERTEX, + ARRAY_FORMAT_NORMAL = RS::ARRAY_FORMAT_NORMAL, + ARRAY_FORMAT_TANGENT = RS::ARRAY_FORMAT_TANGENT, + ARRAY_FORMAT_COLOR = RS::ARRAY_FORMAT_COLOR, + ARRAY_FORMAT_TEX_UV = RS::ARRAY_FORMAT_TEX_UV, + ARRAY_FORMAT_TEX_UV2 = RS::ARRAY_FORMAT_TEX_UV2, + ARRAY_FORMAT_CUSTOM0 = RS::ARRAY_FORMAT_CUSTOM0, + ARRAY_FORMAT_CUSTOM1 = RS::ARRAY_FORMAT_CUSTOM1, + ARRAY_FORMAT_CUSTOM2 = RS::ARRAY_FORMAT_CUSTOM2, + ARRAY_FORMAT_CUSTOM3 = RS::ARRAY_FORMAT_CUSTOM3, + ARRAY_FORMAT_BONES = RS::ARRAY_FORMAT_BONES, + ARRAY_FORMAT_WEIGHTS = RS::ARRAY_FORMAT_WEIGHTS, + ARRAY_FORMAT_INDEX = RS::ARRAY_FORMAT_INDEX, + + ARRAY_FORMAT_BLEND_SHAPE_MASK = RS::ARRAY_FORMAT_BLEND_SHAPE_MASK, + + ARRAY_FORMAT_CUSTOM_BASE = RS::ARRAY_FORMAT_CUSTOM_BASE, + ARRAY_FORMAT_CUSTOM0_SHIFT = RS::ARRAY_FORMAT_CUSTOM0_SHIFT, + ARRAY_FORMAT_CUSTOM1_SHIFT = RS::ARRAY_FORMAT_CUSTOM1_SHIFT, + ARRAY_FORMAT_CUSTOM2_SHIFT = RS::ARRAY_FORMAT_CUSTOM2_SHIFT, + ARRAY_FORMAT_CUSTOM3_SHIFT = RS::ARRAY_FORMAT_CUSTOM3_SHIFT, + + ARRAY_FORMAT_CUSTOM_MASK = RS::ARRAY_FORMAT_CUSTOM_MASK, + ARRAY_COMPRESS_FLAGS_BASE = RS::ARRAY_COMPRESS_FLAGS_BASE, + + ARRAY_FLAG_USE_2D_VERTICES = RS::ARRAY_FLAG_USE_2D_VERTICES, + ARRAY_FLAG_USE_DYNAMIC_UPDATE = RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE, + ARRAY_FLAG_USE_8_BONE_WEIGHTS = RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS, }; @@ -104,11 +128,6 @@ public: PRIMITIVE_MAX = RenderingServer::PRIMITIVE_MAX, }; - enum BlendShapeMode { - BLEND_SHAPE_MODE_NORMALIZED = RS::BLEND_SHAPE_MODE_NORMALIZED, - BLEND_SHAPE_MODE_RELATIVE = RS::BLEND_SHAPE_MODE_RELATIVE, - }; - virtual int get_surface_count() const = 0; virtual int surface_get_array_len(int p_idx) const = 0; virtual int surface_get_array_index_len(int p_idx) const = 0; @@ -152,6 +171,9 @@ class ArrayMesh : public Mesh { GDCLASS(ArrayMesh, Mesh); RES_BASE_EXTENSION("mesh"); + PackedStringArray _get_blend_shape_names() const; + void _set_blend_shape_names(const PackedStringArray &p_names); + Array _get_surfaces() const; void _set_surfaces(const Array &p_data); @@ -187,9 +209,9 @@ protected: static void _bind_methods(); public: - void add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), uint32_t p_flags = ARRAY_COMPRESS_DEFAULT); + void add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), uint32_t p_flags = 0); - void add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<Vector<uint8_t>> &p_blend_shapes = Vector<Vector<uint8_t>>(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>(), const Vector<RS::SurfaceData::LOD> &p_lods = Vector<RS::SurfaceData::LOD>()); + void add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data = Vector<uint8_t>(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>(), const Vector<RS::SurfaceData::LOD> &p_lods = Vector<RS::SurfaceData::LOD>()); Array surface_get_arrays(int p_surface) const override; Array surface_get_blend_shape_arrays(int p_surface) const override; @@ -230,7 +252,7 @@ public: AABB get_aabb() const override; virtual RID get_rid() const override; - void regen_normalmaps(); + void regen_normal_maps(); Error lightmap_unwrap(const Transform &p_base_transform = Transform(), float p_texel_size = 0.05); Error lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform = Transform(), float p_texel_size = 0.05); @@ -244,6 +266,7 @@ public: VARIANT_ENUM_CAST(Mesh::ArrayType); VARIANT_ENUM_CAST(Mesh::ArrayFormat); +VARIANT_ENUM_CAST(Mesh::ArrayCustomFormat); VARIANT_ENUM_CAST(Mesh::PrimitiveType); VARIANT_ENUM_CAST(Mesh::BlendShapeMode); diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index a5c360f123..1b82aca386 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -43,7 +43,7 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf ERR_FAIL_COND_V(p_mesh->surface_get_primitive_type(p_surface) != Mesh::PRIMITIVE_TRIANGLES, ERR_INVALID_PARAMETER); Array arrays = p_mesh->surface_get_arrays(p_surface); - ERR_FAIL_COND_V(arrays.empty(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(arrays.is_empty(), ERR_INVALID_PARAMETER); Vector<Vector3> varray = arrays[Mesh::ARRAY_VERTEX]; diff --git a/scene/resources/mesh_data_tool.h b/scene/resources/mesh_data_tool.h index bf9f0dd25f..d8b8d85900 100644 --- a/scene/resources/mesh_data_tool.h +++ b/scene/resources/mesh_data_tool.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index 09b0d4b038..96d3a5750e 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h index 0d5fb3005b..5c302fcf08 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index f71cf383e5..050e398ca4 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h index 16f5998a04..633d754b8e 100644 --- a/scene/resources/multimesh.h +++ b/scene/resources/multimesh.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index e815da5d45..43c3ef4edc 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index b94f4408e1..693c4184bc 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 5ce253f970..794b281100 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -80,7 +80,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { Node **ret_nodes = (Node **)alloca(sizeof(Node *) * nc); - bool gen_node_path_cache = p_edit_state != GEN_EDIT_STATE_DISABLED && node_path_cache.empty(); + bool gen_node_path_cache = p_edit_state != GEN_EDIT_STATE_DISABLED && node_path_cache.is_empty(); Map<Ref<Resource>, Ref<Resource>> resources_local_to_scene; @@ -603,7 +603,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map // Save the right type. If this node was created by an instance // then flag that the node should not be created but reused - if (pack_state_stack.empty()) { + if (pack_state_stack.is_empty()) { //this node is not part of an instancing process, so save the type nd.type = _nm_get_string(p_node->get_class(), name_map); } else { @@ -1349,7 +1349,7 @@ NodePath SceneState::get_node_path(int p_idx, bool p_for_parent) const { sub_path.insert(0, base_path.get_name(i)); } - if (sub_path.empty()) { + if (sub_path.is_empty()) { return NodePath("."); } @@ -1481,16 +1481,6 @@ int SceneState::add_name(const StringName &p_name) { return names.size() - 1; } -int SceneState::find_name(const StringName &p_name) const { - for (int i = 0; i < names.size(); i++) { - if (names[i] == p_name) { - return i; - } - } - - return -1; -} - int SceneState::add_value(const Variant &p_value) { variants.push_back(p_value); return variants.size() - 1; diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index b8b3f84ecc..a31dcd8d39 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -172,7 +172,6 @@ public: //build API int add_name(const StringName &p_name); - int find_name(const StringName &p_name) const; int add_value(const Variant &p_value); int add_node_path(const NodePath &p_path); int add_node(int p_parent, int p_owner, int p_type, int p_name, int p_instance, int p_index); diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index a286184aee..3aa9f9b3bc 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -329,7 +329,7 @@ void ParticlesMaterial::_update_shader() { code += " float tex_linear_velocity = 0.0;\n"; } - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n"; code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n"; @@ -377,7 +377,7 @@ void ParticlesMaterial::_update_shader() { code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n"; if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) { - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { code += " mat2 rotm;"; code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n"; code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n"; @@ -398,7 +398,7 @@ void ParticlesMaterial::_update_shader() { code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n"; code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n"; - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { code += " VELOCITY.z = 0.0;\n"; code += " TRANSFORM[3].z = 0.0;\n"; } @@ -413,7 +413,7 @@ void ParticlesMaterial::_update_shader() { code += " float tex_linear_velocity = 0.0;\n"; } - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; } else { @@ -471,7 +471,7 @@ void ParticlesMaterial::_update_shader() { code += " vec3 force = gravity;\n"; code += " vec3 pos = TRANSFORM[3].xyz;\n"; - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { code += " pos.z = 0.0;\n"; } code += " // apply linear acceleration\n"; @@ -481,7 +481,7 @@ void ParticlesMaterial::_update_shader() { code += " vec3 diff = pos - org;\n"; code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel + tex_radial_accel) * mix(1.0, rand_from_seed(alt_seed), radial_accel_random) : vec3(0.0);\n"; code += " // apply tangential acceleration;\n"; - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; } else { @@ -495,7 +495,7 @@ void ParticlesMaterial::_update_shader() { code += " // apply attractor forces\n"; code += " VELOCITY += force * DELTA;\n"; code += " // orbit velocity\n"; - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { code += " float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);\n"; code += " if (orbit_amount != 0.0) {\n"; code += " float ang = orbit_amount * DELTA * pi * 2.0;\n"; @@ -562,8 +562,8 @@ void ParticlesMaterial::_update_shader() { } code += "\n"; - if (flags[FLAG_DISABLE_Z]) { - if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { code += " if (length(VELOCITY) > 0.0) {\n"; code += " TRANSFORM[1].xyz = normalize(VELOCITY);\n"; code += " } else {\n"; @@ -579,7 +579,7 @@ void ParticlesMaterial::_update_shader() { } else { // orient particle Y towards velocity - if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { code += " if (length(VELOCITY) > 0.0) {\n"; code += " TRANSFORM[1].xyz = normalize(VELOCITY);\n"; code += " } else {\n"; @@ -598,7 +598,7 @@ void ParticlesMaterial::_update_shader() { code += " TRANSFORM[2].xyz = normalize(TRANSFORM[2].xyz);\n"; } // turn particle by rotation in Y - if (flags[FLAG_ROTATE_Y]) { + if (particle_flags[PARTICLE_FLAG_ROTATE_Y]) { code += " TRANSFORM = TRANSFORM * mat4(vec4(cos(CUSTOM.x), 0.0, -sin(CUSTOM.x), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(sin(CUSTOM.x), 0.0, cos(CUSTOM.x), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; } } @@ -611,7 +611,7 @@ void ParticlesMaterial::_update_shader() { code += " TRANSFORM[0].xyz *= base_scale;\n"; code += " TRANSFORM[1].xyz *= base_scale;\n"; code += " TRANSFORM[2].xyz *= base_scale;\n"; - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { code += " VELOCITY.z = 0.0;\n"; code += " TRANSFORM[3].z = 0.0;\n"; } @@ -646,7 +646,7 @@ void ParticlesMaterial::_update_shader() { code += " for(int i=0;i<emit_count;i++) {\n"; code += " uint flags = FLAG_EMIT_POSITION|FLAG_EMIT_ROT_SCALE;\n"; code += " if (sub_emitter_keep_velocity) flags|=FLAG_EMIT_VELOCITY;\n"; - code += " emit_particle(TRANSFORM,VELOCITY,vec4(0.0),vec4(0.0),flags);\n"; + code += " emit_subparticle(TRANSFORM,VELOCITY,vec4(0.0),vec4(0.0),flags);\n"; code += " }"; } @@ -916,18 +916,18 @@ Ref<Texture2D> ParticlesMaterial::get_color_ramp() const { return color_ramp; } -void ParticlesMaterial::set_flag(Flags p_flag, bool p_enable) { - ERR_FAIL_INDEX(p_flag, FLAG_MAX); - flags[p_flag] = p_enable; +void ParticlesMaterial::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) { + ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX); + particle_flags[p_particle_flag] = p_enable; _queue_shader_change(); - if (p_flag == FLAG_DISABLE_Z) { + if (p_particle_flag == PARTICLE_FLAG_DISABLE_Z) { _change_notify(); } } -bool ParticlesMaterial::get_flag(Flags p_flag) const { - ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); - return flags[p_flag]; +bool ParticlesMaterial::get_particle_flag(ParticleFlags p_particle_flag) const { + ERR_FAIL_INDEX_V(p_particle_flag, PARTICLE_FLAG_MAX, false); + return particle_flags[p_particle_flag]; } void ParticlesMaterial::set_emission_shape(EmissionShape p_shape) { @@ -1056,7 +1056,7 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const { property.usage = 0; } - if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) { + if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { property.usage = 0; } } @@ -1170,8 +1170,8 @@ void ParticlesMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &ParticlesMaterial::set_color_ramp); ClassDB::bind_method(D_METHOD("get_color_ramp"), &ParticlesMaterial::get_color_ramp); - ClassDB::bind_method(D_METHOD("set_flag", "flag", "enable"), &ParticlesMaterial::set_flag); - ClassDB::bind_method(D_METHOD("get_flag", "flag"), &ParticlesMaterial::get_flag); + ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &ParticlesMaterial::set_particle_flag); + ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &ParticlesMaterial::get_particle_flag); ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &ParticlesMaterial::set_emission_shape); ClassDB::bind_method(D_METHOD("get_emission_shape"), &ParticlesMaterial::get_emission_shape); @@ -1238,10 +1238,10 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_emission_normal_texture", "get_emission_normal_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_color_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_emission_color_texture", "get_emission_color_texture"); ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_point_count", PROPERTY_HINT_RANGE, "0,1000000,1"), "set_emission_point_count", "get_emission_point_count"); - ADD_GROUP("Flags", "flag_"); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_flag", "get_flag", FLAG_ALIGN_Y_TO_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_flag", "get_flag", FLAG_ROTATE_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_flag", "get_flag", FLAG_DISABLE_Z); + ADD_GROUP("ParticleFlags", "particle_flag_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_rotate_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ROTATE_Y); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_disable_z"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_DISABLE_Z); ADD_GROUP("Direction", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "direction"), "set_direction", "get_direction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread"); @@ -1327,10 +1327,10 @@ void ParticlesMaterial::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET); BIND_ENUM_CONSTANT(PARAM_MAX); - BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY); - BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); - BIND_ENUM_CONSTANT(FLAG_DISABLE_Z); - BIND_ENUM_CONSTANT(FLAG_MAX); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_ROTATE_Y); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_DISABLE_Z); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_MAX); BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE); @@ -1385,8 +1385,8 @@ ParticlesMaterial::ParticlesMaterial() : set_param_randomness(Parameter(i), 0); } - for (int i = 0; i < FLAG_MAX; i++) { - flags[i] = false; + for (int i = 0; i < PARTICLE_FLAG_MAX; i++) { + particle_flags[i] = false; } set_color(Color(1, 1, 1, 1)); diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h index 12fa53ef29..49b48a01b7 100644 --- a/scene/resources/particles_material.h +++ b/scene/resources/particles_material.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -61,11 +61,11 @@ public: PARAM_MAX }; - enum Flags { - FLAG_ALIGN_Y_TO_VELOCITY, - FLAG_ROTATE_Y, - FLAG_DISABLE_Z, - FLAG_MAX + enum ParticleFlags { + PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, + PARTICLE_FLAG_ROTATE_Y, + PARTICLE_FLAG_DISABLE_Z, + PARTICLE_FLAG_MAX }; enum EmissionShape { @@ -90,7 +90,7 @@ private: struct { uint32_t texture_mask : 16; uint32_t texture_color : 1; - uint32_t flags : 4; + uint32_t particle_flags : 4; uint32_t emission_shape : 2; uint32_t invalid_key : 1; uint32_t has_emission_color : 1; @@ -124,9 +124,9 @@ private: mk.texture_mask |= (1 << i); } } - for (int i = 0; i < FLAG_MAX; i++) { - if (flags[i]) { - mk.flags |= (1 << i); + for (int i = 0; i < PARTICLE_FLAG_MAX; i++) { + if (particle_flags[i]) { + mk.particle_flags |= (1 << i); } } @@ -227,7 +227,7 @@ private: Color color; Ref<Texture2D> color_ramp; - bool flags[FLAG_MAX]; + bool particle_flags[PARTICLE_FLAG_MAX]; EmissionShape emission_shape; float emission_sphere_radius; @@ -284,8 +284,8 @@ public: void set_color_ramp(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_color_ramp() const; - void set_flag(Flags p_flag, bool p_enable); - bool get_flag(Flags p_flag) const; + void set_particle_flag(ParticleFlags p_particle_flag, bool p_enable); + bool get_particle_flag(ParticleFlags p_particle_flag) const; void set_emission_shape(EmissionShape p_shape); void set_emission_sphere_radius(float p_radius); @@ -340,7 +340,7 @@ public: void set_sub_emitter_keep_velocity(bool p_enable); bool get_sub_emitter_keep_velocity() const; - RID get_shader_rid() const; + virtual RID get_shader_rid() const override; virtual Shader::Mode get_shader_mode() const override; @@ -349,7 +349,7 @@ public: }; VARIANT_ENUM_CAST(ParticlesMaterial::Parameter) -VARIANT_ENUM_CAST(ParticlesMaterial::Flags) +VARIANT_ENUM_CAST(ParticlesMaterial::ParticleFlags) VARIANT_ENUM_CAST(ParticlesMaterial::EmissionShape) VARIANT_ENUM_CAST(ParticlesMaterial::SubEmitterMode) diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp index 59bf8c0e13..d65b0c8927 100644 --- a/scene/resources/physics_material.cpp +++ b/scene/resources/physics_material.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/physics_material.h b/scene/resources/physics_material.h index e9222ffa1b..dfb2d1480b 100644 --- a/scene/resources/physics_material.h +++ b/scene/resources/physics_material.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index df98d4cfd4..f292140d6b 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/polygon_path_finder.h b/scene/resources/polygon_path_finder.h index 44a97b4294..410e1dba52 100644 --- a/scene/resources/polygon_path_finder.h +++ b/scene/resources/polygon_path_finder.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 8d9c5f07b2..a6e9e7b5d2 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -148,7 +148,7 @@ Array PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) const { uint32_t PrimitiveMesh::surface_get_format(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, 1, 0); - return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX | RS::ARRAY_COMPRESS_DEFAULT; + return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX; } Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const { @@ -477,10 +477,10 @@ CapsuleMesh::CapsuleMesh() { } /** - CubeMesh + BoxMesh */ -void CubeMesh::_create_mesh_array(Array &p_arr) const { +void BoxMesh::_create_mesh_array(Array &p_arr) const { int i, j, prevrow, thisrow, point; float x, y, z; float onethird = 1.0 / 3.0; @@ -672,16 +672,16 @@ void CubeMesh::_create_mesh_array(Array &p_arr) const { p_arr[RS::ARRAY_INDEX] = indices; } -void CubeMesh::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_size", "size"), &CubeMesh::set_size); - ClassDB::bind_method(D_METHOD("get_size"), &CubeMesh::get_size); +void BoxMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxMesh::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &BoxMesh::get_size); - ClassDB::bind_method(D_METHOD("set_subdivide_width", "subdivide"), &CubeMesh::set_subdivide_width); - ClassDB::bind_method(D_METHOD("get_subdivide_width"), &CubeMesh::get_subdivide_width); - ClassDB::bind_method(D_METHOD("set_subdivide_height", "divisions"), &CubeMesh::set_subdivide_height); - ClassDB::bind_method(D_METHOD("get_subdivide_height"), &CubeMesh::get_subdivide_height); - ClassDB::bind_method(D_METHOD("set_subdivide_depth", "divisions"), &CubeMesh::set_subdivide_depth); - ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &CubeMesh::get_subdivide_depth); + ClassDB::bind_method(D_METHOD("set_subdivide_width", "subdivide"), &BoxMesh::set_subdivide_width); + ClassDB::bind_method(D_METHOD("get_subdivide_width"), &BoxMesh::get_subdivide_width); + ClassDB::bind_method(D_METHOD("set_subdivide_height", "divisions"), &BoxMesh::set_subdivide_height); + ClassDB::bind_method(D_METHOD("get_subdivide_height"), &BoxMesh::get_subdivide_height); + ClassDB::bind_method(D_METHOD("set_subdivide_depth", "divisions"), &BoxMesh::set_subdivide_depth); + ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &BoxMesh::get_subdivide_depth); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width"); @@ -689,43 +689,43 @@ void CubeMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth"); } -void CubeMesh::set_size(const Vector3 &p_size) { +void BoxMesh::set_size(const Vector3 &p_size) { size = p_size; _request_update(); } -Vector3 CubeMesh::get_size() const { +Vector3 BoxMesh::get_size() const { return size; } -void CubeMesh::set_subdivide_width(const int p_divisions) { +void BoxMesh::set_subdivide_width(const int p_divisions) { subdivide_w = p_divisions > 0 ? p_divisions : 0; _request_update(); } -int CubeMesh::get_subdivide_width() const { +int BoxMesh::get_subdivide_width() const { return subdivide_w; } -void CubeMesh::set_subdivide_height(const int p_divisions) { +void BoxMesh::set_subdivide_height(const int p_divisions) { subdivide_h = p_divisions > 0 ? p_divisions : 0; _request_update(); } -int CubeMesh::get_subdivide_height() const { +int BoxMesh::get_subdivide_height() const { return subdivide_h; } -void CubeMesh::set_subdivide_depth(const int p_divisions) { +void BoxMesh::set_subdivide_depth(const int p_divisions) { subdivide_d = p_divisions > 0 ? p_divisions : 0; _request_update(); } -int CubeMesh::get_subdivide_depth() const { +int BoxMesh::get_subdivide_depth() const { return subdivide_d; } -CubeMesh::CubeMesh() { +BoxMesh::BoxMesh() { // defaults size = Vector3(2.0, 2.0, 2.0); subdivide_w = 0; diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index f0ae611b5e..d0ca4b10e7 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -130,10 +130,10 @@ public: }; /** - Similar to test cube but with subdivision support and different texture coordinates + A box */ -class CubeMesh : public PrimitiveMesh { - GDCLASS(CubeMesh, PrimitiveMesh); +class BoxMesh : public PrimitiveMesh { + GDCLASS(BoxMesh, PrimitiveMesh); private: Vector3 size; @@ -158,7 +158,7 @@ public: void set_subdivide_depth(const int p_divisions); int get_subdivide_depth() const; - CubeMesh(); + BoxMesh(); }; /** diff --git a/scene/resources/ray_shape_2d.cpp b/scene/resources/ray_shape_2d.cpp index 67c4f84749..98fd160810 100644 --- a/scene/resources/ray_shape_2d.cpp +++ b/scene/resources/ray_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/ray_shape_2d.h b/scene/resources/ray_shape_2d.h index c8202ca16c..3570b7be52 100644 --- a/scene/resources/ray_shape_2d.h +++ b/scene/resources/ray_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/ray_shape_3d.cpp b/scene/resources/ray_shape_3d.cpp index 1705fb0f55..a332bb575f 100644 --- a/scene/resources/ray_shape_3d.cpp +++ b/scene/resources/ray_shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/ray_shape_3d.h b/scene/resources/ray_shape_3d.h index a1a6702564..2c27d56c63 100644 --- a/scene/resources/ray_shape_3d.h +++ b/scene/resources/ray_shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp index 949fddf2e7..a5b909c9a7 100644 --- a/scene/resources/rectangle_shape_2d.cpp +++ b/scene/resources/rectangle_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,40 +33,40 @@ #include "servers/physics_server_2d.h" #include "servers/rendering_server.h" void RectangleShape2D::_update_shape() { - PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), extents); + PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), size / 2); emit_changed(); } -void RectangleShape2D::set_extents(const Vector2 &p_extents) { - extents = p_extents; +void RectangleShape2D::set_size(const Vector2 &p_size) { + size = p_size; _update_shape(); } -Vector2 RectangleShape2D::get_extents() const { - return extents; +Vector2 RectangleShape2D::get_size() const { + return size; } void RectangleShape2D::draw(const RID &p_to_rid, const Color &p_color) { - RenderingServer::get_singleton()->canvas_item_add_rect(p_to_rid, Rect2(-extents, extents * 2.0), p_color); + RenderingServer::get_singleton()->canvas_item_add_rect(p_to_rid, Rect2(-size / 2, size), p_color); } Rect2 RectangleShape2D::get_rect() const { - return Rect2(-extents, extents * 2.0); + return Rect2(-size / 2, size); } real_t RectangleShape2D::get_enclosing_radius() const { - return extents.length(); + return size.length() / 2; } void RectangleShape2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_extents", "extents"), &RectangleShape2D::set_extents); - ClassDB::bind_method(D_METHOD("get_extents"), &RectangleShape2D::get_extents); + ClassDB::bind_method(D_METHOD("set_size", "size"), &RectangleShape2D::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &RectangleShape2D::get_size); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "extents"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); } RectangleShape2D::RectangleShape2D() : Shape2D(PhysicsServer2D::get_singleton()->rectangle_shape_create()) { - extents = Vector2(10, 10); + size = Vector2(20, 20); _update_shape(); } diff --git a/scene/resources/rectangle_shape_2d.h b/scene/resources/rectangle_shape_2d.h index 6efa7ab9c8..8d747c86af 100644 --- a/scene/resources/rectangle_shape_2d.h +++ b/scene/resources/rectangle_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,15 +36,15 @@ class RectangleShape2D : public Shape2D { GDCLASS(RectangleShape2D, Shape2D); - Vector2 extents; + Vector2 size; void _update_shape(); protected: static void _bind_methods(); public: - void set_extents(const Vector2 &p_extents); - Vector2 get_extents() const; + void set_size(const Vector2 &p_size); + Vector2 get_size() const; virtual void draw(const RID &p_to_rid, const Color &p_color) override; virtual Rect2 get_rect() const override; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 1d5f29ab14..433e3392e3 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -704,7 +704,6 @@ ResourceLoaderText::ResourceLoaderText() { resources_total = 0; resource_current = 0; - use_sub_threads = false; progress = nullptr; lines = false; @@ -838,6 +837,11 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p f->seek(tag_end); uint8_t c = f->get_8(); + if (c == '\n' && !f->eof_reached()) { + // Skip first newline character since we added one + c = f->get_8(); + } + while (!f->eof_reached()) { fw->store_8(c); c = f->get_8(); diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index ca7b0b021f..e67a13d41a 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/segment_shape_2d.cpp b/scene/resources/segment_shape_2d.cpp index b1001203a1..35439634f8 100644 --- a/scene/resources/segment_shape_2d.cpp +++ b/scene/resources/segment_shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/segment_shape_2d.h b/scene/resources/segment_shape_2d.h index 31a61ea564..f218955061 100644 --- a/scene/resources/segment_shape_2d.h +++ b/scene/resources/segment_shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 76d37eaa71..53f3dd1a4b 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/shader.h b/scene/resources/shader.h index 0feaa179b2..a3f9330055 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp index 94cecc76eb..6f3897b0cd 100644 --- a/scene/resources/shape_2d.cpp +++ b/scene/resources/shape_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h index 495ffdd38b..f5814c42f8 100644 --- a/scene/resources/shape_2d.h +++ b/scene/resources/shape_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/shape_3d.cpp b/scene/resources/shape_3d.cpp index 59766f4f1f..5761a405ce 100644 --- a/scene/resources/shape_3d.cpp +++ b/scene/resources/shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -66,7 +66,7 @@ Ref<ArrayMesh> Shape3D::get_debug_mesh() { debug_mesh_cache = Ref<ArrayMesh>(memnew(ArrayMesh)); - if (!lines.empty()) { + if (!lines.is_empty()) { //make mesh Vector<Vector3> array; array.resize(lines.size()); diff --git a/scene/resources/shape_3d.h b/scene/resources/shape_3d.h index 5a9c2e3b9c..0644940fd4 100644 --- a/scene/resources/shape_3d.h +++ b/scene/resources/shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp index e88841a531..e2df965138 100644 --- a/scene/resources/skin.cpp +++ b/scene/resources/skin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/skin.h b/scene/resources/skin.h index e6ed4f1768..64fe24bbe4 100644 --- a/scene/resources/skin.h +++ b/scene/resources/skin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp index 7e32516f94..8fcd92bb89 100644 --- a/scene/resources/sky.cpp +++ b/scene/resources/sky.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/sky.h b/scene/resources/sky.h index 526ca94317..46f645a53d 100644 --- a/scene/resources/sky.h +++ b/scene/resources/sky.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index 69e8e0b5bd..1cdabe4662 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -568,7 +568,7 @@ PhysicalSkyMaterial::PhysicalSkyMaterial() { code += "\tCOLOR = pow(color, vec3(1.0 / (1.2 + (1.2 * sun_fade))));\n"; code += "\tCOLOR *= exposure;\n"; code += "\t// Make optional, eliminates banding\n"; - code += "\tCOLOR += (hash(EYEDIR * 1741.9782) * 0.08 - 0.04) * 0.008 * dither_strength;\n"; + code += "\tCOLOR += (hash(EYEDIR * 1741.9782) * 0.08 - 0.04) * 0.016 * dither_strength;\n"; code += "}\n"; shader = RS::get_singleton()->shader_create(); diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h index 5411994b7d..8fe015519d 100644 --- a/scene/resources/sky_material.h +++ b/scene/resources/sky_material.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -89,7 +89,7 @@ public: float get_sun_curve() const; virtual Shader::Mode get_shader_mode() const override; - RID get_shader_rid() const; + virtual RID get_shader_rid() const override; ProceduralSkyMaterial(); ~ProceduralSkyMaterial(); @@ -114,7 +114,7 @@ public: Ref<Texture2D> get_panorama() const; virtual Shader::Mode get_shader_mode() const override; - RID get_shader_rid() const; + virtual RID get_shader_rid() const override; PanoramaSkyMaterial(); ~PanoramaSkyMaterial(); @@ -180,7 +180,7 @@ public: Ref<Texture2D> get_night_sky() const; virtual Shader::Mode get_shader_mode() const override; - RID get_shader_rid() const; + virtual RID get_shader_rid() const override; PhysicalSkyMaterial(); ~PhysicalSkyMaterial(); diff --git a/scene/resources/sphere_shape_3d.cpp b/scene/resources/sphere_shape_3d.cpp index 64e0a701b7..008cb3e1d6 100644 --- a/scene/resources/sphere_shape_3d.cpp +++ b/scene/resources/sphere_shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/sphere_shape_3d.h b/scene/resources/sphere_shape_3d.h index 5cad67aea5..eddd2a2132 100644 --- a/scene/resources/sphere_shape_3d.h +++ b/scene/resources/sphere_shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index a8bf44c5c0..93bab1b042 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,26 +38,26 @@ bool StyleBox::test_mask(const Point2 &p_point, const Rect2 &p_rect) const { return true; } -void StyleBox::set_default_margin(Margin p_margin, float p_value) { - ERR_FAIL_INDEX((int)p_margin, 4); +void StyleBox::set_default_margin(Side p_side, float p_value) { + ERR_FAIL_INDEX((int)p_side, 4); - margin[p_margin] = p_value; + margin[p_side] = p_value; emit_changed(); } -float StyleBox::get_default_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); +float StyleBox::get_default_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); - return margin[p_margin]; + return margin[p_side]; } -float StyleBox::get_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); +float StyleBox::get_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); - if (margin[p_margin] < 0) { - return get_style_margin(p_margin); + if (margin[p_side] < 0) { + return get_style_margin(p_side); } else { - return margin[p_margin]; + return margin[p_side]; } } @@ -66,11 +66,11 @@ CanvasItem *StyleBox::get_current_item_drawn() const { } Size2 StyleBox::get_minimum_size() const { - return Size2(get_margin(MARGIN_LEFT) + get_margin(MARGIN_RIGHT), get_margin(MARGIN_TOP) + get_margin(MARGIN_BOTTOM)); + return Size2(get_margin(SIDE_LEFT) + get_margin(SIDE_RIGHT), get_margin(SIDE_TOP) + get_margin(SIDE_BOTTOM)); } Point2 StyleBox::get_offset() const { - return Point2(get_margin(MARGIN_LEFT), get_margin(MARGIN_TOP)); + return Point2(get_margin(SIDE_LEFT), get_margin(SIDE_TOP)); } Size2 StyleBox::get_center_size() const { @@ -99,10 +99,10 @@ void StyleBox::_bind_methods() { ClassDB::bind_method(D_METHOD("draw", "canvas_item", "rect"), &StyleBox::draw); ADD_GROUP("Content Margin", "content_margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_left", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_right", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_top", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_bottom", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", MARGIN_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_left", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_right", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_top", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_bottom", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_BOTTOM); } StyleBox::StyleBox() { @@ -130,10 +130,10 @@ Ref<Texture2D> StyleBoxTexture::get_texture() const { return texture; } -void StyleBoxTexture::set_margin_size(Margin p_margin, float p_size) { - ERR_FAIL_INDEX((int)p_margin, 4); +void StyleBoxTexture::set_margin_size(Side p_side, float p_size) { + ERR_FAIL_INDEX((int)p_side, 4); - margin[p_margin] = p_size; + margin[p_side] = p_size; emit_changed(); static const char *margin_prop[4] = { "content_margin_left", @@ -141,23 +141,23 @@ void StyleBoxTexture::set_margin_size(Margin p_margin, float p_size) { "content_margin_right", "content_margin_bottom", }; - _change_notify(margin_prop[p_margin]); + _change_notify(margin_prop[p_side]); } -float StyleBoxTexture::get_margin_size(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); +float StyleBoxTexture::get_margin_size(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); - return margin[p_margin]; + return margin[p_side]; } -float StyleBoxTexture::get_style_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); +float StyleBoxTexture::get_style_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); - return margin[p_margin]; + return margin[p_side]; } Rect2 StyleBoxTexture::get_draw_rect(const Rect2 &p_rect) const { - return p_rect.grow_individual(expand_margin[MARGIN_LEFT], expand_margin[MARGIN_TOP], expand_margin[MARGIN_RIGHT], expand_margin[MARGIN_BOTTOM]); + return p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]); } void StyleBoxTexture::draw(RID p_canvas_item, const Rect2 &p_rect) const { @@ -170,12 +170,12 @@ void StyleBoxTexture::draw(RID p_canvas_item, const Rect2 &p_rect) const { texture->get_rect_region(rect, src_rect, rect, src_rect); - rect.position.x -= expand_margin[MARGIN_LEFT]; - rect.position.y -= expand_margin[MARGIN_TOP]; - rect.size.x += expand_margin[MARGIN_LEFT] + expand_margin[MARGIN_RIGHT]; - rect.size.y += expand_margin[MARGIN_TOP] + expand_margin[MARGIN_BOTTOM]; + rect.position.x -= expand_margin[SIDE_LEFT]; + rect.position.y -= expand_margin[SIDE_TOP]; + rect.size.x += expand_margin[SIDE_LEFT] + expand_margin[SIDE_RIGHT]; + rect.size.y += expand_margin[SIDE_TOP] + expand_margin[SIDE_BOTTOM]; - RenderingServer::get_singleton()->canvas_item_add_nine_patch(p_canvas_item, rect, src_rect, texture->get_rid(), Vector2(margin[MARGIN_LEFT], margin[MARGIN_TOP]), Vector2(margin[MARGIN_RIGHT], margin[MARGIN_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center, modulate); + RenderingServer::get_singleton()->canvas_item_add_nine_patch(p_canvas_item, rect, src_rect, texture->get_rid(), Vector2(margin[SIDE_LEFT], margin[SIDE_TOP]), Vector2(margin[SIDE_RIGHT], margin[SIDE_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center, modulate); } void StyleBoxTexture::set_draw_center(bool p_enabled) { @@ -195,17 +195,17 @@ Size2 StyleBoxTexture::get_center_size() const { return region_rect.size - get_minimum_size(); } -void StyleBoxTexture::set_expand_margin_size(Margin p_expand_margin, float p_size) { - ERR_FAIL_INDEX((int)p_expand_margin, 4); - expand_margin[p_expand_margin] = p_size; +void StyleBoxTexture::set_expand_margin_size(Side p_side, float p_size) { + ERR_FAIL_INDEX((int)p_side, 4); + expand_margin[p_side] = p_size; emit_changed(); } void StyleBoxTexture::set_expand_margin_size_individual(float p_left, float p_top, float p_right, float p_bottom) { - expand_margin[MARGIN_LEFT] = p_left; - expand_margin[MARGIN_TOP] = p_top; - expand_margin[MARGIN_RIGHT] = p_right; - expand_margin[MARGIN_BOTTOM] = p_bottom; + expand_margin[SIDE_LEFT] = p_left; + expand_margin[SIDE_TOP] = p_top; + expand_margin[SIDE_RIGHT] = p_right; + expand_margin[SIDE_BOTTOM] = p_bottom; emit_changed(); } @@ -216,9 +216,9 @@ void StyleBoxTexture::set_expand_margin_size_all(float p_expand_margin_size) { emit_changed(); } -float StyleBoxTexture::get_expand_margin_size(Margin p_expand_margin) const { - ERR_FAIL_INDEX_V((int)p_expand_margin, 4, 0); - return expand_margin[p_expand_margin]; +float StyleBoxTexture::get_expand_margin_size(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); + return expand_margin[p_side]; } void StyleBoxTexture::set_region_rect(const Rect2 &p_region_rect) { @@ -299,15 +299,15 @@ void StyleBoxTexture::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); ADD_GROUP("Margin", "margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", MARGIN_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_BOTTOM); ADD_GROUP("Expand Margin", "expand_margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", MARGIN_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_BOTTOM); ADD_GROUP("Axis Stretch", "axis_stretch_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode"); @@ -367,15 +367,15 @@ int StyleBoxFlat::get_border_width_min() const { return MIN(MIN(border_width[0], border_width[1]), MIN(border_width[2], border_width[3])); } -void StyleBoxFlat::set_border_width(Margin p_margin, int p_width) { - ERR_FAIL_INDEX((int)p_margin, 4); - border_width[p_margin] = p_width; +void StyleBoxFlat::set_border_width(Side p_side, int p_width) { + ERR_FAIL_INDEX((int)p_side, 4); + border_width[p_side] = p_width; emit_changed(); } -int StyleBoxFlat::get_border_width(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, 0); - return border_width[p_margin]; +int StyleBoxFlat::get_border_width(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); + return border_width[p_side]; } void StyleBoxFlat::set_border_blend(bool p_blend) { @@ -404,16 +404,6 @@ void StyleBoxFlat::set_corner_radius_individual(const int radius_top_left, const emit_changed(); } -int StyleBoxFlat::get_corner_radius_min() const { - int smallest = corner_radius[0]; - for (int i = 1; i < 4; i++) { - if (smallest > corner_radius[i]) { - smallest = corner_radius[i]; - } - } - return smallest; -} - void StyleBoxFlat::set_corner_radius(const Corner p_corner, const int radius) { ERR_FAIL_INDEX((int)p_corner, 4); corner_radius[p_corner] = radius; @@ -425,17 +415,17 @@ int StyleBoxFlat::get_corner_radius(const Corner p_corner) const { return corner_radius[p_corner]; } -void StyleBoxFlat::set_expand_margin_size(Margin p_expand_margin, float p_size) { - ERR_FAIL_INDEX((int)p_expand_margin, 4); - expand_margin[p_expand_margin] = p_size; +void StyleBoxFlat::set_expand_margin_size(Side p_side, float p_size) { + ERR_FAIL_INDEX((int)p_side, 4); + expand_margin[p_side] = p_size; emit_changed(); } void StyleBoxFlat::set_expand_margin_size_individual(float p_left, float p_top, float p_right, float p_bottom) { - expand_margin[MARGIN_LEFT] = p_left; - expand_margin[MARGIN_TOP] = p_top; - expand_margin[MARGIN_RIGHT] = p_right; - expand_margin[MARGIN_BOTTOM] = p_bottom; + expand_margin[SIDE_LEFT] = p_left; + expand_margin[SIDE_TOP] = p_top; + expand_margin[SIDE_RIGHT] = p_right; + expand_margin[SIDE_BOTTOM] = p_bottom; emit_changed(); } @@ -446,9 +436,9 @@ void StyleBoxFlat::set_expand_margin_size_all(float p_expand_margin_size) { emit_changed(); } -float StyleBoxFlat::get_expand_margin_size(Margin p_expand_margin) const { - ERR_FAIL_INDEX_V((int)p_expand_margin, 4, 0.0); - return expand_margin[p_expand_margin]; +float StyleBoxFlat::get_expand_margin_size(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); + return expand_margin[p_side]; } void StyleBoxFlat::set_draw_center(bool p_enabled) { @@ -643,7 +633,7 @@ inline void adapt_values(int p_index_a, int p_index_b, int *adapted_values, cons } Rect2 StyleBoxFlat::get_draw_rect(const Rect2 &p_rect) const { - Rect2 draw_rect = p_rect.grow_individual(expand_margin[MARGIN_LEFT], expand_margin[MARGIN_TOP], expand_margin[MARGIN_RIGHT], expand_margin[MARGIN_BOTTOM]); + Rect2 draw_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]); if (shadow_size > 0) { Rect2 shadow_rect = draw_rect.grow(shadow_size); @@ -662,7 +652,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { return; } - Rect2 style_rect = p_rect.grow_individual(expand_margin[MARGIN_LEFT], expand_margin[MARGIN_TOP], expand_margin[MARGIN_RIGHT], expand_margin[MARGIN_BOTTOM]); + Rect2 style_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]); if (Math::is_zero_approx(style_rect.size.width) || Math::is_zero_approx(style_rect.size.height)) { return; } @@ -681,23 +671,23 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { int width = MAX(style_rect.size.width, 0); int height = MAX(style_rect.size.height, 0); int adapted_border[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; - adapt_values(MARGIN_TOP, MARGIN_BOTTOM, adapted_border, border_width, height, height, height); - adapt_values(MARGIN_LEFT, MARGIN_RIGHT, adapted_border, border_width, width, width, width); + adapt_values(SIDE_TOP, SIDE_BOTTOM, adapted_border, border_width, height, height, height); + adapt_values(SIDE_LEFT, SIDE_RIGHT, adapted_border, border_width, width, width, width); //adapt corners (prevent weird overlapping/glitchy drawings) int adapted_corner[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; - adapt_values(CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, height, height - adapted_border[MARGIN_BOTTOM], height - adapted_border[MARGIN_TOP]); - adapt_values(CORNER_TOP_LEFT, CORNER_BOTTOM_LEFT, adapted_corner, corner_radius, height, height - adapted_border[MARGIN_BOTTOM], height - adapted_border[MARGIN_TOP]); - adapt_values(CORNER_TOP_LEFT, CORNER_TOP_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[MARGIN_RIGHT], width - adapted_border[MARGIN_LEFT]); - adapt_values(CORNER_BOTTOM_LEFT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[MARGIN_RIGHT], width - adapted_border[MARGIN_LEFT]); + adapt_values(CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]); + adapt_values(CORNER_TOP_LEFT, CORNER_BOTTOM_LEFT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]); + adapt_values(CORNER_TOP_LEFT, CORNER_TOP_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]); + adapt_values(CORNER_BOTTOM_LEFT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]); - Rect2 infill_rect = style_rect.grow_individual(-adapted_border[MARGIN_LEFT], -adapted_border[MARGIN_TOP], -adapted_border[MARGIN_RIGHT], -adapted_border[MARGIN_BOTTOM]); + Rect2 infill_rect = style_rect.grow_individual(-adapted_border[SIDE_LEFT], -adapted_border[SIDE_TOP], -adapted_border[SIDE_RIGHT], -adapted_border[SIDE_BOTTOM]); Rect2 border_style_rect = style_rect; if (aa_on) { for (int i = 0; i < 4; i++) { if (border_width[i] > 0) { - border_style_rect = border_style_rect.grow_margin((Margin)i, -aa_size_grow); + border_style_rect = border_style_rect.grow_side((Side)i, -aa_size_grow); } } } @@ -758,8 +748,8 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { } } - Rect2 infill_inner_rect = infill_rect.grow_individual(-aa_border_width[MARGIN_LEFT], -aa_border_width[MARGIN_TOP], - -aa_border_width[MARGIN_RIGHT], -aa_border_width[MARGIN_BOTTOM]); + Rect2 infill_inner_rect = infill_rect.grow_individual(-aa_border_width[SIDE_LEFT], -aa_border_width[SIDE_TOP], + -aa_border_width[SIDE_RIGHT], -aa_border_width[SIDE_BOTTOM]); if (draw_center) { if (!blend_on && draw_border) { @@ -769,8 +759,8 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { } if (!blend_on || !draw_border) { - Rect2 infill_aa_rect = infill_rect.grow_individual(aa_fill_width[MARGIN_LEFT], aa_fill_width[MARGIN_TOP], - aa_fill_width[MARGIN_RIGHT], aa_fill_width[MARGIN_BOTTOM]); + Rect2 infill_aa_rect = infill_rect.grow_individual(aa_fill_width[SIDE_LEFT], aa_fill_width[SIDE_TOP], + aa_fill_width[SIDE_RIGHT], aa_fill_width[SIDE_BOTTOM]); Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0); @@ -806,9 +796,9 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors, uvs); } -float StyleBoxFlat::get_style_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); - return border_width[p_margin]; +float StyleBoxFlat::get_style_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); + return border_width[p_side]; } void StyleBoxFlat::_bind_methods() { @@ -864,10 +854,10 @@ void StyleBoxFlat::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled"); ADD_GROUP("Border Width", "border_width_"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", MARGIN_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_BOTTOM); ADD_GROUP("Border", "border_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color"); @@ -883,10 +873,10 @@ void StyleBoxFlat::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "corner_detail", PROPERTY_HINT_RANGE, "1,20,1"), "set_corner_detail", "get_corner_detail"); ADD_GROUP("Expand Margin", "expand_margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", MARGIN_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", MARGIN_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", MARGIN_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", MARGIN_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM); ADD_GROUP("Shadow", "shadow_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color"); @@ -995,8 +985,8 @@ void StyleBoxLine::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } -float StyleBoxLine::get_style_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V((int)p_margin, 4, thickness); +float StyleBoxLine::get_style_margin(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, thickness); return thickness; } diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index a1237776c6..53ce72790a 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -44,15 +44,15 @@ class StyleBox : public Resource { float margin[4]; protected: - virtual float get_style_margin(Margin p_margin) const = 0; + virtual float get_style_margin(Side p_side) const = 0; static void _bind_methods(); public: virtual bool test_mask(const Point2 &p_point, const Rect2 &p_rect) const; - void set_default_margin(Margin p_margin, float p_value); - float get_default_margin(Margin p_margin) const; - float get_margin(Margin p_margin) const; + void set_default_margin(Side p_side, float p_value); + float get_default_margin(Side p_side) const; + float get_margin(Side p_side) const; virtual Size2 get_center_size() const; virtual Rect2 get_draw_rect(const Rect2 &p_rect) const; @@ -68,7 +68,7 @@ public: class StyleBoxEmpty : public StyleBox { GDCLASS(StyleBoxEmpty, StyleBox); - virtual float get_style_margin(Margin p_margin) const override { return 0; } + virtual float get_style_margin(Side p_side) const override { return 0; } public: virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override {} @@ -96,17 +96,17 @@ private: AxisStretchMode axis_v; protected: - virtual float get_style_margin(Margin p_margin) const override; + virtual float get_style_margin(Side p_side) const override; static void _bind_methods(); public: - void set_expand_margin_size(Margin p_expand_margin, float p_size); + void set_expand_margin_size(Side p_expand_side, float p_size); void set_expand_margin_size_all(float p_expand_margin_size); void set_expand_margin_size_individual(float p_left, float p_top, float p_right, float p_bottom); - float get_expand_margin_size(Margin p_expand_margin) const; + float get_expand_margin_size(Side p_expand_side) const; - void set_margin_size(Margin p_margin, float p_size); - float get_margin_size(Margin p_margin) const; + void set_margin_size(Side p_side, float p_size); + float get_margin_size(Side p_side) const; void set_region_rect(const Rect2 &p_region_rect); Rect2 get_region_rect() const; @@ -157,7 +157,7 @@ class StyleBoxFlat : public StyleBox { int aa_size; protected: - virtual float get_style_margin(Margin p_margin) const override; + virtual float get_style_margin(Side p_side) const override; static void _bind_methods(); public: @@ -174,8 +174,8 @@ public: void set_border_width_all(int p_size); int get_border_width_min() const; - void set_border_width(Margin p_margin, int p_width); - int get_border_width(Margin p_margin) const; + void set_border_width(Side p_side, int p_width); + int get_border_width(Side p_side) const; //blend void set_border_blend(bool p_blend); @@ -184,7 +184,6 @@ public: //CORNER void set_corner_radius_all(int radius); void set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_botton_right, const int radius_bottom_left); - int get_corner_radius_min() const; void set_corner_radius(Corner p_corner, const int radius); int get_corner_radius(Corner p_corner) const; @@ -193,10 +192,10 @@ public: int get_corner_detail() const; //EXPANDS - void set_expand_margin_size(Margin p_expand_margin, float p_size); + void set_expand_margin_size(Side p_expand_side, float p_size); void set_expand_margin_size_all(float p_expand_margin_size); void set_expand_margin_size_individual(float p_left, float p_top, float p_right, float p_bottom); - float get_expand_margin_size(Margin p_expand_margin) const; + float get_expand_margin_size(Side p_expand_side) const; //DRAW CENTER void set_draw_center(bool p_enabled); @@ -238,7 +237,7 @@ class StyleBoxLine : public StyleBox { float grow_end; protected: - virtual float get_style_margin(Margin p_margin) const override; + virtual float get_style_margin(Side p_side) const override; static void _bind_methods(); public: diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index ff14a5a292..dbf5268762 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,11 @@ #define _VERTEX_SNAP 0.0001 #define EQ_VERTEX_DIST 0.00001 +SurfaceTool::OptimizeVertexCacheFunc SurfaceTool::optimize_vertex_cache_func = nullptr; +SurfaceTool::SimplifyFunc SurfaceTool::simplify_func = nullptr; +SurfaceTool::SimplifyScaleFunc SurfaceTool::simplify_scale_func = nullptr; +SurfaceTool::SimplifySloppyFunc SurfaceTool::simplify_sloppy_func = nullptr; + bool SurfaceTool::Vertex::operator==(const Vertex &p_vertex) const { if (vertex != p_vertex.vertex) { return false; @@ -74,6 +79,16 @@ bool SurfaceTool::Vertex::operator==(const Vertex &p_vertex) const { } } + for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) { + if (custom[i] != p_vertex.custom[i]) { + return false; + } + } + + if (smooth_group != p_vertex.smooth_group) { + return false; + } + return true; } @@ -87,6 +102,8 @@ uint32_t SurfaceTool::VertexHasher::hash(const Vertex &p_vtx) { h = hash_djb2_buffer((const uint8_t *)&p_vtx.color, sizeof(real_t) * 4, h); h = hash_djb2_buffer((const uint8_t *)p_vtx.bones.ptr(), p_vtx.bones.size() * sizeof(int), h); h = hash_djb2_buffer((const uint8_t *)p_vtx.weights.ptr(), p_vtx.weights.size() * sizeof(float), h); + h = hash_djb2_buffer((const uint8_t *)&p_vtx.custom[0], sizeof(Color) * RS::ARRAY_CUSTOM_COUNT, h); + h = hash_djb2_one_32(p_vtx.smooth_group, h); return h; } @@ -111,8 +128,13 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) { vtx.bones = last_bones; vtx.tangent = last_tangent.normal; vtx.binormal = last_normal.cross(last_tangent.normal).normalized() * last_tangent.d; + vtx.smooth_group = last_smooth_group; + + for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) { + vtx.custom[i] = last_custom[i]; + } - const int expected_vertices = 4; + const int expected_vertices = skin_weights == SKIN_8_WEIGHTS ? 8 : 4; if ((format & Mesh::ARRAY_FORMAT_WEIGHTS || format & Mesh::ARRAY_FORMAT_BONES) && (vtx.weights.size() != expected_vertices || vtx.bones.size() != expected_vertices)) { //ensure vertices are the expected amount @@ -163,7 +185,7 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) { format |= Mesh::ARRAY_FORMAT_VERTEX; } -void SurfaceTool::add_color(Color p_color) { +void SurfaceTool::set_color(Color p_color) { ERR_FAIL_COND(!begun); ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_COLOR)); @@ -172,7 +194,7 @@ void SurfaceTool::add_color(Color p_color) { last_color = p_color; } -void SurfaceTool::add_normal(const Vector3 &p_normal) { +void SurfaceTool::set_normal(const Vector3 &p_normal) { ERR_FAIL_COND(!begun); ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_NORMAL)); @@ -181,7 +203,7 @@ void SurfaceTool::add_normal(const Vector3 &p_normal) { last_normal = p_normal; } -void SurfaceTool::add_tangent(const Plane &p_tangent) { +void SurfaceTool::set_tangent(const Plane &p_tangent) { ERR_FAIL_COND(!begun); ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_TANGENT)); @@ -189,7 +211,7 @@ void SurfaceTool::add_tangent(const Plane &p_tangent) { last_tangent = p_tangent; } -void SurfaceTool::add_uv(const Vector2 &p_uv) { +void SurfaceTool::set_uv(const Vector2 &p_uv) { ERR_FAIL_COND(!begun); ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_TEX_UV)); @@ -197,7 +219,7 @@ void SurfaceTool::add_uv(const Vector2 &p_uv) { last_uv = p_uv; } -void SurfaceTool::add_uv2(const Vector2 &p_uv2) { +void SurfaceTool::set_uv2(const Vector2 &p_uv2) { ERR_FAIL_COND(!begun); ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_TEX_UV2)); @@ -205,29 +227,45 @@ void SurfaceTool::add_uv2(const Vector2 &p_uv2) { last_uv2 = p_uv2; } -void SurfaceTool::add_bones(const Vector<int> &p_bones) { +void SurfaceTool::set_custom(int p_index, const Color &p_custom) { + ERR_FAIL_INDEX(p_index, RS::ARRAY_CUSTOM_COUNT); + ERR_FAIL_COND(!begun); + ERR_FAIL_COND(last_custom_format[p_index] == CUSTOM_MAX); + static const uint32_t mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 }; + static const uint32_t shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT }; + ERR_FAIL_COND(!first && !(format & mask[p_index])); + + if (first) { + format |= mask[p_index]; + format |= last_custom_format[p_index] << shift[p_index]; + } + last_custom[p_index] = p_custom; +} + +void SurfaceTool::set_bones(const Vector<int> &p_bones) { ERR_FAIL_COND(!begun); ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_BONES)); format |= Mesh::ARRAY_FORMAT_BONES; + if (skin_weights == SKIN_8_WEIGHTS) { + format |= Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS; + } last_bones = p_bones; } -void SurfaceTool::add_weights(const Vector<float> &p_weights) { +void SurfaceTool::set_weights(const Vector<float> &p_weights) { ERR_FAIL_COND(!begun); ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_WEIGHTS)); format |= Mesh::ARRAY_FORMAT_WEIGHTS; + if (skin_weights == SKIN_8_WEIGHTS) { + format |= Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS; + } last_weights = p_weights; } -void SurfaceTool::add_smooth_group(bool p_smooth) { - ERR_FAIL_COND(!begun); - if (index_array.size()) { - smooth_groups[index_array.size()] = p_smooth; - } else { - smooth_groups[vertex_array.size()] = p_smooth; - } +void SurfaceTool::set_smooth_group(uint32_t p_group) { + last_smooth_group = p_group; } void SurfaceTool::add_triangle_fan(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<Color> &p_colors, const Vector<Vector2> &p_uv2s, const Vector<Vector3> &p_normals, const Vector<Plane> &p_tangents) { @@ -238,15 +276,15 @@ void SurfaceTool::add_triangle_fan(const Vector<Vector3> &p_vertices, const Vect #define ADD_POINT(n) \ { \ if (p_colors.size() > n) \ - add_color(p_colors[n]); \ + set_color(p_colors[n]); \ if (p_uvs.size() > n) \ - add_uv(p_uvs[n]); \ + set_uv(p_uvs[n]); \ if (p_uv2s.size() > n) \ - add_uv2(p_uv2s[n]); \ + set_uv2(p_uv2s[n]); \ if (p_normals.size() > n) \ - add_normal(p_normals[n]); \ + set_normal(p_normals[n]); \ if (p_tangents.size() > n) \ - add_tangent(p_tangents[n]); \ + set_tangent(p_tangents[n]); \ add_vertex(p_vertices[n]); \ } @@ -284,9 +322,8 @@ Array SurfaceTool::commit_to_arrays() { array.resize(varr_len); Vector3 *w = array.ptrw(); - int idx = 0; - for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) { - const Vertex &v = E->get(); + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; switch (i) { case Mesh::ARRAY_VERTEX: { @@ -308,9 +345,8 @@ Array SurfaceTool::commit_to_arrays() { array.resize(varr_len); Vector2 *w = array.ptrw(); - int idx = 0; - for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) { - const Vertex &v = E->get(); + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; switch (i) { case Mesh::ARRAY_TEX_UV: { @@ -329,9 +365,8 @@ Array SurfaceTool::commit_to_arrays() { array.resize(varr_len * 4); float *w = array.ptrw(); - int idx = 0; - for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx += 4) { - const Vertex &v = E->get(); + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; w[idx + 0] = v.tangent.x; w[idx + 1] = v.tangent.y; @@ -350,27 +385,165 @@ Array SurfaceTool::commit_to_arrays() { array.resize(varr_len); Color *w = array.ptrw(); - int idx = 0; - for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) { - const Vertex &v = E->get(); + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; + w[idx] = v.color; } a[i] = array; } break; + case Mesh::ARRAY_CUSTOM0: + case Mesh::ARRAY_CUSTOM1: + case Mesh::ARRAY_CUSTOM2: + case Mesh::ARRAY_CUSTOM3: { + int fmt = i - Mesh::ARRAY_CUSTOM0; + switch (last_custom_format[fmt]) { + case CUSTOM_RGBA8_UNORM: { + Vector<uint8_t> array; + array.resize(varr_len * 4); + uint8_t *w = array.ptrw(); + + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; + + const Color &c = v.custom[idx]; + w[idx * 4 + 0] = CLAMP(int32_t(c.r * 255.0), 0, 255); + w[idx * 4 + 1] = CLAMP(int32_t(c.g * 255.0), 0, 255); + w[idx * 4 + 2] = CLAMP(int32_t(c.b * 255.0), 0, 255); + w[idx * 4 + 3] = CLAMP(int32_t(c.a * 255.0), 0, 255); + } + + a[i] = array; + } break; + case CUSTOM_RGBA8_SNORM: { + Vector<uint8_t> array; + array.resize(varr_len * 4); + uint8_t *w = array.ptrw(); + + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; + + const Color &c = v.custom[idx]; + w[idx * 4 + 0] = uint8_t(int8_t(CLAMP(int32_t(c.r * 127.0), -128, 127))); + w[idx * 4 + 1] = uint8_t(int8_t(CLAMP(int32_t(c.g * 127.0), -128, 127))); + w[idx * 4 + 2] = uint8_t(int8_t(CLAMP(int32_t(c.b * 127.0), -128, 127))); + w[idx * 4 + 3] = uint8_t(int8_t(CLAMP(int32_t(c.a * 127.0), -128, 127))); + } + + a[i] = array; + } break; + case CUSTOM_RG_HALF: { + Vector<uint8_t> array; + array.resize(varr_len * 4); + uint16_t *w = (uint16_t *)array.ptrw(); + + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; + + const Color &c = v.custom[idx]; + w[idx * 2 + 0] = Math::make_half_float(c.r); + w[idx * 2 + 1] = Math::make_half_float(c.g); + } + + a[i] = array; + } break; + case CUSTOM_RGBA_HALF: { + Vector<uint8_t> array; + array.resize(varr_len * 8); + uint16_t *w = (uint16_t *)array.ptrw(); + + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; + + const Color &c = v.custom[idx]; + w[idx * 4 + 0] = Math::make_half_float(c.r); + w[idx * 4 + 1] = Math::make_half_float(c.g); + w[idx * 4 + 2] = Math::make_half_float(c.b); + w[idx * 4 + 3] = Math::make_half_float(c.a); + } + + a[i] = array; + } break; + case CUSTOM_R_FLOAT: { + Vector<float> array; + array.resize(varr_len); + float *w = (float *)array.ptrw(); + + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; + + const Color &c = v.custom[idx]; + w[idx] = c.r; + } + + a[i] = array; + } break; + case CUSTOM_RG_FLOAT: { + Vector<float> array; + array.resize(varr_len * 2); + float *w = (float *)array.ptrw(); + + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; + + const Color &c = v.custom[idx]; + w[idx * 2 + 0] = c.r; + w[idx * 2 + 1] = c.g; + } + + a[i] = array; + } break; + case CUSTOM_RGB_FLOAT: { + Vector<float> array; + array.resize(varr_len * 3); + float *w = (float *)array.ptrw(); + + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; + + const Color &c = v.custom[idx]; + w[idx * 3 + 0] = c.r; + w[idx * 3 + 1] = c.g; + w[idx * 3 + 2] = c.b; + } + + a[i] = array; + } break; + case CUSTOM_RGBA_FLOAT: { + Vector<float> array; + array.resize(varr_len * 4); + float *w = (float *)array.ptrw(); + + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; + + const Color &c = v.custom[idx]; + w[idx * 4 + 0] = c.r; + w[idx * 4 + 1] = c.g; + w[idx * 4 + 2] = c.b; + w[idx * 4 + 3] = c.a; + } + + a[i] = array; + } break; + default: { + } //unreachable but compiler warning anyway + } + } break; case Mesh::ARRAY_BONES: { + int count = skin_weights == SKIN_8_WEIGHTS ? 8 : 4; Vector<int> array; - array.resize(varr_len * 4); + array.resize(varr_len * count); int *w = array.ptrw(); - int idx = 0; - for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx += 4) { - const Vertex &v = E->get(); + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; - ERR_CONTINUE(v.bones.size() != 4); + ERR_CONTINUE(v.bones.size() != count); - for (int j = 0; j < 4; j++) { - w[idx + j] = v.bones[j]; + for (int j = 0; j < count; j++) { + w[idx * count + j] = v.bones[j]; } } @@ -379,16 +552,18 @@ Array SurfaceTool::commit_to_arrays() { } break; case Mesh::ARRAY_WEIGHTS: { Vector<float> array; - array.resize(varr_len * 4); + int count = skin_weights == SKIN_8_WEIGHTS ? 8 : 4; + + array.resize(varr_len * count); float *w = array.ptrw(); - int idx = 0; - for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx += 4) { - const Vertex &v = E->get(); - ERR_CONTINUE(v.weights.size() != 4); + for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { + const Vertex &v = vertex_array[idx]; + + ERR_CONTINUE(v.weights.size() != count); - for (int j = 0; j < 4; j++) { - w[idx + j] = v.weights[j]; + for (int j = 0; j < count; j++) { + w[idx * count + j] = v.weights[j]; } } @@ -402,9 +577,8 @@ Array SurfaceTool::commit_to_arrays() { array.resize(index_array.size()); int *w = array.ptrw(); - int idx = 0; - for (List<int>::Element *E = index_array.front(); E; E = E->next(), idx++) { - w[idx] = E->get(); + for (uint32_t idx = 0; idx < index_array.size(); idx++) { + w[idx] = index_array[idx]; } a[i] = array; @@ -451,15 +625,16 @@ void SurfaceTool::index() { } HashMap<Vertex, int, VertexHasher> indices; - List<Vertex> new_vertices; + LocalVector<Vertex> old_vertex_array = vertex_array; + vertex_array.clear(); - for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) { - int *idxptr = indices.getptr(E->get()); + for (uint32_t i = 0; i < old_vertex_array.size(); i++) { + int *idxptr = indices.getptr(old_vertex_array[i]); int idx; if (!idxptr) { idx = indices.size(); - new_vertices.push_back(E->get()); - indices[E->get()] = idx; + vertex_array.push_back(old_vertex_array[i]); + indices[old_vertex_array[i]] = idx; } else { idx = *idxptr; } @@ -467,9 +642,6 @@ void SurfaceTool::index() { index_array.push_back(idx); } - vertex_array.clear(); - vertex_array = new_vertices; - format |= Mesh::ARRAY_FORMAT_INDEX; } @@ -477,29 +649,26 @@ void SurfaceTool::deindex() { if (index_array.size() == 0) { return; //nothing to deindex } - Vector<Vertex> varr; - varr.resize(vertex_array.size()); - int idx = 0; - for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) { - varr.write[idx++] = E->get(); - } + + LocalVector<Vertex> old_vertex_array = vertex_array; vertex_array.clear(); - for (List<int>::Element *E = index_array.front(); E; E = E->next()) { - ERR_FAIL_INDEX(E->get(), varr.size()); - vertex_array.push_back(varr[E->get()]); + for (uint32_t i = 0; i < index_array.size(); i++) { + uint32_t index = index_array[i]; + ERR_FAIL_COND(index >= old_vertex_array.size()); + vertex_array.push_back(old_vertex_array[index]); } format &= ~Mesh::ARRAY_FORMAT_INDEX; index_array.clear(); } -void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, List<Vertex> *r_vertex, List<int> *r_index, int &lformat) { +void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat) { Array arr = p_existing->surface_get_arrays(p_surface); ERR_FAIL_COND(arr.size() != RS::ARRAY_MAX); _create_list_from_arrays(arr, r_vertex, r_index, lformat); } -Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays) { - Vector<SurfaceTool::Vertex> ret; +void SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<SurfaceTool::Vertex> &ret, uint32_t *r_format) { + ret.clear(); Vector<Vector3> varr = p_arrays[RS::ARRAY_VERTEX]; Vector<Vector3> narr = p_arrays[RS::ARRAY_NORMAL]; @@ -509,10 +678,14 @@ Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_array Vector<Vector2> uv2arr = p_arrays[RS::ARRAY_TEX_UV2]; Vector<int> barr = p_arrays[RS::ARRAY_BONES]; Vector<float> warr = p_arrays[RS::ARRAY_WEIGHTS]; + Vector<float> custom_float[RS::ARRAY_CUSTOM_COUNT]; int vc = varr.size(); if (vc == 0) { - return ret; + if (r_format) { + *r_format = 0; + } + return; } int lformat = 0; @@ -534,100 +707,40 @@ Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_array if (uv2arr.size()) { lformat |= RS::ARRAY_FORMAT_TEX_UV2; } - if (barr.size()) { + int wcount = 0; + if (barr.size() && warr.size()) { lformat |= RS::ARRAY_FORMAT_BONES; - } - if (warr.size()) { lformat |= RS::ARRAY_FORMAT_WEIGHTS; - } - for (int i = 0; i < vc; i++) { - Vertex v; - if (lformat & RS::ARRAY_FORMAT_VERTEX) { - v.vertex = varr[i]; - } - if (lformat & RS::ARRAY_FORMAT_NORMAL) { - v.normal = narr[i]; - } - if (lformat & RS::ARRAY_FORMAT_TANGENT) { - Plane p(tarr[i * 4 + 0], tarr[i * 4 + 1], tarr[i * 4 + 2], tarr[i * 4 + 3]); - v.tangent = p.normal; - v.binormal = p.normal.cross(v.tangent).normalized() * p.d; - } - if (lformat & RS::ARRAY_FORMAT_COLOR) { - v.color = carr[i]; - } - if (lformat & RS::ARRAY_FORMAT_TEX_UV) { - v.uv = uvarr[i]; - } - if (lformat & RS::ARRAY_FORMAT_TEX_UV2) { - v.uv2 = uv2arr[i]; + wcount = barr.size() / varr.size(); + if (wcount == 8) { + lformat |= RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS; } - if (lformat & RS::ARRAY_FORMAT_BONES) { - Vector<int> b; - b.resize(4); - b.write[0] = barr[i * 4 + 0]; - b.write[1] = barr[i * 4 + 1]; - b.write[2] = barr[i * 4 + 2]; - b.write[3] = barr[i * 4 + 3]; - v.bones = b; - } - if (lformat & RS::ARRAY_FORMAT_WEIGHTS) { - Vector<float> w; - w.resize(4); - w.write[0] = warr[i * 4 + 0]; - w.write[1] = warr[i * 4 + 1]; - w.write[2] = warr[i * 4 + 2]; - w.write[3] = warr[i * 4 + 3]; - v.weights = w; - } - - ret.push_back(v); } - return ret; -} - -void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, int &lformat) { - Vector<Vector3> varr = arr[RS::ARRAY_VERTEX]; - Vector<Vector3> narr = arr[RS::ARRAY_NORMAL]; - Vector<float> tarr = arr[RS::ARRAY_TANGENT]; - Vector<Color> carr = arr[RS::ARRAY_COLOR]; - Vector<Vector2> uvarr = arr[RS::ARRAY_TEX_UV]; - Vector<Vector2> uv2arr = arr[RS::ARRAY_TEX_UV2]; - Vector<int> barr = arr[RS::ARRAY_BONES]; - Vector<float> warr = arr[RS::ARRAY_WEIGHTS]; - - int vc = varr.size(); - if (vc == 0) { - return; - } - - lformat = 0; - if (varr.size()) { - lformat |= RS::ARRAY_FORMAT_VERTEX; - } - if (narr.size()) { - lformat |= RS::ARRAY_FORMAT_NORMAL; - } - if (tarr.size()) { - lformat |= RS::ARRAY_FORMAT_TANGENT; - } - if (carr.size()) { - lformat |= RS::ARRAY_FORMAT_COLOR; - } - if (uvarr.size()) { - lformat |= RS::ARRAY_FORMAT_TEX_UV; - } - if (uv2arr.size()) { - lformat |= RS::ARRAY_FORMAT_TEX_UV2; - } - if (barr.size()) { - lformat |= RS::ARRAY_FORMAT_BONES; - } if (warr.size()) { lformat |= RS::ARRAY_FORMAT_WEIGHTS; } + static const uint32_t custom_mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 }; + static const uint32_t custom_shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT }; + + for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) { + ERR_CONTINUE_MSG(p_arrays[RS::ARRAY_CUSTOM0 + i].get_type() == Variant::PACKED_BYTE_ARRAY, "Extracting Byte/Half formats is not supported"); + if (p_arrays[RS::ARRAY_CUSTOM0 + i].get_type() == Variant::PACKED_FLOAT32_ARRAY) { + lformat |= custom_mask[i]; + custom_float[i] = p_arrays[RS::ARRAY_CUSTOM0 + i]; + int fmt = custom_float[i].size() / varr.size(); + if (fmt == 1) { + lformat |= CUSTOM_R_FLOAT << custom_shift[i]; + } else if (fmt == 2) { + lformat |= CUSTOM_RG_FLOAT << custom_shift[i]; + } else if (fmt == 3) { + lformat |= CUSTOM_RGB_FLOAT << custom_shift[i]; + } else if (fmt == 4) { + lformat |= CUSTOM_RGBA_FLOAT << custom_shift[i]; + } + } + } for (int i = 0; i < vc; i++) { Vertex v; @@ -653,27 +766,44 @@ void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, Li } if (lformat & RS::ARRAY_FORMAT_BONES) { Vector<int> b; - b.resize(4); - b.write[0] = barr[i * 4 + 0]; - b.write[1] = barr[i * 4 + 1]; - b.write[2] = barr[i * 4 + 2]; - b.write[3] = barr[i * 4 + 3]; + b.resize(wcount); + for (int j = 0; j < wcount; j++) { + b.write[j] = barr[i * wcount + j]; + } v.bones = b; } if (lformat & RS::ARRAY_FORMAT_WEIGHTS) { Vector<float> w; - w.resize(4); - w.write[0] = warr[i * 4 + 0]; - w.write[1] = warr[i * 4 + 1]; - w.write[2] = warr[i * 4 + 2]; - w.write[3] = warr[i * 4 + 3]; + w.resize(wcount); + for (int j = 0; j < wcount; j++) { + w.write[j] = warr[i * wcount + j]; + } v.weights = w; } - r_vertex->push_back(v); + for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) { + if (lformat & custom_mask[j]) { + int cc = custom_float[j].size() / varr.size(); + for (int k = 0; k < cc; k++) { + v.custom[j][k] = custom_float[j][i * cc + k]; + } + } + } + + ret.push_back(v); } + if (r_format) { + *r_format = lformat; + } +} + +void SurfaceTool::_create_list_from_arrays(Array arr, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat) { + create_vertex_array_from_triangle_arrays(arr, *r_vertex, &lformat); + ERR_FAIL_COND(r_vertex->size() == 0); + //indices + r_index->clear(); Vector<int> idx = arr[RS::ARRAY_INDEX]; int is = idx.size(); @@ -725,15 +855,15 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const format = 0; } - int nformat; - List<Vertex> nvertices; - List<int> nindices; + uint32_t nformat; + LocalVector<Vertex> nvertices; + LocalVector<int> nindices; _create_list(p_existing, p_surface, &nvertices, &nindices, nformat); format |= nformat; int vfrom = vertex_array.size(); - for (List<Vertex>::Element *E = nvertices.front(); E; E = E->next()) { - Vertex v = E->get(); + for (uint32_t vi = 0; vi < nvertices.size(); vi++) { + Vertex v = nvertices[vi]; v.vertex = p_xform.xform(v.vertex); if (nformat & RS::ARRAY_FORMAT_NORMAL) { v.normal = p_xform.basis.xform(v.normal); @@ -746,8 +876,8 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const vertex_array.push_back(v); } - for (List<int>::Element *E = nindices.front(); E; E = E->next()) { - int dst_index = E->get() + vfrom; + for (uint32_t i = 0; i < nindices.size(); i++) { + int dst_index = nindices[i] + vfrom; index_array.push_back(dst_index); } if (index_array.size() % 3) { @@ -758,18 +888,18 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const //mikktspace callbacks namespace { struct TangentGenerationContextUserData { - Vector<List<SurfaceTool::Vertex>::Element *> vertices; - Vector<List<int>::Element *> indices; + LocalVector<SurfaceTool::Vertex> *vertices; + LocalVector<int> *indices; }; } // namespace int SurfaceTool::mikktGetNumFaces(const SMikkTSpaceContext *pContext) { TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData); - if (triangle_data.indices.size() > 0) { - return triangle_data.indices.size() / 3; + if (triangle_data.indices->size() > 0) { + return triangle_data.indices->size() / 3; } else { - return triangle_data.vertices.size() / 3; + return triangle_data.vertices->size() / 3; } } @@ -780,13 +910,13 @@ int SurfaceTool::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, c void SurfaceTool::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert) { TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData); Vector3 v; - if (triangle_data.indices.size() > 0) { - int index = triangle_data.indices[iFace * 3 + iVert]->get(); - if (index < triangle_data.vertices.size()) { - v = triangle_data.vertices[index]->get().vertex; + if (triangle_data.indices->size() > 0) { + uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert); + if (index < triangle_data.vertices->size()) { + v = triangle_data.vertices->operator[](index).vertex; } } else { - v = triangle_data.vertices[iFace * 3 + iVert]->get().vertex; + v = triangle_data.vertices->operator[](iFace * 3 + iVert).vertex; } fvPosOut[0] = v.x; @@ -797,13 +927,13 @@ void SurfaceTool::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvP void SurfaceTool::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert) { TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData); Vector3 v; - if (triangle_data.indices.size() > 0) { - int index = triangle_data.indices[iFace * 3 + iVert]->get(); - if (index < triangle_data.vertices.size()) { - v = triangle_data.vertices[index]->get().normal; + if (triangle_data.indices->size() > 0) { + uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert); + if (index < triangle_data.vertices->size()) { + v = triangle_data.vertices->operator[](index).normal; } } else { - v = triangle_data.vertices[iFace * 3 + iVert]->get().normal; + v = triangle_data.vertices->operator[](iFace * 3 + iVert).normal; } fvNormOut[0] = v.x; @@ -814,13 +944,13 @@ void SurfaceTool::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNor void SurfaceTool::mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert) { TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData); Vector2 v; - if (triangle_data.indices.size() > 0) { - int index = triangle_data.indices[iFace * 3 + iVert]->get(); - if (index < triangle_data.vertices.size()) { - v = triangle_data.vertices[index]->get().uv; + if (triangle_data.indices->size() > 0) { + uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert); + if (index < triangle_data.vertices->size()) { + v = triangle_data.vertices->operator[](index).uv; } } else { - v = triangle_data.vertices[iFace * 3 + iVert]->get().uv; + v = triangle_data.vertices->operator[](iFace * 3 + iVert).uv; } fvTexcOut[0] = v.x; @@ -831,13 +961,13 @@ void SurfaceTool::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, cons const tbool bIsOrientationPreserving, const int iFace, const int iVert) { TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData); Vertex *vtx = nullptr; - if (triangle_data.indices.size() > 0) { - int index = triangle_data.indices[iFace * 3 + iVert]->get(); - if (index < triangle_data.vertices.size()) { - vtx = &triangle_data.vertices[index]->get(); + if (triangle_data.indices->size() > 0) { + uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert); + if (index < triangle_data.vertices->size()) { + vtx = &triangle_data.vertices->operator[](index); } } else { - vtx = &triangle_data.vertices[iFace * 3 + iVert]->get(); + vtx = &triangle_data.vertices->operator[](iFace * 3 + iVert); } if (vtx != nullptr) { @@ -863,18 +993,12 @@ void SurfaceTool::generate_tangents() { msc.m_pInterface = &mkif; TangentGenerationContextUserData triangle_data; - triangle_data.vertices.resize(vertex_array.size()); - int idx = 0; - for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) { - triangle_data.vertices.write[idx++] = E; - E->get().binormal = Vector3(); - E->get().tangent = Vector3(); - } - triangle_data.indices.resize(index_array.size()); - idx = 0; - for (List<int>::Element *E = index_array.front(); E; E = E->next()) { - triangle_data.indices.write[idx++] = E; + triangle_data.vertices = &vertex_array; + for (uint32_t i = 0; i < vertex_array.size(); i++) { + vertex_array[i].binormal = Vector3(); + vertex_array[i].tangent = Vector3(); } + triangle_data.indices = &index_array; msc.m_pUserData = &triangle_data; bool res = genTangSpaceDefault(&msc); @@ -890,66 +1014,36 @@ void SurfaceTool::generate_normals(bool p_flip) { deindex(); - HashMap<Vertex, Vector3, VertexHasher> vertex_hash; + ERR_FAIL_COND((vertex_array.size() % 3) != 0); - int count = 0; - bool smooth = false; - if (smooth_groups.has(0)) { - smooth = smooth_groups[0]; - } + HashMap<Vertex, Vector3, VertexHasher> vertex_hash; - List<Vertex>::Element *B = vertex_array.front(); - for (List<Vertex>::Element *E = B; E;) { - List<Vertex>::Element *v[3]; - v[0] = E; - v[1] = v[0]->next(); - ERR_FAIL_COND(!v[1]); - v[2] = v[1]->next(); - ERR_FAIL_COND(!v[2]); - E = v[2]->next(); + for (uint32_t vi = 0; vi < vertex_array.size(); vi += 3) { + Vertex *v = &vertex_array[vi]; Vector3 normal; if (!p_flip) { - normal = Plane(v[0]->get().vertex, v[1]->get().vertex, v[2]->get().vertex).normal; - } else { - normal = Plane(v[2]->get().vertex, v[1]->get().vertex, v[0]->get().vertex).normal; - } - - if (smooth) { - for (int i = 0; i < 3; i++) { - Vector3 *lv = vertex_hash.getptr(v[i]->get()); - if (!lv) { - vertex_hash.set(v[i]->get(), normal); - } else { - (*lv) += normal; - } - } + normal = Plane(v[0].vertex, v[1].vertex, v[2].vertex).normal; } else { - for (int i = 0; i < 3; i++) { - v[i]->get().normal = normal; - } + normal = Plane(v[2].vertex, v[1].vertex, v[0].vertex).normal; } - count += 3; - - if (smooth_groups.has(count) || !E) { - if (vertex_hash.size()) { - while (B != E) { - Vector3 *lv = vertex_hash.getptr(B->get()); - if (lv) { - B->get().normal = lv->normalized(); - } - - B = B->next(); - } + for (int i = 0; i < 3; i++) { + Vector3 *lv = vertex_hash.getptr(v[i]); + if (!lv) { + vertex_hash.set(v[i], normal); } else { - B = E; + (*lv) += normal; } + } + } - vertex_hash.clear(); - if (E) { - smooth = smooth_groups[count]; - } + for (uint32_t vi = 0; vi < vertex_array.size(); vi++) { + Vector3 *lv = vertex_hash.getptr(vertex_array[vi]); + if (!lv) { + vertex_array[vi].normal = Vector3(); + } else { + vertex_array[vi].normal = lv->normalized(); } } @@ -957,7 +1051,6 @@ void SurfaceTool::generate_normals(bool p_flip) { if (was_indexed) { index(); - smooth_groups.clear(); } } @@ -973,22 +1066,97 @@ void SurfaceTool::clear() { last_weights.clear(); index_array.clear(); vertex_array.clear(); - smooth_groups.clear(); material.unref(); + last_smooth_group = 0; + for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) { + last_custom_format[i] = CUSTOM_MAX; + } + skin_weights = SKIN_4_WEIGHTS; +} + +void SurfaceTool::set_skin_weight_count(SkinWeightCount p_weights) { + ERR_FAIL_COND(begun); + skin_weights = p_weights; +} +SurfaceTool::SkinWeightCount SurfaceTool::get_skin_weight_count() const { + return skin_weights; +} + +void SurfaceTool::set_custom_format(int p_index, CustomFormat p_format) { + ERR_FAIL_INDEX(p_index, RS::ARRAY_CUSTOM_COUNT); + ERR_FAIL_COND(begun); + last_custom_format[p_index] = p_format; +} +SurfaceTool::CustomFormat SurfaceTool::get_custom_format(int p_index) const { + ERR_FAIL_INDEX_V(p_index, RS::ARRAY_CUSTOM_COUNT, CUSTOM_MAX); + return last_custom_format[p_index]; +} +void SurfaceTool::optimize_indices_for_cache() { + ERR_FAIL_COND(optimize_vertex_cache_func == nullptr); + ERR_FAIL_COND(index_array.size() == 0); + + LocalVector old_index_array = index_array; + zeromem(index_array.ptr(), index_array.size() * sizeof(int)); + optimize_vertex_cache_func((unsigned int *)index_array.ptr(), (unsigned int *)old_index_array.ptr(), old_index_array.size(), vertex_array.size()); +} + +float SurfaceTool::get_max_axis_length() const { + ERR_FAIL_COND_V(vertex_array.size() == 0, 0); + + AABB aabb; + for (uint32_t i = 0; i < vertex_array.size(); i++) { + if (i == 0) { + aabb.position = vertex_array[i].vertex; + } else { + aabb.expand_to(vertex_array[i].vertex); + } + } + + return aabb.get_longest_axis_size(); +} +Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_count) { + Vector<int> lod; + + ERR_FAIL_COND_V(simplify_func == nullptr, lod); + ERR_FAIL_COND_V(vertex_array.size() == 0, lod); + ERR_FAIL_COND_V(index_array.size() == 0, lod); + + lod.resize(index_array.size()); + LocalVector<float> vertices; //uses floats + vertices.resize(vertex_array.size() * 3); + for (uint32_t i = 0; i < vertex_array.size(); i++) { + vertices[i * 3 + 0] = vertex_array[i].vertex.x; + vertices[i * 3 + 1] = vertex_array[i].vertex.y; + vertices[i * 3 + 2] = vertex_array[i].vertex.z; + } + + float error; + uint32_t index_count = simplify_func((unsigned int *)lod.ptrw(), (unsigned int *)index_array.ptr(), index_array.size(), vertices.ptr(), vertex_array.size(), sizeof(float) * 3, p_target_index_count, p_threshold, &error); + ERR_FAIL_COND_V(index_count == 0, lod); + lod.resize(index_count); + + return lod; } void SurfaceTool::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_skin_weight_count", "count"), &SurfaceTool::set_skin_weight_count); + ClassDB::bind_method(D_METHOD("get_skin_weight_count"), &SurfaceTool::get_skin_weight_count); + + ClassDB::bind_method(D_METHOD("set_custom_format", "index", "format"), &SurfaceTool::set_custom_format); + ClassDB::bind_method(D_METHOD("get_custom_format", "index"), &SurfaceTool::get_custom_format); + ClassDB::bind_method(D_METHOD("begin", "primitive"), &SurfaceTool::begin); ClassDB::bind_method(D_METHOD("add_vertex", "vertex"), &SurfaceTool::add_vertex); - ClassDB::bind_method(D_METHOD("add_color", "color"), &SurfaceTool::add_color); - ClassDB::bind_method(D_METHOD("add_normal", "normal"), &SurfaceTool::add_normal); - ClassDB::bind_method(D_METHOD("add_tangent", "tangent"), &SurfaceTool::add_tangent); - ClassDB::bind_method(D_METHOD("add_uv", "uv"), &SurfaceTool::add_uv); - ClassDB::bind_method(D_METHOD("add_uv2", "uv2"), &SurfaceTool::add_uv2); - ClassDB::bind_method(D_METHOD("add_bones", "bones"), &SurfaceTool::add_bones); - ClassDB::bind_method(D_METHOD("add_weights", "weights"), &SurfaceTool::add_weights); - ClassDB::bind_method(D_METHOD("add_smooth_group", "smooth"), &SurfaceTool::add_smooth_group); + ClassDB::bind_method(D_METHOD("set_color", "color"), &SurfaceTool::set_color); + ClassDB::bind_method(D_METHOD("set_normal", "normal"), &SurfaceTool::set_normal); + ClassDB::bind_method(D_METHOD("set_tangent", "tangent"), &SurfaceTool::set_tangent); + ClassDB::bind_method(D_METHOD("set_uv", "uv"), &SurfaceTool::set_uv); + ClassDB::bind_method(D_METHOD("set_uv2", "uv2"), &SurfaceTool::set_uv2); + ClassDB::bind_method(D_METHOD("set_bones", "bones"), &SurfaceTool::set_bones); + ClassDB::bind_method(D_METHOD("set_weights", "weights"), &SurfaceTool::set_weights); + ClassDB::bind_method(D_METHOD("set_custom", "index", "custom"), &SurfaceTool::set_custom); + ClassDB::bind_method(D_METHOD("set_smooth_group", "index"), &SurfaceTool::set_smooth_group); ClassDB::bind_method(D_METHOD("add_triangle_fan", "vertices", "uvs", "colors", "uv2s", "normals", "tangents"), &SurfaceTool::add_triangle_fan, DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Color>()), DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Vector3>()), DEFVAL(Vector<Plane>())); @@ -999,6 +1167,11 @@ void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("generate_normals", "flip"), &SurfaceTool::generate_normals, DEFVAL(false)); ClassDB::bind_method(D_METHOD("generate_tangents"), &SurfaceTool::generate_tangents); + ClassDB::bind_method(D_METHOD("optimize_indices_for_cache"), &SurfaceTool::optimize_indices_for_cache); + + ClassDB::bind_method(D_METHOD("get_max_axis_length"), &SurfaceTool::get_max_axis_length); + ClassDB::bind_method(D_METHOD("generate_lod", "nd_threshold", "target_index_count"), &SurfaceTool::generate_lod, DEFVAL(3)); + ClassDB::bind_method(D_METHOD("set_material", "material"), &SurfaceTool::set_material); ClassDB::bind_method(D_METHOD("clear"), &SurfaceTool::clear); @@ -1006,13 +1179,29 @@ void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("create_from", "existing", "surface"), &SurfaceTool::create_from); ClassDB::bind_method(D_METHOD("create_from_blend_shape", "existing", "surface", "blend_shape"), &SurfaceTool::create_from_blend_shape); ClassDB::bind_method(D_METHOD("append_from", "existing", "surface", "transform"), &SurfaceTool::append_from); - ClassDB::bind_method(D_METHOD("commit", "existing", "flags"), &SurfaceTool::commit, DEFVAL(Variant()), DEFVAL(Mesh::ARRAY_COMPRESS_DEFAULT)); + ClassDB::bind_method(D_METHOD("commit", "existing", "flags"), &SurfaceTool::commit, DEFVAL(Variant()), DEFVAL(0)); ClassDB::bind_method(D_METHOD("commit_to_arrays"), &SurfaceTool::commit_to_arrays); + + BIND_ENUM_CONSTANT(CUSTOM_RGBA8_UNORM); + BIND_ENUM_CONSTANT(CUSTOM_RGBA8_SNORM); + BIND_ENUM_CONSTANT(CUSTOM_RG_HALF); + BIND_ENUM_CONSTANT(CUSTOM_RGBA_HALF); + BIND_ENUM_CONSTANT(CUSTOM_R_FLOAT); + BIND_ENUM_CONSTANT(CUSTOM_RG_FLOAT); + BIND_ENUM_CONSTANT(CUSTOM_RGB_FLOAT); + BIND_ENUM_CONSTANT(CUSTOM_RGBA_FLOAT); + BIND_ENUM_CONSTANT(CUSTOM_MAX); + BIND_ENUM_CONSTANT(SKIN_4_WEIGHTS); + BIND_ENUM_CONSTANT(SKIN_8_WEIGHTS); } SurfaceTool::SurfaceTool() { first = false; begun = false; + for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) { + last_custom_format[i] = CUSTOM_MAX; + } primitive = Mesh::PRIMITIVE_LINES; + skin_weights = SKIN_4_WEIGHTS; format = 0; } diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index d7b255e695..ea6069e7c1 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,8 +31,8 @@ #ifndef SURFACE_TOOL_H #define SURFACE_TOOL_H +#include "core/templates/local_vector.h" #include "scene/resources/mesh.h" - #include "thirdparty/misc/mikktspace.h" class SurfaceTool : public Reference { @@ -49,12 +49,40 @@ public: Vector2 uv2; Vector<int> bones; Vector<float> weights; + Color custom[RS::ARRAY_CUSTOM_COUNT]; + uint32_t smooth_group = 0; bool operator==(const Vertex &p_vertex) const; Vertex() {} }; + enum CustomFormat { + CUSTOM_RGBA8_UNORM = RS::ARRAY_CUSTOM_RGBA8_UNORM, + CUSTOM_RGBA8_SNORM = RS::ARRAY_CUSTOM_RGBA8_SNORM, + CUSTOM_RG_HALF = RS::ARRAY_CUSTOM_RG_HALF, + CUSTOM_RGBA_HALF = RS::ARRAY_CUSTOM_RGBA_HALF, + CUSTOM_R_FLOAT = RS::ARRAY_CUSTOM_R_FLOAT, + CUSTOM_RG_FLOAT = RS::ARRAY_CUSTOM_RG_FLOAT, + CUSTOM_RGB_FLOAT = RS::ARRAY_CUSTOM_RGB_FLOAT, + CUSTOM_RGBA_FLOAT = RS::ARRAY_CUSTOM_RGBA_FLOAT, + CUSTOM_MAX = RS::ARRAY_CUSTOM_MAX + }; + + enum SkinWeightCount { + SKIN_4_WEIGHTS, + SKIN_8_WEIGHTS + }; + + typedef void (*OptimizeVertexCacheFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count); + static OptimizeVertexCacheFunc optimize_vertex_cache_func; + typedef size_t (*SimplifyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float *r_error); + static SimplifyFunc simplify_func; + typedef float (*SimplifyScaleFunc)(const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride); + static SimplifyScaleFunc simplify_scale_func; + typedef size_t (*SimplifySloppyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float *out_result_error); + static SimplifySloppyFunc simplify_sloppy_func; + private: struct VertexHasher { static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx); @@ -71,12 +99,11 @@ private: bool begun; bool first; Mesh::PrimitiveType primitive; - int format; + uint32_t format; Ref<Material> material; //arrays - List<Vertex> vertex_array; - List<int> index_array; - Map<int, bool> smooth_groups; + LocalVector<Vertex> vertex_array; + LocalVector<int> index_array; //memory Color last_color; @@ -86,9 +113,16 @@ private: Vector<int> last_bones; Vector<float> last_weights; Plane last_tangent; + uint32_t last_smooth_group = 0; + + SkinWeightCount skin_weights; - void _create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, int &lformat); - void _create_list(const Ref<Mesh> &p_existing, int p_surface, List<Vertex> *r_vertex, List<int> *r_index, int &lformat); + Color last_custom[RS::ARRAY_CUSTOM_COUNT]; + + CustomFormat last_custom_format[RS::ARRAY_CUSTOM_COUNT]; + + void _create_list_from_arrays(Array arr, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat); + void _create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat); //mikktspace callbacks static int mikktGetNumFaces(const SMikkTSpaceContext *pContext); @@ -103,17 +137,25 @@ protected: static void _bind_methods(); public: + void set_skin_weight_count(SkinWeightCount p_weights); + SkinWeightCount get_skin_weight_count() const; + + void set_custom_format(int p_index, CustomFormat p_format); + CustomFormat get_custom_format(int p_index) const; + void begin(Mesh::PrimitiveType p_primitive); + void set_color(Color p_color); + void set_normal(const Vector3 &p_normal); + void set_tangent(const Plane &p_tangent); + void set_uv(const Vector2 &p_uv); + void set_uv2(const Vector2 &p_uv2); + void set_custom(int p_index, const Color &p_custom); + void set_bones(const Vector<int> &p_bones); + void set_weights(const Vector<float> &p_weights); + void set_smooth_group(uint32_t p_group); + void add_vertex(const Vector3 &p_vertex); - void add_color(Color p_color); - void add_normal(const Vector3 &p_normal); - void add_tangent(const Plane &p_tangent); - void add_uv(const Vector2 &p_uv); - void add_uv2(const Vector2 &p_uv2); - void add_bones(const Vector<int> &p_bones); - void add_weights(const Vector<float> &p_weights); - void add_smooth_group(bool p_smooth); void add_triangle_fan(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs = Vector<Vector2>(), const Vector<Color> &p_colors = Vector<Color>(), const Vector<Vector2> &p_uv2s = Vector<Vector2>(), const Vector<Vector3> &p_normals = Vector<Vector3>(), const Vector<Plane> &p_tangents = Vector<Plane>()); @@ -124,21 +166,28 @@ public: void generate_normals(bool p_flip = false); void generate_tangents(); + void optimize_indices_for_cache(); + float get_max_axis_length() const; + Vector<int> generate_lod(float p_threshold, int p_target_index_count = 3); + void set_material(const Ref<Material> &p_material); void clear(); - List<Vertex> &get_vertex_array() { return vertex_array; } + LocalVector<Vertex> &get_vertex_array() { return vertex_array; } void create_from_triangle_arrays(const Array &p_arrays); - static Vector<Vertex> create_vertex_array_from_triangle_arrays(const Array &p_arrays); + static void create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<Vertex> &ret, uint32_t *r_format = nullptr); Array commit_to_arrays(); void create_from(const Ref<Mesh> &p_existing, int p_surface); void create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String &p_blend_shape_name); void append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform); - Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_flags = Mesh::ARRAY_COMPRESS_DEFAULT); + Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_flags = 0); SurfaceTool(); }; +VARIANT_ENUM_CAST(SurfaceTool::CustomFormat) +VARIANT_ENUM_CAST(SurfaceTool::SkinWeightCount) + #endif diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp index e3e4373fa9..f3f881a774 100644 --- a/scene/resources/syntax_highlighter.cpp +++ b/scene/resources/syntax_highlighter.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -552,7 +552,7 @@ Dictionary CodeHighlighter::get_color_regions() const { Dictionary r_color_regions; for (int i = 0; i < color_regions.size(); i++) { ColorRegion region = color_regions[i]; - r_color_regions[region.start_key + (region.end_key.empty() ? "" : " " + region.end_key)] = region.color; + r_color_regions[region.start_key + (region.end_key.is_empty() ? "" : " " + region.end_key)] = region.color; } return r_color_regions; } diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h index 62865920d3..c9db8e31a2 100644 --- a/scene/resources/syntax_highlighter.h +++ b/scene/resources/syntax_highlighter.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp index e3bd5ce0ae..cf07003720 100644 --- a/scene/resources/text_file.cpp +++ b/scene/resources/text_file.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/text_file.h b/scene/resources/text_file.h index fa812b1e67..005075a218 100644 --- a/scene/resources/text_file.h +++ b/scene/resources/text_file.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp new file mode 100644 index 0000000000..ed69c093cf --- /dev/null +++ b/scene/resources/text_line.cpp @@ -0,0 +1,369 @@ +/*************************************************************************/ +/* text_line.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "text_line.h" + +void TextLine::_bind_methods() { + ClassDB::bind_method(D_METHOD("clear"), &TextLine::clear); + + ClassDB::bind_method(D_METHOD("set_direction", "direction"), &TextLine::set_direction); + ClassDB::bind_method(D_METHOD("get_direction"), &TextLine::get_direction); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction"); + + ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextLine::set_orientation); + ClassDB::bind_method(D_METHOD("get_orientation"), &TextLine::get_orientation); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "orientation", PROPERTY_HINT_ENUM, "Horizontal,Orientation"), "set_orientation", "get_orientation"); + + ClassDB::bind_method(D_METHOD("set_preserve_invalid", "enabled"), &TextLine::set_preserve_invalid); + ClassDB::bind_method(D_METHOD("get_preserve_invalid"), &TextLine::get_preserve_invalid); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_invalid"), "set_preserve_invalid", "get_preserve_invalid"); + + ClassDB::bind_method(D_METHOD("set_preserve_control", "enabled"), &TextLine::set_preserve_control); + ClassDB::bind_method(D_METHOD("get_preserve_control"), &TextLine::get_preserve_control); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control"); + + ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::_set_bidi_override); + + ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL("")); + ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(VALIGN_CENTER)); + + ClassDB::bind_method(D_METHOD("set_width", "width"), &TextLine::set_width); + ClassDB::bind_method(D_METHOD("get_width"), &TextLine::get_width); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width"); + + ClassDB::bind_method(D_METHOD("set_align", "align"), &TextLine::set_align); + ClassDB::bind_method(D_METHOD("get_align"), &TextLine::get_align); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align"); + + ClassDB::bind_method(D_METHOD("tab_align", "tab_stops"), &TextLine::tab_align); + + ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextLine::set_flags); + ClassDB::bind_method(D_METHOD("get_flags"), &TextLine::get_flags); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida justification,Word justification,Trim edge spaces after justification,Justification only after last tab"), "set_flags", "get_flags"); + + ClassDB::bind_method(D_METHOD("get_objects"), &TextLine::get_objects); + ClassDB::bind_method(D_METHOD("get_object_rect", "key"), &TextLine::get_object_rect); + + ClassDB::bind_method(D_METHOD("get_size"), &TextLine::get_size); + + ClassDB::bind_method(D_METHOD("get_rid"), &TextLine::get_rid); + + ClassDB::bind_method(D_METHOD("get_line_ascent"), &TextLine::get_line_ascent); + ClassDB::bind_method(D_METHOD("get_line_descent"), &TextLine::get_line_descent); + ClassDB::bind_method(D_METHOD("get_line_width"), &TextLine::get_line_width); + ClassDB::bind_method(D_METHOD("get_line_underline_position"), &TextLine::get_line_underline_position); + ClassDB::bind_method(D_METHOD("get_line_underline_thickness"), &TextLine::get_line_underline_thickness); + + ClassDB::bind_method(D_METHOD("draw", "canvas", "pos", "color"), &TextLine::draw, DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "pos", "outline_size", "color"), &TextLine::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1))); + + ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextLine::hit_test); +} + +void TextLine::_shape() { + if (dirty) { + if (!tab_stops.is_empty()) { + TS->shaped_text_tab_align(rid, tab_stops); + } + if (align == HALIGN_FILL) { + TS->shaped_text_fit_to_width(rid, width, flags); + } + dirty = false; + } +} + +RID TextLine::get_rid() const { + return rid; +} + +void TextLine::clear() { + TS->shaped_text_clear(rid); + spacing_top = 0; + spacing_bottom = 0; +} + +void TextLine::set_preserve_invalid(bool p_enabled) { + TS->shaped_text_set_preserve_invalid(rid, p_enabled); + dirty = true; +} + +bool TextLine::get_preserve_invalid() const { + return TS->shaped_text_get_preserve_invalid(rid); +} + +void TextLine::set_preserve_control(bool p_enabled) { + TS->shaped_text_set_preserve_control(rid, p_enabled); + dirty = true; +} + +bool TextLine::get_preserve_control() const { + return TS->shaped_text_get_preserve_control(rid); +} + +void TextLine::set_direction(TextServer::Direction p_direction) { + TS->shaped_text_set_direction(rid, p_direction); + dirty = true; +} + +TextServer::Direction TextLine::get_direction() const { + return TS->shaped_text_get_direction(rid); +} + +void TextLine::set_orientation(TextServer::Orientation p_orientation) { + TS->shaped_text_set_orientation(rid, p_orientation); + dirty = true; +} + +TextServer::Orientation TextLine::get_orientation() const { + return TS->shaped_text_get_orientation(rid); +} + +void TextLine::_set_bidi_override(const Array &p_override) { + Vector<Vector2i> overrides; + for (int i = 0; i < p_override.size(); i++) { + overrides.push_back(p_override[i]); + } + set_bidi_override(overrides); +} + +void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) { + TS->shaped_text_set_bidi_override(rid, p_override); + dirty = true; +} + +bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { + bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); + spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); + spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); + dirty = true; + return res; +} + +bool TextLine::add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) { + bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); + dirty = true; + return res; +} + +bool TextLine::resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align) { + const_cast<TextLine *>(this)->_shape(); + return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); +} + +Array TextLine::get_objects() const { + return TS->shaped_text_get_objects(rid); +} + +Rect2 TextLine::get_object_rect(Variant p_key) const { + return TS->shaped_text_get_object_rect(rid, p_key); +} + +void TextLine::set_align(HAlign p_align) { + if (align != p_align) { + if (align == HALIGN_FILL || p_align == HALIGN_FILL) { + align = p_align; + dirty = true; + } else { + align = p_align; + } + } +} + +HAlign TextLine::get_align() const { + return align; +} + +void TextLine::tab_align(const Vector<float> &p_tab_stops) { + tab_stops = p_tab_stops; + dirty = true; +} + +void TextLine::set_flags(uint8_t p_flags) { + if (flags != p_flags) { + flags = p_flags; + dirty = true; + } +} + +uint8_t TextLine::get_flags() const { + return flags; +} + +void TextLine::set_width(float p_width) { + width = p_width; + if (align == HALIGN_FILL) { + dirty = true; + } +} + +float TextLine::get_width() const { + return width; +} + +Size2 TextLine::get_size() const { + const_cast<TextLine *>(this)->_shape(); + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom); + } else { + return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y); + } +} + +float TextLine::get_line_ascent() const { + const_cast<TextLine *>(this)->_shape(); + return TS->shaped_text_get_ascent(rid) + spacing_top; +} + +float TextLine::get_line_descent() const { + const_cast<TextLine *>(this)->_shape(); + return TS->shaped_text_get_descent(rid) + spacing_bottom; +} + +float TextLine::get_line_width() const { + const_cast<TextLine *>(this)->_shape(); + return TS->shaped_text_get_width(rid); +} + +float TextLine::get_line_underline_position() const { + const_cast<TextLine *>(this)->_shape(); + return TS->shaped_text_get_underline_position(rid); +} + +float TextLine::get_line_underline_thickness() const { + const_cast<TextLine *>(this)->_shape(); + return TS->shaped_text_get_underline_thickness(rid); +} + +void TextLine::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const { + const_cast<TextLine *>(this)->_shape(); + + Vector2 ofs = p_pos; + + float length = TS->shaped_text_get_width(rid); + if (width > 0) { + switch (align) { + case HALIGN_FILL: + case HALIGN_LEFT: + break; + case HALIGN_CENTER: { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((width - length) / 2.0); + } else { + ofs.y += Math::floor((width - length) / 2.0); + } + } break; + case HALIGN_RIGHT: { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - length; + } else { + ofs.y += width - length; + } + } break; + } + } + + float clip_l; + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top; + clip_l = MAX(0, p_pos.x - ofs.x); + } else { + ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top; + clip_l = MAX(0, p_pos.y - ofs.y); + } + return TS->shaped_text_draw(rid, p_canvas, ofs, clip_l, clip_l + width, p_color); +} + +void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color) const { + const_cast<TextLine *>(this)->_shape(); + + Vector2 ofs = p_pos; + + float length = TS->shaped_text_get_width(rid); + if (width > 0) { + switch (align) { + case HALIGN_FILL: + case HALIGN_LEFT: + break; + case HALIGN_CENTER: { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((width - length) / 2.0); + } else { + ofs.y += Math::floor((width - length) / 2.0); + } + } break; + case HALIGN_RIGHT: { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - length; + } else { + ofs.y += width - length; + } + } break; + } + } + + float clip_l; + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top; + clip_l = MAX(0, p_pos.x - ofs.x); + } else { + ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top; + clip_l = MAX(0, p_pos.y - ofs.y); + } + return TS->shaped_text_draw_outline(rid, p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color); +} + +int TextLine::hit_test(float p_coords) const { + const_cast<TextLine *>(this)->_shape(); + + return TS->shaped_text_hit_test_position(rid, p_coords); +} + +TextLine::TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { + rid = TS->create_shaped_text(p_direction, p_orientation); + spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); + spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); + TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); +} + +TextLine::TextLine() { + rid = TS->create_shaped_text(); +} + +TextLine::~TextLine() { + TS->free(rid); +} diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h new file mode 100644 index 0000000000..3e0a74a84b --- /dev/null +++ b/scene/resources/text_line.h @@ -0,0 +1,116 @@ +/*************************************************************************/ +/* text_line.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEXT_LINE_H +#define TEXT_LINE_H + +#include "scene/resources/font.h" +#include "servers/text_server.h" + +/*************************************************************************/ + +class TextLine : public Reference { + GDCLASS(TextLine, Reference); + + RID rid; + int spacing_top = 0; + int spacing_bottom = 0; + + bool dirty = true; + + float width = -1; + uint8_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; + HAlign align = HALIGN_LEFT; + + Vector<float> tab_stops; + +protected: + static void _bind_methods(); + + void _shape(); + +public: + RID get_rid() const; + + void clear(); + + void set_direction(TextServer::Direction p_direction); + TextServer::Direction get_direction() const; + + void set_bidi_override(const Vector<Vector2i> &p_override); + + void set_orientation(TextServer::Orientation p_orientation); + TextServer::Orientation get_orientation() const; + + void set_preserve_invalid(bool p_enabled); + bool get_preserve_invalid() const; + + void set_preserve_control(bool p_enabled); + bool get_preserve_control() const; + + bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); + bool add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1); + bool resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER); + + void set_align(HAlign p_align); + HAlign get_align() const; + + void tab_align(const Vector<float> &p_tab_stops); + + void set_flags(uint8_t p_flags); + uint8_t get_flags() const; + + void set_width(float p_width); + float get_width() const; + + Array get_objects() const; + Rect2 get_object_rect(Variant p_key) const; + + Size2 get_size() const; + + float get_line_ascent() const; + float get_line_descent() const; + float get_line_width() const; + float get_line_underline_position() const; + float get_line_underline_thickness() const; + + void draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1)) const; + void draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const; + + int hit_test(float p_coords) const; + + void _set_bidi_override(const Array &p_override); + + TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); + TextLine(); + ~TextLine(); +}; + +#endif // TEXT_LINE_H diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp new file mode 100644 index 0000000000..94957df510 --- /dev/null +++ b/scene/resources/text_paragraph.cpp @@ -0,0 +1,723 @@ +/*************************************************************************/ +/* text_paragraph.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "scene/resources/text_paragraph.h" + +void TextParagraph::_bind_methods() { + ClassDB::bind_method(D_METHOD("clear"), &TextParagraph::clear); + + ClassDB::bind_method(D_METHOD("set_direction", "direction"), &TextParagraph::set_direction); + ClassDB::bind_method(D_METHOD("get_direction"), &TextParagraph::get_direction); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction"); + + ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextParagraph::set_orientation); + ClassDB::bind_method(D_METHOD("get_orientation"), &TextParagraph::get_orientation); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "orientation", PROPERTY_HINT_ENUM, "Horizontal,Orientation"), "set_orientation", "get_orientation"); + + ClassDB::bind_method(D_METHOD("set_preserve_invalid", "enabled"), &TextParagraph::set_preserve_invalid); + ClassDB::bind_method(D_METHOD("get_preserve_invalid"), &TextParagraph::get_preserve_invalid); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_invalid"), "set_preserve_invalid", "get_preserve_invalid"); + + ClassDB::bind_method(D_METHOD("set_preserve_control", "enabled"), &TextParagraph::set_preserve_control); + ClassDB::bind_method(D_METHOD("get_preserve_control"), &TextParagraph::get_preserve_control); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control"); + + ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::_set_bidi_override); + + ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL("")); + ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap); + + ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL("")); + ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(VALIGN_CENTER)); + + ClassDB::bind_method(D_METHOD("set_align", "align"), &TextParagraph::set_align); + ClassDB::bind_method(D_METHOD("get_align"), &TextParagraph::get_align); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align"); + + ClassDB::bind_method(D_METHOD("tab_align", "tab_stops"), &TextParagraph::tab_align); + + ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextParagraph::set_flags); + ClassDB::bind_method(D_METHOD("get_flags"), &TextParagraph::get_flags); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida justification,Word justification,Trim edge spaces after justification,Justification only after last tab,Break mandatory,Break words,Break graphemes"), "set_flags", "get_flags"); + + ClassDB::bind_method(D_METHOD("set_width", "width"), &TextParagraph::set_width); + ClassDB::bind_method(D_METHOD("get_width"), &TextParagraph::get_width); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width"); + + ClassDB::bind_method(D_METHOD("get_non_wraped_size"), &TextParagraph::get_non_wraped_size); + ClassDB::bind_method(D_METHOD("get_size"), &TextParagraph::get_size); + + ClassDB::bind_method(D_METHOD("get_rid"), &TextParagraph::get_rid); + ClassDB::bind_method(D_METHOD("get_line_rid", "line"), &TextParagraph::get_line_rid); + ClassDB::bind_method(D_METHOD("get_dropcap_rid"), &TextParagraph::get_dropcap_rid); + + ClassDB::bind_method(D_METHOD("get_line_count"), &TextParagraph::get_line_count); + + ClassDB::bind_method(D_METHOD("get_line_objects", "line"), &TextParagraph::get_line_objects); + ClassDB::bind_method(D_METHOD("get_line_object_rect", "line", "key"), &TextParagraph::get_line_object_rect); + ClassDB::bind_method(D_METHOD("get_line_size", "line"), &TextParagraph::get_line_size); + ClassDB::bind_method(D_METHOD("get_line_range", "line"), &TextParagraph::get_line_range); + ClassDB::bind_method(D_METHOD("get_line_ascent", "line"), &TextParagraph::get_line_ascent); + ClassDB::bind_method(D_METHOD("get_line_descent", "line"), &TextParagraph::get_line_descent); + ClassDB::bind_method(D_METHOD("get_line_width", "line"), &TextParagraph::get_line_width); + ClassDB::bind_method(D_METHOD("get_line_underline_position", "line"), &TextParagraph::get_line_underline_position); + ClassDB::bind_method(D_METHOD("get_line_underline_thickness", "line"), &TextParagraph::get_line_underline_thickness); + + ClassDB::bind_method(D_METHOD("get_dropcap_size"), &TextParagraph::get_dropcap_size); + ClassDB::bind_method(D_METHOD("get_dropcap_lines"), &TextParagraph::get_dropcap_lines); + + ClassDB::bind_method(D_METHOD("draw", "canvas", "pos", "color", "dc_color"), &TextParagraph::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "pos", "outline_size", "color", "dc_color"), &TextParagraph::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1))); + + ClassDB::bind_method(D_METHOD("draw_line", "canvas", "pos", "line", "color"), &TextParagraph::draw_line, DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("draw_line_outline", "canvas", "pos", "line", "outline_size", "color"), &TextParagraph::draw_line_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1))); + + ClassDB::bind_method(D_METHOD("draw_dropcap", "canvas", "pos", "color"), &TextParagraph::draw_dropcap, DEFVAL(Color(1, 1, 1))); + ClassDB::bind_method(D_METHOD("draw_dropcap_outline", "canvas", "pos", "outline_size", "color"), &TextParagraph::draw_dropcap_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1))); + + ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextParagraph::hit_test); +} + +void TextParagraph::_shape_lines() { + if (dirty_lines) { + for (int i = 0; i < lines.size(); i++) { + TS->free(lines[i]); + } + lines.clear(); + + if (!tab_stops.is_empty()) { + TS->shaped_text_tab_align(rid, tab_stops); + } + + float h_offset = 0.f; + float v_offset = 0.f; + int start = 0; + dropcap_lines = 0; + + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x; + v_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y; + } else { + h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y; + v_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x; + } + + if (h_offset > 0) { + // Dropcap, flow around. + Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags); + for (int i = 0; i < line_breaks.size(); i++) { + RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x); + float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x; + if (v_offset < h) { + TS->free(line); + break; + } + if (!tab_stops.is_empty()) { + TS->shaped_text_tab_align(line, tab_stops); + } + if (align == HALIGN_FILL && (line_breaks.size() == 1 || i < line_breaks.size() - 1)) { + TS->shaped_text_fit_to_width(line, width - h_offset, flags); + } + dropcap_lines++; + v_offset -= h; + start = line_breaks[i].y; + lines.push_back(line); + } + } + // Use fixed for the rest of lines. + Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags); + for (int i = 0; i < line_breaks.size(); i++) { + RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x); + if (!tab_stops.is_empty()) { + TS->shaped_text_tab_align(line, tab_stops); + } + if (align == HALIGN_FILL && (line_breaks.size() == 1 || i < line_breaks.size() - 1)) { + TS->shaped_text_fit_to_width(line, width, flags); + } + lines.push_back(line); + } + dirty_lines = false; + } +} + +RID TextParagraph::get_rid() const { + return rid; +} + +RID TextParagraph::get_line_rid(int p_line) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), RID()); + return lines[p_line]; +} + +RID TextParagraph::get_dropcap_rid() const { + return dropcap_rid; +} + +void TextParagraph::clear() { + spacing_top = 0; + spacing_bottom = 0; + for (int i = 0; i < lines.size(); i++) { + TS->free(lines[i]); + } + lines.clear(); + TS->shaped_text_clear(rid); + TS->shaped_text_clear(dropcap_rid); +} + +void TextParagraph::set_preserve_invalid(bool p_enabled) { + TS->shaped_text_set_preserve_invalid(rid, p_enabled); + TS->shaped_text_set_preserve_invalid(dropcap_rid, p_enabled); + dirty_lines = true; +} + +bool TextParagraph::get_preserve_invalid() const { + return TS->shaped_text_get_preserve_invalid(rid); +} + +void TextParagraph::set_preserve_control(bool p_enabled) { + TS->shaped_text_set_preserve_control(rid, p_enabled); + TS->shaped_text_set_preserve_control(dropcap_rid, p_enabled); + dirty_lines = true; +} + +bool TextParagraph::get_preserve_control() const { + return TS->shaped_text_get_preserve_control(rid); +} + +void TextParagraph::set_direction(TextServer::Direction p_direction) { + TS->shaped_text_set_direction(rid, p_direction); + TS->shaped_text_set_direction(dropcap_rid, p_direction); + dirty_lines = true; +} + +TextServer::Direction TextParagraph::get_direction() const { + const_cast<TextParagraph *>(this)->_shape_lines(); + return TS->shaped_text_get_direction(rid); +} + +void TextParagraph::set_orientation(TextServer::Orientation p_orientation) { + TS->shaped_text_set_orientation(rid, p_orientation); + TS->shaped_text_set_orientation(dropcap_rid, p_orientation); + dirty_lines = true; +} + +TextServer::Orientation TextParagraph::get_orientation() const { + const_cast<TextParagraph *>(this)->_shape_lines(); + return TS->shaped_text_get_orientation(rid); +} + +bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins, const Dictionary &p_opentype_features, const String &p_language) { + TS->shaped_text_clear(dropcap_rid); + dropcap_margins = p_dropcap_margins; + bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); + dirty_lines = true; + return res; +} + +void TextParagraph::clear_dropcap() { + dropcap_margins = Rect2(); + TS->shaped_text_clear(dropcap_rid); + dirty_lines = true; +} + +bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { + bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); + spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); + spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); + dirty_lines = true; + return res; +} + +void TextParagraph::_set_bidi_override(const Array &p_override) { + Vector<Vector2i> overrides; + for (int i = 0; i < p_override.size(); i++) { + overrides.push_back(p_override[i]); + } + set_bidi_override(overrides); +} + +void TextParagraph::set_bidi_override(const Vector<Vector2i> &p_override) { + TS->shaped_text_set_bidi_override(rid, p_override); + dirty_lines = true; +} + +bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) { + bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); + dirty_lines = true; + return res; +} + +bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align) { + bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); + dirty_lines = true; + return res; +} + +void TextParagraph::set_align(HAlign p_align) { + if (align != p_align) { + if (align == HALIGN_FILL || p_align == HALIGN_FILL) { + align = p_align; + dirty_lines = true; + } else { + align = p_align; + } + } +} + +HAlign TextParagraph::get_align() const { + return align; +} + +void TextParagraph::tab_align(const Vector<float> &p_tab_stops) { + tab_stops = p_tab_stops; + dirty_lines = true; +} + +void TextParagraph::set_flags(uint8_t p_flags) { + if (flags != p_flags) { + flags = p_flags; + dirty_lines = true; + } +} + +uint8_t TextParagraph::get_flags() const { + return flags; +} + +void TextParagraph::set_width(float p_width) { + if (width != p_width) { + width = p_width; + dirty_lines = true; + } +} + +float TextParagraph::get_width() const { + return width; +} + +Size2 TextParagraph::get_non_wraped_size() const { + const_cast<TextParagraph *>(this)->_shape_lines(); + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom); + } else { + return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y); + } +} + +Size2 TextParagraph::get_size() const { + const_cast<TextParagraph *>(this)->_shape_lines(); + Size2 size; + for (int i = 0; i < lines.size(); i++) { + Size2 lsize = TS->shaped_text_get_size(lines[i]); + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + size.x = MAX(size.x, lsize.x); + size.y += lsize.y + spacing_top + spacing_bottom; + } else { + size.x += lsize.x + spacing_top + spacing_bottom; + size.y = MAX(size.y, lsize.y); + } + } + return size; +} + +int TextParagraph::get_line_count() const { + const_cast<TextParagraph *>(this)->_shape_lines(); + return lines.size(); +} + +Array TextParagraph::get_line_objects(int p_line) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), Array()); + return TS->shaped_text_get_objects(lines[p_line]); +} + +Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), Rect2()); + Rect2 xrect = TS->shaped_text_get_object_rect(lines[p_line], p_key); + for (int i = 0; i < p_line; i++) { + Size2 lsize = TS->shaped_text_get_size(lines[i]); + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + xrect.position.y += lsize.y + spacing_top + spacing_bottom; + } else { + xrect.position.x += lsize.x + spacing_top + spacing_bottom; + } + } + return xrect; +} + +Size2 TextParagraph::get_line_size(int p_line) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), Size2()); + if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { + return Size2(TS->shaped_text_get_size(lines[p_line]).x, TS->shaped_text_get_size(lines[p_line]).y + spacing_top + spacing_bottom); + } else { + return Size2(TS->shaped_text_get_size(lines[p_line]).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(lines[p_line]).y); + } +} + +Vector2i TextParagraph::get_line_range(int p_line) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), Vector2i()); + return TS->shaped_text_get_range(lines[p_line]); +} + +float TextParagraph::get_line_ascent(int p_line) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f); + return TS->shaped_text_get_ascent(lines[p_line]) + spacing_top; +} + +float TextParagraph::get_line_descent(int p_line) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f); + return TS->shaped_text_get_descent(lines[p_line]) + spacing_bottom; +} + +float TextParagraph::get_line_width(int p_line) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f); + return TS->shaped_text_get_width(lines[p_line]); +} + +float TextParagraph::get_line_underline_position(int p_line) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f); + return TS->shaped_text_get_underline_position(lines[p_line]); +} + +float TextParagraph::get_line_underline_thickness(int p_line) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f); + return TS->shaped_text_get_underline_thickness(lines[p_line]); +} + +Size2 TextParagraph::get_dropcap_size() const { + return TS->shaped_text_get_size(dropcap_rid) + dropcap_margins.size + dropcap_margins.position; +} + +int TextParagraph::get_dropcap_lines() const { + return dropcap_lines; +} + +void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color, const Color &p_dc_color) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + Vector2 ofs = p_pos; + float h_offset = 0.f; + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x; + } else { + h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y; + } + + if (h_offset > 0) { + // Draw dropcap. + Vector2 dc_off = ofs; + if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + dc_off.x += width - h_offset; + } else { + dc_off.y += width - h_offset; + } + } + TS->shaped_text_draw(dropcap_rid, p_canvas, dc_off + Vector2(0, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.size.y + dropcap_margins.position.y / 2), -1, -1, p_dc_color); + } + + for (int i = 0; i < lines.size(); i++) { + float l_width = width; + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x = p_pos.x; + ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top; + if (i <= dropcap_lines) { + if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { + ofs.x -= h_offset; + } + l_width -= h_offset; + } + } else { + ofs.y = p_pos.y; + ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top; + if (i <= dropcap_lines) { + if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { + ofs.x -= h_offset; + } + l_width -= h_offset; + } + } + float length = TS->shaped_text_get_width(lines[i]); + if (width > 0) { + switch (align) { + case HALIGN_FILL: + case HALIGN_LEFT: + break; + case HALIGN_CENTER: { + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((l_width - length) / 2.0); + } else { + ofs.y += Math::floor((l_width - length) / 2.0); + } + } break; + case HALIGN_RIGHT: { + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - length; + } else { + ofs.y += l_width - length; + } + } break; + } + } + float clip_l; + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + clip_l = MAX(0, p_pos.x - ofs.x); + } else { + clip_l = MAX(0, p_pos.y - ofs.y); + } + TS->shaped_text_draw(lines[i], p_canvas, ofs, clip_l, clip_l + l_width, p_color); + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x = p_pos.x; + ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom; + } else { + ofs.y = p_pos.y; + ofs.x += TS->shaped_text_get_descent(lines[i]) + spacing_bottom; + } + } +} + +void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color, const Color &p_dc_color) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + Vector2 ofs = p_pos; + + float h_offset = 0.f; + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x; + } else { + h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y; + } + + if (h_offset > 0) { + // Draw dropcap. + Vector2 dc_off = ofs; + if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + dc_off.x += width - h_offset; + } else { + dc_off.y += width - h_offset; + } + } + TS->shaped_text_draw_outline(dropcap_rid, p_canvas, dc_off + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_dc_color); + } + + for (int i = 0; i < lines.size(); i++) { + float l_width = width; + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x = p_pos.x; + ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top; + if (i <= dropcap_lines) { + if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { + ofs.x -= h_offset; + } + l_width -= h_offset; + } + } else { + ofs.y = p_pos.y; + ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top; + if (i <= dropcap_lines) { + if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { + ofs.x -= h_offset; + } + l_width -= h_offset; + } + } + float length = TS->shaped_text_get_width(lines[i]); + if (width > 0) { + switch (align) { + case HALIGN_FILL: + case HALIGN_LEFT: + break; + case HALIGN_CENTER: { + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((l_width - length) / 2.0); + } else { + ofs.y += Math::floor((l_width - length) / 2.0); + } + } break; + case HALIGN_RIGHT: { + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - length; + } else { + ofs.y += l_width - length; + } + } break; + } + } + float clip_l; + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + clip_l = MAX(0, p_pos.x - ofs.x); + } else { + clip_l = MAX(0, p_pos.y - ofs.y); + } + TS->shaped_text_draw_outline(lines[i], p_canvas, ofs, clip_l, clip_l + l_width, p_outline_size, p_color); + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x = p_pos.x; + ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom; + } else { + ofs.y = p_pos.y; + ofs.x += TS->shaped_text_get_descent(lines[i]) + spacing_bottom; + } + } +} + +int TextParagraph::hit_test(const Point2 &p_coords) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + Vector2 ofs; + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + if (ofs.y < 0) + return 0; + } else { + if (ofs.x < 0) + return 0; + } + for (int i = 0; i < lines.size(); i++) { + if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { + if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines[i]).y)) { + return TS->shaped_text_hit_test_position(lines[i], p_coords.x); + } + ofs.y += TS->shaped_text_get_size(lines[i]).y + spacing_bottom + spacing_top; + } else { + if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(lines[i]).x)) { + return TS->shaped_text_hit_test_position(lines[i], p_coords.y); + } + ofs.y += TS->shaped_text_get_size(lines[i]).x + spacing_bottom + spacing_top; + } + } + return TS->shaped_text_get_range(rid).y; +} + +void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const { + Vector2 ofs = p_pos; + float h_offset = 0.f; + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x; + } else { + h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y; + } + + if (h_offset > 0) { + // Draw dropcap. + if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - h_offset; + } else { + ofs.y += width - h_offset; + } + } + TS->shaped_text_draw(dropcap_rid, p_canvas, ofs + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_color); + } +} + +void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color) const { + Vector2 ofs = p_pos; + float h_offset = 0.f; + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x; + } else { + h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y; + } + + if (h_offset > 0) { + // Draw dropcap. + if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - h_offset; + } else { + ofs.y += width - h_offset; + } + } + TS->shaped_text_draw_outline(dropcap_rid, p_canvas, ofs + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_color); + } +} + +void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND(p_line < 0 || p_line >= lines.size()); + + Vector2 ofs = p_pos; + + if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top; + } else { + ofs.x += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top; + } + return TS->shaped_text_draw(lines[p_line], p_canvas, ofs, -1, -1, p_color); +} + +void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size, const Color &p_color) const { + const_cast<TextParagraph *>(this)->_shape_lines(); + ERR_FAIL_COND(p_line < 0 || p_line >= lines.size()); + + Vector2 ofs = p_pos; + if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top; + } else { + ofs.x += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top; + } + return TS->shaped_text_draw_outline(lines[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color); +} + +TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { + rid = TS->create_shaped_text(p_direction, p_orientation); + TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); + spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); + spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); + width = p_width; +} + +TextParagraph::TextParagraph() { + rid = TS->create_shaped_text(); + dropcap_rid = TS->create_shaped_text(); +} + +TextParagraph::~TextParagraph() { + for (int i = 0; i < lines.size(); i++) { + TS->free(lines[i]); + } + lines.clear(); + TS->free(rid); + TS->free(dropcap_rid); +} diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h new file mode 100644 index 0000000000..e58c157b01 --- /dev/null +++ b/scene/resources/text_paragraph.h @@ -0,0 +1,140 @@ +/*************************************************************************/ +/* text_paragraph.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEXT_PARAGRAPH_H +#define TEXT_PARAGRAPH_H + +#include "scene/resources/font.h" +#include "servers/text_server.h" + +/*************************************************************************/ + +class TextParagraph : public Reference { + GDCLASS(TextParagraph, Reference); + + RID dropcap_rid; + int dropcap_lines = 0; + Rect2 dropcap_margins; + + RID rid; + Vector<RID> lines; + int spacing_top = 0; + int spacing_bottom = 0; + + bool dirty_lines = true; + + float width = -1; + uint8_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; + HAlign align = HALIGN_LEFT; + + Vector<float> tab_stops; + +protected: + static void _bind_methods(); + + void _shape_lines(); + +public: + RID get_rid() const; + RID get_line_rid(int p_line) const; + RID get_dropcap_rid() const; + + void clear(); + + void set_direction(TextServer::Direction p_direction); + TextServer::Direction get_direction() const; + + void set_orientation(TextServer::Orientation p_orientation); + TextServer::Orientation get_orientation() const; + + void set_preserve_invalid(bool p_enabled); + bool get_preserve_invalid() const; + + void set_preserve_control(bool p_enabled); + bool get_preserve_control() const; + + void set_bidi_override(const Vector<Vector2i> &p_override); + + bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); + void clear_dropcap(); + + bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); + bool add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1); + bool resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER); + + void set_align(HAlign p_align); + HAlign get_align() const; + + void tab_align(const Vector<float> &p_tab_stops); + + void set_flags(uint8_t p_flags); + uint8_t get_flags() const; + + void set_width(float p_width); + float get_width() const; + + Size2 get_non_wraped_size() const; + + Size2 get_size() const; + + int get_line_count() const; + + Array get_line_objects(int p_line) const; + Rect2 get_line_object_rect(int p_line, Variant p_key) const; + Size2 get_line_size(int p_line) const; + float get_line_ascent(int p_line) const; + float get_line_descent(int p_line) const; + float get_line_width(int p_line) const; + Vector2i get_line_range(int p_line) const; + float get_line_underline_position(int p_line) const; + float get_line_underline_thickness(int p_line) const; + + Size2 get_dropcap_size() const; + int get_dropcap_lines() const; + + void draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1), const Color &p_dc_color = Color(1, 1, 1)) const; + void draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1), const Color &p_dc_color = Color(1, 1, 1)) const; + + void draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color = Color(1, 1, 1)) const; + void draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const; + + void draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1)) const; + void draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const; + + int hit_test(const Point2 &p_coords) const; + + void _set_bidi_override(const Array &p_override); + + TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); + TextParagraph(); + ~TextParagraph(); +}; + +#endif // TEXT_PARAGRAPH_H diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 1507537cd0..342a97fd85 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -371,8 +371,8 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit img = Image::lossy_unpacker(pv); } - if (img.is_null() || img->empty()) { - ERR_FAIL_COND_V(img.is_null() || img->empty(), Ref<Image>()); + if (img.is_null() || img->is_empty()) { + ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>()); } if (first) { @@ -548,7 +548,7 @@ Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &t memdelete(f); - if (image.is_null() || image->empty()) { + if (image.is_null() || image->is_empty()) { return ERR_CANT_OPEN; } @@ -924,7 +924,7 @@ Error StreamTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_da for (int i = 0; i < (r_depth + mipmaps); i++) { Ref<Image> image = StreamTexture2D::load_image_from_file(f, 0); - ERR_FAIL_COND_V(image.is_null() || image->empty(), ERR_CANT_OPEN); + ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN); if (i == 0) { r_format = image->get_format(); r_width = image->get_width(); @@ -1253,7 +1253,7 @@ bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Vector2 scale = p_rect.size / src.size; src.position += (rc.position - margin.position); - Rect2 src_c = rc.clip(src); + Rect2 src_c = rc.intersection(src); if (src_c.size == Size2()) { return false; } @@ -1575,7 +1575,7 @@ void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons if (!p_src_rect.intersects(rect)) { continue; } - Rect2 local = p_src_rect.clip(rect); + Rect2 local = p_src_rect.intersection(rect); Rect2 target = local; target.size *= scale; target.position = p_rect.position + (p_src_rect.position + rect.position) * scale; @@ -2223,7 +2223,7 @@ Error ImageTextureLayered::create_from_images(Vector<Ref<Image>> p_images) { "Cubemap array layers must be a multiple of 6"); } - ERR_FAIL_COND_V(p_images[0].is_null() || p_images[0]->empty(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_images[0].is_null() || p_images[0]->is_empty(), ERR_INVALID_PARAMETER); Image::Format new_format = p_images[0]->get_format(); int new_width = p_images[0]->get_width(); @@ -2361,7 +2361,7 @@ Error StreamTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> for (uint32_t i = 0; i < layer_count; i++) { Ref<Image> image = StreamTexture2D::load_image_from_file(f, p_size_limit); - ERR_FAIL_COND_V(image.is_null() || image->empty(), ERR_CANT_OPEN); + ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN); images.write[i] = image; } diff --git a/scene/resources/texture.h b/scene/resources/texture.h index a8d8b785fa..3bbce050f7 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index ccff49829e..5279f59d77 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -51,6 +51,21 @@ Vector<String> Theme::_get_icon_list(const String &p_node_type) const { return ilret; } +Vector<String> Theme::_get_icon_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_icon_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + Vector<String> Theme::_get_stylebox_list(const String &p_node_type) const { Vector<String> ilret; List<StringName> il; @@ -66,11 +81,11 @@ Vector<String> Theme::_get_stylebox_list(const String &p_node_type) const { return ilret; } -Vector<String> Theme::_get_stylebox_types() const { +Vector<String> Theme::_get_stylebox_type_list() const { Vector<String> ilret; List<StringName> il; - get_stylebox_types(&il); + get_stylebox_type_list(&il); ilret.resize(il.size()); int i = 0; @@ -96,6 +111,36 @@ Vector<String> Theme::_get_font_list(const String &p_node_type) const { return ilret; } +Vector<String> Theme::_get_font_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_font_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_font_size_list(const String &p_node_type) const { + Vector<String> ilret; + List<StringName> il; + + get_font_size_list(p_node_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + Vector<String> Theme::_get_color_list(const String &p_node_type) const { Vector<String> ilret; List<StringName> il; @@ -111,6 +156,21 @@ Vector<String> Theme::_get_color_list(const String &p_node_type) const { return ilret; } +Vector<String> Theme::_get_color_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_color_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + Vector<String> Theme::_get_constant_list(const String &p_node_type) const { Vector<String> ilret; List<StringName> il; @@ -126,7 +186,22 @@ Vector<String> Theme::_get_constant_list(const String &p_node_type) const { return ilret; } -Vector<String> Theme::_get_type_list(const String &p_node_type) const { +Vector<String> Theme::_get_constant_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_constant_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_type_list() const { Vector<String> ilret; List<StringName> il; @@ -291,11 +366,27 @@ Ref<Font> Theme::get_default_theme_font() const { return default_theme_font; } +void Theme::set_default_theme_font_size(int p_font_size) { + if (default_theme_font_size == p_font_size) { + return; + } + + default_theme_font_size = p_font_size; + + _change_notify(); + emit_changed(); +} + +int Theme::get_default_theme_font_size() const { + return default_theme_font_size; +} + Ref<Theme> Theme::project_default_theme; Ref<Theme> Theme::default_theme; Ref<Texture2D> Theme::default_icon; Ref<StyleBox> Theme::default_style; Ref<Font> Theme::default_font; +int Theme::default_font_size = 16; Ref<Theme> Theme::get_default() { return default_theme; @@ -325,6 +416,10 @@ void Theme::set_default_font(const Ref<Font> &p_font) { default_font = p_font; } +void Theme::set_default_font_size(int p_font_size) { + default_font_size = p_font_size; +} + void Theme::set_icon(const StringName &p_name, const StringName &p_node_type, const Ref<Texture2D> &p_icon) { //ERR_FAIL_COND(p_icon.is_null()); @@ -386,48 +481,11 @@ void Theme::get_icon_list(StringName p_node_type, List<StringName> *p_list) cons } } -void Theme::set_shader(const StringName &p_name, const StringName &p_node_type, const Ref<Shader> &p_shader) { - bool new_value = !shader_map.has(p_node_type) || !shader_map[p_node_type].has(p_name); - - shader_map[p_node_type][p_name] = p_shader; - - if (new_value) { - _change_notify(); - emit_changed(); - } -} - -Ref<Shader> Theme::get_shader(const StringName &p_name, const StringName &p_node_type) const { - if (shader_map.has(p_node_type) && shader_map[p_node_type].has(p_name) && shader_map[p_node_type][p_name].is_valid()) { - return shader_map[p_node_type][p_name]; - } else { - return nullptr; - } -} - -bool Theme::has_shader(const StringName &p_name, const StringName &p_node_type) const { - return (shader_map.has(p_node_type) && shader_map[p_node_type].has(p_name) && shader_map[p_node_type][p_name].is_valid()); -} - -void Theme::clear_shader(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND(!shader_map.has(p_node_type)); - ERR_FAIL_COND(!shader_map[p_node_type].has(p_name)); - - shader_map[p_node_type].erase(p_name); - _change_notify(); - emit_changed(); -} - -void Theme::get_shader_list(const StringName &p_node_type, List<StringName> *p_list) const { +void Theme::get_icon_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - if (!shader_map.has(p_node_type)) { - return; - } - const StringName *key = nullptr; - - while ((key = shader_map[p_node_type].next(key))) { + while ((key = icon_map.next(key))) { p_list->push_back(*key); } } @@ -493,7 +551,7 @@ void Theme::get_stylebox_list(StringName p_node_type, List<StringName> *p_list) } } -void Theme::get_stylebox_types(List<StringName> *p_list) const { +void Theme::get_stylebox_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); const StringName *key = nullptr; @@ -534,7 +592,7 @@ Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_node_typ } bool Theme::has_font(const StringName &p_name, const StringName &p_node_type) const { - return (font_map.has(p_node_type) && font_map[p_node_type].has(p_name) && font_map[p_node_type][p_name].is_valid()); + return ((font_map.has(p_node_type) && font_map[p_node_type].has(p_name) && font_map[p_node_type][p_name].is_valid()) || default_theme_font.is_valid()); } void Theme::clear_font(const StringName &p_name, const StringName &p_node_type) { @@ -564,6 +622,63 @@ void Theme::get_font_list(StringName p_node_type, List<StringName> *p_list) cons } } +void Theme::get_font_type_list(List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + + const StringName *key = nullptr; + while ((key = font_map.next(key))) { + p_list->push_back(*key); + } +} + +void Theme::set_font_size(const StringName &p_name, const StringName &p_node_type, int p_font_size) { + bool new_value = !font_size_map.has(p_node_type) || !font_size_map[p_node_type].has(p_name); + + font_size_map[p_node_type][p_name] = p_font_size; + + if (new_value) { + _change_notify(); + emit_changed(); + } +} + +int Theme::get_font_size(const StringName &p_name, const StringName &p_node_type) const { + if (font_size_map.has(p_node_type) && font_size_map[p_node_type].has(p_name) && (font_size_map[p_node_type][p_name] > 0)) { + return font_size_map[p_node_type][p_name]; + } else if (default_theme_font_size > 0) { + return default_theme_font_size; + } else { + return default_font_size; + } +} + +bool Theme::has_font_size(const StringName &p_name, const StringName &p_node_type) const { + return ((font_size_map.has(p_node_type) && font_size_map[p_node_type].has(p_name) && (font_size_map[p_node_type][p_name] > 0)) || (default_theme_font_size > 0)); +} + +void Theme::clear_font_size(const StringName &p_name, const StringName &p_node_type) { + ERR_FAIL_COND(!font_size_map.has(p_node_type)); + ERR_FAIL_COND(!font_size_map[p_node_type].has(p_name)); + + font_size_map[p_node_type].erase(p_name); + _change_notify(); + emit_changed(); +} + +void Theme::get_font_size_list(StringName p_node_type, List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + + if (!font_size_map.has(p_node_type)) { + return; + } + + const StringName *key = nullptr; + + while ((key = font_size_map[p_node_type].next(key))) { + p_list->push_back(*key); + } +} + void Theme::set_color(const StringName &p_name, const StringName &p_node_type, const Color &p_color) { bool new_value = !color_map.has(p_node_type) || !color_map[p_node_type].has(p_name); @@ -610,6 +725,15 @@ void Theme::get_color_list(StringName p_node_type, List<StringName> *p_list) con } } +void Theme::get_color_type_list(List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + + const StringName *key = nullptr; + while ((key = color_map.next(key))) { + p_list->push_back(*key); + } +} + void Theme::set_constant(const StringName &p_name, const StringName &p_node_type, int p_constant) { bool new_value = !constant_map.has(p_node_type) || !constant_map[p_node_type].has(p_name); constant_map[p_node_type][p_name] = p_constant; @@ -655,6 +779,15 @@ void Theme::get_constant_list(StringName p_node_type, List<StringName> *p_list) } } +void Theme::get_constant_type_list(List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + + const StringName *key = nullptr; + while ((key = constant_map.next(key))) { + p_list->push_back(*key); + } +} + void Theme::clear() { //these need disconnecting { @@ -699,7 +832,6 @@ void Theme::clear() { icon_map.clear(); style_map.clear(); font_map.clear(); - shader_map.clear(); color_map.clear(); constant_map.clear(); @@ -754,7 +886,6 @@ void Theme::copy_theme(const Ref<Theme> &p_other) { color_map = p_other->color_map; constant_map = p_other->constant_map; - shader_map = p_other->shader_map; _change_notify(); emit_changed(); @@ -805,43 +936,57 @@ void Theme::_bind_methods() { ClassDB::bind_method(D_METHOD("has_icon", "name", "node_type"), &Theme::has_icon); ClassDB::bind_method(D_METHOD("clear_icon", "name", "node_type"), &Theme::clear_icon); ClassDB::bind_method(D_METHOD("get_icon_list", "node_type"), &Theme::_get_icon_list); + ClassDB::bind_method(D_METHOD("get_icon_type_list"), &Theme::_get_icon_type_list); ClassDB::bind_method(D_METHOD("set_stylebox", "name", "node_type", "texture"), &Theme::set_stylebox); ClassDB::bind_method(D_METHOD("get_stylebox", "name", "node_type"), &Theme::get_stylebox); ClassDB::bind_method(D_METHOD("has_stylebox", "name", "node_type"), &Theme::has_stylebox); ClassDB::bind_method(D_METHOD("clear_stylebox", "name", "node_type"), &Theme::clear_stylebox); ClassDB::bind_method(D_METHOD("get_stylebox_list", "node_type"), &Theme::_get_stylebox_list); - ClassDB::bind_method(D_METHOD("get_stylebox_types"), &Theme::_get_stylebox_types); + ClassDB::bind_method(D_METHOD("get_stylebox_type_list"), &Theme::_get_stylebox_type_list); ClassDB::bind_method(D_METHOD("set_font", "name", "node_type", "font"), &Theme::set_font); ClassDB::bind_method(D_METHOD("get_font", "name", "node_type"), &Theme::get_font); ClassDB::bind_method(D_METHOD("has_font", "name", "node_type"), &Theme::has_font); ClassDB::bind_method(D_METHOD("clear_font", "name", "node_type"), &Theme::clear_font); ClassDB::bind_method(D_METHOD("get_font_list", "node_type"), &Theme::_get_font_list); + ClassDB::bind_method(D_METHOD("get_font_type_list"), &Theme::_get_font_type_list); + + ClassDB::bind_method(D_METHOD("set_font_size", "name", "node_type", "font_size"), &Theme::set_font_size); + ClassDB::bind_method(D_METHOD("get_font_size", "name", "node_type"), &Theme::get_font_size); + ClassDB::bind_method(D_METHOD("has_font_size", "name", "node_type"), &Theme::has_font_size); + ClassDB::bind_method(D_METHOD("clear_font_size", "name", "node_type"), &Theme::clear_font_size); + ClassDB::bind_method(D_METHOD("get_font_size_list", "node_type"), &Theme::_get_font_size_list); ClassDB::bind_method(D_METHOD("set_color", "name", "node_type", "color"), &Theme::set_color); ClassDB::bind_method(D_METHOD("get_color", "name", "node_type"), &Theme::get_color); ClassDB::bind_method(D_METHOD("has_color", "name", "node_type"), &Theme::has_color); ClassDB::bind_method(D_METHOD("clear_color", "name", "node_type"), &Theme::clear_color); ClassDB::bind_method(D_METHOD("get_color_list", "node_type"), &Theme::_get_color_list); + ClassDB::bind_method(D_METHOD("get_color_type_list"), &Theme::_get_color_type_list); ClassDB::bind_method(D_METHOD("set_constant", "name", "node_type", "constant"), &Theme::set_constant); ClassDB::bind_method(D_METHOD("get_constant", "name", "node_type"), &Theme::get_constant); ClassDB::bind_method(D_METHOD("has_constant", "name", "node_type"), &Theme::has_constant); ClassDB::bind_method(D_METHOD("clear_constant", "name", "node_type"), &Theme::clear_constant); ClassDB::bind_method(D_METHOD("get_constant_list", "node_type"), &Theme::_get_constant_list); + ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list); ClassDB::bind_method(D_METHOD("clear"), &Theme::clear); ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_theme_font); ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_theme_font); - ClassDB::bind_method(D_METHOD("get_type_list", "node_type"), &Theme::_get_type_list); + ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_theme_font_size); + ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_theme_font_size); + + ClassDB::bind_method(D_METHOD("get_type_list"), &Theme::_get_type_list); ClassDB::bind_method("copy_default_theme", &Theme::copy_default_theme); ClassDB::bind_method(D_METHOD("copy_theme", "other"), &Theme::copy_theme); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size"); } Theme::Theme() { diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 9c17a69e5d..c802ba2536 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,7 +34,6 @@ #include "core/io/resource.h" #include "core/io/resource_loader.h" #include "scene/resources/font.h" -#include "scene/resources/shader.h" #include "scene/resources/style_box.h" #include "scene/resources/texture.h" @@ -47,17 +46,22 @@ class Theme : public Resource { HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map; HashMap<StringName, HashMap<StringName, Ref<StyleBox>>> style_map; HashMap<StringName, HashMap<StringName, Ref<Font>>> font_map; - HashMap<StringName, HashMap<StringName, Ref<Shader>>> shader_map; + HashMap<StringName, HashMap<StringName, int>> font_size_map; HashMap<StringName, HashMap<StringName, Color>> color_map; HashMap<StringName, HashMap<StringName, int>> constant_map; Vector<String> _get_icon_list(const String &p_node_type) const; + Vector<String> _get_icon_type_list() const; Vector<String> _get_stylebox_list(const String &p_node_type) const; - Vector<String> _get_stylebox_types() const; + Vector<String> _get_stylebox_type_list() const; Vector<String> _get_font_list(const String &p_node_type) const; + Vector<String> _get_font_type_list() const; + Vector<String> _get_font_size_list(const String &p_node_type) const; Vector<String> _get_color_list(const String &p_node_type) const; + Vector<String> _get_color_type_list() const; Vector<String> _get_constant_list(const String &p_node_type) const; - Vector<String> _get_type_list(const String &p_node_type) const; + Vector<String> _get_constant_type_list() const; + Vector<String> _get_type_list() const; protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -69,8 +73,10 @@ protected: static Ref<Texture2D> default_icon; static Ref<StyleBox> default_style; static Ref<Font> default_font; + static int default_font_size; Ref<Font> default_theme_font; + int default_theme_font_size = -1; static void _bind_methods(); @@ -84,46 +90,54 @@ public: static void set_default_icon(const Ref<Texture2D> &p_icon); static void set_default_style(const Ref<StyleBox> &p_style); static void set_default_font(const Ref<Font> &p_font); + static void set_default_font_size(int p_font_size); void set_default_theme_font(const Ref<Font> &p_default_font); Ref<Font> get_default_theme_font() const; + void set_default_theme_font_size(int p_font_size); + int get_default_theme_font_size() const; + void set_icon(const StringName &p_name, const StringName &p_node_type, const Ref<Texture2D> &p_icon); Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_node_type) const; bool has_icon(const StringName &p_name, const StringName &p_node_type) const; void clear_icon(const StringName &p_name, const StringName &p_node_type); void get_icon_list(StringName p_node_type, List<StringName> *p_list) const; - - void set_shader(const StringName &p_name, const StringName &p_node_type, const Ref<Shader> &p_shader); - Ref<Shader> get_shader(const StringName &p_name, const StringName &p_node_type) const; - bool has_shader(const StringName &p_name, const StringName &p_node_type) const; - void clear_shader(const StringName &p_name, const StringName &p_node_type); - void get_shader_list(const StringName &p_node_type, List<StringName> *p_list) const; + void get_icon_type_list(List<StringName> *p_list) const; void set_stylebox(const StringName &p_name, const StringName &p_node_type, const Ref<StyleBox> &p_style); Ref<StyleBox> get_stylebox(const StringName &p_name, const StringName &p_node_type) const; bool has_stylebox(const StringName &p_name, const StringName &p_node_type) const; void clear_stylebox(const StringName &p_name, const StringName &p_node_type); void get_stylebox_list(StringName p_node_type, List<StringName> *p_list) const; - void get_stylebox_types(List<StringName> *p_list) const; + void get_stylebox_type_list(List<StringName> *p_list) const; void set_font(const StringName &p_name, const StringName &p_node_type, const Ref<Font> &p_font); Ref<Font> get_font(const StringName &p_name, const StringName &p_node_type) const; bool has_font(const StringName &p_name, const StringName &p_node_type) const; void clear_font(const StringName &p_name, const StringName &p_node_type); void get_font_list(StringName p_node_type, List<StringName> *p_list) const; + void get_font_type_list(List<StringName> *p_list) const; + + void set_font_size(const StringName &p_name, const StringName &p_node_type, int p_font_size); + int get_font_size(const StringName &p_name, const StringName &p_node_type) const; + bool has_font_size(const StringName &p_name, const StringName &p_node_type) const; + void clear_font_size(const StringName &p_name, const StringName &p_node_type); + void get_font_size_list(StringName p_node_type, List<StringName> *p_list) const; void set_color(const StringName &p_name, const StringName &p_node_type, const Color &p_color); Color get_color(const StringName &p_name, const StringName &p_node_type) const; bool has_color(const StringName &p_name, const StringName &p_node_type) const; void clear_color(const StringName &p_name, const StringName &p_node_type); void get_color_list(StringName p_node_type, List<StringName> *p_list) const; + void get_color_type_list(List<StringName> *p_list) const; void set_constant(const StringName &p_name, const StringName &p_node_type, int p_constant); int get_constant(const StringName &p_name, const StringName &p_node_type) const; bool has_constant(const StringName &p_name, const StringName &p_node_type) const; void clear_constant(const StringName &p_name, const StringName &p_node_type); void get_constant_list(StringName p_node_type, List<StringName> *p_list) const; + void get_constant_type_list(List<StringName> *p_list) const; void get_type_list(List<StringName> *p_list) const; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 4581763e9c..ae536a2928 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 79f1b4aa95..210520350f 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h index 379ba53a34..f960f85521 100644 --- a/scene/resources/video_stream.h +++ b/scene/resources/video_stream.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 283d89e5cf..2e2077d811 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -811,8 +811,12 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port #define IS_SYMBOL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z') || ((m_d) >= '0' && (m_d) <= '9') || (m_d) == '_') -String VisualShader::validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const { - String name = p_name; +String VisualShader::validate_port_name(const String &p_port_name, VisualShaderNode *p_node, int p_port_id, bool p_output) const { + String name = p_port_name; + + if (name == String()) { + return String(); + } while (name.length() && !IS_INITIAL_CHAR(name[0])) { name = name.substr(1, name.length() - 1); @@ -830,29 +834,28 @@ String VisualShader::validate_port_name(const String &p_name, const List<String> } name = valid_name; + } else { + return String(); } - String valid_name = name; - bool is_equal = false; + List<String> input_names; + List<String> output_names; - for (int i = 0; i < p_input_ports.size(); i++) { - if (name == p_input_ports[i]) { - is_equal = true; - break; + for (int i = 0; i < p_node->get_input_port_count(); i++) { + if (!p_output && i == p_port_id) { + continue; } - } - - if (!is_equal) { - for (int i = 0; i < p_output_ports.size(); i++) { - if (name == p_output_ports[i]) { - is_equal = true; - break; - } + if (name == p_node->get_input_port_name(i)) { + return String(); } } - - if (is_equal) { - name = ""; + for (int i = 0; i < p_node->get_output_port_count(); i++) { + if (p_output && i == p_port_id) { + continue; + } + if (name == p_node->get_output_port_name(i)) { + return String(); + } } return name; @@ -1706,9 +1709,9 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "attenuation", "ATTENUATION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "attenuation", "ATTENUATION" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "backlight", "BACKLIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, @@ -1724,17 +1727,17 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, // Canvas Item, Vertex { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "extra", "EXTRA_MATRIX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "canvas", "CANVAS_MATRIX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen", "SCREEN_MATRIX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" }, // Canvas Item, Fragment { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, @@ -1745,29 +1748,34 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_pixel_size", "vec3(SCREEN_PIXEL_SIZE, 1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_texture", "NORMAL_TEXTURE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" }, // Canvas Item, Light { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vec", "vec3(LIGHT_VEC, 0.0)" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_height", "LIGHT_HEIGHT" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT_COLOR.a" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_uv", "vec3(LIGHT_UV, 0.0)" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_color", "SHADOW_COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_COLOR.a" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_vec", "vec3(SHADOW_VEC, 0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_color_alpha", "LIGHT_COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_position", "LIGHT_POSITION" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_color", "SHADOW_MODULATE.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_MODULATE.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" }, // Particles, Emit { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, @@ -1862,7 +1870,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "1.0" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0,1.0, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" }, // Spatial, Light { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" }, @@ -1954,7 +1962,7 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T code = "\t" + p_output_vars[0] + " = vec3(0.0);\n"; } break; case PORT_TYPE_TRANSFORM: { - code = "\t" + p_output_vars[0] + " = mat4( vec4(1.0,0.0,0.0,0.0), vec4(0.0,1.0,0.0,0.0), vec4(0.0,0.0,1.0,0.0), vec4(0.0,0.0,0.0,1.0) );\n"; + code = "\t" + p_output_vars[0] + " = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; } break; case PORT_TYPE_BOOLEAN: { code = "\t" + p_output_vars[0] + " = false;\n"; @@ -2351,8 +2359,8 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao", "AO" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal_map", "NORMAL_MAP" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim_tint", "RIM_TINT" }, @@ -2361,9 +2369,9 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "anisotropy_flow", "ANISOTROPY_FLOW:xy" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "backlight", "BACKLIGHT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor", "ALPHA_SCISSOR" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor_threshold", "ALPHA_SCISSOR_THRESHOLD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" }, // Spatial, Light @@ -2378,8 +2386,8 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal_map", "NORMAL_MAP" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" }, // Canvas Item, Light { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" }, @@ -2481,7 +2489,7 @@ String VisualShaderNodeOutput::get_output_port_name(int p_port) const { bool VisualShaderNodeOutput::is_port_separator(int p_index) const { if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) { String name = get_input_port_name(p_index); - return (name == "Normal" || name == "Rim" || name == "Alpha Scissor"); + return (name == "Normal" || name == "Rim" || name == "Alpha Scissor Threshold"); } return false; } @@ -2762,8 +2770,10 @@ void VisualShaderNodeGroupBase::remove_input_port(int p_id) { inputs.erase(index, count); inputs_strings = inputs.split(";", false); + inputs = inputs.substr(0, index); + for (int i = p_id; i < inputs_strings.size(); i++) { - inputs = inputs.replace_first(inputs_strings[i].split(",")[0], itos(i)); + inputs += inputs_strings[i].replace_first(inputs_strings[i].split(",")[0], itos(i)) + ";"; } _apply_port_changes(); @@ -2832,8 +2842,10 @@ void VisualShaderNodeGroupBase::remove_output_port(int p_id) { outputs.erase(index, count); outputs_strings = outputs.split(";", false); + outputs = outputs.substr(0, index); + for (int i = p_id; i < outputs_strings.size(); i++) { - outputs = outputs.replace_first(outputs_strings[i].split(",")[0], itos(i)); + outputs += outputs_strings[i].replace_first(outputs_strings[i].split(",")[0], itos(i)) + ";"; } _apply_port_changes(); @@ -3112,7 +3124,7 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad _expression = _expression.replace("\n", "\n\t\t"); static Vector<String> pre_symbols; - if (pre_symbols.empty()) { + if (pre_symbols.is_empty()) { pre_symbols.push_back("\t"); pre_symbols.push_back(","); pre_symbols.push_back(";"); @@ -3132,7 +3144,7 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad } static Vector<String> post_symbols; - if (post_symbols.empty()) { + if (post_symbols.is_empty()) { post_symbols.push_back("\t"); post_symbols.push_back("\n"); post_symbols.push_back(","); diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 41c4642ee3..3c59a922fc 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -175,7 +175,7 @@ public: String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const; - String validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const; + String validate_port_name(const String &p_port_name, VisualShaderNode *p_node, int p_port_id, bool p_output) const; String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const; VisualShader(); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index a3358ea8c7..7629f435a5 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -480,7 +480,7 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade case TYPE_COLOR: u += " : hint_albedo"; break; - case TYPE_NORMALMAP: + case TYPE_NORMAL_MAP: u += " : hint_normal"; break; } @@ -780,7 +780,7 @@ void VisualShaderNodeTexture::_bind_methods() { BIND_ENUM_CONSTANT(SOURCE_PORT); BIND_ENUM_CONSTANT(TYPE_DATA); BIND_ENUM_CONSTANT(TYPE_COLOR); - BIND_ENUM_CONSTANT(TYPE_NORMALMAP); + BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP); } VisualShaderNodeTexture::VisualShaderNodeTexture() { @@ -1181,7 +1181,7 @@ String VisualShaderNodeCubemap::generate_global(Shader::Mode p_mode, VisualShade case TYPE_COLOR: u += " : hint_albedo"; break; - case TYPE_NORMALMAP: + case TYPE_NORMAL_MAP: u += " : hint_normal"; break; } @@ -1310,7 +1310,7 @@ void VisualShaderNodeCubemap::_bind_methods() { BIND_ENUM_CONSTANT(TYPE_DATA); BIND_ENUM_CONSTANT(TYPE_COLOR); - BIND_ENUM_CONSTANT(TYPE_NORMALMAP); + BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP); } VisualShaderNodeCubemap::VisualShaderNodeCubemap() { @@ -4350,7 +4350,7 @@ String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, Visu code += " : hint_albedo;\n"; } break; - case TYPE_NORMALMAP: + case TYPE_NORMAL_MAP: code += " : hint_normal;\n"; break; case TYPE_ANISO: @@ -4431,7 +4431,7 @@ void VisualShaderNodeTextureUniform::_bind_methods() { BIND_ENUM_CONSTANT(TYPE_DATA); BIND_ENUM_CONSTANT(TYPE_COLOR); - BIND_ENUM_CONSTANT(TYPE_NORMALMAP); + BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP); BIND_ENUM_CONSTANT(TYPE_ANISO); BIND_ENUM_CONSTANT(COLOR_DEFAULT_WHITE); @@ -4608,7 +4608,7 @@ String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mod else code += " : hint_albedo;\n"; break; - case TYPE_NORMALMAP: + case TYPE_NORMAL_MAP: code += " : hint_normal;\n"; break; case TYPE_ANISO: @@ -4676,7 +4676,7 @@ String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, Vi else code += " : hint_albedo;\n"; break; - case TYPE_NORMALMAP: + case TYPE_NORMAL_MAP: code += " : hint_normal;\n"; break; case TYPE_ANISO: @@ -4746,7 +4746,7 @@ String VisualShaderNodeCubemapUniform::generate_global(Shader::Mode p_mode, Visu code += " : hint_albedo;\n"; } break; - case TYPE_NORMALMAP: + case TYPE_NORMAL_MAP: code += " : hint_normal;\n"; break; case TYPE_ANISO: diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 4b39c76388..35ecaaacb5 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -236,7 +236,7 @@ public: enum TextureType { TYPE_DATA, TYPE_COLOR, - TYPE_NORMALMAP, + TYPE_NORMAL_MAP, }; private: @@ -412,7 +412,7 @@ public: enum TextureType { TYPE_DATA, TYPE_COLOR, - TYPE_NORMALMAP + TYPE_NORMAL_MAP }; private: @@ -1834,7 +1834,7 @@ public: enum TextureType { TYPE_DATA, TYPE_COLOR, - TYPE_NORMALMAP, + TYPE_NORMAL_MAP, TYPE_ANISO, }; diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 41d3fe20be..cadbd93fed 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -111,7 +111,7 @@ struct SpatialIndexer2D { ERR_CONTINUE(!E); if (E->get().notifiers[p_notifier].dec() == 0) { E->get().notifiers.erase(p_notifier); - if (E->get().notifiers.empty()) { + if (E->get().notifiers.is_empty()) { cells.erase(E); } } @@ -156,7 +156,7 @@ struct SpatialIndexer2D { } } - while (!removed.empty()) { + while (!removed.is_empty()) { p_notifier->_exit_viewport(removed.front()->get()); removed.pop_front(); } @@ -189,7 +189,7 @@ struct SpatialIndexer2D { removed.push_back(E->key()); } - while (!removed.empty()) { + while (!removed.is_empty()) { removed.front()->get()->_exit_viewport(p_viewport); removed.pop_front(); } @@ -271,12 +271,12 @@ struct SpatialIndexer2D { } } - while (!added.empty()) { + while (!added.is_empty()) { added.front()->get()->_enter_viewport(E->key()); added.pop_front(); } - while (!removed.empty()) { + while (!removed.is_empty()) { E->get().notifiers.erase(removed.front()->get()); removed.front()->get()->_exit_viewport(E->key()); removed.pop_front(); diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index 11614f9aa4..ae13367421 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index b8fb3825ce..9c0317454b 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -97,7 +97,7 @@ struct SpatialIndexer { } } - while (!removed.empty()) { + while (!removed.is_empty()) { p_notifier->_exit_camera(removed.front()->get()); removed.pop_front(); } @@ -125,7 +125,7 @@ struct SpatialIndexer { removed.push_back(E->key()); } - while (!removed.empty()) { + while (!removed.is_empty()) { removed.front()->get()->_exit_camera(p_camera); removed.pop_front(); } @@ -175,12 +175,12 @@ struct SpatialIndexer { } } - while (!added.empty()) { + while (!added.is_empty()) { added.front()->get()->_enter_camera(E->key()); added.pop_front(); } - while (!removed.empty()) { + while (!removed.is_empty()) { E->get().notifiers.erase(removed.front()->get()); removed.front()->get()->_exit_camera(E->key()); removed.pop_front(); diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index 93e9c72e59..3d6c33997e 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/world_margin_shape_3d.cpp b/scene/resources/world_margin_shape_3d.cpp index 0936fcc657..79cbb3bbe0 100644 --- a/scene/resources/world_margin_shape_3d.cpp +++ b/scene/resources/world_margin_shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/world_margin_shape_3d.h b/scene/resources/world_margin_shape_3d.h index 8099592d80..00417c4408 100644 --- a/scene/resources/world_margin_shape_3d.h +++ b/scene/resources/world_margin_shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 7cf9a4fedd..892802c103 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,7 +41,7 @@ SceneStringNames::SceneStringNames() { doubledot = StaticCString::create(".."); draw = StaticCString::create("draw"); _draw = StaticCString::create("_draw"); - hide = StaticCString::create("hide"); + hidden = StaticCString::create("hidden"); visibility_changed = StaticCString::create("visibility_changed"); input_event = StaticCString::create("input_event"); shader = StaticCString::create("shader"); @@ -104,6 +104,7 @@ SceneStringNames::SceneStringNames() { _update_xform = StaticCString::create("_update_xform"); _clips_input = StaticCString::create("_clips_input"); + _structured_text_parser = StaticCString::create("_structured_text_parser"); _proxgroup_add = StaticCString::create("_proxgroup_add"); _proxgroup_remove = StaticCString::create("_proxgroup_remove"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index b1168c84b9..655e49c6f9 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -58,7 +58,7 @@ public: StringName dot; StringName doubledot; StringName draw; - StringName hide; + StringName hidden; StringName visibility_changed; StringName input_event; StringName _input_event; @@ -130,6 +130,7 @@ public: StringName _update_xform; StringName _clips_input; + StringName _structured_text_parser; StringName _proxgroup_add; StringName _proxgroup_remove; |