summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite_2d.cpp53
-rw-r--r--scene/2d/animated_sprite_2d.h43
-rw-r--r--scene/2d/area_2d.cpp28
-rw-r--r--scene/2d/area_2d.h2
-rw-r--r--scene/2d/audio_stream_player_2d.cpp2
-rw-r--r--scene/2d/camera_2d.cpp22
-rw-r--r--scene/2d/camera_2d.h2
-rw-r--r--scene/2d/canvas_group.cpp87
-rw-r--r--scene/2d/canvas_group.h59
-rw-r--r--scene/2d/canvas_modulate.cpp9
-rw-r--r--scene/2d/collision_polygon_2d.cpp16
-rw-r--r--scene/2d/collision_shape_2d.cpp2
-rw-r--r--scene/2d/cpu_particles_2d.cpp66
-rw-r--r--scene/2d/cpu_particles_2d.h25
-rw-r--r--scene/2d/gpu_particles_2d.cpp27
-rw-r--r--scene/2d/gpu_particles_2d.h6
-rw-r--r--scene/2d/joints_2d.cpp55
-rw-r--r--scene/2d/joints_2d.h3
-rw-r--r--scene/2d/light_2d.cpp318
-rw-r--r--scene/2d/light_2d.h105
-rw-r--r--scene/2d/light_occluder_2d.cpp34
-rw-r--r--scene/2d/light_occluder_2d.h5
-rw-r--r--scene/2d/line_2d.cpp2
-rw-r--r--scene/2d/line_builder.cpp33
-rw-r--r--scene/2d/line_builder.h2
-rw-r--r--scene/2d/mesh_instance_2d.cpp2
-rw-r--r--scene/2d/multimesh_instance_2d.cpp2
-rw-r--r--scene/2d/navigation_agent_2d.cpp11
-rw-r--r--scene/2d/navigation_agent_2d.h2
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp9
-rw-r--r--scene/2d/navigation_region_2d.cpp17
-rw-r--r--scene/2d/node_2d.cpp2
-rw-r--r--scene/2d/parallax_background.cpp2
-rw-r--r--scene/2d/parallax_layer.cpp11
-rw-r--r--scene/2d/path_2d.cpp25
-rw-r--r--scene/2d/path_2d.h4
-rw-r--r--scene/2d/physics_body_2d.cpp38
-rw-r--r--scene/2d/physics_body_2d.h8
-rw-r--r--scene/2d/polygon_2d.cpp58
-rw-r--r--scene/2d/polygon_2d.h16
-rw-r--r--scene/2d/position_2d.cpp2
-rw-r--r--scene/2d/ray_cast_2d.cpp26
-rw-r--r--scene/2d/ray_cast_2d.h6
-rw-r--r--scene/2d/remote_transform_2d.cpp9
-rw-r--r--scene/2d/skeleton_2d.cpp4
-rw-r--r--scene/2d/sprite_2d.cpp66
-rw-r--r--scene/2d/sprite_2d.h14
-rw-r--r--scene/2d/tile_map.cpp31
-rw-r--r--scene/2d/tile_map.h8
-rw-r--r--scene/2d/visibility_notifier_2d.cpp11
-rw-r--r--scene/3d/area_3d.cpp40
-rw-r--r--scene/3d/area_3d.h2
-rw-r--r--scene/3d/audio_stream_player_3d.cpp5
-rw-r--r--scene/3d/baked_lightmap.cpp2
-rw-r--r--scene/3d/baked_lightmap.h2
-rw-r--r--scene/3d/camera_3d.cpp6
-rw-r--r--scene/3d/camera_3d.h2
-rw-r--r--scene/3d/collision_polygon_3d.cpp14
-rw-r--r--scene/3d/collision_shape_3d.cpp49
-rw-r--r--scene/3d/collision_shape_3d.h2
-rw-r--r--scene/3d/cpu_particles_3d.cpp92
-rw-r--r--scene/3d/cpu_particles_3d.h21
-rw-r--r--scene/3d/gi_probe.cpp10
-rw-r--r--scene/3d/gpu_particles_3d.cpp68
-rw-r--r--scene/3d/gpu_particles_3d.h22
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp901
-rw-r--r--scene/3d/gpu_particles_collision_3d.h342
-rw-r--r--scene/3d/light_3d.cpp28
-rw-r--r--scene/3d/light_3d.h5
-rw-r--r--scene/3d/navigation_agent_3d.cpp11
-rw-r--r--scene/3d/navigation_agent_3d.h2
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp9
-rw-r--r--scene/3d/navigation_region_3d.cpp16
-rw-r--r--scene/3d/node_3d.cpp41
-rw-r--r--scene/3d/node_3d.h9
-rw-r--r--scene/3d/path_3d.cpp16
-rw-r--r--scene/3d/path_3d.h1
-rw-r--r--scene/3d/physics_body_3d.cpp53
-rw-r--r--scene/3d/physics_body_3d.h11
-rw-r--r--scene/3d/physics_joint_3d.cpp55
-rw-r--r--scene/3d/physics_joint_3d.h5
-rw-r--r--scene/3d/ray_cast_3d.cpp22
-rw-r--r--scene/3d/ray_cast_3d.h6
-rw-r--r--scene/3d/remote_transform_3d.cpp9
-rw-r--r--scene/3d/skeleton_3d.cpp10
-rw-r--r--scene/3d/skeleton_3d.h3
-rw-r--r--scene/3d/skeleton_ik_3d.h4
-rw-r--r--scene/3d/soft_body_3d.cpp15
-rw-r--r--scene/3d/soft_body_3d.h2
-rw-r--r--scene/3d/spring_arm_3d.cpp2
-rw-r--r--scene/3d/sprite_3d.cpp18
-rw-r--r--scene/3d/vehicle_body_3d.cpp10
-rw-r--r--scene/3d/velocity_tracker_3d.cpp2
-rw-r--r--scene/3d/visibility_notifier_3d.cpp2
-rw-r--r--scene/3d/visual_instance_3d.h2
-rw-r--r--scene/3d/voxelizer.cpp1
-rw-r--r--scene/3d/world_environment.cpp16
-rw-r--r--scene/3d/xr_nodes.cpp54
-rw-r--r--scene/animation/animation_node_state_machine.cpp4
-rw-r--r--scene/animation/animation_player.cpp14
-rw-r--r--scene/animation/animation_player.h1
-rw-r--r--scene/animation/animation_tree.cpp39
-rw-r--r--scene/animation/tween.cpp2
-rw-r--r--scene/animation/tween.h1
-rw-r--r--scene/audio/audio_stream_player.cpp2
-rw-r--r--scene/debugger/scene_debugger.cpp2
-rw-r--r--scene/debugger/scene_debugger.h8
-rw-r--r--scene/gui/aspect_ratio_container.cpp172
-rw-r--r--scene/gui/aspect_ratio_container.h80
-rw-r--r--scene/gui/base_button.cpp68
-rw-r--r--scene/gui/base_button.h18
-rw-r--r--scene/gui/box_container.cpp64
-rw-r--r--scene/gui/button.cpp304
-rw-r--r--scene/gui/button.h23
-rw-r--r--scene/gui/center_container.cpp4
-rw-r--r--scene/gui/check_box.cpp24
-rw-r--r--scene/gui/check_button.cpp40
-rw-r--r--scene/gui/code_edit.cpp456
-rw-r--r--scene/gui/code_edit.h135
-rw-r--r--scene/gui/color_picker.cpp8
-rw-r--r--scene/gui/color_rect.cpp14
-rw-r--r--scene/gui/color_rect.h8
-rw-r--r--scene/gui/container.cpp11
-rw-r--r--scene/gui/control.cpp605
-rw-r--r--scene/gui/control.h106
-rw-r--r--scene/gui/dialogs.cpp8
-rw-r--r--scene/gui/file_dialog.cpp7
-rw-r--r--scene/gui/gradient_edit.cpp2
-rw-r--r--scene/gui/graph_edit.cpp23
-rw-r--r--scene/gui/graph_edit.h1
-rw-r--r--scene/gui/graph_node.cpp152
-rw-r--r--scene/gui/graph_node.h18
-rw-r--r--scene/gui/grid_container.cpp28
-rw-r--r--scene/gui/item_list.cpp232
-rw-r--r--scene/gui/item_list.h17
-rw-r--r--scene/gui/label.cpp709
-rw-r--r--scene/gui/label.h56
-rw-r--r--scene/gui/line_edit.cpp1149
-rw-r--r--scene/gui/line_edit.h133
-rw-r--r--scene/gui/link_button.cpp188
-rw-r--r--scene/gui/link_button.h30
-rw-r--r--scene/gui/margin_container.cpp4
-rw-r--r--scene/gui/menu_button.cpp18
-rw-r--r--scene/gui/menu_button.h2
-rw-r--r--scene/gui/nine_patch_rect.h1
-rw-r--r--scene/gui/option_button.cpp31
-rw-r--r--scene/gui/panel.cpp2
-rw-r--r--scene/gui/panel_container.cpp4
-rw-r--r--scene/gui/popup.cpp108
-rw-r--r--scene/gui/popup.h14
-rw-r--r--scene/gui/popup_menu.cpp622
-rw-r--r--scene/gui/popup_menu.h77
-rw-r--r--scene/gui/progress_bar.cpp18
-rw-r--r--scene/gui/range.cpp2
-rw-r--r--scene/gui/reference_rect.cpp22
-rw-r--r--scene/gui/reference_rect.h11
-rw-r--r--scene/gui/rich_text_effect.cpp2
-rw-r--r--scene/gui/rich_text_effect.h6
-rw-r--r--scene/gui/rich_text_label.cpp48
-rw-r--r--scene/gui/rich_text_label.h6
-rw-r--r--scene/gui/scroll_bar.cpp14
-rw-r--r--scene/gui/scroll_bar.h5
-rw-r--r--scene/gui/scroll_container.cpp41
-rw-r--r--scene/gui/scroll_container.h1
-rw-r--r--scene/gui/shortcut.cpp24
-rw-r--r--scene/gui/shortcut.h8
-rw-r--r--scene/gui/spin_box.cpp23
-rw-r--r--scene/gui/split_container.cpp25
-rw-r--r--scene/gui/subviewport_container.cpp2
-rw-r--r--scene/gui/tab_container.cpp451
-rw-r--r--scene/gui/tab_container.h10
-rw-r--r--scene/gui/tabs.cpp267
-rw-r--r--scene/gui/tabs.h23
-rw-r--r--scene/gui/text_edit.cpp2811
-rw-r--r--scene/gui/text_edit.h421
-rw-r--r--scene/gui/texture_progress.cpp2
-rw-r--r--scene/gui/texture_rect.h7
-rw-r--r--scene/gui/tree.cpp531
-rw-r--r--scene/gui/tree.h52
-rw-r--r--scene/main/canvas_item.cpp372
-rw-r--r--scene/main/canvas_item.h100
-rw-r--r--scene/main/http_request.cpp114
-rw-r--r--scene/main/http_request.h13
-rw-r--r--scene/main/node.cpp14
-rw-r--r--scene/main/node.h25
-rw-r--r--scene/main/scene_tree.cpp32
-rw-r--r--scene/main/scene_tree.h8
-rw-r--r--scene/main/shader_globals_override.cpp11
-rw-r--r--scene/main/timer.cpp6
-rw-r--r--scene/main/viewport.cpp332
-rw-r--r--scene/main/viewport.h48
-rw-r--r--scene/main/window.cpp87
-rw-r--r--scene/main/window.h21
-rw-r--r--scene/register_scene_types.cpp115
-rw-r--r--scene/resources/animation.cpp4
-rw-r--r--scene/resources/animation.h2
-rw-r--r--scene/resources/audio_stream_sample.cpp3
-rw-r--r--scene/resources/bit_map.h4
-rw-r--r--scene/resources/box_shape_3d.cpp2
-rw-r--r--scene/resources/box_shape_3d.h2
-rw-r--r--scene/resources/camera_effects.h4
-rw-r--r--scene/resources/capsule_shape_3d.cpp6
-rw-r--r--scene/resources/capsule_shape_3d.h2
-rw-r--r--scene/resources/concave_polygon_shape_3d.cpp2
-rw-r--r--scene/resources/concave_polygon_shape_3d.h2
-rw-r--r--scene/resources/convex_polygon_shape_3d.cpp2
-rw-r--r--scene/resources/convex_polygon_shape_3d.h2
-rw-r--r--scene/resources/curve.h2
-rw-r--r--scene/resources/cylinder_shape_3d.cpp6
-rw-r--r--scene/resources/cylinder_shape_3d.h2
-rw-r--r--scene/resources/default_theme/arrow_left.pngbin0 -> 159 bytes
-rw-r--r--scene/resources/default_theme/bookmark.pngbin0 -> 160 bytes
-rw-r--r--scene/resources/default_theme/default_theme.cpp175
-rw-r--r--scene/resources/default_theme/font_lodpi.inc2
-rw-r--r--scene/resources/default_theme/option_button_disabled_mirrored.pngbin0 -> 704 bytes
-rw-r--r--scene/resources/default_theme/option_button_hover_mirrored.pngbin0 -> 728 bytes
-rw-r--r--scene/resources/default_theme/option_button_normal_mirrored.pngbin0 -> 726 bytes
-rw-r--r--scene/resources/default_theme/option_button_pressed_mirrored.pngbin0 -> 736 bytes
-rw-r--r--scene/resources/default_theme/submenu_mirrored.pngbin0 -> 157 bytes
-rw-r--r--scene/resources/default_theme/theme_data.h48
-rw-r--r--scene/resources/default_theme/toggle_off_disabled_mirrored.pngbin0 -> 535 bytes
-rw-r--r--scene/resources/default_theme/toggle_off_mirrored.pngbin0 -> 1169 bytes
-rw-r--r--scene/resources/default_theme/toggle_on_disabled_mirrored.pngbin0 -> 496 bytes
-rw-r--r--scene/resources/default_theme/toggle_on_mirrored.pngbin0 -> 1133 bytes
-rw-r--r--scene/resources/default_theme/window_resizer_mirrored.pngbin0 -> 109 bytes
-rw-r--r--scene/resources/dynamic_font.cpp1143
-rw-r--r--scene/resources/dynamic_font.h316
-rw-r--r--scene/resources/environment.cpp413
-rw-r--r--scene/resources/environment.h122
-rw-r--r--scene/resources/font.cpp1229
-rw-r--r--scene/resources/font.h257
-rw-r--r--scene/resources/gradient.cpp41
-rw-r--r--scene/resources/gradient.h17
-rw-r--r--scene/resources/height_map_shape_3d.cpp2
-rw-r--r--scene/resources/height_map_shape_3d.h2
-rw-r--r--scene/resources/material.cpp176
-rw-r--r--scene/resources/material.h142
-rw-r--r--scene/resources/mesh.cpp207
-rw-r--r--scene/resources/mesh.h91
-rw-r--r--scene/resources/mesh_library.h10
-rw-r--r--scene/resources/packed_scene.cpp18
-rw-r--r--scene/resources/packed_scene.h3
-rw-r--r--scene/resources/particles_material.cpp443
-rw-r--r--scene/resources/particles_material.h119
-rw-r--r--scene/resources/physics_material.cpp4
-rw-r--r--scene/resources/physics_material.h2
-rw-r--r--scene/resources/polygon_path_finder.h2
-rw-r--r--scene/resources/primitive_meshes.cpp42
-rw-r--r--scene/resources/primitive_meshes.h8
-rw-r--r--scene/resources/ray_shape_3d.cpp4
-rw-r--r--scene/resources/ray_shape_3d.h2
-rw-r--r--scene/resources/resource_format_text.cpp23
-rw-r--r--scene/resources/resource_format_text.h2
-rw-r--r--scene/resources/shader.cpp5
-rw-r--r--scene/resources/shader.h3
-rw-r--r--scene/resources/shape_2d.h2
-rw-r--r--scene/resources/shape_3d.h4
-rw-r--r--scene/resources/skin.h2
-rw-r--r--scene/resources/sky_material.cpp2
-rw-r--r--scene/resources/sky_material.h2
-rw-r--r--scene/resources/sphere_shape_3d.cpp4
-rw-r--r--scene/resources/sphere_shape_3d.h2
-rw-r--r--scene/resources/style_box.cpp36
-rw-r--r--scene/resources/style_box.h7
-rw-r--r--scene/resources/surface_tool.cpp435
-rw-r--r--scene/resources/surface_tool.h61
-rw-r--r--scene/resources/syntax_highlighter.cpp52
-rw-r--r--scene/resources/syntax_highlighter.h4
-rw-r--r--scene/resources/text_line.cpp369
-rw-r--r--scene/resources/text_line.h116
-rw-r--r--scene/resources/text_paragraph.cpp532
-rw-r--r--scene/resources/text_paragraph.h126
-rw-r--r--scene/resources/texture.cpp439
-rw-r--r--scene/resources/texture.h158
-rw-r--r--scene/resources/theme.cpp397
-rw-r--r--scene/resources/theme.h92
-rw-r--r--scene/resources/tile_set.cpp26
-rw-r--r--scene/resources/tile_set.h8
-rw-r--r--scene/resources/visual_shader.cpp394
-rw-r--r--scene/resources/visual_shader.h83
-rw-r--r--scene/resources/visual_shader_nodes.cpp436
-rw-r--r--scene/resources/visual_shader_nodes.h207
-rw-r--r--scene/resources/world_2d.cpp6
-rw-r--r--scene/resources/world_2d.h4
-rw-r--r--scene/resources/world_3d.cpp4
-rw-r--r--scene/resources/world_3d.h2
-rw-r--r--scene/resources/world_margin_shape_3d.cpp2
-rw-r--r--scene/resources/world_margin_shape_3d.h2
-rw-r--r--scene/scene_string_names.cpp3
-rw-r--r--scene/scene_string_names.h7
290 files changed, 16139 insertions, 8397 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp
index 3268544519..0f98fad824 100644
--- a/scene/2d/animated_sprite_2d.cpp
+++ b/scene/2d/animated_sprite_2d.cpp
@@ -31,11 +31,9 @@
#include "animated_sprite_2d.h"
#include "core/os/os.h"
+#include "scene/main/viewport.h"
#include "scene/scene_string_names.h"
-#define NORMAL_SUFFIX "_normal"
-#define SPECULAR_SUFFIX "_specular"
-
#ifdef TOOLS_ENABLED
Dictionary AnimatedSprite2D::_edit_get_state() const {
Dictionary state = Node2D::_edit_get_state();
@@ -152,8 +150,6 @@ void SpriteFrames::add_animation(const StringName &p_anim) {
ERR_FAIL_COND_MSG(animations.has(p_anim), "SpriteFrames already has animation '" + p_anim + "'.");
animations[p_anim] = Anim();
- animations[p_anim].normal_name = String(p_anim) + NORMAL_SUFFIX;
- animations[p_anim].specular_name = String(p_anim) + SPECULAR_SUFFIX;
}
bool SpriteFrames::has_animation(const StringName &p_anim) const {
@@ -171,8 +167,6 @@ void SpriteFrames::rename_animation(const StringName &p_prev, const StringName &
Anim anim = animations[p_prev];
animations.erase(p_prev);
animations[p_next] = anim;
- animations[p_next].normal_name = String(p_next) + NORMAL_SUFFIX;
- animations[p_next].specular_name = String(p_next) + SPECULAR_SUFFIX;
}
Vector<String> SpriteFrames::_get_animation_list() const {
@@ -441,9 +435,6 @@ void AnimatedSprite2D::_notification(int p_what) {
return;
}
- Ref<Texture2D> normal = frames->get_normal_frame(animation, frame);
- Ref<Texture2D> specular = frames->get_specular_frame(animation, frame);
-
RID ci = get_canvas_item();
Size2i s;
@@ -453,7 +444,7 @@ void AnimatedSprite2D::_notification(int p_what) {
ofs -= s / 2;
}
- if (Engine::get_singleton()->get_use_pixel_snap()) {
+ if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
ofs = ofs.floor();
}
Rect2 dst_rect(ofs, s);
@@ -465,7 +456,7 @@ void AnimatedSprite2D::_notification(int p_what) {
dst_rect.size.y = -dst_rect.size.y;
}
- texture->draw_rect_region(ci, dst_rect, Rect2(Vector2(), texture->get_size()), Color(1, 1, 1), false, normal, specular, Color(specular_color.r, specular_color.g, specular_color.b, shininess));
+ texture->draw_rect_region(ci, dst_rect, Rect2(Vector2(), texture->get_size()), Color(1, 1, 1), false);
} break;
}
@@ -660,29 +651,16 @@ StringName AnimatedSprite2D::get_animation() const {
}
String AnimatedSprite2D::get_configuration_warning() const {
+ String warning = Node2D::get_configuration_warning();
+
if (frames.is_null()) {
- return TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite to display frames.");
+ if (!warning.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.");
}
- return String();
-}
-
-void AnimatedSprite2D::set_specular_color(const Color &p_color) {
- specular_color = p_color;
- update();
-}
-
-Color AnimatedSprite2D::get_specular_color() const {
- return specular_color;
-}
-
-void AnimatedSprite2D::set_shininess(float p_shininess) {
- shininess = CLAMP(p_shininess, 0.0, 1.0);
- update();
-}
-
-float AnimatedSprite2D::get_shininess() const {
- return shininess;
+ return warning;
}
void AnimatedSprite2D::_bind_methods() {
@@ -717,12 +695,6 @@ void AnimatedSprite2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_speed_scale", "speed_scale"), &AnimatedSprite2D::set_speed_scale);
ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedSprite2D::get_speed_scale);
- ClassDB::bind_method(D_METHOD("set_specular_color", "color"), &AnimatedSprite2D::set_specular_color);
- ClassDB::bind_method(D_METHOD("get_specular_color"), &AnimatedSprite2D::get_specular_color);
-
- ClassDB::bind_method(D_METHOD("set_shininess", "shininess"), &AnimatedSprite2D::set_shininess);
- ClassDB::bind_method(D_METHOD("get_shininess"), &AnimatedSprite2D::get_shininess);
-
ADD_SIGNAL(MethodInfo("frame_changed"));
ADD_SIGNAL(MethodInfo("animation_finished"));
@@ -732,9 +704,6 @@ void AnimatedSprite2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing");
- ADD_GROUP("Lighting", "");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "specular_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_specular_color", "get_specular_color");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "shininess", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_shininess", "get_shininess");
ADD_GROUP("Offset", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
@@ -754,6 +723,4 @@ AnimatedSprite2D::AnimatedSprite2D() {
animation = "default";
timeout = 0;
is_over = false;
- specular_color = Color(1, 1, 1, 1);
- shininess = 1.0;
}
diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h
index cefed56620..fddbf39be2 100644
--- a/scene/2d/animated_sprite_2d.h
+++ b/scene/2d/animated_sprite_2d.h
@@ -46,14 +46,8 @@ class SpriteFrames : public Resource {
loop = true;
speed = 5;
}
-
- StringName normal_name;
- StringName specular_name;
};
- Color specular_color;
- float shininess;
-
Map<StringName, Anim> animations;
Array _get_frames() const;
@@ -95,34 +89,6 @@ public:
return E->get().frames[p_idx];
}
- _FORCE_INLINE_ Ref<Texture2D> get_normal_frame(const StringName &p_anim, int p_idx) const {
- const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V_MSG(!E, Ref<Texture2D>(), "Animation '" + String(p_anim) + "' doesn't exist.");
- ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>());
-
- const Map<StringName, Anim>::Element *EN = animations.find(E->get().normal_name);
-
- if (!EN || p_idx >= EN->get().frames.size()) {
- return Ref<Texture2D>();
- }
-
- return EN->get().frames[p_idx];
- }
-
- _FORCE_INLINE_ Ref<Texture2D> get_specular_frame(const StringName &p_anim, int p_idx) const {
- const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V(!E, Ref<Texture2D>());
- ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>());
-
- const Map<StringName, Anim>::Element *EN = animations.find(E->get().specular_name);
-
- if (!EN || p_idx >= EN->get().frames.size()) {
- return Ref<Texture2D>();
- }
-
- return EN->get().frames[p_idx];
- }
-
void set_frame(const StringName &p_anim, int p_idx, const Ref<Texture2D> &p_frame) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
@@ -166,9 +132,6 @@ class AnimatedSprite2D : public Node2D {
bool _is_playing() const;
Rect2 _get_rect() const;
- Color specular_color;
- float shininess;
-
protected:
static void _bind_methods();
void _notification(int p_what);
@@ -216,12 +179,6 @@ public:
void set_flip_v(bool p_flip);
bool is_flipped_v() const;
- void set_specular_color(const Color &p_color);
- Color get_specular_color() const;
-
- void set_shininess(float p_shininess);
- float get_shininess() const;
-
virtual String get_configuration_warning() const override;
AnimatedSprite2D();
};
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index ebfcb9cad6..d51ee3f9a8 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -180,26 +180,20 @@ void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i
E->get().shapes.erase(ShapePair(p_body_shape, p_area_shape));
}
- bool eraseit = false;
-
+ bool in_tree = E->get().in_tree;
if (E->get().rc == 0) {
+ body_map.erase(E);
if (node) {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree));
- if (E->get().in_tree) {
+ if (in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_exited, obj);
}
}
-
- eraseit = true;
}
- if (!node || E->get().in_tree) {
+ if (!node || in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_shape_exited, objid, obj, p_body_shape, p_area_shape);
}
-
- if (eraseit) {
- body_map.erase(E);
- }
}
locked = false;
@@ -278,26 +272,20 @@ void Area2D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i
E->get().shapes.erase(AreaShapePair(p_area_shape, p_self_shape));
}
- bool eraseit = false;
-
+ bool in_tree = E->get().in_tree;
if (E->get().rc == 0) {
+ area_map.erase(E);
if (node) {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_area_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_area_exit_tree));
- if (E->get().in_tree) {
+ if (in_tree) {
emit_signal(SceneStringNames::get_singleton()->area_exited, obj);
}
}
-
- eraseit = true;
}
- if (!node || E->get().in_tree) {
+ if (!node || in_tree) {
emit_signal(SceneStringNames::get_singleton()->area_shape_exited, objid, obj, p_area_shape, p_self_shape);
}
-
- if (eraseit) {
- area_map.erase(E);
- }
}
locked = false;
diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h
index 32226ef5a4..01426db999 100644
--- a/scene/2d/area_2d.h
+++ b/scene/2d/area_2d.h
@@ -31,7 +31,7 @@
#ifndef AREA_2D_H
#define AREA_2D_H
-#include "core/vset.h"
+#include "core/templates/vset.h"
#include "scene/2d/collision_object_2d.h"
class Area2D : public CollisionObject2D {
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 5b89ac15b1..9bd716aeaa 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -30,7 +30,7 @@
#include "audio_stream_player_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "scene/2d/area_2d.h"
#include "scene/main/window.h"
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 68e99445d8..79b0b64efb 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -30,7 +30,7 @@
#include "camera_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "core/math/math_funcs.h"
#include "scene/scene_string_names.h"
#include "servers/rendering_server.h"
@@ -56,7 +56,7 @@ void Camera2D::_update_scroll() {
viewport->set_canvas_transform(xform);
- Size2 screen_size = viewport->get_visible_rect().size;
+ Size2 screen_size = _get_camera_screen_size();
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2());
get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_camera_moved", xform, screen_offset);
@@ -94,7 +94,7 @@ Transform2D Camera2D::get_camera_transform() {
ERR_FAIL_COND_V(custom_viewport && !ObjectDB::get_instance(custom_viewport_id), Transform2D());
- Size2 screen_size = viewport->get_visible_rect().size;
+ Size2 screen_size = _get_camera_screen_size();
Point2 new_camera_pos = get_global_transform().get_origin();
Point2 ret_camera_pos;
@@ -198,10 +198,10 @@ Transform2D Camera2D::get_camera_transform() {
camera_screen_center = screen_rect.position + screen_rect.size * 0.5;
Transform2D xform;
+ xform.scale_basis(zoom);
if (rotating) {
xform.set_rotation(angle);
}
- xform.scale_basis(zoom);
xform.set_origin(screen_rect.position /*.floor()*/);
/*
@@ -274,7 +274,7 @@ void Camera2D::_notification(int p_what) {
}
Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
- Size2 screen_size = get_viewport_rect().size;
+ Size2 screen_size = _get_camera_screen_size();
Vector2 screen_endpoints[4] = {
inv_camera_transform.xform(Vector2(0, 0)),
@@ -321,7 +321,7 @@ void Camera2D::_notification(int p_what) {
}
Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
- Size2 screen_size = get_viewport_rect().size;
+ 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]))),
@@ -469,7 +469,7 @@ void Camera2D::reset_smoothing() {
void Camera2D::align() {
ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
- Size2 screen_size = viewport->get_visible_rect().size;
+ Size2 screen_size = _get_camera_screen_size();
Point2 current_camera_pos = get_global_transform().get_origin();
if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
@@ -507,6 +507,14 @@ Point2 Camera2D::get_camera_screen_center() const {
return camera_screen_center;
}
+Size2 Camera2D::_get_camera_screen_size() const {
+ // special case if the camera2D is in the root viewport
+ if (Engine::get_singleton()->is_editor_hint() && get_viewport()->get_parent_viewport() == get_tree()->get_root()) {
+ return Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ }
+ return get_viewport_rect().size;
+}
+
void Camera2D::set_h_drag_enabled(bool p_enabled) {
h_drag_enabled = p_enabled;
}
diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h
index 0a4e269c40..867a5562b2 100644
--- a/scene/2d/camera_2d.h
+++ b/scene/2d/camera_2d.h
@@ -94,6 +94,8 @@ protected:
Camera2DProcessMode process_mode;
+ Size2 _get_camera_screen_size() const;
+
protected:
virtual Transform2D get_camera_transform();
void _notification(int p_what);
diff --git a/scene/2d/canvas_group.cpp b/scene/2d/canvas_group.cpp
new file mode 100644
index 0000000000..39cae8e0c6
--- /dev/null
+++ b/scene/2d/canvas_group.cpp
@@ -0,0 +1,87 @@
+/*************************************************************************/
+/* canvas_group.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 "canvas_group.h"
+
+void CanvasGroup::set_fit_margin(float p_fit_margin) {
+ ERR_FAIL_COND(p_fit_margin < 0.0);
+
+ fit_margin = p_fit_margin;
+ RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CANVAS_GROUP_MODE_TRANSPARENT, clear_margin, true, fit_margin, use_mipmaps);
+
+ update();
+}
+
+float CanvasGroup::get_fit_margin() const {
+ return fit_margin;
+}
+
+void CanvasGroup::set_clear_margin(float p_clear_margin) {
+ ERR_FAIL_COND(p_clear_margin < 0.0);
+
+ clear_margin = p_clear_margin;
+ RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CANVAS_GROUP_MODE_TRANSPARENT, clear_margin, true, clear_margin, use_mipmaps);
+
+ update();
+}
+
+float CanvasGroup::get_clear_margin() const {
+ return clear_margin;
+}
+
+void CanvasGroup::set_use_mipmaps(bool p_use_mipmaps) {
+ use_mipmaps = p_use_mipmaps;
+ RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CANVAS_GROUP_MODE_TRANSPARENT, clear_margin, true, fit_margin, use_mipmaps);
+}
+bool CanvasGroup::is_using_mipmaps() const {
+ return use_mipmaps;
+}
+
+void CanvasGroup::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_fit_margin", "fit_margin"), &CanvasGroup::set_fit_margin);
+ ClassDB::bind_method(D_METHOD("get_fit_margin"), &CanvasGroup::get_fit_margin);
+
+ ClassDB::bind_method(D_METHOD("set_clear_margin", "clear_margin"), &CanvasGroup::set_clear_margin);
+ ClassDB::bind_method(D_METHOD("get_clear_margin"), &CanvasGroup::get_clear_margin);
+
+ ClassDB::bind_method(D_METHOD("set_use_mipmaps", "use_mipmaps"), &CanvasGroup::set_use_mipmaps);
+ ClassDB::bind_method(D_METHOD("is_using_mipmaps"), &CanvasGroup::is_using_mipmaps);
+
+ ADD_GROUP("Tweaks", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fit_margin", PROPERTY_HINT_RANGE, "0,1024,1.0,or_greater"), "set_fit_margin", "get_fit_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "clear_margin", PROPERTY_HINT_RANGE, "0,1024,1.0,or_greater"), "set_clear_margin", "get_clear_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_mipmaps"), "set_use_mipmaps", "is_using_mipmaps");
+}
+
+CanvasGroup::CanvasGroup() {
+ set_fit_margin(10.0); //sets things
+}
+CanvasGroup::~CanvasGroup() {
+}
diff --git a/scene/2d/canvas_group.h b/scene/2d/canvas_group.h
new file mode 100644
index 0000000000..19630befc7
--- /dev/null
+++ b/scene/2d/canvas_group.h
@@ -0,0 +1,59 @@
+/*************************************************************************/
+/* canvas_group.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 CANVASGROUP_H
+#define CANVASGROUP_H
+
+#include "scene/2d/node_2d.h"
+
+class CanvasGroup : public Node2D {
+ GDCLASS(CanvasGroup, Node2D)
+ float fit_margin = 10.0;
+ float clear_margin = 10.0;
+ bool use_mipmaps = false;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_fit_margin(float p_fit_margin);
+ float get_fit_margin() const;
+
+ void set_clear_margin(float p_clear_margin);
+ float get_clear_margin() const;
+
+ void set_use_mipmaps(bool p_use_mipmaps);
+ bool is_using_mipmaps() const;
+
+ CanvasGroup();
+ ~CanvasGroup();
+};
+
+#endif // CANVASGROUP_H
diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp
index 56643542a8..8fb16534e8 100644
--- a/scene/2d/canvas_modulate.cpp
+++ b/scene/2d/canvas_modulate.cpp
@@ -78,14 +78,19 @@ String CanvasModulate::get_configuration_warning() const {
return String();
}
+ String warning = Node2D::get_configuration_warning();
+
List<Node *> nodes;
get_tree()->get_nodes_in_group("_canvas_modulate_" + itos(get_canvas().get_id()), &nodes);
if (nodes.size() > 1) {
- return 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.");
+ if (!warning.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.");
}
- return String();
+ return warning;
}
CanvasModulate::CanvasModulate() {
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index d23398713a..64d82d715c 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -31,7 +31,7 @@
#include "collision_polygon_2d.h"
#include "collision_object_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "core/math/geometry_2d.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
@@ -230,15 +230,23 @@ bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, doubl
#endif
String CollisionPolygon2D::get_configuration_warning() const {
+ String warning = Node2D::get_configuration_warning();
+
if (!Object::cast_to<CollisionObject2D>(get_parent())) {
- return 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 (!warning.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()) {
- return TTR("An empty CollisionPolygon2D has no effect on collision.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("An empty CollisionPolygon2D has no effect on collision.");
}
- return String();
+ return warning;
}
void CollisionPolygon2D::set_disabled(bool p_disabled) {
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index d022c857f3..a5cd624235 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -31,7 +31,7 @@
#include "collision_shape_2d.h"
#include "collision_object_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "scene/resources/capsule_shape_2d.h"
#include "scene/resources/circle_shape_2d.h"
#include "scene/resources/concave_polygon_shape_2d.h"
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 526951976e..3649746c40 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -228,15 +228,6 @@ Ref<Texture2D> CPUParticles2D::get_texture() const {
return texture;
}
-void CPUParticles2D::set_normalmap(const Ref<Texture2D> &p_normalmap) {
- normalmap = p_normalmap;
- update();
-}
-
-Ref<Texture2D> CPUParticles2D::get_normalmap() const {
- return normalmap;
-}
-
void CPUParticles2D::set_fixed_fps(int p_count) {
fixed_fps = p_count;
}
@@ -254,7 +245,7 @@ bool CPUParticles2D::get_fractional_delta() const {
}
String CPUParticles2D::get_configuration_warning() const {
- String warnings;
+ String warnings = Node2D::get_configuration_warning();
CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr());
@@ -406,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) {
@@ -749,7 +740,11 @@ void CPUParticles2D::_particles_process(float p_delta) {
p.transform[2] = emission_points.get(random_idx);
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS && emission_normals.size() == pc) {
- p.velocity = emission_normals.get(random_idx);
+ Vector2 normal = emission_normals.get(random_idx);
+ Transform2D m2;
+ m2.set_axis(0, normal);
+ m2.set_axis(1, normal.tangent());
+ p.velocity = m2.basis_xform(p.velocity);
}
if (emission_colors.size() == pc) {
@@ -910,7 +905,7 @@ 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();
@@ -1056,12 +1051,7 @@ void CPUParticles2D::_notification(int p_what) {
texrid = texture->get_rid();
}
- RID normrid;
- if (normalmap.is_valid()) {
- normrid = normalmap->get_rid();
- }
-
- RS::get_singleton()->canvas_item_add_multimesh(get_canvas_item(), multimesh, texrid, normrid);
+ RS::get_singleton()->canvas_item_add_multimesh(get_canvas_item(), multimesh, texrid);
}
if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
@@ -1140,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());
@@ -1210,9 +1200,6 @@ void CPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &CPUParticles2D::set_texture);
ClassDB::bind_method(D_METHOD("get_texture"), &CPUParticles2D::get_texture);
- ClassDB::bind_method(D_METHOD("set_normalmap", "normalmap"), &CPUParticles2D::set_normalmap);
- ClassDB::bind_method(D_METHOD("get_normalmap"), &CPUParticles2D::get_normalmap);
-
ClassDB::bind_method(D_METHOD("restart"), &CPUParticles2D::restart);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
@@ -1232,7 +1219,6 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime"), "set_draw_order", "get_draw_order");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normalmap", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normalmap", "get_normalmap");
BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
@@ -1260,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);
@@ -1287,14 +1273,14 @@ void CPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles2D::convert_from_particles);
ADD_GROUP("Emission Shape", "emission_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "emission_rect_extents"), "set_emission_rect_extents", "get_emission_rect_extents");
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");
@@ -1365,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);
@@ -1429,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 dfc875ceb0..857f19b20f 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -31,7 +31,7 @@
#ifndef CPU_PARTICLES_2D_H
#define CPU_PARTICLES_2D_H
-#include "core/rid.h"
+#include "core/templates/rid.h"
#include "scene/2d/node_2d.h"
#include "scene/resources/texture.h"
@@ -46,7 +46,6 @@ public:
};
enum Parameter {
-
PARAM_INITIAL_LINEAR_VELOCITY,
PARAM_ANGULAR_VELOCITY,
PARAM_ORBIT_VELOCITY,
@@ -62,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 {
@@ -147,7 +146,6 @@ private:
DrawOrder draw_order;
Ref<Texture2D> texture;
- Ref<Texture2D> normalmap;
////////
@@ -161,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;
@@ -232,9 +230,6 @@ public:
void set_texture(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_texture() const;
- void set_normalmap(const Ref<Texture2D> &p_normalmap);
- Ref<Texture2D> get_normalmap() const;
-
///////////////////
void set_direction(Vector2 p_direction);
@@ -258,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);
@@ -292,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 0814fbb549..46096d7460 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -35,7 +35,7 @@
#include "scene/scene_string_names.h"
#ifdef TOOLS_ENABLED
-#include "core/engine.h"
+#include "core/config/engine.h"
#endif
void GPUParticles2D::set_emitting(bool p_emitting) {
@@ -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;
@@ -222,7 +222,7 @@ String GPUParticles2D::get_configuration_warning() const {
return TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose.");
}
- String warnings;
+ String warnings = Node2D::get_configuration_warning();
if (process_material.is_null()) {
if (warnings != String()) {
@@ -267,15 +267,6 @@ Ref<Texture2D> GPUParticles2D::get_texture() const {
return texture;
}
-void GPUParticles2D::set_normal_map(const Ref<Texture2D> &p_normal_map) {
- normal_map = p_normal_map;
- update();
-}
-
-Ref<Texture2D> GPUParticles2D::get_normal_map() const {
- return normal_map;
-}
-
void GPUParticles2D::_validate_property(PropertyInfo &property) const {
}
@@ -290,12 +281,8 @@ void GPUParticles2D::_notification(int p_what) {
if (texture.is_valid()) {
texture_rid = texture->get_rid();
}
- RID normal_rid;
- if (normal_map.is_valid()) {
- normal_rid = normal_map->get_rid();
- }
- RS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid);
+ RS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid);
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) {
@@ -359,9 +346,6 @@ void GPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticles2D::set_texture);
ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticles2D::get_texture);
- ClassDB::bind_method(D_METHOD("set_normal_map", "texture"), &GPUParticles2D::set_normal_map);
- ClassDB::bind_method(D_METHOD("get_normal_map"), &GPUParticles2D::get_normal_map);
-
ClassDB::bind_method(D_METHOD("capture_rect"), &GPUParticles2D::capture_rect);
ClassDB::bind_method(D_METHOD("restart"), &GPUParticles2D::restart);
@@ -385,7 +369,6 @@ void GPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material");
ADD_GROUP("Textures", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_map", "get_normal_map");
BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h
index 3258237f92..0d1b82d93e 100644
--- a/scene/2d/gpu_particles_2d.h
+++ b/scene/2d/gpu_particles_2d.h
@@ -31,7 +31,7 @@
#ifndef PARTICLES_2D_H
#define PARTICLES_2D_H
-#include "core/rid.h"
+#include "core/templates/rid.h"
#include "scene/2d/node_2d.h"
#include "scene/resources/texture.h"
@@ -65,7 +65,6 @@ private:
DrawOrder draw_order;
Ref<Texture2D> texture;
- Ref<Texture2D> normal_map;
void _update_particle_emission_transform();
@@ -111,9 +110,6 @@ public:
void set_texture(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_texture() const;
- void set_normal_map(const Ref<Texture2D> &p_normal_map);
- Ref<Texture2D> get_normal_map() const;
-
virtual String get_configuration_warning() const override;
void restart();
diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp
index 8df72d7aac..f5d13fd641 100644
--- a/scene/2d/joints_2d.cpp
+++ b/scene/2d/joints_2d.cpp
@@ -30,7 +30,7 @@
#include "joints_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "physics_body_2d.h"
#include "servers/physics_server_2d.h"
@@ -47,29 +47,53 @@ void Joint2D::_update_joint(bool p_only_free) {
}
if (p_only_free || !is_inside_tree()) {
+ warning = String();
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;
- if (!node_a || !node_b) {
+ PhysicsBody2D *body_a = Object::cast_to<PhysicsBody2D>(node_a);
+ PhysicsBody2D *body_b = Object::cast_to<PhysicsBody2D>(node_b);
+
+ if (node_a && !body_a && node_b && !body_b) {
+ warning = TTR("Node A and Node B must be PhysicsBody2Ds");
+ update_configuration_warning();
return;
}
- PhysicsBody2D *body_a = Object::cast_to<PhysicsBody2D>(node_a);
- PhysicsBody2D *body_b = Object::cast_to<PhysicsBody2D>(node_b);
+ if (node_a && !body_a) {
+ warning = TTR("Node A must be a PhysicsBody2D");
+ update_configuration_warning();
+ return;
+ }
- if (!body_a || !body_b) {
+ if (node_b && !body_b) {
+ warning = TTR("Node B must be a PhysicsBody2D");
+ update_configuration_warning();
return;
}
- joint = _configure_joint(body_a, body_b);
+ if (!body_a || !body_b) {
+ warning = TTR("Joint is not connected to two PhysicsBody2Ds");
+ update_configuration_warning();
+ return;
+ }
- if (!joint.is_valid()) {
+ if (body_a == body_b) {
+ warning = TTR("Node A and Node B must be different PhysicsBody2Ds");
+ update_configuration_warning();
return;
}
+ warning = String();
+ update_configuration_warning();
+
+ joint = _configure_joint(body_a, body_b);
+
+ ERR_FAIL_COND_MSG(!joint.is_valid(), "Failed to configure the joint.");
+
PhysicsServer2D::get_singleton()->get_singleton()->joint_set_param(joint, PhysicsServer2D::JOINT_PARAM_BIAS, bias);
ba = body_a->get_rid();
@@ -141,6 +165,19 @@ bool Joint2D::get_exclude_nodes_from_collision() const {
return exclude_from_collision;
}
+String Joint2D::get_configuration_warning() const {
+ String node_warning = Node2D::get_configuration_warning();
+
+ if (!warning.empty()) {
+ if (!node_warning.empty()) {
+ node_warning += "\n\n";
+ }
+ node_warning += warning;
+ }
+
+ return node_warning;
+}
+
void Joint2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_node_a", "node"), &Joint2D::set_node_a);
ClassDB::bind_method(D_METHOD("get_node_a"), &Joint2D::get_node_a);
@@ -154,8 +191,8 @@ void Joint2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude_nodes_from_collision", "enable"), &Joint2D::set_exclude_nodes_from_collision);
ClassDB::bind_method(D_METHOD("get_exclude_nodes_from_collision"), &Joint2D::get_exclude_nodes_from_collision);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject2D"), "set_node_a", "get_node_a");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject2D"), "set_node_b", "get_node_b");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody2D"), "set_node_a", "get_node_a");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody2D"), "set_node_b", "get_node_b");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bias", PROPERTY_HINT_RANGE, "0,0.9,0.001"), "set_bias", "get_bias");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_collision"), "set_exclude_nodes_from_collision", "get_exclude_nodes_from_collision");
}
diff --git a/scene/2d/joints_2d.h b/scene/2d/joints_2d.h
index 60534cae65..759e7de8a0 100644
--- a/scene/2d/joints_2d.h
+++ b/scene/2d/joints_2d.h
@@ -46,6 +46,7 @@ class Joint2D : public Node2D {
real_t bias;
bool exclude_from_collision;
+ String warning;
protected:
void _update_joint(bool p_only_free = false);
@@ -56,6 +57,8 @@ protected:
static void _bind_methods();
public:
+ virtual String get_configuration_warning() const override;
+
void set_node_a(const NodePath &p_node_a);
NodePath get_node_a() const;
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 1e7e9f6b6a..2b373a669b 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -30,57 +30,9 @@
#include "light_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "servers/rendering_server.h"
-#ifdef TOOLS_ENABLED
-Dictionary Light2D::_edit_get_state() const {
- Dictionary state = Node2D::_edit_get_state();
- state["offset"] = get_texture_offset();
- return state;
-}
-
-void Light2D::_edit_set_state(const Dictionary &p_state) {
- Node2D::_edit_set_state(p_state);
- set_texture_offset(p_state["offset"]);
-}
-
-void Light2D::_edit_set_pivot(const Point2 &p_pivot) {
- set_position(get_transform().xform(p_pivot));
- set_texture_offset(get_texture_offset() - p_pivot);
-}
-
-Point2 Light2D::_edit_get_pivot() const {
- return Vector2();
-}
-
-bool Light2D::_edit_use_pivot() const {
- return true;
-}
-
-Rect2 Light2D::_edit_get_rect() const {
- if (texture.is_null()) {
- return Rect2();
- }
-
- Size2 s = texture->get_size() * _scale;
- return Rect2(texture_offset - s / 2.0, s);
-}
-
-bool Light2D::_edit_use_rect() const {
- return !texture.is_null();
-}
-#endif
-
-Rect2 Light2D::get_anchorable_rect() const {
- if (texture.is_null()) {
- return Rect2();
- }
-
- Size2 s = texture->get_size() * _scale;
- return Rect2(texture_offset - s / 2.0, s);
-}
-
void Light2D::_update_light_visibility() {
if (!is_inside_tree()) {
return;
@@ -123,32 +75,6 @@ bool Light2D::is_editor_only() const {
return editor_only;
}
-void Light2D::set_texture(const Ref<Texture2D> &p_texture) {
- texture = p_texture;
- if (texture.is_valid()) {
- RS::get_singleton()->canvas_light_set_texture(canvas_light, texture->get_rid());
- } else {
- RS::get_singleton()->canvas_light_set_texture(canvas_light, RID());
- }
-
- update_configuration_warning();
-}
-
-Ref<Texture2D> Light2D::get_texture() const {
- return texture;
-}
-
-void Light2D::set_texture_offset(const Vector2 &p_offset) {
- texture_offset = p_offset;
- RS::get_singleton()->canvas_light_set_texture_offset(canvas_light, texture_offset);
- item_rect_changed();
- _change_notify("offset");
-}
-
-Vector2 Light2D::get_texture_offset() const {
- return texture_offset;
-}
-
void Light2D::set_color(const Color &p_color) {
color = p_color;
RS::get_singleton()->canvas_light_set_color(canvas_light, color);
@@ -176,20 +102,6 @@ float Light2D::get_energy() const {
return energy;
}
-void Light2D::set_texture_scale(float p_scale) {
- _scale = p_scale;
- // Avoid having 0 scale values, can lead to errors in physics and rendering.
- if (_scale == 0) {
- _scale = CMP_EPSILON;
- }
- RS::get_singleton()->canvas_light_set_scale(canvas_light, _scale);
- item_rect_changed();
-}
-
-float Light2D::get_texture_scale() const {
- return _scale;
-}
-
void Light2D::set_z_range_min(int p_min_z) {
z_min = p_min_z;
RS::get_singleton()->canvas_light_set_z_range(canvas_light, z_min, z_max);
@@ -244,15 +156,6 @@ int Light2D::get_item_shadow_cull_mask() const {
return item_shadow_mask;
}
-void Light2D::set_mode(Mode p_mode) {
- mode = p_mode;
- RS::get_singleton()->canvas_light_set_mode(canvas_light, RS::CanvasLightMode(p_mode));
-}
-
-Light2D::Mode Light2D::get_mode() const {
- return mode;
-}
-
void Light2D::set_shadow_enabled(bool p_enabled) {
shadow = p_enabled;
RS::get_singleton()->canvas_light_set_shadow_enabled(canvas_light, shadow);
@@ -262,15 +165,6 @@ bool Light2D::is_shadow_enabled() const {
return shadow;
}
-void Light2D::set_shadow_buffer_size(int p_size) {
- shadow_buffer_size = p_size;
- RS::get_singleton()->canvas_light_set_shadow_buffer_size(canvas_light, shadow_buffer_size);
-}
-
-int Light2D::get_shadow_buffer_size() const {
- return shadow_buffer_size;
-}
-
void Light2D::set_shadow_filter(ShadowFilter p_filter) {
ERR_FAIL_INDEX(p_filter, SHADOW_FILTER_MAX);
shadow_filter = p_filter;
@@ -290,6 +184,15 @@ Color Light2D::get_shadow_color() const {
return shadow_color;
}
+void Light2D::set_blend_mode(BlendMode p_mode) {
+ blend_mode = p_mode;
+ RS::get_singleton()->canvas_light_set_blend_mode(_get_light(), RS::CanvasLightBlendMode(p_mode));
+}
+
+Light2D::BlendMode Light2D::get_blend_mode() const {
+ return blend_mode;
+}
+
void Light2D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
RS::get_singleton()->canvas_light_attach_to_canvas(canvas_light, get_canvas());
@@ -309,14 +212,6 @@ void Light2D::_notification(int p_what) {
}
}
-String Light2D::get_configuration_warning() const {
- if (!texture.is_valid()) {
- return TTR("A texture with the shape of the light must be supplied to the \"Texture\" property.");
- }
-
- return String();
-}
-
void Light2D::set_shadow_smooth(float p_amount) {
shadow_smooth = p_amount;
RS::get_singleton()->canvas_light_set_shadow_smooth(canvas_light, shadow_smooth);
@@ -333,24 +228,12 @@ void Light2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_editor_only", "editor_only"), &Light2D::set_editor_only);
ClassDB::bind_method(D_METHOD("is_editor_only"), &Light2D::is_editor_only);
- ClassDB::bind_method(D_METHOD("set_texture", "texture"), &Light2D::set_texture);
- ClassDB::bind_method(D_METHOD("get_texture"), &Light2D::get_texture);
-
- ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &Light2D::set_texture_offset);
- ClassDB::bind_method(D_METHOD("get_texture_offset"), &Light2D::get_texture_offset);
-
ClassDB::bind_method(D_METHOD("set_color", "color"), &Light2D::set_color);
ClassDB::bind_method(D_METHOD("get_color"), &Light2D::get_color);
- ClassDB::bind_method(D_METHOD("set_height", "height"), &Light2D::set_height);
- ClassDB::bind_method(D_METHOD("get_height"), &Light2D::get_height);
-
ClassDB::bind_method(D_METHOD("set_energy", "energy"), &Light2D::set_energy);
ClassDB::bind_method(D_METHOD("get_energy"), &Light2D::get_energy);
- ClassDB::bind_method(D_METHOD("set_texture_scale", "texture_scale"), &Light2D::set_texture_scale);
- ClassDB::bind_method(D_METHOD("get_texture_scale"), &Light2D::get_texture_scale);
-
ClassDB::bind_method(D_METHOD("set_z_range_min", "z"), &Light2D::set_z_range_min);
ClassDB::bind_method(D_METHOD("get_z_range_min"), &Light2D::get_z_range_min);
@@ -369,15 +252,9 @@ void Light2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_shadow_cull_mask", "item_shadow_cull_mask"), &Light2D::set_item_shadow_cull_mask);
ClassDB::bind_method(D_METHOD("get_item_shadow_cull_mask"), &Light2D::get_item_shadow_cull_mask);
- ClassDB::bind_method(D_METHOD("set_mode", "mode"), &Light2D::set_mode);
- ClassDB::bind_method(D_METHOD("get_mode"), &Light2D::get_mode);
-
ClassDB::bind_method(D_METHOD("set_shadow_enabled", "enabled"), &Light2D::set_shadow_enabled);
ClassDB::bind_method(D_METHOD("is_shadow_enabled"), &Light2D::is_shadow_enabled);
- ClassDB::bind_method(D_METHOD("set_shadow_buffer_size", "size"), &Light2D::set_shadow_buffer_size);
- ClassDB::bind_method(D_METHOD("get_shadow_buffer_size"), &Light2D::get_shadow_buffer_size);
-
ClassDB::bind_method(D_METHOD("set_shadow_smooth", "smooth"), &Light2D::set_shadow_smooth);
ClassDB::bind_method(D_METHOD("get_shadow_smooth"), &Light2D::get_shadow_smooth);
@@ -387,16 +264,18 @@ void Light2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shadow_color", "shadow_color"), &Light2D::set_shadow_color);
ClassDB::bind_method(D_METHOD("get_shadow_color"), &Light2D::get_shadow_color);
+ ClassDB::bind_method(D_METHOD("set_blend_mode", "mode"), &Light2D::set_blend_mode);
+ ClassDB::bind_method(D_METHOD("get_blend_mode"), &Light2D::get_blend_mode);
+
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &Light2D::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &Light2D::get_height);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_texture_offset", "get_texture_offset");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_scale", PROPERTY_HINT_RANGE, "0.01,50,0.01"), "set_texture_scale", "get_texture_scale");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_energy", "get_energy");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Add,Sub,Mix,Mask"), "set_mode", "get_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Add,Sub,Mix"), "set_blend_mode", "get_blend_mode");
ADD_GROUP("Range", "range_");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "range_height", PROPERTY_HINT_RANGE, "-2048,2048,0.1,or_lesser,or_greater"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_min", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_min", "get_z_range_min");
ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_max", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_max", "get_z_range_max");
ADD_PROPERTY(PropertyInfo(Variant::INT, "range_layer_min", PROPERTY_HINT_RANGE, "-512,512,1"), "set_layer_range_min", "get_layer_range_min");
@@ -406,19 +285,17 @@ void Light2D::_bind_methods() {
ADD_GROUP("Shadow", "shadow_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_enabled"), "set_shadow_enabled", "is_shadow_enabled");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_buffer_size", PROPERTY_HINT_RANGE, "32,16384,1"), "set_shadow_buffer_size", "get_shadow_buffer_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_filter", PROPERTY_HINT_ENUM, "None,PCF5,PCF13"), "set_shadow_filter", "get_shadow_filter");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "shadow_filter_smooth", PROPERTY_HINT_RANGE, "0,64,0.1"), "set_shadow_smooth", "get_shadow_smooth");
ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_item_cull_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_item_shadow_cull_mask", "get_item_shadow_cull_mask");
- BIND_ENUM_CONSTANT(MODE_ADD);
- BIND_ENUM_CONSTANT(MODE_SUB);
- BIND_ENUM_CONSTANT(MODE_MIX);
- BIND_ENUM_CONSTANT(MODE_MASK);
-
BIND_ENUM_CONSTANT(SHADOW_FILTER_NONE);
BIND_ENUM_CONSTANT(SHADOW_FILTER_PCF5);
BIND_ENUM_CONSTANT(SHADOW_FILTER_PCF13);
+
+ BIND_ENUM_CONSTANT(BLEND_MODE_ADD);
+ BIND_ENUM_CONSTANT(BLEND_MODE_SUB);
+ BIND_ENUM_CONSTANT(BLEND_MODE_MIX);
}
Light2D::Light2D() {
@@ -428,23 +305,168 @@ Light2D::Light2D() {
shadow = false;
color = Color(1, 1, 1);
height = 0;
- _scale = 1.0;
z_min = -1024;
z_max = 1024;
layer_min = 0;
layer_max = 0;
item_mask = 1;
item_shadow_mask = 1;
- mode = MODE_ADD;
- shadow_buffer_size = 2048;
energy = 1.0;
shadow_color = Color(0, 0, 0, 0);
shadow_filter = SHADOW_FILTER_NONE;
shadow_smooth = 0;
-
+ blend_mode = BLEND_MODE_ADD;
set_notify_transform(true);
}
Light2D::~Light2D() {
RenderingServer::get_singleton()->free(canvas_light);
}
+
+//////////////////////////////
+
+#ifdef TOOLS_ENABLED
+
+Dictionary PointLight2D::_edit_get_state() const {
+ Dictionary state = Node2D::_edit_get_state();
+ state["offset"] = get_texture_offset();
+ return state;
+}
+
+void PointLight2D::_edit_set_state(const Dictionary &p_state) {
+ Node2D::_edit_set_state(p_state);
+ set_texture_offset(p_state["offset"]);
+}
+
+void PointLight2D::_edit_set_pivot(const Point2 &p_pivot) {
+ set_position(get_transform().xform(p_pivot));
+ set_texture_offset(get_texture_offset() - p_pivot);
+}
+
+Point2 PointLight2D::_edit_get_pivot() const {
+ return Vector2();
+}
+
+bool PointLight2D::_edit_use_pivot() const {
+ return true;
+}
+
+Rect2 PointLight2D::_edit_get_rect() const {
+ if (texture.is_null()) {
+ return Rect2();
+ }
+
+ Size2 s = texture->get_size() * _scale;
+ return Rect2(texture_offset - s / 2.0, s);
+}
+
+bool PointLight2D::_edit_use_rect() const {
+ return !texture.is_null();
+}
+#endif
+
+Rect2 PointLight2D::get_anchorable_rect() const {
+ if (texture.is_null()) {
+ return Rect2();
+ }
+
+ Size2 s = texture->get_size() * _scale;
+ return Rect2(texture_offset - s / 2.0, s);
+}
+
+void PointLight2D::set_texture(const Ref<Texture2D> &p_texture) {
+ texture = p_texture;
+ if (texture.is_valid()) {
+ RS::get_singleton()->canvas_light_set_texture(_get_light(), texture->get_rid());
+ } else {
+ RS::get_singleton()->canvas_light_set_texture(_get_light(), RID());
+ }
+
+ update_configuration_warning();
+}
+
+Ref<Texture2D> PointLight2D::get_texture() const {
+ return texture;
+}
+
+void PointLight2D::set_texture_offset(const Vector2 &p_offset) {
+ texture_offset = p_offset;
+ RS::get_singleton()->canvas_light_set_texture_offset(_get_light(), texture_offset);
+ item_rect_changed();
+ _change_notify("offset");
+}
+
+Vector2 PointLight2D::get_texture_offset() const {
+ return texture_offset;
+}
+
+String PointLight2D::get_configuration_warning() const {
+ String warning = Node2D::get_configuration_warning();
+
+ if (!texture.is_valid()) {
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("A texture with the shape of the light must be supplied to the \"Texture\" property.");
+ }
+
+ return warning;
+}
+
+void PointLight2D::set_texture_scale(float p_scale) {
+ _scale = p_scale;
+ // Avoid having 0 scale values, can lead to errors in physics and rendering.
+ if (_scale == 0) {
+ _scale = CMP_EPSILON;
+ }
+ RS::get_singleton()->canvas_light_set_texture_scale(_get_light(), _scale);
+ item_rect_changed();
+}
+
+float PointLight2D::get_texture_scale() const {
+ return _scale;
+}
+
+void PointLight2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &PointLight2D::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &PointLight2D::get_texture);
+
+ ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &PointLight2D::set_texture_offset);
+ ClassDB::bind_method(D_METHOD("get_texture_offset"), &PointLight2D::get_texture_offset);
+
+ ClassDB::bind_method(D_METHOD("set_texture_scale", "texture_scale"), &PointLight2D::set_texture_scale);
+ ClassDB::bind_method(D_METHOD("get_texture_scale"), &PointLight2D::get_texture_scale);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_texture_offset", "get_texture_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_scale", PROPERTY_HINT_RANGE, "0.01,50,0.01"), "set_texture_scale", "get_texture_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_height", "get_height");
+}
+
+PointLight2D::PointLight2D() {
+ RS::get_singleton()->canvas_light_set_mode(_get_light(), RS::CANVAS_LIGHT_MODE_POINT);
+}
+
+//////////
+
+void DirectionalLight2D::set_max_distance(float p_distance) {
+ max_distance = p_distance;
+ RS::get_singleton()->canvas_light_set_directional_distance(_get_light(), max_distance);
+}
+
+float DirectionalLight2D::get_max_distance() const {
+ return max_distance;
+}
+
+void DirectionalLight2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_max_distance", "pixels"), &DirectionalLight2D::set_max_distance);
+ ClassDB::bind_method(D_METHOD("get_max_distance"), &DirectionalLight2D::get_max_distance);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,16384.0,1.0,or_greater"), "set_max_distance", "get_max_distance");
+}
+
+DirectionalLight2D::DirectionalLight2D() {
+ RS::get_singleton()->canvas_light_set_mode(_get_light(), RS::CANVAS_LIGHT_MODE_DIRECTIONAL);
+ set_max_distance(max_distance); // Update RenderingServer.
+}
diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h
index 45444800fe..7dfeddc8e5 100644
--- a/scene/2d/light_2d.h
+++ b/scene/2d/light_2d.h
@@ -37,13 +37,6 @@ class Light2D : public Node2D {
GDCLASS(Light2D, Node2D);
public:
- enum Mode {
- MODE_ADD,
- MODE_SUB,
- MODE_MIX,
- MODE_MASK,
- };
-
enum ShadowFilter {
SHADOW_FILTER_NONE,
SHADOW_FILTER_PCF5,
@@ -51,6 +44,12 @@ public:
SHADOW_FILTER_MAX
};
+ enum BlendMode {
+ BLEND_MODE_ADD,
+ BLEND_MODE_SUB,
+ BLEND_MODE_MIX,
+ };
+
private:
RID canvas_light;
bool enabled;
@@ -59,7 +58,6 @@ private:
Color color;
Color shadow_color;
float height;
- float _scale;
float energy;
int z_min;
int z_max;
@@ -67,45 +65,26 @@ private:
int layer_max;
int item_mask;
int item_shadow_mask;
- int shadow_buffer_size;
float shadow_smooth;
- Mode mode;
Ref<Texture2D> texture;
Vector2 texture_offset;
ShadowFilter shadow_filter;
+ BlendMode blend_mode;
void _update_light_visibility();
protected:
+ _FORCE_INLINE_ RID _get_light() const { return canvas_light; }
void _notification(int p_what);
static void _bind_methods();
public:
-#ifdef TOOLS_ENABLED
- virtual Dictionary _edit_get_state() const override;
- virtual void _edit_set_state(const Dictionary &p_state) override;
-
- virtual void _edit_set_pivot(const Point2 &p_pivot) override;
- virtual Point2 _edit_get_pivot() const override;
- virtual bool _edit_use_pivot() const override;
- virtual Rect2 _edit_get_rect() const override;
- virtual bool _edit_use_rect() const override;
-#endif
-
- virtual Rect2 get_anchorable_rect() const override;
-
void set_enabled(bool p_enabled);
bool is_enabled() const;
void set_editor_only(bool p_editor_only);
bool is_editor_only() const;
- void set_texture(const Ref<Texture2D> &p_texture);
- Ref<Texture2D> get_texture() const;
-
- void set_texture_offset(const Vector2 &p_offset);
- Vector2 get_texture_offset() const;
-
void set_color(const Color &p_color);
Color get_color() const;
@@ -115,9 +94,6 @@ public:
void set_energy(float p_energy);
float get_energy() const;
- void set_texture_scale(float p_scale);
- float get_texture_scale() const;
-
void set_z_range_min(int p_min_z);
int get_z_range_min() const;
@@ -136,15 +112,9 @@ public:
void set_item_shadow_cull_mask(int p_mask);
int get_item_shadow_cull_mask() const;
- void set_mode(Mode p_mode);
- Mode get_mode() const;
-
void set_shadow_enabled(bool p_enabled);
bool is_shadow_enabled() const;
- void set_shadow_buffer_size(int p_size);
- int get_shadow_buffer_size() const;
-
void set_shadow_filter(ShadowFilter p_filter);
ShadowFilter get_shadow_filter() const;
@@ -154,13 +124,68 @@ public:
void set_shadow_smooth(float p_amount);
float get_shadow_smooth() const;
- String get_configuration_warning() const override;
+ void set_blend_mode(BlendMode p_mode);
+ BlendMode get_blend_mode() const;
Light2D();
~Light2D();
};
-VARIANT_ENUM_CAST(Light2D::Mode);
VARIANT_ENUM_CAST(Light2D::ShadowFilter);
+VARIANT_ENUM_CAST(Light2D::BlendMode);
+
+class PointLight2D : public Light2D {
+ GDCLASS(PointLight2D, Light2D);
+
+private:
+ float _scale = 1.0;
+ Ref<Texture2D> texture;
+ Vector2 texture_offset;
+
+protected:
+ static void _bind_methods();
+
+public:
+#ifdef TOOLS_ENABLED
+ virtual Dictionary _edit_get_state() const override;
+ virtual void _edit_set_state(const Dictionary &p_state) override;
+
+ virtual void _edit_set_pivot(const Point2 &p_pivot) override;
+ virtual Point2 _edit_get_pivot() const override;
+ virtual bool _edit_use_pivot() const override;
+ virtual Rect2 _edit_get_rect() const override;
+ virtual bool _edit_use_rect() const override;
+#endif
+
+ virtual Rect2 get_anchorable_rect() const override;
+
+ void set_texture(const Ref<Texture2D> &p_texture);
+ Ref<Texture2D> get_texture() const;
+
+ void set_texture_offset(const Vector2 &p_offset);
+ Vector2 get_texture_offset() const;
+
+ void set_texture_scale(float p_scale);
+ float get_texture_scale() const;
+
+ String get_configuration_warning() const override;
+
+ PointLight2D();
+};
+
+class DirectionalLight2D : public Light2D {
+ GDCLASS(DirectionalLight2D, Light2D);
+
+ float max_distance = 10000.0;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_max_distance(float p_distance);
+ float get_max_distance() const;
+
+ DirectionalLight2D();
+};
#endif // LIGHT_2D_H
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index 023cfa6d03..b5b39ccc8f 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -31,7 +31,7 @@
#include "light_occluder_2d.h"
#include "core/math/geometry_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#define LINE_GRAB_WIDTH 8
@@ -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 {
@@ -246,15 +246,31 @@ int LightOccluder2D::get_occluder_light_mask() const {
}
String LightOccluder2D::get_configuration_warning() const {
+ String warning = Node2D::get_configuration_warning();
+
if (!occluder_polygon.is_valid()) {
- return TTR("An occluder polygon must be set (or drawn) for this occluder to take effect.");
+ if (!warning.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) {
- return TTR("The occluder polygon for this occluder is empty. Please draw a polygon.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("The occluder polygon for this occluder is empty. Please draw a polygon.");
}
- return String();
+ return warning;
+}
+
+void LightOccluder2D::set_as_sdf_collision(bool p_enable) {
+ sdf_collision = p_enable;
+ RS::get_singleton()->canvas_light_occluder_set_as_sdf_collision(occluder, sdf_collision);
+}
+bool LightOccluder2D::is_set_as_sdf_collision() const {
+ return sdf_collision;
}
void LightOccluder2D::_bind_methods() {
@@ -264,14 +280,20 @@ void LightOccluder2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_occluder_light_mask", "mask"), &LightOccluder2D::set_occluder_light_mask);
ClassDB::bind_method(D_METHOD("get_occluder_light_mask"), &LightOccluder2D::get_occluder_light_mask);
+ ClassDB::bind_method(D_METHOD("set_as_sdf_collision", "enable"), &LightOccluder2D::set_as_sdf_collision);
+ ClassDB::bind_method(D_METHOD("is_set_as_sdf_collision"), &LightOccluder2D::is_set_as_sdf_collision);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D"), "set_occluder_polygon", "get_occluder_polygon");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdf_collision"), "set_as_sdf_collision", "is_set_as_sdf_collision");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "occluder_light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask");
}
LightOccluder2D::LightOccluder2D() {
occluder = RS::get_singleton()->canvas_light_occluder_create();
mask = 1;
+
set_notify_transform(true);
+ set_as_sdf_collision(true);
}
LightOccluder2D::~LightOccluder2D() {
diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h
index 694097f985..97574af542 100644
--- a/scene/2d/light_occluder_2d.h
+++ b/scene/2d/light_occluder_2d.h
@@ -84,7 +84,7 @@ class LightOccluder2D : public Node2D {
bool enabled;
int mask;
Ref<OccluderPolygon2D> occluder_polygon;
-
+ bool sdf_collision;
void _poly_changed();
protected:
@@ -103,6 +103,9 @@ public:
void set_occluder_light_mask(int p_mask);
int get_occluder_light_mask() const;
+ void set_as_sdf_collision(bool p_enable);
+ bool is_set_as_sdf_collision() const;
+
String get_configuration_warning() const override;
LightOccluder2D();
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index b120b115b0..e990e9f53e 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -177,7 +177,7 @@ void Line2D::set_gradient(const Ref<Gradient> &p_gradient) {
_gradient = p_gradient;
- // Connect to the gradient so the line will update when the ColorRamp is changed
+ // Connect to the gradient so the line will update when the Gradient is changed
if (_gradient.is_valid()) {
_gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Line2D::_gradient_changed));
}
diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp
index f1522dbaeb..e0116d9bad 100644
--- a/scene/2d/line_builder.cpp
+++ b/scene/2d/line_builder.cpp
@@ -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 9e3dbbd6c1..0e033d9be1 100644
--- a/scene/2d/line_builder.h
+++ b/scene/2d/line_builder.h
@@ -31,7 +31,7 @@
#ifndef LINE_BUILDER_H
#define LINE_BUILDER_H
-#include "core/color.h"
+#include "core/math/color.h"
#include "core/math/vector2.h"
#include "line_2d.h"
#include "scene/resources/gradient.h"
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index 897595ad1f..037e423ce9 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -33,7 +33,7 @@
void MeshInstance2D::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
if (mesh.is_valid()) {
- draw_mesh(mesh, texture, normal_map);
+ draw_mesh(mesh, texture);
}
}
}
diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp
index b99c0a3fa9..c258e30eab 100644
--- a/scene/2d/multimesh_instance_2d.cpp
+++ b/scene/2d/multimesh_instance_2d.cpp
@@ -33,7 +33,7 @@
void MultiMeshInstance2D::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
if (multimesh.is_valid()) {
- draw_multimesh(multimesh, texture, normal_map);
+ draw_multimesh(multimesh, texture);
}
}
}
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index e5cdade4a4..1c7063d0d3 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -30,7 +30,7 @@
#include "navigation_agent_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "core/math/geometry_2d.h"
#include "scene/2d/navigation_2d.h"
#include "servers/navigation_server_2d.h"
@@ -271,11 +271,16 @@ void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) {
}
String NavigationAgent2D::get_configuration_warning() const {
+ String warning = Node::get_configuration_warning();
+
if (!Object::cast_to<Node2D>(get_parent())) {
- return TTR("The NavigationAgent2D can be used only under a Node2D node");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("The NavigationAgent2D can be used only under a Node2D node");
}
- return String();
+ return warning;
}
void NavigationAgent2D::update_navigation() {
diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h
index 9474392ddf..1f2377837b 100644
--- a/scene/2d/navigation_agent_2d.h
+++ b/scene/2d/navigation_agent_2d.h
@@ -31,7 +31,7 @@
#ifndef NAVIGATION_AGENT_2D_H
#define NAVIGATION_AGENT_2D_H
-#include "core/vector.h"
+#include "core/templates/vector.h"
#include "scene/main/node.h"
class Node2D;
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index 568023bbe2..252d7cbb96 100644
--- a/scene/2d/navigation_obstacle_2d.cpp
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -106,11 +106,16 @@ Node *NavigationObstacle2D::get_navigation_node() const {
}
String NavigationObstacle2D::get_configuration_warning() const {
+ String warning = Node::get_configuration_warning();
+
if (!Object::cast_to<Node2D>(get_parent())) {
- return TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object.");
}
- return String();
+ return warning;
}
void NavigationObstacle2D::update_agent_shape() {
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 671bda558d..98817ec03e 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -30,8 +30,8 @@
#include "navigation_region_2d.h"
+#include "core/config/engine.h"
#include "core/core_string_names.h"
-#include "core/engine.h"
#include "core/math/geometry_2d.h"
#include "core/os/mutex.h"
#include "navigation_2d.h"
@@ -500,19 +500,26 @@ String NavigationRegion2D::get_configuration_warning() const {
return String();
}
+ String warning = Node2D::get_configuration_warning();
+
if (!navpoly.is_valid()) {
- return TTR("A NavigationPolygon resource must be set or created for this node to work. Please set a property or draw a polygon.");
+ if (!warning.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.");
}
const Node2D *c = this;
while (c) {
if (Object::cast_to<Navigation2D>(c)) {
- return String();
+ return warning;
}
c = Object::cast_to<Node2D>(c->get_parent());
}
-
- return TTR("NavigationRegion2D must be a child or grandchild to a Navigation2D node. It only provides navigation data.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ return warning + TTR("NavigationRegion2D must be a child or grandchild to a Navigation2D node. It only provides navigation data.");
}
void NavigationRegion2D::_bind_methods() {
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 72250e96b3..42c2585487 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -30,7 +30,7 @@
#include "node_2d.h"
-#include "core/message_queue.h"
+#include "core/object/message_queue.h"
#include "scene/gui/control.h"
#include "scene/main/window.h"
#include "servers/rendering_server.h"
diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp
index 416622e6d5..8c9432f2fa 100644
--- a/scene/2d/parallax_background.cpp
+++ b/scene/2d/parallax_background.cpp
@@ -101,7 +101,7 @@ void ParallaxBackground::_update_scroll() {
}
if (ignore_camera_zoom) {
- l->set_base_offset_and_scale(ofs, 1.0, screen_offset);
+ l->set_base_offset_and_scale((ofs + screen_offset * (scale - 1)) / scale, 1.0, screen_offset);
} else {
l->set_base_offset_and_scale(ofs, scale, screen_offset);
}
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index 4ed335dec8..01aa5838b4 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -30,7 +30,7 @@
#include "parallax_layer.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "parallax_background.h"
void ParallaxLayer::set_motion_scale(const Size2 &p_scale) {
@@ -136,11 +136,16 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, float p_sc
}
String ParallaxLayer::get_configuration_warning() const {
+ String warning = Node2D::get_configuration_warning();
+
if (!Object::cast_to<ParallaxBackground>(get_parent())) {
- return TTR("ParallaxLayer node only works when set as child of a ParallaxBackground node.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("ParallaxLayer node only works when set as child of a ParallaxBackground node.");
}
- return String();
+ return warning;
}
void ParallaxLayer::_bind_methods() {
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index f2f549e851..f40a993423 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -30,7 +30,7 @@
#include "path_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "core/math/geometry_2d.h"
#include "scene/scene_string_names.h"
@@ -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) {
@@ -254,11 +254,16 @@ String PathFollow2D::get_configuration_warning() const {
return String();
}
+ String warning = Node2D::get_configuration_warning();
+
if (!Object::cast_to<Path2D>(get_parent())) {
- return TTR("PathFollow2D only works when set as a child of a Path2D node.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("PathFollow2D only works when set as a child of a Path2D node.");
}
- return String();
+ return warning;
}
void PathFollow2D::_bind_methods() {
@@ -274,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);
@@ -290,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");
@@ -366,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) {
@@ -388,7 +393,7 @@ PathFollow2D::PathFollow2D() {
h_offset = 0;
v_offset = 0;
path = nullptr;
- rotate = true;
+ rotates = true;
cubic = true;
loop = true;
lookahead = 4;
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index 7fea75cd7c..fcb8b40125 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -70,7 +70,7 @@ private:
real_t lookahead;
bool cubic;
bool loop;
- bool rotate;
+ bool rotates;
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);
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 0a9de20664..e314669fb0 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -30,27 +30,17 @@
#include "physics_body_2d.h"
+#include "core/config/engine.h"
#include "core/core_string_names.h"
-#include "core/engine.h"
-#include "core/list.h"
#include "core/math/math_funcs.h"
-#include "core/method_bind_ext.gen.inc"
-#include "core/object.h"
-#include "core/rid.h"
+#include "core/object/class_db.h"
+#include "core/templates/list.h"
+#include "core/templates/rid.h"
#include "scene/scene_string_names.h"
void PhysicsBody2D::_notification(int p_what) {
}
-void PhysicsBody2D::_set_layers(uint32_t p_mask) {
- set_collision_layer(p_mask);
- set_collision_mask(p_mask);
-}
-
-uint32_t PhysicsBody2D::_get_layers() const {
- return get_collision_layer();
-}
-
void PhysicsBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &PhysicsBody2D::set_collision_layer);
ClassDB::bind_method(D_METHOD("get_collision_layer"), &PhysicsBody2D::get_collision_layer);
@@ -63,13 +53,9 @@ void PhysicsBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &PhysicsBody2D::set_collision_layer_bit);
ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &PhysicsBody2D::get_collision_layer_bit);
- ClassDB::bind_method(D_METHOD("_set_layers", "mask"), &PhysicsBody2D::_set_layers);
- ClassDB::bind_method(D_METHOD("_get_layers"), &PhysicsBody2D::_get_layers);
-
ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions);
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with);
ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody2D::remove_collision_exception_with);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_2D_PHYSICS, "", 0), "_set_layers", "_get_layers"); //for backwards compat
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
@@ -474,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);
}
@@ -492,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))) {
@@ -748,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 != String()) {
+ if (!warning.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.");
@@ -767,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);
@@ -832,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 e83ab6557d..294b57eb13 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -31,7 +31,7 @@
#ifndef PHYSICS_BODY_2D_H
#define PHYSICS_BODY_2D_H
-#include "core/vset.h"
+#include "core/templates/vset.h"
#include "scene/2d/collision_object_2d.h"
#include "scene/resources/physics_material.h"
#include "servers/physics_server_2d.h"
@@ -44,9 +44,6 @@ class PhysicsBody2D : public CollisionObject2D {
uint32_t collision_layer;
uint32_t collision_mask;
- void _set_layers(uint32_t p_mask);
- uint32_t _get_layers() const;
-
protected:
void _notification(int p_what);
PhysicsBody2D(PhysicsServer2D::BodyMode p_mode);
@@ -195,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 13b62816a4..26340bb861 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -302,7 +302,7 @@ void Polygon2D::_notification(int p_what) {
if (invert || polygons.size() == 0) {
Vector<int> indices = Geometry2D::triangulate_polygon(points);
if (indices.size()) {
- RS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID(), -1, normal_map.is_valid() ? normal_map->get_rid() : RID(), specular_map.is_valid() ? specular_map->get_rid() : RID(), Color(specular_color.r, specular_color.g, specular_color.b, shininess));
+ RS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID(), -1);
}
} else {
//draw individual polygons
@@ -417,42 +417,6 @@ Ref<Texture2D> Polygon2D::get_texture() const {
return texture;
}
-void Polygon2D::set_normal_map(const Ref<Texture2D> &p_normal_map) {
- normal_map = p_normal_map;
- update();
-}
-
-Ref<Texture2D> Polygon2D::get_normal_map() const {
- return normal_map;
-}
-
-void Polygon2D::set_specular_map(const Ref<Texture2D> &p_specular_map) {
- specular_map = p_specular_map;
- update();
-}
-
-Ref<Texture2D> Polygon2D::get_specular_map() const {
- return specular_map;
-}
-
-void Polygon2D::set_specular_color(const Color &p_specular_color) {
- specular_color = p_specular_color;
- update();
-}
-
-Color Polygon2D::get_specular_color() const {
- return specular_color;
-}
-
-void Polygon2D::set_shininess(float p_shininess) {
- shininess = CLAMP(p_shininess, 0.0, 1.0);
- update();
-}
-
-float Polygon2D::get_shininess() const {
- return shininess;
-}
-
void Polygon2D::set_texture_offset(const Vector2 &p_offset) {
tex_ofs = p_offset;
update();
@@ -616,18 +580,6 @@ void Polygon2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &Polygon2D::set_texture);
ClassDB::bind_method(D_METHOD("get_texture"), &Polygon2D::get_texture);
- ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &Polygon2D::set_normal_map);
- ClassDB::bind_method(D_METHOD("get_normal_map"), &Polygon2D::get_normal_map);
-
- ClassDB::bind_method(D_METHOD("set_specular_map", "specular_map"), &Polygon2D::set_specular_map);
- ClassDB::bind_method(D_METHOD("get_specular_map"), &Polygon2D::get_specular_map);
-
- ClassDB::bind_method(D_METHOD("set_specular_color", "specular_color"), &Polygon2D::set_specular_color);
- ClassDB::bind_method(D_METHOD("get_specular_color"), &Polygon2D::get_specular_color);
-
- ClassDB::bind_method(D_METHOD("set_shininess", "shininess"), &Polygon2D::set_shininess);
- ClassDB::bind_method(D_METHOD("get_shininess"), &Polygon2D::get_shininess);
-
ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &Polygon2D::set_texture_offset);
ClassDB::bind_method(D_METHOD("get_texture_offset"), &Polygon2D::get_texture_offset);
@@ -680,11 +632,6 @@ void Polygon2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_scale"), "set_texture_scale", "get_texture_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_rotation_degrees", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater"), "set_texture_rotation_degrees", "get_texture_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_rotation", PROPERTY_HINT_NONE, "", 0), "set_texture_rotation", "get_texture_rotation");
- ADD_GROUP("Lighting", "");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_map", "get_normal_map");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "specular_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_specular_map", "get_specular_map");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "specular_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_specular_color", "get_specular_color");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "shininess", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_shininess", "get_shininess");
ADD_GROUP("Skeleton", "");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton2D"), "set_skeleton", "get_skeleton");
@@ -711,7 +658,4 @@ Polygon2D::Polygon2D() {
color = Color(1, 1, 1);
rect_cache_dirty = true;
internal_vertices = 0;
-
- specular_color = Color(1, 1, 1, 1);
- shininess = 1.0;
}
diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h
index c5ff05aace..e2a8db414a 100644
--- a/scene/2d/polygon_2d.h
+++ b/scene/2d/polygon_2d.h
@@ -51,10 +51,6 @@ class Polygon2D : public Node2D {
Color color;
Ref<Texture2D> texture;
- Ref<Texture2D> normal_map;
- Ref<Texture2D> specular_map;
- Color specular_color;
- float shininess;
Size2 tex_scale;
Vector2 tex_ofs;
@@ -115,18 +111,6 @@ public:
void set_texture(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_texture() const;
- void set_normal_map(const Ref<Texture2D> &p_normal_map);
- Ref<Texture2D> get_normal_map() const;
-
- void set_specular_map(const Ref<Texture2D> &p_specular_map);
- Ref<Texture2D> get_specular_map() const;
-
- void set_specular_color(const Color &p_specular_color);
- Color get_specular_color() const;
-
- void set_shininess(float p_shininess);
- float get_shininess() const;
-
void set_texture_offset(const Vector2 &p_offset);
Vector2 get_texture_offset() const;
diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp
index a94a9f7085..8e4165cf50 100644
--- a/scene/2d/position_2d.cpp
+++ b/scene/2d/position_2d.cpp
@@ -30,7 +30,7 @@
#include "position_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "scene/resources/texture.h"
const float DEFAULT_GIZMO_EXTENTS = 10.0;
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 9fd24b5294..e53f89c46d 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -31,19 +31,19 @@
#include "ray_cast_2d.h"
#include "collision_object_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "physics_body_2d.h"
#include "servers/physics_server_2d.h"
-void RayCast2D::set_cast_to(const Vector2 &p_point) {
- cast_to = p_point;
+void RayCast2D::set_target_position(const Vector2 &p_point) {
+ target_position = p_point;
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint())) {
update();
}
}
-Vector2 RayCast2D::get_cast_to() const {
- return cast_to;
+Vector2 RayCast2D::get_target_position() const {
+ return target_position;
}
void RayCast2D::set_collision_mask(uint32_t p_mask) {
@@ -160,8 +160,8 @@ void RayCast2D::_notification(int p_what) {
break;
}
Transform2D xf;
- xf.rotate(cast_to.angle());
- xf.translate(Vector2(cast_to.length(), 0));
+ xf.rotate(target_position.angle());
+ xf.translate(Vector2(target_position.length(), 0));
// Draw an arrow indicating where the RayCast is pointing to
Color draw_col = get_tree()->get_debug_collisions_color();
@@ -171,7 +171,7 @@ void RayCast2D::_notification(int p_what) {
draw_col.g = g;
draw_col.b = g;
}
- draw_line(Vector2(), cast_to, draw_col, 2);
+ draw_line(Vector2(), target_position, draw_col, 2);
Vector<Vector2> pts;
float tsize = 8;
pts.push_back(xf.xform(Vector2(tsize, 0)));
@@ -206,7 +206,7 @@ void RayCast2D::_update_raycast_state() {
Transform2D gt = get_global_transform();
- Vector2 to = cast_to;
+ Vector2 to = target_position;
if (to == Vector2()) {
to = Vector2(0, 0.01);
}
@@ -280,8 +280,8 @@ void RayCast2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &RayCast2D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &RayCast2D::is_enabled);
- ClassDB::bind_method(D_METHOD("set_cast_to", "local_point"), &RayCast2D::set_cast_to);
- ClassDB::bind_method(D_METHOD("get_cast_to"), &RayCast2D::get_cast_to);
+ ClassDB::bind_method(D_METHOD("set_target_position", "local_point"), &RayCast2D::set_target_position);
+ ClassDB::bind_method(D_METHOD("get_target_position"), &RayCast2D::get_target_position);
ClassDB::bind_method(D_METHOD("is_colliding"), &RayCast2D::is_colliding);
ClassDB::bind_method(D_METHOD("force_raycast_update"), &RayCast2D::force_raycast_update);
@@ -316,7 +316,7 @@ void RayCast2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cast_to"), "set_cast_to", "get_cast_to");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position"), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
ADD_GROUP("Collide With", "collide_with");
@@ -329,7 +329,7 @@ RayCast2D::RayCast2D() {
collided = false;
against_shape = 0;
collision_mask = 1;
- cast_to = Vector2(0, 50);
+ target_position = Vector2(0, 50);
exclude_parent_body = true;
collide_with_bodies = true;
collide_with_areas = false;
diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h
index 6accc264a0..14932f782b 100644
--- a/scene/2d/ray_cast_2d.h
+++ b/scene/2d/ray_cast_2d.h
@@ -46,7 +46,7 @@ class RayCast2D : public Node2D {
uint32_t collision_mask;
bool exclude_parent_body;
- Vector2 cast_to;
+ Vector2 target_position;
bool collide_with_areas;
bool collide_with_bodies;
@@ -66,8 +66,8 @@ public:
void set_enabled(bool p_enabled);
bool is_enabled() const;
- void set_cast_to(const Vector2 &p_point);
- Vector2 get_cast_to() const;
+ void set_target_position(const Vector2 &p_point);
+ Vector2 get_target_position() const;
void set_collision_mask(uint32_t p_mask);
uint32_t get_collision_mask() const;
diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp
index 3104436dbe..7655416ce2 100644
--- a/scene/2d/remote_transform_2d.cpp
+++ b/scene/2d/remote_transform_2d.cpp
@@ -186,11 +186,16 @@ void RemoteTransform2D::force_update_cache() {
}
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))) {
- return TTR("Path property must point to a valid Node2D node to work.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("Path property must point to a valid Node2D node to work.");
}
- return String();
+ return warning;
}
void RemoteTransform2D::_bind_methods() {
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index ea37c8dfe7..ea1d9f5930 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -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 != String()) {
+ if (!warning.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 != String()) {
+ if (!warning.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/sprite_2d.cpp b/scene/2d/sprite_2d.cpp
index 7e07019578..a065565a0f 100644
--- a/scene/2d/sprite_2d.cpp
+++ b/scene/2d/sprite_2d.cpp
@@ -99,7 +99,8 @@ void Sprite2D::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_c
if (centered) {
dest_offset -= frame_size / 2;
}
- if (Engine::get_singleton()->get_use_pixel_snap()) {
+
+ if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
dest_offset = dest_offset.floor();
}
@@ -130,8 +131,8 @@ void Sprite2D::_notification(int p_what) {
Rect2 src_rect, dst_rect;
bool filter_clip;
_get_rects(src_rect, dst_rect, filter_clip);
- texture->draw_rect_region(ci, dst_rect, src_rect, Color(1, 1, 1), false, normal_map, specular, Color(specular_color.r, specular_color.g, specular_color.b, shininess), RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, filter_clip);
+ texture->draw_rect_region(ci, dst_rect, src_rect, Color(1, 1, 1), false, filter_clip);
} break;
}
}
@@ -157,42 +158,6 @@ void Sprite2D::set_texture(const Ref<Texture2D> &p_texture) {
_change_notify("texture");
}
-void Sprite2D::set_normal_map(const Ref<Texture2D> &p_texture) {
- normal_map = p_texture;
- update();
-}
-
-Ref<Texture2D> Sprite2D::get_normal_map() const {
- return normal_map;
-}
-
-void Sprite2D::set_specular_map(const Ref<Texture2D> &p_texture) {
- specular = p_texture;
- update();
-}
-
-Ref<Texture2D> Sprite2D::get_specular_map() const {
- return specular;
-}
-
-void Sprite2D::set_specular_color(const Color &p_color) {
- specular_color = p_color;
- update();
-}
-
-Color Sprite2D::get_specular_color() const {
- return specular_color;
-}
-
-void Sprite2D::set_shininess(float p_shininess) {
- shininess = CLAMP(p_shininess, 0.0, 1.0);
- update();
-}
-
-float Sprite2D::get_shininess() const {
- return shininess;
-}
-
Ref<Texture2D> Sprite2D::get_texture() const {
return texture;
}
@@ -403,6 +368,10 @@ Rect2 Sprite2D::get_rect() const {
ofs -= Size2(s) / 2;
}
+ if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
+ ofs = ofs.floor();
+ }
+
if (s == Size2(0, 0)) {
s = Size2(1, 1);
}
@@ -434,18 +403,6 @@ void Sprite2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &Sprite2D::set_texture);
ClassDB::bind_method(D_METHOD("get_texture"), &Sprite2D::get_texture);
- ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &Sprite2D::set_normal_map);
- ClassDB::bind_method(D_METHOD("get_normal_map"), &Sprite2D::get_normal_map);
-
- ClassDB::bind_method(D_METHOD("set_specular_map", "specular_map"), &Sprite2D::set_specular_map);
- ClassDB::bind_method(D_METHOD("get_specular_map"), &Sprite2D::get_specular_map);
-
- ClassDB::bind_method(D_METHOD("set_specular_color", "specular_color"), &Sprite2D::set_specular_color);
- ClassDB::bind_method(D_METHOD("get_specular_color"), &Sprite2D::get_specular_color);
-
- ClassDB::bind_method(D_METHOD("set_shininess", "shininess"), &Sprite2D::set_shininess);
- ClassDB::bind_method(D_METHOD("get_shininess"), &Sprite2D::get_shininess);
-
ClassDB::bind_method(D_METHOD("set_centered", "centered"), &Sprite2D::set_centered);
ClassDB::bind_method(D_METHOD("is_centered"), &Sprite2D::is_centered);
@@ -487,19 +444,14 @@ void Sprite2D::_bind_methods() {
ADD_SIGNAL(MethodInfo("texture_changed"));
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_GROUP("Lighting", "");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_map", "get_normal_map");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "specular_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_specular_map", "get_specular_map");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "specular_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_specular_color", "get_specular_color");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "shininess", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_shininess", "get_shininess");
ADD_GROUP("Offset", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v");
ADD_GROUP("Animation", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes");
ADD_PROPERTY(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes");
ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frame_coords", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_frame_coords", "get_frame_coords");
@@ -515,8 +467,6 @@ Sprite2D::Sprite2D() {
vflip = false;
region = false;
region_filter_clip = false;
- shininess = 1.0;
- specular_color = Color(1, 1, 1, 1);
frame = 0;
diff --git a/scene/2d/sprite_2d.h b/scene/2d/sprite_2d.h
index f6b752575f..2875d333bb 100644
--- a/scene/2d/sprite_2d.h
+++ b/scene/2d/sprite_2d.h
@@ -38,8 +38,6 @@ class Sprite2D : public Node2D {
GDCLASS(Sprite2D, Node2D);
Ref<Texture2D> texture;
- Ref<Texture2D> normal_map;
- Ref<Texture2D> specular;
Color specular_color;
float shininess;
@@ -87,18 +85,6 @@ public:
void set_texture(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_texture() const;
- void set_normal_map(const Ref<Texture2D> &p_texture);
- Ref<Texture2D> get_normal_map() const;
-
- void set_specular_map(const Ref<Texture2D> &p_texture);
- Ref<Texture2D> get_specular_map() const;
-
- void set_specular_color(const Color &p_color);
- Color get_specular_color() const;
-
- void set_shininess(float p_shininess);
- float get_shininess() const;
-
void set_centered(bool p_center);
bool is_centered() const;
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index c7a809f6d8..bff191a2bf 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -32,7 +32,6 @@
#include "collision_object_2d.h"
#include "core/io/marshalls.h"
-#include "core/method_bind_ext.gen.inc"
#include "core/os/os.h"
#include "scene/2d/area_2d.h"
#include "servers/navigation_server_2d.h"
@@ -412,6 +411,9 @@ void TileMap::update_dirty_quadrants() {
vs->canvas_item_set_light_mask(canvas_item, get_light_mask());
vs->canvas_item_set_z_index(canvas_item, z_index);
+ vs->canvas_item_set_default_texture_filter(canvas_item, RS::CanvasItemTextureFilter(CanvasItem::get_texture_filter()));
+ vs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(CanvasItem::get_texture_repeat()));
+
q.canvas_items.push_back(canvas_item);
if (debug_shapes) {
@@ -526,15 +528,14 @@ void TileMap::update_dirty_quadrants() {
rect.position += tile_ofs;
}
- Ref<Texture2D> normal_map = tile_set->tile_get_normal_map(c.id);
Color modulate = tile_set->tile_get_modulate(c.id);
Color self_modulate = get_self_modulate();
modulate = Color(modulate.r * self_modulate.r, modulate.g * self_modulate.g,
modulate.b * self_modulate.b, modulate.a * self_modulate.a);
if (r == Rect2()) {
- tex->draw_rect(canvas_item, rect, false, modulate, c.transpose, normal_map);
+ tex->draw_rect(canvas_item, rect, false, modulate, c.transpose);
} else {
- tex->draw_rect_region(canvas_item, rect, r, modulate, c.transpose, normal_map, Ref<Texture2D>(), Color(1, 1, 1, 1), RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, clip_uv);
+ tex->draw_rect_region(canvas_item, rect, r, modulate, c.transpose, clip_uv);
}
Vector<TileSet::ShapeData> shapes = tile_set->tile_get_shapes(c.id);
@@ -1687,6 +1688,28 @@ bool TileMap::get_clip_uv() const {
return clip_uv;
}
+void TileMap::set_texture_filter(TextureFilter p_texture_filter) {
+ CanvasItem::set_texture_filter(p_texture_filter);
+ for (Map<PosKey, Quadrant>::Element *F = quadrant_map.front(); F; F = F->next()) {
+ Quadrant &q = F->get();
+ for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) {
+ RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(E->get(), RS::CanvasItemTextureFilter(p_texture_filter));
+ _make_quadrant_dirty(F);
+ }
+ }
+}
+
+void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) {
+ CanvasItem::set_texture_repeat(p_texture_repeat);
+ for (Map<PosKey, Quadrant>::Element *F = quadrant_map.front(); F; F = F->next()) {
+ Quadrant &q = F->get();
+ for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) {
+ RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(E->get(), RS::CanvasItemTextureRepeat(p_texture_repeat));
+ _make_quadrant_dirty(F);
+ }
+ }
+}
+
String TileMap::get_configuration_warning() const {
String warning = Node2D::get_configuration_warning();
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 7a2a3e412c..22b615a379 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -31,8 +31,8 @@
#ifndef TILE_MAP_H
#define TILE_MAP_H
-#include "core/self_list.h"
-#include "core/vset.h"
+#include "core/templates/self_list.h"
+#include "core/templates/vset.h"
#include "scene/2d/navigation_2d.h"
#include "scene/2d/node_2d.h"
#include "scene/resources/tile_set.h"
@@ -342,6 +342,10 @@ public:
String get_configuration_warning() const override;
+ virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override;
+
+ virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override;
+
void fix_invalid_tiles();
void clear();
diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp
index 75154c7acb..e217f2a394 100644
--- a/scene/2d/visibility_notifier_2d.cpp
+++ b/scene/2d/visibility_notifier_2d.cpp
@@ -30,7 +30,7 @@
#include "visibility_notifier_2d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "gpu_particles_2d.h"
#include "scene/2d/animated_sprite_2d.h"
#include "scene/2d/physics_body_2d.h"
@@ -313,12 +313,17 @@ void VisibilityEnabler2D::_node_removed(Node *p_node) {
}
String VisibilityEnabler2D::get_configuration_warning() const {
+ String warning = VisibilityNotifier2D::get_configuration_warning();
+
#ifdef TOOLS_ENABLED
if (is_inside_tree() && get_parent() && (get_parent()->get_filename() == String() && get_parent() != get_tree()->get_edited_scene_root())) {
- return TTR("VisibilityEnabler2D works best when used with the edited scene root directly as parent.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("VisibilityEnabler2D works best when used with the edited scene root directly as parent.");
}
#endif
- return String();
+ return warning;
}
void VisibilityEnabler2D::_bind_methods() {
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp
index a024757927..b1ffe76662 100644
--- a/scene/3d/area_3d.cpp
+++ b/scene/3d/area_3d.cpp
@@ -180,26 +180,20 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i
E->get().shapes.erase(ShapePair(p_body_shape, p_area_shape));
}
- bool eraseit = false;
-
+ bool in_tree = E->get().in_tree;
if (E->get().rc == 0) {
+ body_map.erase(E);
if (node) {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree));
- if (E->get().in_tree) {
+ if (in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_exited, obj);
}
}
-
- eraseit = true;
}
- if (node && E->get().in_tree) {
+ if (node && in_tree) {
emit_signal(SceneStringNames::get_singleton()->body_shape_exited, objid, obj, p_body_shape, p_area_shape);
}
-
- if (eraseit) {
- body_map.erase(E);
- }
}
locked = false;
@@ -222,6 +216,9 @@ void Area3D::_clear_monitoring() {
}
//ERR_CONTINUE(!node);
+ node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree));
+ node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree));
+
if (!E->get().in_tree) {
continue;
}
@@ -231,9 +228,6 @@ void Area3D::_clear_monitoring() {
}
emit_signal(SceneStringNames::get_singleton()->body_exited, node);
-
- node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree));
- node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree));
}
}
@@ -251,6 +245,9 @@ void Area3D::_clear_monitoring() {
}
//ERR_CONTINUE(!node);
+ node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree));
+ node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree));
+
if (!E->get().in_tree) {
continue;
}
@@ -260,9 +257,6 @@ void Area3D::_clear_monitoring() {
}
emit_signal(SceneStringNames::get_singleton()->area_exited, obj);
-
- node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree));
- node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree));
}
}
}
@@ -366,26 +360,20 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i
E->get().shapes.erase(AreaShapePair(p_area_shape, p_self_shape));
}
- bool eraseit = false;
-
+ bool in_tree = E->get().in_tree;
if (E->get().rc == 0) {
+ area_map.erase(E);
if (node) {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree));
- if (E->get().in_tree) {
+ if (in_tree) {
emit_signal(SceneStringNames::get_singleton()->area_exited, obj);
}
}
-
- eraseit = true;
}
- if (!node || E->get().in_tree) {
+ if (!node || in_tree) {
emit_signal(SceneStringNames::get_singleton()->area_shape_exited, objid, obj, p_area_shape, p_self_shape);
}
-
- if (eraseit) {
- area_map.erase(E);
- }
}
locked = false;
diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h
index 07d24f39a7..7d1f030baf 100644
--- a/scene/3d/area_3d.h
+++ b/scene/3d/area_3d.h
@@ -31,7 +31,7 @@
#ifndef AREA_3D_H
#define AREA_3D_H
-#include "core/vset.h"
+#include "core/templates/vset.h"
#include "scene/3d/collision_object_3d.h"
class Area3D : public CollisionObject3D {
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 6e4db8f382..37bc032356 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -30,7 +30,7 @@
#include "audio_stream_player_3d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "scene/3d/area_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/listener_3d.h"
@@ -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);
diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp
index e867891ec0..38c9e96fbc 100644
--- a/scene/3d/baked_lightmap.cpp
+++ b/scene/3d/baked_lightmap.cpp
@@ -37,7 +37,7 @@
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/sort_array.h"
+#include "core/templates/sort_array.h"
#include "lightmap_probe.h"
void BakedLightmapData::add_user(const NodePath &p_path, const Rect2 &p_uv_scale, int p_slice_index, int32_t p_sub_instance) {
diff --git a/scene/3d/baked_lightmap.h b/scene/3d/baked_lightmap.h
index bebb579252..8808569215 100644
--- a/scene/3d/baked_lightmap.h
+++ b/scene/3d/baked_lightmap.h
@@ -31,7 +31,7 @@
#ifndef BAKED_LIGHTMAP_H
#define BAKED_LIGHTMAP_H
-#include "core/local_vector.h"
+#include "core/templates/local_vector.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/lightmapper.h"
#include "scene/3d/mesh_instance_3d.h"
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index ecbaca7bd1..191159448a 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -31,7 +31,7 @@
#include "camera_3d.h"
#include "collision_object_3d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "core/math/camera_matrix.h"
#include "scene/resources/material.h"
#include "scene/resources/surface_tool.h"
@@ -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.01,8192,0.01,or_greater"), "set_znear", "get_znear");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar");
+ 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");
BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE);
BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL);
diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h
index 6a778d45a2..04cec92b14 100644
--- a/scene/3d/camera_3d.h
+++ b/scene/3d/camera_3d.h
@@ -42,7 +42,6 @@ class Camera3D : public Node3D {
public:
enum Projection {
-
PROJECTION_PERSPECTIVE,
PROJECTION_ORTHOGONAL,
PROJECTION_FRUSTUM
@@ -103,7 +102,6 @@ protected:
public:
enum {
-
NOTIFICATION_BECAME_CURRENT = 50,
NOTIFICATION_LOST_CURRENT = 51
};
diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp
index e2d11c740a..b8a4ab74ee 100644
--- a/scene/3d/collision_polygon_3d.cpp
+++ b/scene/3d/collision_polygon_3d.cpp
@@ -156,15 +156,23 @@ bool CollisionPolygon3D::is_disabled() const {
}
String CollisionPolygon3D::get_configuration_warning() const {
+ String warning = Node3D::get_configuration_warning();
+
if (!Object::cast_to<CollisionObject3D>(get_parent())) {
- return 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 (!warning.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()) {
- return TTR("An empty CollisionPolygon3D has no effect on collision.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("An empty CollisionPolygon3D has no effect on collision.");
}
- return String();
+ return warning;
}
bool CollisionPolygon3D::_is_editable_3d_polygon() const {
diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp
index 56367e9bdd..e1c691b89a 100644
--- a/scene/3d/collision_shape_3d.cpp
+++ b/scene/3d/collision_shape_3d.cpp
@@ -44,23 +44,36 @@
//TODO: Implement CylinderShape and HeightMapShape?
-void CollisionShape3D::make_convex_from_brothers() {
+void CollisionShape3D::make_convex_from_siblings() {
Node *p = get_parent();
if (!p) {
return;
}
+ Vector<Vector3> vertices;
+
for (int i = 0; i < p->get_child_count(); i++) {
Node *n = p->get_child(i);
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(n);
if (mi) {
Ref<Mesh> m = mi->get_mesh();
if (m.is_valid()) {
- Ref<Shape3D> s = m->create_convex_shape();
- set_shape(s);
+ for (int j = 0; j < m->get_surface_count(); j++) {
+ Array a = m->surface_get_arrays(j);
+ if (!a.empty()) {
+ Vector<Vector3> v = a[RenderingServer::ARRAY_VERTEX];
+ for (int k = 0; k < v.size(); k++) {
+ vertices.append(mi->get_transform().xform(v[k]));
+ }
+ }
+ }
}
}
}
+
+ Ref<ConvexPolygonShape3D> shape = memnew(ConvexPolygonShape3D);
+ shape->set_points(vertices);
+ set_shape(shape);
}
void CollisionShape3D::_update_in_shape_owner(bool p_xform_only) {
@@ -111,23 +124,33 @@ void CollisionShape3D::resource_changed(RES res) {
}
String CollisionShape3D::get_configuration_warning() const {
+ String warning = Node3D::get_configuration_warning();
+
if (!Object::cast_to<CollisionObject3D>(get_parent())) {
- return 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 (!warning.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()) {
- return TTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it.");
}
- if (Object::cast_to<RigidBody3D>(get_parent())) {
- if (Object::cast_to<ConcavePolygonShape3D>(*shape)) {
- if (Object::cast_to<RigidBody3D>(get_parent())->get_mode() != RigidBody3D::MODE_STATIC) {
- return TTR("ConcavePolygonShape3D doesn't support RigidBody3D in another mode than static.");
- }
+ if (shape.is_valid() &&
+ 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()) {
+ warning += "\n\n";
}
+ warning += TTR("ConcavePolygonShape3D doesn't support RigidBody3D in another mode than static.");
}
- return String();
+ return warning;
}
void CollisionShape3D::_bind_methods() {
@@ -137,8 +160,8 @@ void CollisionShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_shape"), &CollisionShape3D::get_shape);
ClassDB::bind_method(D_METHOD("set_disabled", "enable"), &CollisionShape3D::set_disabled);
ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionShape3D::is_disabled);
- ClassDB::bind_method(D_METHOD("make_convex_from_brothers"), &CollisionShape3D::make_convex_from_brothers);
- ClassDB::set_method_flags("CollisionShape3D", "make_convex_from_brothers", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
+ ClassDB::bind_method(D_METHOD("make_convex_from_siblings"), &CollisionShape3D::make_convex_from_siblings);
+ ClassDB::set_method_flags("CollisionShape3D", "make_convex_from_siblings", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
ClassDB::bind_method(D_METHOD("_update_debug_shape"), &CollisionShape3D::_update_debug_shape);
diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h
index a32a3efeb6..35f40d27b1 100644
--- a/scene/3d/collision_shape_3d.h
+++ b/scene/3d/collision_shape_3d.h
@@ -60,7 +60,7 @@ protected:
static void _bind_methods();
public:
- void make_convex_from_brothers();
+ void make_convex_from_siblings();
void set_shape(const Ref<Shape3D> &p_shape);
Ref<Shape3D> get_shape() const;
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index 4244a11592..215d9e062c 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -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>();
}
@@ -189,7 +189,7 @@ bool CPUParticles3D::get_fractional_delta() const {
}
String CPUParticles3D::get_configuration_warning() const {
- String warnings;
+ String warnings = GeometryInstance3D::get_configuration_warning();
bool mesh_found = false;
bool anim_material_found = false;
@@ -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,14 +725,16 @@ 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]) {
- /*
- mat2 rotm;
- ";
- rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;
- rotm[1] = rotm[0].yx * vec2(1.0, -1.0);
- VELOCITY.xy = rotm * VELOCITY.xy;
- */
+ 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());
+ Vector2 velocity_2d(p.velocity.x, p.velocity.y);
+ velocity_2d = m2.basis_xform(velocity_2d);
+ p.velocity.x = velocity_2d.x;
+ p.velocity.y = velocity_2d.y;
} else {
Vector3 normal = emission_normals.get(random_idx);
Vector3 v0 = Math::abs(normal.z) < 0.999 ? Vector3(0.0, 0.0, 1.0) : Vector3(0, 1.0, 0.0);
@@ -760,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;
}
@@ -781,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]);
}
@@ -828,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
@@ -838,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();
@@ -850,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;
@@ -921,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 {
@@ -939,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 {
@@ -957,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;
}
@@ -971,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;
}
@@ -1197,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());
@@ -1316,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);
@@ -1343,16 +1345,16 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles3D::convert_from_particles);
ADD_GROUP("Emission Shape", "emission_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
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");
@@ -1424,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);
@@ -1491,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 f44d0dfcfa..8c1b8a684c 100644
--- a/scene/3d/cpu_particles_3d.h
+++ b/scene/3d/cpu_particles_3d.h
@@ -31,7 +31,7 @@
#ifndef CPU_PARTICLES_H
#define CPU_PARTICLES_H
-#include "core/rid.h"
+#include "core/templates/rid.h"
#include "scene/3d/visual_instance_3d.h"
class CPUParticles3D : public GeometryInstance3D {
@@ -46,7 +46,6 @@ public:
};
enum Parameter {
-
PARAM_INITIAL_LINEAR_VELOCITY,
PARAM_ANGULAR_VELOCITY,
PARAM_ORBIT_VELOCITY,
@@ -62,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 {
@@ -161,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;
@@ -257,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);
@@ -291,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/gi_probe.cpp b/scene/3d/gi_probe.cpp
index 1b6f9b45b9..fd592012f8 100644
--- a/scene/3d/gi_probe.cpp
+++ b/scene/3d/gi_probe.cpp
@@ -32,7 +32,6 @@
#include "core/os/os.h"
-#include "core/method_bind_ext.gen.inc"
#include "mesh_instance_3d.h"
#include "voxelizer.h"
@@ -514,10 +513,15 @@ Vector<Face3> GIProbe::get_faces(uint32_t p_usage_flags) const {
}
String GIProbe::get_configuration_warning() const {
+ String warning = VisualInstance3D::get_configuration_warning();
+
if (RenderingServer::get_singleton()->is_low_end()) {
- return TTR("GIProbes are not supported by the GLES2 video driver.\nUse a BakedLightmap instead.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("GIProbes are not supported by the GLES2 video driver.\nUse a BakedLightmap instead.");
}
- return String();
+ return warning;
}
void GIProbe::_bind_methods() {
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index c4480e3ed2..ec33d7bcab 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -124,6 +124,11 @@ void GPUParticles3D::set_speed_scale(float p_scale) {
RS::get_singleton()->particles_set_speed_scale(particles, p_scale);
}
+void GPUParticles3D::set_collision_base_size(float p_size) {
+ collision_base_size = p_size;
+ RS::get_singleton()->particles_set_collision_base_size(particles, p_size);
+}
+
bool GPUParticles3D::is_emitting() const {
return RS::get_singleton()->particles_get_emitting(particles);
}
@@ -168,6 +173,10 @@ float GPUParticles3D::get_speed_scale() const {
return speed_scale;
}
+float GPUParticles3D::get_collision_base_size() const {
+ return collision_base_size;
+}
+
void GPUParticles3D::set_draw_order(DrawOrder p_order) {
draw_order = p_order;
RS::get_singleton()->particles_set_draw_order(particles, RS::ParticlesDrawOrder(p_order));
@@ -232,7 +241,7 @@ String GPUParticles3D::get_configuration_warning() const {
return TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose.");
}
- String warnings;
+ String warnings = GeometryInstance3D::get_configuration_warning();
bool meshes_found = false;
bool anim_material_found = false;
@@ -301,6 +310,36 @@ void GPUParticles3D::_validate_property(PropertyInfo &property) const {
}
}
+void GPUParticles3D::emit_particle(const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
+ RS::get_singleton()->particles_emit(particles, p_transform, p_velocity, p_color, p_custom, p_emit_flags);
+}
+
+void GPUParticles3D::_attach_sub_emitter() {
+ Node *n = get_node_or_null(sub_emitter);
+ if (n) {
+ GPUParticles3D *sen = Object::cast_to<GPUParticles3D>(n);
+ if (sen && sen != this) {
+ RS::get_singleton()->particles_set_subemitter(particles, sen->particles);
+ }
+ }
+}
+
+void GPUParticles3D::set_sub_emitter(const NodePath &p_path) {
+ if (is_inside_tree()) {
+ RS::get_singleton()->particles_set_subemitter(particles, RID());
+ }
+
+ sub_emitter = p_path;
+
+ if (is_inside_tree() && sub_emitter != NodePath()) {
+ _attach_sub_emitter();
+ }
+}
+
+NodePath GPUParticles3D::get_sub_emitter() const {
+ return sub_emitter;
+}
+
void GPUParticles3D::_notification(int p_what) {
if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) {
if (can_process()) {
@@ -319,6 +358,16 @@ void GPUParticles3D::_notification(int p_what) {
}
}
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ if (sub_emitter != NodePath()) {
+ _attach_sub_emitter();
+ }
+ }
+
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ RS::get_singleton()->particles_set_subemitter(particles, RID());
+ }
+
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
// make sure particles are updated before rendering occurs if they were active before
if (is_visible_in_tree() && !RS::get_singleton()->particles_is_inactive(particles)) {
@@ -341,6 +390,7 @@ void GPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &GPUParticles3D::set_fractional_delta);
ClassDB::bind_method(D_METHOD("set_process_material", "material"), &GPUParticles3D::set_process_material);
ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &GPUParticles3D::set_speed_scale);
+ ClassDB::bind_method(D_METHOD("set_collision_base_size", "size"), &GPUParticles3D::set_collision_base_size);
ClassDB::bind_method(D_METHOD("is_emitting"), &GPUParticles3D::is_emitting);
ClassDB::bind_method(D_METHOD("get_amount"), &GPUParticles3D::get_amount);
@@ -355,6 +405,7 @@ void GPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_fractional_delta"), &GPUParticles3D::get_fractional_delta);
ClassDB::bind_method(D_METHOD("get_process_material"), &GPUParticles3D::get_process_material);
ClassDB::bind_method(D_METHOD("get_speed_scale"), &GPUParticles3D::get_speed_scale);
+ ClassDB::bind_method(D_METHOD("get_collision_base_size"), &GPUParticles3D::get_collision_base_size);
ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &GPUParticles3D::set_draw_order);
@@ -369,8 +420,14 @@ void GPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("restart"), &GPUParticles3D::restart);
ClassDB::bind_method(D_METHOD("capture_aabb"), &GPUParticles3D::capture_aabb);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter", "path"), &GPUParticles3D::set_sub_emitter);
+ ClassDB::bind_method(D_METHOD("get_sub_emitter"), &GPUParticles3D::get_sub_emitter);
+
+ ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles3D::emit_particle);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles3D"), "set_sub_emitter", "get_sub_emitter");
ADD_GROUP("Time", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
@@ -380,6 +437,8 @@ void GPUParticles3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
+ ADD_GROUP("Collision", "collision_");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size");
ADD_GROUP("Drawing", "");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "visibility_aabb"), "set_visibility_aabb", "get_visibility_aabb");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
@@ -396,6 +455,12 @@ void GPUParticles3D::_bind_methods() {
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
BIND_ENUM_CONSTANT(DRAW_ORDER_VIEW_DEPTH);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_POSITION);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_ROTATION_SCALE);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_VELOCITY);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_COLOR);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM);
+
BIND_CONSTANT(MAX_DRAW_PASSES);
}
@@ -417,6 +482,7 @@ GPUParticles3D::GPUParticles3D() {
set_draw_passes(1);
set_draw_order(DRAW_ORDER_INDEX);
set_speed_scale(1);
+ set_collision_base_size(0.01);
}
GPUParticles3D::~GPUParticles3D() {
diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h
index e04473727d..f0e5f05e5b 100644
--- a/scene/3d/gpu_particles_3d.h
+++ b/scene/3d/gpu_particles_3d.h
@@ -31,7 +31,7 @@
#ifndef PARTICLES_H
#define PARTICLES_H
-#include "core/rid.h"
+#include "core/templates/rid.h"
#include "scene/3d/visual_instance_3d.h"
#include "scene/resources/material.h"
@@ -64,6 +64,8 @@ private:
bool local_coords;
int fixed_fps;
bool fractional_delta;
+ NodePath sub_emitter;
+ float collision_base_size;
Ref<Material> process_material;
@@ -71,6 +73,8 @@ private:
Vector<Ref<Mesh>> draw_passes;
+ void _attach_sub_emitter();
+
protected:
static void _bind_methods();
void _notification(int p_what);
@@ -91,6 +95,7 @@ public:
void set_use_local_coordinates(bool p_enable);
void set_process_material(const Ref<Material> &p_material);
void set_speed_scale(float p_scale);
+ void set_collision_base_size(float p_ratio);
bool is_emitting() const;
int get_amount() const;
@@ -103,6 +108,7 @@ public:
bool get_use_local_coordinates() const;
Ref<Material> get_process_material() const;
float get_speed_scale() const;
+ float get_collision_base_size() const;
void set_fixed_fps(int p_count);
int get_fixed_fps() const;
@@ -121,13 +127,27 @@ public:
virtual String get_configuration_warning() const override;
+ void set_sub_emitter(const NodePath &p_path);
+ NodePath get_sub_emitter() const;
+
void restart();
+ enum EmitFlags {
+ EMIT_FLAG_POSITION = RS::PARTICLES_EMIT_FLAG_POSITION,
+ EMIT_FLAG_ROTATION_SCALE = RS::PARTICLES_EMIT_FLAG_ROTATION_SCALE,
+ EMIT_FLAG_VELOCITY = RS::PARTICLES_EMIT_FLAG_VELOCITY,
+ EMIT_FLAG_COLOR = RS::PARTICLES_EMIT_FLAG_COLOR,
+ EMIT_FLAG_CUSTOM = RS::PARTICLES_EMIT_FLAG_CUSTOM
+ };
+
+ void emit_particle(const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags);
+
AABB capture_aabb() const;
GPUParticles3D();
~GPUParticles3D();
};
VARIANT_ENUM_CAST(GPUParticles3D::DrawOrder)
+VARIANT_ENUM_CAST(GPUParticles3D::EmitFlags)
#endif // PARTICLES_H
diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp
new file mode 100644
index 0000000000..1f0d5d587d
--- /dev/null
+++ b/scene/3d/gpu_particles_collision_3d.cpp
@@ -0,0 +1,901 @@
+/*************************************************************************/
+/* gpu_particles_collision_3d.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 "gpu_particles_collision_3d.h"
+
+#include "core/templates/thread_work_pool.h"
+#include "mesh_instance_3d.h"
+#include "scene/3d/camera_3d.h"
+#include "scene/main/viewport.h"
+
+void GPUParticlesCollision3D::set_cull_mask(uint32_t p_cull_mask) {
+ cull_mask = p_cull_mask;
+ RS::get_singleton()->particles_collision_set_cull_mask(collision, p_cull_mask);
+}
+
+uint32_t GPUParticlesCollision3D::get_cull_mask() const {
+ return cull_mask;
+}
+
+void GPUParticlesCollision3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &GPUParticlesCollision3D::set_cull_mask);
+ ClassDB::bind_method(D_METHOD("get_cull_mask"), &GPUParticlesCollision3D::get_cull_mask);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
+}
+
+GPUParticlesCollision3D::GPUParticlesCollision3D(RS::ParticlesCollisionType p_type) {
+ collision = RS::get_singleton()->particles_collision_create();
+ RS::get_singleton()->particles_collision_set_collision_type(collision, p_type);
+ set_base(collision);
+}
+
+GPUParticlesCollision3D::~GPUParticlesCollision3D() {
+ RS::get_singleton()->free(collision);
+}
+
+/////////////////////////////////
+
+void GPUParticlesCollisionSphere::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesCollisionSphere::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesCollisionSphere::get_radius);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius");
+}
+
+void GPUParticlesCollisionSphere::set_radius(float p_radius) {
+ radius = p_radius;
+ RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius);
+ update_gizmo();
+}
+
+float GPUParticlesCollisionSphere::get_radius() const {
+ return radius;
+}
+
+AABB GPUParticlesCollisionSphere::get_aabb() const {
+ return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2));
+}
+
+GPUParticlesCollisionSphere::GPUParticlesCollisionSphere() :
+ GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE) {
+}
+
+GPUParticlesCollisionSphere::~GPUParticlesCollisionSphere() {
+}
+
+///////////////////////////
+
+void GPUParticlesCollisionBox::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionBox::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionBox::get_extents);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
+}
+
+void GPUParticlesCollisionBox::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
+ update_gizmo();
+}
+
+Vector3 GPUParticlesCollisionBox::get_extents() const {
+ return extents;
+}
+
+AABB GPUParticlesCollisionBox::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+
+GPUParticlesCollisionBox::GPUParticlesCollisionBox() :
+ GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_BOX_COLLIDE) {
+}
+
+GPUParticlesCollisionBox::~GPUParticlesCollisionBox() {
+}
+
+///////////////////////////////
+///////////////////////////
+
+void GPUParticlesCollisionSDF::_find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes) {
+ MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node);
+ if (mi && mi->is_visible_in_tree()) {
+ Ref<Mesh> mesh = mi->get_mesh();
+ if (mesh.is_valid()) {
+ AABB aabb = mesh->get_aabb();
+
+ Transform xf = get_global_transform().affine_inverse() * mi->get_global_transform();
+
+ if (p_aabb.intersects(xf.xform(aabb))) {
+ PlotMesh pm;
+ pm.local_xform = xf;
+ pm.mesh = mesh;
+ plot_meshes.push_back(pm);
+ }
+ }
+ }
+
+ Node3D *s = Object::cast_to<Node3D>(p_at_node);
+ if (s) {
+ if (s->is_visible_in_tree()) {
+ Array meshes = p_at_node->call("get_meshes");
+ for (int i = 0; i < meshes.size(); i += 2) {
+ Transform mxf = meshes[i];
+ Ref<Mesh> mesh = meshes[i + 1];
+ if (!mesh.is_valid()) {
+ continue;
+ }
+
+ AABB aabb = mesh->get_aabb();
+
+ Transform xf = get_global_transform().affine_inverse() * (s->get_global_transform() * mxf);
+
+ if (p_aabb.intersects(xf.xform(aabb))) {
+ PlotMesh pm;
+ pm.local_xform = xf;
+ pm.mesh = mesh;
+ plot_meshes.push_back(pm);
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < p_at_node->get_child_count(); i++) {
+ Node *child = p_at_node->get_child(i);
+ _find_meshes(p_aabb, child, plot_meshes);
+ }
+}
+
+uint32_t GPUParticlesCollisionSDF::_create_bvh(LocalVector<BVH> &bvh_tree, FacePos *p_faces, uint32_t p_face_count, const Face3 *p_triangles, float p_thickness) {
+ if (p_face_count == 1) {
+ return BVH::LEAF_BIT | p_faces[0].index;
+ }
+
+ uint32_t index = bvh_tree.size();
+ {
+ BVH bvh;
+
+ for (uint32_t i = 0; i < p_face_count; i++) {
+ const Face3 &f = p_triangles[p_faces[i].index];
+ AABB aabb(f.vertex[0], Vector3());
+ aabb.expand_to(f.vertex[1]);
+ aabb.expand_to(f.vertex[2]);
+ if (p_thickness > 0.0) {
+ Vector3 normal = p_triangles[p_faces[i].index].get_plane().normal;
+ aabb.expand_to(f.vertex[0] - normal * p_thickness);
+ aabb.expand_to(f.vertex[1] - normal * p_thickness);
+ aabb.expand_to(f.vertex[2] - normal * p_thickness);
+ }
+ if (i == 0) {
+ bvh.bounds = aabb;
+ } else {
+ bvh.bounds.merge_with(aabb);
+ }
+ }
+ bvh_tree.push_back(bvh);
+ }
+
+ uint32_t middle = p_face_count / 2;
+
+ SortArray<FacePos, FaceSort> s;
+ s.compare.axis = bvh_tree[index].bounds.get_longest_axis_index();
+ s.sort(p_faces, p_face_count);
+
+ uint32_t left = _create_bvh(bvh_tree, p_faces, middle, p_triangles, p_thickness);
+ uint32_t right = _create_bvh(bvh_tree, p_faces + middle, p_face_count - middle, p_triangles, p_thickness);
+
+ bvh_tree[index].children[0] = left;
+ bvh_tree[index].children[1] = right;
+
+ return index;
+}
+
+static _FORCE_INLINE_ float Vector3_dot2(const Vector3 &p_vec3) {
+ return p_vec3.dot(p_vec3);
+}
+
+void GPUParticlesCollisionSDF::_find_closest_distance(const Vector3 &p_pos, const BVH *bvh, uint32_t p_bvh_cell, const Face3 *triangles, float thickness, float &closest_distance) {
+ if (p_bvh_cell & BVH::LEAF_BIT) {
+ p_bvh_cell &= BVH::LEAF_MASK; //remove bit
+
+ Vector3 point = p_pos;
+ Plane p = triangles[p_bvh_cell].get_plane();
+ float d = p.distance_to(point);
+ float inside_d = 1e20;
+ if (d < 0 && d > -thickness) {
+ //inside planes, do this in 2D
+
+ Vector3 x_axis = (triangles[p_bvh_cell].vertex[0] - triangles[p_bvh_cell].vertex[1]).normalized();
+ Vector3 y_axis = p.normal.cross(x_axis).normalized();
+
+ Vector2 points[3];
+ for (int i = 0; i < 3; i++) {
+ points[i] = Vector2(x_axis.dot(triangles[p_bvh_cell].vertex[i]), y_axis.dot(triangles[p_bvh_cell].vertex[i]));
+ }
+
+ Vector2 p2d = Vector2(x_axis.dot(point), y_axis.dot(point));
+
+ {
+ // https://www.shadertoy.com/view/XsXSz4
+
+ Vector2 e0 = points[1] - points[0];
+ Vector2 e1 = points[2] - points[1];
+ Vector2 e2 = points[0] - points[2];
+
+ Vector2 v0 = p2d - points[0];
+ Vector2 v1 = p2d - points[1];
+ Vector2 v2 = p2d - points[2];
+
+ Vector2 pq0 = v0 - e0 * CLAMP(v0.dot(e0) / e0.dot(e0), 0.0, 1.0);
+ Vector2 pq1 = v1 - e1 * CLAMP(v1.dot(e1) / e1.dot(e1), 0.0, 1.0);
+ Vector2 pq2 = v2 - e2 * CLAMP(v2.dot(e2) / e2.dot(e2), 0.0, 1.0);
+
+ float s = SGN(e0.x * e2.y - e0.y * e2.x);
+ Vector2 d2 = Vector2(pq0.dot(pq0), s * (v0.x * e0.y - v0.y * e0.x)).min(Vector2(pq1.dot(pq1), s * (v1.x * e1.y - v1.y * e1.x))).min(Vector2(pq2.dot(pq2), s * (v2.x * e2.y - v2.y * e2.x)));
+
+ inside_d = -Math::sqrt(d2.x) * SGN(d2.y);
+ }
+
+ //make sure distance to planes is not shorter if inside
+ if (inside_d < 0) {
+ inside_d = MAX(inside_d, d);
+ inside_d = MAX(inside_d, -(thickness + d));
+ }
+
+ closest_distance = MIN(closest_distance, inside_d);
+ } else {
+ if (d < 0) {
+ point -= p.normal * thickness; //flatten
+ }
+
+ // https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
+ Vector3 a = triangles[p_bvh_cell].vertex[0];
+ Vector3 b = triangles[p_bvh_cell].vertex[1];
+ Vector3 c = triangles[p_bvh_cell].vertex[2];
+
+ Vector3 ba = b - a;
+ Vector3 pa = point - a;
+ Vector3 cb = c - b;
+ Vector3 pb = point - b;
+ Vector3 ac = a - c;
+ Vector3 pc = point - c;
+ Vector3 nor = ba.cross(ac);
+
+ inside_d = Math::sqrt(
+ (SGN(ba.cross(nor).dot(pa)) +
+ SGN(cb.cross(nor).dot(pb)) +
+ SGN(ac.cross(nor).dot(pc)) <
+ 2.0) ?
+ 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));
+
+ closest_distance = MIN(closest_distance, inside_d);
+ }
+
+ } else {
+ bool pass = true;
+ if (!bvh[p_bvh_cell].bounds.has_point(p_pos)) {
+ //outside, find closest point
+ Vector3 he = bvh[p_bvh_cell].bounds.size * 0.5;
+ Vector3 center = bvh[p_bvh_cell].bounds.position + he;
+
+ Vector3 rel = (p_pos - center).abs();
+ Vector3 closest(MIN(rel.x, he.x), MIN(rel.y, he.y), MIN(rel.z, he.z));
+ float d = rel.distance_to(closest);
+
+ if (d >= closest_distance) {
+ pass = false; //already closer than this aabb, discard
+ }
+ }
+
+ if (pass) {
+ _find_closest_distance(p_pos, bvh, bvh[p_bvh_cell].children[0], triangles, thickness, closest_distance);
+ _find_closest_distance(p_pos, bvh, bvh[p_bvh_cell].children[1], triangles, thickness, closest_distance);
+ }
+ }
+}
+
+void GPUParticlesCollisionSDF::_compute_sdf_z(uint32_t p_z, ComputeSDFParams *params) {
+ int32_t z_ofs = p_z * params->size.y * params->size.x;
+ for (int32_t y = 0; y < params->size.y; y++) {
+ int32_t y_ofs = z_ofs + y * params->size.x;
+ for (int32_t x = 0; x < params->size.x; x++) {
+ int32_t x_ofs = y_ofs + x;
+ float &cell = params->cells[x_ofs];
+
+ Vector3 pos = params->cell_offset + Vector3(x, y, p_z) * params->cell_size;
+
+ cell = 1e20;
+
+ _find_closest_distance(pos, params->bvh, 0, params->triangles, params->thickness, cell);
+ }
+ }
+}
+
+void GPUParticlesCollisionSDF::_compute_sdf(ComputeSDFParams *params) {
+ ThreadWorkPool work_pool;
+ work_pool.init();
+ work_pool.begin_work(params->size.z, this, &GPUParticlesCollisionSDF::_compute_sdf_z, params);
+ while (work_pool.get_work_index() < (uint32_t)params->size.z) {
+ OS::get_singleton()->delay_usec(10000);
+ bake_step_function(work_pool.get_work_index() * 100 / params->size.z, "Baking SDF");
+ }
+ work_pool.end_work();
+ work_pool.finish();
+}
+
+Vector3i GPUParticlesCollisionSDF::get_estimated_cell_size() const {
+ static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
+ int subdiv = subdivs[get_resolution()];
+
+ AABB aabb(-extents, extents * 2);
+
+ float cell_size = aabb.get_longest_axis_size() / float(subdiv);
+
+ Vector3i sdf_size = Vector3i(aabb.size / cell_size);
+ sdf_size.x = MAX(1, sdf_size.x);
+ sdf_size.y = MAX(1, sdf_size.y);
+ sdf_size.z = MAX(1, sdf_size.z);
+ return sdf_size;
+}
+
+Ref<Image> GPUParticlesCollisionSDF::bake() {
+ static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
+ int subdiv = subdivs[get_resolution()];
+
+ AABB aabb(-extents, extents * 2);
+
+ float cell_size = aabb.get_longest_axis_size() / float(subdiv);
+
+ Vector3i sdf_size = Vector3i(aabb.size / cell_size);
+ sdf_size.x = MAX(1, sdf_size.x);
+ sdf_size.y = MAX(1, sdf_size.y);
+ sdf_size.z = MAX(1, sdf_size.z);
+
+ if (bake_begin_function) {
+ bake_begin_function(100);
+ }
+
+ aabb.size = Vector3(sdf_size) * cell_size;
+
+ List<PlotMesh> plot_meshes;
+ _find_meshes(aabb, get_parent(), plot_meshes);
+
+ LocalVector<Face3> faces;
+
+ if (bake_step_function) {
+ bake_step_function(0, "Finding Meshes");
+ }
+
+ for (List<PlotMesh>::Element *E = plot_meshes.front(); E; E = E->next()) {
+ const PlotMesh &pm = E->get();
+
+ for (int i = 0; i < pm.mesh->get_surface_count(); i++) {
+ if (pm.mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
+ continue; //only triangles
+ }
+
+ Array a = pm.mesh->surface_get_arrays(i);
+
+ Vector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];
+ const Vector3 *vr = vertices.ptr();
+ Vector<int> index = a[Mesh::ARRAY_INDEX];
+
+ if (index.size()) {
+ int facecount = index.size() / 3;
+ const int *ir = index.ptr();
+
+ for (int j = 0; j < facecount; j++) {
+ Face3 face;
+
+ for (int k = 0; k < 3; k++) {
+ face.vertex[k] = pm.local_xform.xform(vr[ir[j * 3 + k]]);
+ }
+
+ //test against original bounds
+ if (!Geometry3D::triangle_box_overlap(aabb.position + aabb.size * 0.5, aabb.size * 0.5, face.vertex)) {
+ continue;
+ }
+
+ faces.push_back(face);
+ }
+
+ } else {
+ int facecount = vertices.size() / 3;
+
+ for (int j = 0; j < facecount; j++) {
+ Face3 face;
+
+ for (int k = 0; k < 3; k++) {
+ face.vertex[k] = pm.local_xform.xform(vr[j * 3 + k]);
+ }
+
+ //test against original bounds
+ if (!Geometry3D::triangle_box_overlap(aabb.position + aabb.size * 0.5, aabb.size * 0.5, face.vertex)) {
+ continue;
+ }
+
+ faces.push_back(face);
+ }
+ }
+ }
+ }
+
+ //compute bvh
+
+ ERR_FAIL_COND_V(faces.size() <= 1, Ref<Image>());
+
+ LocalVector<FacePos> face_pos;
+
+ face_pos.resize(faces.size());
+
+ float th = cell_size * thickness;
+
+ for (uint32_t i = 0; i < faces.size(); i++) {
+ face_pos[i].index = i;
+ face_pos[i].center = (faces[i].vertex[0] + faces[i].vertex[1] + faces[i].vertex[2]) / 2;
+ if (th > 0.0) {
+ face_pos[i].center -= faces[i].get_plane().normal * th * 0.5;
+ }
+ }
+
+ if (bake_step_function) {
+ bake_step_function(0, "Creating BVH");
+ }
+
+ LocalVector<BVH> bvh;
+
+ _create_bvh(bvh, face_pos.ptr(), face_pos.size(), faces.ptr(), th);
+
+ Vector<uint8_t> data;
+ data.resize(sdf_size.z * sdf_size.y * sdf_size.x * sizeof(float));
+
+ if (bake_step_function) {
+ bake_step_function(0, "Baking SDF");
+ }
+
+ ComputeSDFParams params;
+ params.cells = (float *)data.ptrw();
+ params.size = sdf_size;
+ params.cell_size = cell_size;
+ params.cell_offset = aabb.position + Vector3(cell_size * 0.5, cell_size * 0.5, cell_size * 0.5);
+ params.bvh = bvh.ptr();
+ params.triangles = faces.ptr();
+ params.thickness = th;
+ _compute_sdf(&params);
+
+ Ref<Image> ret;
+ ret.instance();
+ ret->create(sdf_size.x, sdf_size.y * sdf_size.z, false, Image::FORMAT_RF, data);
+ ret->convert(Image::FORMAT_RH); //convert to half, save space
+ ret->set_meta("depth", sdf_size.z); //hack, make sure to add to the docs of this function
+
+ if (bake_end_function) {
+ bake_end_function();
+ }
+
+ return ret;
+}
+
+void GPUParticlesCollisionSDF::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionSDF::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionSDF::get_extents);
+
+ ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionSDF::set_resolution);
+ ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionSDF::get_resolution);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesCollisionSDF::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesCollisionSDF::get_texture);
+
+ ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &GPUParticlesCollisionSDF::set_thickness);
+ ClassDB::bind_method(D_METHOD("get_thickness"), &GPUParticlesCollisionSDF::get_thickness);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "16,32,64,128,256,512"), "set_resolution", "get_resolution");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "thickness", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"), "set_thickness", "get_thickness");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture");
+
+ BIND_ENUM_CONSTANT(RESOLUTION_16);
+ BIND_ENUM_CONSTANT(RESOLUTION_32);
+ BIND_ENUM_CONSTANT(RESOLUTION_64);
+ BIND_ENUM_CONSTANT(RESOLUTION_128);
+ BIND_ENUM_CONSTANT(RESOLUTION_256);
+ BIND_ENUM_CONSTANT(RESOLUTION_512);
+ BIND_ENUM_CONSTANT(RESOLUTION_MAX);
+}
+
+void GPUParticlesCollisionSDF::set_thickness(float p_thickness) {
+ thickness = p_thickness;
+}
+
+float GPUParticlesCollisionSDF::get_thickness() const {
+ return thickness;
+}
+
+void GPUParticlesCollisionSDF::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
+ update_gizmo();
+}
+
+Vector3 GPUParticlesCollisionSDF::get_extents() const {
+ return extents;
+}
+
+void GPUParticlesCollisionSDF::set_resolution(Resolution p_resolution) {
+ resolution = p_resolution;
+ update_gizmo();
+}
+
+GPUParticlesCollisionSDF::Resolution GPUParticlesCollisionSDF::get_resolution() const {
+ return resolution;
+}
+
+void GPUParticlesCollisionSDF::set_texture(const Ref<Texture3D> &p_texture) {
+ texture = p_texture;
+ RID tex = texture.is_valid() ? texture->get_rid() : RID();
+ RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex);
+}
+
+Ref<Texture3D> GPUParticlesCollisionSDF::get_texture() const {
+ return texture;
+}
+
+AABB GPUParticlesCollisionSDF::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+
+GPUParticlesCollisionSDF::BakeBeginFunc GPUParticlesCollisionSDF::bake_begin_function = nullptr;
+GPUParticlesCollisionSDF::BakeStepFunc GPUParticlesCollisionSDF::bake_step_function = nullptr;
+GPUParticlesCollisionSDF::BakeEndFunc GPUParticlesCollisionSDF::bake_end_function = nullptr;
+
+GPUParticlesCollisionSDF::GPUParticlesCollisionSDF() :
+ GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE) {
+}
+
+GPUParticlesCollisionSDF::~GPUParticlesCollisionSDF() {
+}
+
+////////////////////////////
+////////////////////////////
+
+void GPUParticlesCollisionHeightField::_notification(int p_what) {
+ if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
+ if (update_mode == UPDATE_MODE_ALWAYS) {
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+ }
+
+ if (follow_camera_mode && get_viewport()) {
+ Camera3D *cam = get_viewport()->get_camera();
+ if (cam) {
+ Transform xform = get_global_transform();
+ Vector3 x_axis = xform.basis.get_axis(Vector3::AXIS_X).normalized();
+ Vector3 z_axis = xform.basis.get_axis(Vector3::AXIS_Z).normalized();
+ float x_len = xform.basis.get_scale().x;
+ float z_len = xform.basis.get_scale().z;
+
+ Vector3 cam_pos = cam->get_global_transform().origin;
+ Transform new_xform = xform;
+
+ while (x_axis.dot(cam_pos - new_xform.origin) > x_len) {
+ new_xform.origin += x_axis * x_len;
+ }
+ while (x_axis.dot(cam_pos - new_xform.origin) < -x_len) {
+ new_xform.origin -= x_axis * x_len;
+ }
+
+ while (z_axis.dot(cam_pos - new_xform.origin) > z_len) {
+ new_xform.origin += z_axis * z_len;
+ }
+ while (z_axis.dot(cam_pos - new_xform.origin) < -z_len) {
+ new_xform.origin -= z_axis * z_len;
+ }
+
+ if (new_xform != xform) {
+ set_global_transform(new_xform);
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+ }
+ }
+ }
+ }
+
+ if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+ }
+}
+
+void GPUParticlesCollisionHeightField::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionHeightField::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionHeightField::get_extents);
+
+ ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionHeightField::set_resolution);
+ ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionHeightField::get_resolution);
+
+ ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &GPUParticlesCollisionHeightField::set_update_mode);
+ ClassDB::bind_method(D_METHOD("get_update_mode"), &GPUParticlesCollisionHeightField::get_update_mode);
+
+ ClassDB::bind_method(D_METHOD("set_follow_camera_mode", "enabled"), &GPUParticlesCollisionHeightField::set_follow_camera_mode);
+ ClassDB::bind_method(D_METHOD("is_follow_camera_mode_enabled"), &GPUParticlesCollisionHeightField::is_follow_camera_mode_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_follow_camera_push_ratio", "ratio"), &GPUParticlesCollisionHeightField::set_follow_camera_push_ratio);
+ ClassDB::bind_method(D_METHOD("get_follow_camera_push_ratio"), &GPUParticlesCollisionHeightField::get_follow_camera_push_ratio);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256,512,1024,2048,4096,8192"), "set_resolution", "get_resolution");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "WhenMoved,Always"), "set_update_mode", "get_update_mode");
+ ADD_GROUP("Folow Camera", "follow_camera_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_mode", "is_follow_camera_mode_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_camera_push_ratio", PROPERTY_HINT_RANGE, "0.01,1,0.01"), "set_follow_camera_push_ratio", "get_follow_camera_push_ratio");
+
+ BIND_ENUM_CONSTANT(RESOLUTION_256);
+ BIND_ENUM_CONSTANT(RESOLUTION_512);
+ BIND_ENUM_CONSTANT(RESOLUTION_1024);
+ BIND_ENUM_CONSTANT(RESOLUTION_2048);
+ BIND_ENUM_CONSTANT(RESOLUTION_4096);
+ BIND_ENUM_CONSTANT(RESOLUTION_8192);
+ BIND_ENUM_CONSTANT(RESOLUTION_MAX);
+
+ BIND_ENUM_CONSTANT(UPDATE_MODE_WHEN_MOVED);
+ BIND_ENUM_CONSTANT(UPDATE_MODE_ALWAYS);
+}
+
+void GPUParticlesCollisionHeightField::set_follow_camera_push_ratio(float p_follow_camera_push_ratio) {
+ follow_camera_push_ratio = p_follow_camera_push_ratio;
+}
+
+float GPUParticlesCollisionHeightField::get_follow_camera_push_ratio() const {
+ return follow_camera_push_ratio;
+}
+
+void GPUParticlesCollisionHeightField::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
+ update_gizmo();
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+}
+
+Vector3 GPUParticlesCollisionHeightField::get_extents() const {
+ return extents;
+}
+
+void GPUParticlesCollisionHeightField::set_resolution(Resolution p_resolution) {
+ resolution = p_resolution;
+ RS::get_singleton()->particles_collision_set_height_field_resolution(_get_collision(), RS::ParticlesCollisionHeightfieldResolution(resolution));
+ update_gizmo();
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+}
+
+GPUParticlesCollisionHeightField::Resolution GPUParticlesCollisionHeightField::get_resolution() const {
+ return resolution;
+}
+
+void GPUParticlesCollisionHeightField::set_update_mode(UpdateMode p_update_mode) {
+ update_mode = p_update_mode;
+ set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS);
+}
+
+GPUParticlesCollisionHeightField::UpdateMode GPUParticlesCollisionHeightField::get_update_mode() const {
+ return update_mode;
+}
+
+void GPUParticlesCollisionHeightField::set_follow_camera_mode(bool p_enabled) {
+ follow_camera_mode = p_enabled;
+ set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS);
+}
+
+bool GPUParticlesCollisionHeightField::is_follow_camera_mode_enabled() const {
+ return follow_camera_mode;
+}
+
+AABB GPUParticlesCollisionHeightField::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+
+GPUParticlesCollisionHeightField::GPUParticlesCollisionHeightField() :
+ GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE) {
+}
+
+GPUParticlesCollisionHeightField::~GPUParticlesCollisionHeightField() {
+}
+
+////////////////////////////
+////////////////////////////
+
+void GPUParticlesAttractor3D::set_cull_mask(uint32_t p_cull_mask) {
+ cull_mask = p_cull_mask;
+ RS::get_singleton()->particles_collision_set_cull_mask(collision, p_cull_mask);
+}
+
+uint32_t GPUParticlesAttractor3D::get_cull_mask() const {
+ return cull_mask;
+}
+
+void GPUParticlesAttractor3D::set_strength(float p_strength) {
+ strength = p_strength;
+ RS::get_singleton()->particles_collision_set_attractor_strength(collision, p_strength);
+}
+
+float GPUParticlesAttractor3D::get_strength() const {
+ return strength;
+}
+
+void GPUParticlesAttractor3D::set_attenuation(float p_attenuation) {
+ attenuation = p_attenuation;
+ RS::get_singleton()->particles_collision_set_attractor_attenuation(collision, p_attenuation);
+}
+
+float GPUParticlesAttractor3D::get_attenuation() const {
+ return attenuation;
+}
+
+void GPUParticlesAttractor3D::set_directionality(float p_directionality) {
+ directionality = p_directionality;
+ RS::get_singleton()->particles_collision_set_attractor_directionality(collision, p_directionality);
+ update_gizmo();
+}
+
+float GPUParticlesAttractor3D::get_directionality() const {
+ return directionality;
+}
+
+void GPUParticlesAttractor3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &GPUParticlesAttractor3D::set_cull_mask);
+ ClassDB::bind_method(D_METHOD("get_cull_mask"), &GPUParticlesAttractor3D::get_cull_mask);
+
+ ClassDB::bind_method(D_METHOD("set_strength", "strength"), &GPUParticlesAttractor3D::set_strength);
+ ClassDB::bind_method(D_METHOD("get_strength"), &GPUParticlesAttractor3D::get_strength);
+
+ ClassDB::bind_method(D_METHOD("set_attenuation", "attenuation"), &GPUParticlesAttractor3D::set_attenuation);
+ ClassDB::bind_method(D_METHOD("get_attenuation"), &GPUParticlesAttractor3D::get_attenuation);
+
+ ClassDB::bind_method(D_METHOD("set_directionality", "amount"), &GPUParticlesAttractor3D::set_directionality);
+ ClassDB::bind_method(D_METHOD("get_directionality"), &GPUParticlesAttractor3D::get_directionality);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "-128,128,0.01,or_greater,or_lesser"), "set_strength", "get_strength");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation", PROPERTY_HINT_EXP_EASING, "0,8,0.01"), "set_attenuation", "get_attenuation");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "directionality", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_directionality", "get_directionality");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
+}
+
+GPUParticlesAttractor3D::GPUParticlesAttractor3D(RS::ParticlesCollisionType p_type) {
+ collision = RS::get_singleton()->particles_collision_create();
+ RS::get_singleton()->particles_collision_set_collision_type(collision, p_type);
+ set_base(collision);
+}
+GPUParticlesAttractor3D::~GPUParticlesAttractor3D() {
+ RS::get_singleton()->free(collision);
+}
+
+/////////////////////////////////
+
+void GPUParticlesAttractorSphere::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesAttractorSphere::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesAttractorSphere::get_radius);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius");
+}
+
+void GPUParticlesAttractorSphere::set_radius(float p_radius) {
+ radius = p_radius;
+ RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius);
+ update_gizmo();
+}
+
+float GPUParticlesAttractorSphere::get_radius() const {
+ return radius;
+}
+
+AABB GPUParticlesAttractorSphere::get_aabb() const {
+ return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2));
+}
+
+GPUParticlesAttractorSphere::GPUParticlesAttractorSphere() :
+ GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT) {
+}
+
+GPUParticlesAttractorSphere::~GPUParticlesAttractorSphere() {
+}
+
+///////////////////////////
+
+void GPUParticlesAttractorBox::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesAttractorBox::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesAttractorBox::get_extents);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
+}
+
+void GPUParticlesAttractorBox::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
+ update_gizmo();
+}
+
+Vector3 GPUParticlesAttractorBox::get_extents() const {
+ return extents;
+}
+
+AABB GPUParticlesAttractorBox::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+
+GPUParticlesAttractorBox::GPUParticlesAttractorBox() :
+ GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_BOX_ATTRACT) {
+}
+
+GPUParticlesAttractorBox::~GPUParticlesAttractorBox() {
+}
+
+///////////////////////////
+
+void GPUParticlesAttractorVectorField::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesAttractorVectorField::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesAttractorVectorField::get_extents);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesAttractorVectorField::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesAttractorVectorField::get_texture);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture");
+}
+
+void GPUParticlesAttractorVectorField::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
+ update_gizmo();
+}
+
+Vector3 GPUParticlesAttractorVectorField::get_extents() const {
+ return extents;
+}
+
+void GPUParticlesAttractorVectorField::set_texture(const Ref<Texture3D> &p_texture) {
+ texture = p_texture;
+ RID tex = texture.is_valid() ? texture->get_rid() : RID();
+ RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex);
+}
+
+Ref<Texture3D> GPUParticlesAttractorVectorField::get_texture() const {
+ return texture;
+}
+
+AABB GPUParticlesAttractorVectorField::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+
+GPUParticlesAttractorVectorField::GPUParticlesAttractorVectorField() :
+ GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT) {
+}
+
+GPUParticlesAttractorVectorField::~GPUParticlesAttractorVectorField() {
+}
diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h
new file mode 100644
index 0000000000..9b644ade6b
--- /dev/null
+++ b/scene/3d/gpu_particles_collision_3d.h
@@ -0,0 +1,342 @@
+/*************************************************************************/
+/* gpu_particles_collision_3d.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 GPU_PARTICLES_COLLISION_3D_H
+#define GPU_PARTICLES_COLLISION_3D_H
+
+#include "core/templates/local_vector.h"
+#include "core/templates/rid.h"
+#include "scene/3d/visual_instance_3d.h"
+#include "scene/resources/material.h"
+
+class GPUParticlesCollision3D : public VisualInstance3D {
+ GDCLASS(GPUParticlesCollision3D, VisualInstance3D);
+
+ uint32_t cull_mask = 0xFFFFFFFF;
+ RID collision;
+
+protected:
+ _FORCE_INLINE_ RID _get_collision() { return collision; }
+ static void _bind_methods();
+
+ GPUParticlesCollision3D(RS::ParticlesCollisionType p_type);
+
+public:
+ void set_cull_mask(uint32_t p_cull_mask);
+ uint32_t get_cull_mask() const;
+
+ virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); }
+
+ ~GPUParticlesCollision3D();
+};
+
+class GPUParticlesCollisionSphere : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionSphere, GPUParticlesCollision3D);
+
+ float radius = 1.0;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_radius(float p_radius);
+ float get_radius() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesCollisionSphere();
+ ~GPUParticlesCollisionSphere();
+};
+
+class GPUParticlesCollisionBox : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionBox, GPUParticlesCollision3D);
+
+ Vector3 extents = Vector3(1, 1, 1);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesCollisionBox();
+ ~GPUParticlesCollisionBox();
+};
+
+class GPUParticlesCollisionSDF : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionSDF, GPUParticlesCollision3D);
+
+public:
+ enum Resolution {
+ RESOLUTION_16,
+ RESOLUTION_32,
+ RESOLUTION_64,
+ RESOLUTION_128,
+ RESOLUTION_256,
+ RESOLUTION_512,
+ RESOLUTION_MAX,
+ };
+
+ typedef void (*BakeBeginFunc)(int);
+ typedef void (*BakeStepFunc)(int, const String &);
+ typedef void (*BakeEndFunc)();
+
+private:
+ Vector3 extents = Vector3(1, 1, 1);
+ Resolution resolution = RESOLUTION_64;
+ Ref<Texture3D> texture;
+ float thickness = 1.0;
+
+ struct PlotMesh {
+ Ref<Mesh> mesh;
+ Transform local_xform;
+ };
+
+ void _find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes);
+
+ struct BVH {
+ enum {
+ LEAF_BIT = 1 << 30,
+ LEAF_MASK = LEAF_BIT - 1
+ };
+ AABB bounds;
+ uint32_t children[2];
+ };
+
+ struct FacePos {
+ Vector3 center;
+ uint32_t index;
+ };
+
+ struct FaceSort {
+ uint32_t axis;
+ bool operator()(const FacePos &p_left, const FacePos &p_right) const {
+ return p_left.center[axis] < p_right.center[axis];
+ }
+ };
+
+ uint32_t _create_bvh(LocalVector<BVH> &bvh_tree, FacePos *p_faces, uint32_t p_face_count, const Face3 *p_triangles, float p_thickness);
+
+ struct ComputeSDFParams {
+ float *cells;
+ Vector3i size;
+ float cell_size;
+ Vector3 cell_offset;
+ const BVH *bvh;
+ const Face3 *triangles;
+ float thickness;
+ };
+
+ void _find_closest_distance(const Vector3 &p_pos, const BVH *bvh, uint32_t p_bvh_cell, const Face3 *triangles, float thickness, float &closest_distance);
+ void _compute_sdf_z(uint32_t p_z, ComputeSDFParams *params);
+ void _compute_sdf(ComputeSDFParams *params);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_thickness(float p_thickness);
+ float get_thickness() const;
+
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ void set_resolution(Resolution p_resolution);
+ Resolution get_resolution() const;
+
+ void set_texture(const Ref<Texture3D> &p_texture);
+ Ref<Texture3D> get_texture() const;
+
+ Vector3i get_estimated_cell_size() const;
+ Ref<Image> bake();
+
+ virtual AABB get_aabb() const override;
+
+ static BakeBeginFunc bake_begin_function;
+ static BakeStepFunc bake_step_function;
+ static BakeEndFunc bake_end_function;
+
+ GPUParticlesCollisionSDF();
+ ~GPUParticlesCollisionSDF();
+};
+
+VARIANT_ENUM_CAST(GPUParticlesCollisionSDF::Resolution)
+
+class GPUParticlesCollisionHeightField : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionHeightField, GPUParticlesCollision3D);
+
+public:
+ enum Resolution {
+ RESOLUTION_256,
+ RESOLUTION_512,
+ RESOLUTION_1024,
+ RESOLUTION_2048,
+ RESOLUTION_4096,
+ RESOLUTION_8192,
+ RESOLUTION_MAX,
+ };
+
+ enum UpdateMode {
+ UPDATE_MODE_WHEN_MOVED,
+ UPDATE_MODE_ALWAYS,
+ };
+
+private:
+ Vector3 extents = Vector3(1, 1, 1);
+ Resolution resolution = RESOLUTION_1024;
+ bool follow_camera_mode = false;
+ float follow_camera_push_ratio = 0.1;
+
+ UpdateMode update_mode = UPDATE_MODE_WHEN_MOVED;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ void set_resolution(Resolution p_resolution);
+ Resolution get_resolution() const;
+
+ void set_update_mode(UpdateMode p_update_mode);
+ UpdateMode get_update_mode() const;
+
+ void set_follow_camera_mode(bool p_enabled);
+ bool is_follow_camera_mode_enabled() const;
+
+ void set_follow_camera_push_ratio(float p_ratio);
+ float get_follow_camera_push_ratio() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesCollisionHeightField();
+ ~GPUParticlesCollisionHeightField();
+};
+
+VARIANT_ENUM_CAST(GPUParticlesCollisionHeightField::Resolution)
+VARIANT_ENUM_CAST(GPUParticlesCollisionHeightField::UpdateMode)
+
+class GPUParticlesAttractor3D : public VisualInstance3D {
+ GDCLASS(GPUParticlesAttractor3D, VisualInstance3D);
+
+ uint32_t cull_mask = 0xFFFFFFFF;
+ RID collision;
+ float strength = 1.0;
+ float attenuation = 1.0;
+ float directionality = 0.0;
+
+protected:
+ _FORCE_INLINE_ RID _get_collision() { return collision; }
+ static void _bind_methods();
+
+ GPUParticlesAttractor3D(RS::ParticlesCollisionType p_type);
+
+public:
+ void set_cull_mask(uint32_t p_cull_mask);
+ uint32_t get_cull_mask() const;
+
+ void set_strength(float p_strength);
+ float get_strength() const;
+
+ void set_attenuation(float p_attenuation);
+ float get_attenuation() const;
+
+ void set_directionality(float p_directionality);
+ float get_directionality() const;
+
+ virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); }
+
+ ~GPUParticlesAttractor3D();
+};
+
+class GPUParticlesAttractorSphere : public GPUParticlesAttractor3D {
+ GDCLASS(GPUParticlesAttractorSphere, GPUParticlesAttractor3D);
+
+ float radius = 1.0;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_radius(float p_radius);
+ float get_radius() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesAttractorSphere();
+ ~GPUParticlesAttractorSphere();
+};
+
+class GPUParticlesAttractorBox : public GPUParticlesAttractor3D {
+ GDCLASS(GPUParticlesAttractorBox, GPUParticlesAttractor3D);
+
+ Vector3 extents = Vector3(1, 1, 1);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesAttractorBox();
+ ~GPUParticlesAttractorBox();
+};
+
+class GPUParticlesAttractorVectorField : public GPUParticlesAttractor3D {
+ GDCLASS(GPUParticlesAttractorVectorField, GPUParticlesAttractor3D);
+
+ Vector3 extents = Vector3(1, 1, 1);
+ Ref<Texture3D> texture;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ void set_texture(const Ref<Texture3D> &p_texture);
+ Ref<Texture3D> get_texture() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesAttractorVectorField();
+ ~GPUParticlesAttractorVectorField();
+};
+
+#endif // GPU_PARTICLES_COLLISION_3D_H
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index cc1622722e..3f816535f5 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -30,8 +30,8 @@
#include "light_3d.h"
-#include "core/engine.h"
-#include "core/project_settings.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "scene/resources/surface_tool.h"
bool Light3D::_can_gizmo_scale() const {
@@ -270,6 +270,7 @@ void Light3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_normal_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_reverse_cull_face"), "set_shadow_reverse_cull_face", "get_shadow_reverse_cull_face");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_fog_fade", PROPERTY_HINT_RANGE, "0.01,10,0.01"), "set_param", "get_param", PARAM_SHADOW_VOLUMETRIC_FOG_FADE);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0.1,8,0.01"), "set_param", "get_param", PARAM_SHADOW_BLUR);
ADD_GROUP("Editor", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only");
@@ -292,6 +293,7 @@ void Light3D::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS);
BIND_ENUM_CONSTANT(PARAM_SHADOW_PANCAKE_SIZE);
BIND_ENUM_CONSTANT(PARAM_SHADOW_BLUR);
+ BIND_ENUM_CONSTANT(PARAM_SHADOW_VOLUMETRIC_FOG_FADE);
BIND_ENUM_CONSTANT(PARAM_TRANSMITTANCE_BIAS);
BIND_ENUM_CONSTANT(PARAM_MAX);
@@ -345,6 +347,7 @@ Light3D::Light3D(RenderingServer::LightType p_type) {
set_param(PARAM_SHADOW_BIAS, 0.02);
set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0);
set_param(PARAM_TRANSMITTANCE_BIAS, 0.05);
+ set_param(PARAM_SHADOW_VOLUMETRIC_FOG_FADE, 1.0);
set_param(PARAM_SHADOW_FADE_START, 1);
set_disable_scale(true);
}
@@ -391,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);
@@ -401,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);
@@ -412,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);
@@ -445,7 +462,7 @@ String OmniLight3D::get_configuration_warning() const {
String warning = Light3D::get_configuration_warning();
if (!has_shadow() && get_projector().is_valid()) {
- if (warning != String()) {
+ if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("Projector texture only works with shadows active.");
@@ -479,15 +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 != String()) {
+ if (!warning.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 != String()) {
+ if (!warning.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 f106151472..08287a3313 100644
--- a/scene/3d/light_3d.h
+++ b/scene/3d/light_3d.h
@@ -58,6 +58,7 @@ public:
PARAM_SHADOW_BIAS = RS::LIGHT_PARAM_SHADOW_BIAS,
PARAM_SHADOW_PANCAKE_SIZE = RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE,
PARAM_SHADOW_BLUR = RS::LIGHT_PARAM_SHADOW_BLUR,
+ PARAM_SHADOW_VOLUMETRIC_FOG_FADE = RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE,
PARAM_TRANSMITTANCE_BIAS = RS::LIGHT_PARAM_TRANSMITTANCE_BIAS,
PARAM_MAX = RS::LIGHT_PARAM_MAX
};
@@ -157,6 +158,7 @@ private:
bool blend_splits;
ShadowMode shadow_mode;
ShadowDepthRange shadow_depth_range;
+ bool sky_only = false;
protected:
static void _bind_methods();
@@ -171,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/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index e179261002..f9f8f276a3 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -30,7 +30,7 @@
#include "navigation_agent_3d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "scene/3d/navigation_3d.h"
#include "servers/navigation_server_3d.h"
@@ -287,11 +287,16 @@ void NavigationAgent3D::_avoidance_done(Vector3 p_new_velocity) {
}
String NavigationAgent3D::get_configuration_warning() const {
+ String warning = Node::get_configuration_warning();
+
if (!Object::cast_to<Node3D>(get_parent())) {
- return TTR("The NavigationAgent3D can be used only under a spatial node.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("The NavigationAgent3D can be used only under a spatial node.");
}
- return String();
+ return warning;
}
void NavigationAgent3D::update_navigation() {
diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h
index e80367ea50..dcfd302561 100644
--- a/scene/3d/navigation_agent_3d.h
+++ b/scene/3d/navigation_agent_3d.h
@@ -31,7 +31,7 @@
#ifndef NAVIGATION_AGENT_H
#define NAVIGATION_AGENT_H
-#include "core/vector.h"
+#include "core/templates/vector.h"
#include "scene/main/node.h"
class Node3D;
diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp
index 69fd5b02fc..adbff06ed6 100644
--- a/scene/3d/navigation_obstacle_3d.cpp
+++ b/scene/3d/navigation_obstacle_3d.cpp
@@ -113,11 +113,16 @@ Node *NavigationObstacle3D::get_navigation_node() const {
}
String NavigationObstacle3D::get_configuration_warning() const {
+ String warning = Node::get_configuration_warning();
+
if (!Object::cast_to<Node3D>(get_parent())) {
- return TTR("The NavigationObstacle3D only serves to provide collision avoidance to a spatial object.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("The NavigationObstacle3D only serves to provide collision avoidance to a spatial object.");
}
- return String();
+ return warning;
}
void NavigationObstacle3D::update_agent_shape() {
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 15ed448a65..2ae01c7ab8 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -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 {
@@ -189,19 +190,28 @@ String NavigationRegion3D::get_configuration_warning() const {
return String();
}
+ String warning = Node3D::get_configuration_warning();
+
if (!navmesh.is_valid()) {
- return TTR("A NavigationMesh resource must be set or created for this node to work.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("A NavigationMesh resource must be set or created for this node to work.");
}
+
const Node3D *c = this;
while (c) {
if (Object::cast_to<Navigation3D>(c)) {
- return String();
+ return warning;
}
c = Object::cast_to<Node3D>(c->get_parent());
}
- return TTR("NavigationRegion3D must be a child or grandchild to a Navigation3D node. It only provides navigation data.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ return warning + TTR("NavigationRegion3D must be a child or grandchild to a Navigation3D node. It only provides navigation data.");
}
void NavigationRegion3D::_bind_methods() {
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index bd9e4f5bde..e8005f38ed 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -30,8 +30,8 @@
#include "node_3d.h"
-#include "core/engine.h"
-#include "core/message_queue.h"
+#include "core/config/engine.h"
+#include "core/object/message_queue.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
#include "scene/scene_string_names.h"
@@ -103,8 +103,8 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) {
data.children_lock++;
for (List<Node3D *>::Element *E = data.children.front(); E; E = E->next()) {
- if (E->get()->data.toplevel_active) {
- continue; //don't propagate to a toplevel
+ if (E->get()->data.top_level_active) {
+ continue; //don't propagate to a top_level
}
E->get()->_propagate_transform_changed(p_origin);
}
@@ -136,12 +136,12 @@ void Node3D::_notification(int p_what) {
data.C = nullptr;
}
- if (data.toplevel && !Engine::get_singleton()->is_editor_hint()) {
+ if (data.top_level && !Engine::get_singleton()->is_editor_hint()) {
if (data.parent) {
data.local_transform = data.parent->get_global_transform() * get_transform();
data.dirty = DIRTY_VECTORS; //global is always dirty upon entering a scene
}
- data.toplevel_active = true;
+ data.top_level_active = true;
}
data.dirty |= DIRTY_GLOBAL; //global is always dirty upon entering a scene
@@ -160,7 +160,7 @@ void Node3D::_notification(int p_what) {
}
data.parent = nullptr;
data.C = nullptr;
- data.toplevel_active = false;
+ data.top_level_active = false;
} break;
case NOTIFICATION_ENTER_WORLD: {
data.inside_world = true;
@@ -238,7 +238,7 @@ void Node3D::set_transform(const Transform &p_transform) {
void Node3D::set_global_transform(const Transform &p_transform) {
Transform xform =
- (data.parent && !data.toplevel_active) ?
+ (data.parent && !data.top_level_active) ?
data.parent->get_global_transform().affine_inverse() * p_transform :
p_transform;
@@ -261,7 +261,7 @@ Transform Node3D::get_global_transform() const {
_update_local_transform();
}
- if (data.parent && !data.toplevel_active) {
+ if (data.parent && !data.top_level_active) {
data.global_transform = data.parent->get_global_transform() * data.local_transform;
} else {
data.global_transform = data.local_transform;
@@ -462,8 +462,8 @@ bool Node3D::is_scale_disabled() const {
return data.disable_scale;
}
-void Node3D::set_as_toplevel(bool p_enabled) {
- if (data.toplevel == p_enabled) {
+void Node3D::set_as_top_level(bool p_enabled) {
+ if (data.top_level == p_enabled) {
return;
}
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {
@@ -473,16 +473,16 @@ void Node3D::set_as_toplevel(bool p_enabled) {
set_transform(data.parent->get_global_transform().affine_inverse() * get_global_transform());
}
- data.toplevel = p_enabled;
- data.toplevel_active = p_enabled;
+ data.top_level = p_enabled;
+ data.top_level_active = p_enabled;
} else {
- data.toplevel = p_enabled;
+ data.top_level = p_enabled;
}
}
-bool Node3D::is_set_as_toplevel() const {
- return data.toplevel;
+bool Node3D::is_set_as_top_level() const {
+ return data.top_level;
}
Ref<World3D> Node3D::get_world_3d() const {
@@ -715,8 +715,8 @@ void Node3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform);
ClassDB::bind_method(D_METHOD("get_parent_spatial"), &Node3D::get_parent_spatial);
ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Node3D::set_ignore_transform_notification);
- ClassDB::bind_method(D_METHOD("set_as_toplevel", "enable"), &Node3D::set_as_toplevel);
- ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &Node3D::is_set_as_toplevel);
+ ClassDB::bind_method(D_METHOD("set_as_top_level", "enable"), &Node3D::set_as_top_level);
+ ClassDB::bind_method(D_METHOD("is_set_as_top_level"), &Node3D::is_set_as_top_level);
ClassDB::bind_method(D_METHOD("set_disable_scale", "disable"), &Node3D::set_disable_scale);
ClassDB::bind_method(D_METHOD("is_scale_disabled"), &Node3D::is_scale_disabled);
ClassDB::bind_method(D_METHOD("get_world_3d"), &Node3D::get_world_3d);
@@ -773,6 +773,7 @@ void Node3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_NONE, "", 0), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level");
ADD_GROUP("Matrix", "");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform");
ADD_GROUP("Visibility", "");
@@ -788,8 +789,8 @@ Node3D::Node3D() :
data.children_lock = 0;
data.ignore_notification = false;
- data.toplevel = false;
- data.toplevel_active = false;
+ data.top_level = false;
+ data.top_level_active = false;
data.scale = Vector3(1, 1, 1);
data.viewport = nullptr;
data.inside_world = false;
diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h
index 327d4671e9..5fb421c930 100644
--- a/scene/3d/node_3d.h
+++ b/scene/3d/node_3d.h
@@ -71,8 +71,8 @@ class Node3D : public Node {
Viewport *viewport;
- bool toplevel_active;
- bool toplevel;
+ bool top_level_active;
+ bool top_level;
bool inside_world;
int children_lock;
@@ -111,7 +111,6 @@ protected:
public:
enum {
-
NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED,
NOTIFICATION_ENTER_WORLD = 41,
NOTIFICATION_EXIT_WORLD = 42,
@@ -144,8 +143,8 @@ public:
virtual Transform get_local_gizmo_transform() const;
#endif
- void set_as_toplevel(bool p_enabled);
- bool is_set_as_toplevel() const;
+ void set_as_top_level(bool p_enabled);
+ bool is_set_as_top_level() const;
void set_disable_scale(bool p_enabled);
bool is_scale_disabled() const;
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index bf69a8598d..f25a64c567 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -30,7 +30,7 @@
#include "path_3d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "scene/scene_string_names.h"
void Path3D::_notification(int p_what) {
@@ -250,16 +250,24 @@ String PathFollow3D::get_configuration_warning() const {
return String();
}
+ String warning = Node3D::get_configuration_warning();
+
if (!Object::cast_to<Path3D>(get_parent())) {
- return TTR("PathFollow3D only works when set as a child of a Path3D node.");
+ if (!warning.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) {
- return TTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource.");
}
}
- return String();
+ return warning;
}
void PathFollow3D::_bind_methods() {
diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h
index 1aa9f7ffd2..1b0f5fa4e0 100644
--- a/scene/3d/path_3d.h
+++ b/scene/3d/path_3d.h
@@ -57,7 +57,6 @@ class PathFollow3D : public Node3D {
public:
enum RotationMode {
-
ROTATION_NONE,
ROTATION_Y,
ROTATION_XY,
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index fc021e5532..15cd2238e1 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -30,12 +30,11 @@
#include "physics_body_3d.h"
+#include "core/config/engine.h"
#include "core/core_string_names.h"
-#include "core/engine.h"
-#include "core/list.h"
-#include "core/method_bind_ext.gen.inc"
-#include "core/object.h"
-#include "core/rid.h"
+#include "core/object/class_db.h"
+#include "core/templates/list.h"
+#include "core/templates/rid.h"
#include "scene/3d/collision_shape_3d.h"
#include "scene/scene_string_names.h"
#include "servers/navigation_server_3d.h"
@@ -130,15 +129,6 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) {
PhysicsServer3D::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid());
}
-void PhysicsBody3D::_set_layers(uint32_t p_mask) {
- set_collision_layer(p_mask);
- set_collision_mask(p_mask);
-}
-
-uint32_t PhysicsBody3D::_get_layers() const {
- return get_collision_layer();
-}
-
void PhysicsBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &PhysicsBody3D::set_collision_layer);
ClassDB::bind_method(D_METHOD("get_collision_layer"), &PhysicsBody3D::get_collision_layer);
@@ -152,9 +142,6 @@ void PhysicsBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &PhysicsBody3D::set_collision_layer_bit);
ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &PhysicsBody3D::get_collision_layer_bit);
- ClassDB::bind_method(D_METHOD("_set_layers", "mask"), &PhysicsBody3D::_set_layers);
- ClassDB::bind_method(D_METHOD("_get_layers"), &PhysicsBody3D::_get_layers);
-
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
@@ -491,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);
}
@@ -500,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))) {
@@ -737,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 != String()) {
+ if (!warning.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.");
@@ -753,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);
@@ -815,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");
@@ -2134,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);
@@ -2167,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");
@@ -2469,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);
@@ -2606,7 +2567,7 @@ void PhysicalBone3D::_start_physics_simulation() {
PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer());
PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask());
PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed");
- set_as_toplevel(true);
+ set_as_top_level(true);
_internal_simulate_physics = true;
}
@@ -2626,7 +2587,7 @@ void PhysicalBone3D::_stop_physics_simulation() {
if (_internal_simulate_physics) {
PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), nullptr, "");
parent_skeleton->set_bone_global_pose_override(bone_id, Transform(), 0.0, false);
- set_as_toplevel(false);
+ set_as_top_level(false);
_internal_simulate_physics = false;
}
}
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index 9830a55183..58eebc90ce 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -31,7 +31,7 @@
#ifndef PHYSICS_BODY_3D_H
#define PHYSICS_BODY_3D_H
-#include "core/vset.h"
+#include "core/templates/vset.h"
#include "scene/3d/collision_object_3d.h"
#include "scene/resources/physics_material.h"
#include "servers/physics_server_3d.h"
@@ -43,9 +43,6 @@ class PhysicsBody3D : public CollisionObject3D {
uint32_t collision_layer;
uint32_t collision_mask;
- void _set_layers(uint32_t p_mask);
- uint32_t _get_layers() const;
-
protected:
static void _bind_methods();
PhysicsBody3D(PhysicsServer3D::BodyMode p_mode);
@@ -188,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;
@@ -575,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 af4d6ae152..ab9cdb9fd8 100644
--- a/scene/3d/physics_joint_3d.cpp
+++ b/scene/3d/physics_joint_3d.cpp
@@ -43,6 +43,7 @@ void Joint3D::_update_joint(bool p_only_free) {
}
if (p_only_free || !is_inside_tree()) {
+ warning = String();
return;
}
@@ -52,20 +53,47 @@ void Joint3D::_update_joint(bool p_only_free) {
PhysicsBody3D *body_a = Object::cast_to<PhysicsBody3D>(node_a);
PhysicsBody3D *body_b = Object::cast_to<PhysicsBody3D>(node_b);
- if (!body_a && body_b) {
- SWAP(body_a, body_b);
+ if (node_a && !body_a && node_b && !body_b) {
+ warning = TTR("Node A and Node B must be PhysicsBody3Ds");
+ update_configuration_warning();
+ return;
}
- if (!body_a) {
+ if (node_a && !body_a) {
+ warning = TTR("Node A must be a PhysicsBody3D");
+ update_configuration_warning();
return;
}
- joint = _configure_joint(body_a, body_b);
+ if (node_b && !body_b) {
+ warning = TTR("Node B must be a PhysicsBody3D");
+ update_configuration_warning();
+ return;
+ }
- if (!joint.is_valid()) {
+ if (!body_a && !body_b) {
+ warning = TTR("Joint is not connected to any PhysicsBody3Ds");
+ update_configuration_warning();
return;
}
+ if (body_a == body_b) {
+ warning = TTR("Node A and Node B must be different PhysicsBody3Ds");
+ update_configuration_warning();
+ return;
+ }
+
+ if (!body_a) {
+ SWAP(body_a, body_b);
+ }
+
+ warning = String();
+ update_configuration_warning();
+
+ joint = _configure_joint(body_a, body_b);
+
+ 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();
@@ -137,6 +165,19 @@ bool Joint3D::get_exclude_nodes_from_collision() const {
return exclude_from_collision;
}
+String Joint3D::get_configuration_warning() const {
+ String node_warning = Node3D::get_configuration_warning();
+
+ if (!warning.empty()) {
+ if (!node_warning.empty()) {
+ node_warning += "\n\n";
+ }
+ node_warning += warning;
+ }
+
+ return node_warning;
+}
+
void Joint3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_node_a", "node"), &Joint3D::set_node_a);
ClassDB::bind_method(D_METHOD("get_node_a"), &Joint3D::get_node_a);
@@ -150,8 +191,8 @@ void Joint3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude_nodes_from_collision", "enable"), &Joint3D::set_exclude_nodes_from_collision);
ClassDB::bind_method(D_METHOD("get_exclude_nodes_from_collision"), &Joint3D::get_exclude_nodes_from_collision);
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject3D"), "set_node_a", "get_node_a");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject3D"), "set_node_b", "get_node_b");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody3D"), "set_node_a", "get_node_a");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody3D"), "set_node_b", "get_node_b");
ADD_PROPERTY(PropertyInfo(Variant::INT, "solver/priority", PROPERTY_HINT_RANGE, "1,8,1"), "set_solver_priority", "get_solver_priority");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision/exclude_nodes"), "set_exclude_nodes_from_collision", "get_exclude_nodes_from_collision");
diff --git a/scene/3d/physics_joint_3d.h b/scene/3d/physics_joint_3d.h
index 8e2de82527..a65f6db3bf 100644
--- a/scene/3d/physics_joint_3d.h
+++ b/scene/3d/physics_joint_3d.h
@@ -46,6 +46,7 @@ class Joint3D : public Node3D {
int solver_priority;
bool exclude_from_collision;
+ String warning;
protected:
void _update_joint(bool p_only_free = false);
@@ -57,6 +58,8 @@ protected:
static void _bind_methods();
public:
+ virtual String get_configuration_warning() const override;
+
void set_node_a(const NodePath &p_node_a);
NodePath get_node_a() const;
@@ -203,7 +206,6 @@ class ConeTwistJoint3D : public Joint3D {
public:
enum Param {
-
PARAM_SWING_SPAN,
PARAM_TWIST_SPAN,
PARAM_BIAS,
@@ -237,7 +239,6 @@ class Generic6DOFJoint3D : public Joint3D {
public:
enum Param {
-
PARAM_LINEAR_LOWER_LIMIT = PhysicsServer3D::G6DOF_JOINT_LINEAR_LOWER_LIMIT,
PARAM_LINEAR_UPPER_LIMIT = PhysicsServer3D::G6DOF_JOINT_LINEAR_UPPER_LIMIT,
PARAM_LINEAR_LIMIT_SOFTNESS = PhysicsServer3D::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS,
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index 2a922e3cda..811e8a331b 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -31,12 +31,12 @@
#include "ray_cast_3d.h"
#include "collision_object_3d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "mesh_instance_3d.h"
#include "servers/physics_server_3d.h"
-void RayCast3D::set_cast_to(const Vector3 &p_point) {
- cast_to = p_point;
+void RayCast3D::set_target_position(const Vector3 &p_point) {
+ target_position = p_point;
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint())) {
update_gizmo();
}
@@ -45,8 +45,8 @@ void RayCast3D::set_cast_to(const Vector3 &p_point) {
}
}
-Vector3 RayCast3D::get_cast_to() const {
- return cast_to;
+Vector3 RayCast3D::get_target_position() const {
+ return target_position;
}
void RayCast3D::set_collision_mask(uint32_t p_mask) {
@@ -202,7 +202,7 @@ void RayCast3D::_update_raycast_state() {
Transform gt = get_global_transform();
- Vector3 to = cast_to;
+ Vector3 to = target_position;
if (to == Vector3()) {
to = Vector3(0, 0.01, 0);
}
@@ -276,8 +276,8 @@ void RayCast3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &RayCast3D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &RayCast3D::is_enabled);
- ClassDB::bind_method(D_METHOD("set_cast_to", "local_point"), &RayCast3D::set_cast_to);
- ClassDB::bind_method(D_METHOD("get_cast_to"), &RayCast3D::get_cast_to);
+ ClassDB::bind_method(D_METHOD("set_target_position", "local_point"), &RayCast3D::set_target_position);
+ ClassDB::bind_method(D_METHOD("get_target_position"), &RayCast3D::get_target_position);
ClassDB::bind_method(D_METHOD("is_colliding"), &RayCast3D::is_colliding);
ClassDB::bind_method(D_METHOD("force_raycast_update"), &RayCast3D::force_raycast_update);
@@ -312,7 +312,7 @@ void RayCast3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "cast_to"), "set_cast_to", "get_cast_to");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position"), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
ADD_GROUP("Collide With", "collide_with");
@@ -360,7 +360,7 @@ void RayCast3D::_update_debug_shape() {
Vector<Vector3> verts;
verts.push_back(Vector3());
- verts.push_back(cast_to);
+ verts.push_back(target_position);
a[Mesh::ARRAY_VERTEX] = verts;
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a);
@@ -387,7 +387,7 @@ RayCast3D::RayCast3D() {
collided = false;
against_shape = 0;
collision_mask = 1;
- cast_to = Vector3(0, -1, 0);
+ target_position = Vector3(0, -1, 0);
debug_shape = nullptr;
exclude_parent_body = true;
collide_with_areas = false;
diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h
index 8f617e5491..f4fe7ba621 100644
--- a/scene/3d/ray_cast_3d.h
+++ b/scene/3d/ray_cast_3d.h
@@ -43,7 +43,7 @@ class RayCast3D : public Node3D {
Vector3 collision_point;
Vector3 collision_normal;
- Vector3 cast_to;
+ Vector3 target_position;
Set<RID> exclude;
uint32_t collision_mask;
@@ -74,8 +74,8 @@ public:
void set_enabled(bool p_enabled);
bool is_enabled() const;
- void set_cast_to(const Vector3 &p_point);
- Vector3 get_cast_to() const;
+ void set_target_position(const Vector3 &p_point);
+ Vector3 get_target_position() const;
void set_collision_mask(uint32_t p_mask);
uint32_t get_collision_mask() const;
diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp
index 95fce6b802..358f9346f8 100644
--- a/scene/3d/remote_transform_3d.cpp
+++ b/scene/3d/remote_transform_3d.cpp
@@ -180,11 +180,16 @@ void RemoteTransform3D::force_update_cache() {
}
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))) {
- return TTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work.");
}
- return String();
+ return warning;
}
void RemoteTransform3D::_bind_methods() {
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 6723ca04b9..4425af26f9 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -30,10 +30,10 @@
#include "skeleton_3d.h"
-#include "core/engine.h"
-#include "core/message_queue.h"
-#include "core/project_settings.h"
-#include "core/type_info.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
+#include "core/object/message_queue.h"
+#include "core/variant/type_info.h"
#include "scene/3d/physics_body_3d.h"
#include "scene/resources/surface_tool.h"
#include "scene/scene_string_names.h"
@@ -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 a21891a32e..c54f89d3ce 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -31,7 +31,7 @@
#ifndef SKELETON_3D_H
#define SKELETON_3D_H
-#include "core/rid.h"
+#include "core/templates/rid.h"
#include "scene/3d/node_3d.h"
#include "scene/resources/skin.h"
@@ -149,7 +149,6 @@ protected:
public:
enum {
-
NOTIFICATION_UPDATE_SKELETON = 50
};
diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h
index 80acc3e937..6c1db6dd33 100644
--- a/scene/3d/skeleton_ik_3d.h
+++ b/scene/3d/skeleton_ik_3d.h
@@ -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 a267c57f5e..d811b2e852 100644
--- a/scene/3d/soft_body_3d.cpp
+++ b/scene/3d/soft_body_3d.cpp
@@ -30,10 +30,10 @@
#include "soft_body_3d.h"
-#include "core/list.h"
-#include "core/object.h"
+#include "core/object/class_db.h"
#include "core/os/os.h"
-#include "core/rid.h"
+#include "core/templates/list.h"
+#include "core/templates/rid.h"
#include "scene/3d/collision_object_3d.h"
#include "scene/3d/physics_body_3d.h"
#include "scene/3d/skeleton_3d.h"
@@ -106,7 +106,7 @@ SoftBody3D::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) {
offset = obj_tocopy.offset;
}
-SoftBody3D::PinnedPoint SoftBody3D::PinnedPoint::operator=(const PinnedPoint &obj) {
+SoftBody3D::PinnedPoint &SoftBody3D::PinnedPoint::operator=(const PinnedPoint &obj) {
point_index = obj.point_index;
spatial_attachment_path = obj.spatial_attachment_path;
spatial_attachment = obj.spatial_attachment;
@@ -282,7 +282,7 @@ void SoftBody3D::_notification(int p_what) {
set_notify_transform(false);
// Required to be top level with Transform at center of world in order to modify RenderingServer only to support custom Transform
- set_as_toplevel(true);
+ set_as_top_level(true);
set_transform(Transform());
set_notify_transform(true);
@@ -425,7 +425,7 @@ void SoftBody3D::_draw_soft_mesh() {
/// Necessary in order to render the mesh correctly (Soft body nodes are in global space)
simulation_started = true;
- call_deferred("set_as_toplevel", true);
+ call_deferred("set_as_top_level", true);
call_deferred("set_transform", Transform());
}
@@ -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;
@@ -725,7 +724,7 @@ void SoftBody3D::_update_cache_pin_points_datas() {
w[i].spatial_attachment = Object::cast_to<Node3D>(get_node(w[i].spatial_attachment_path));
}
if (!w[i].spatial_attachment) {
- ERR_PRINT("Node3D node not defined in the pinned point, Softbody undefined behaviour!");
+ ERR_PRINT("Node3D node not defined in the pinned point, this is undefined behavior for SoftBody3D!");
}
}
}
diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_body_3d.h
index 85cfb81637..c59a0b3aa3 100644
--- a/scene/3d/soft_body_3d.h
+++ b/scene/3d/soft_body_3d.h
@@ -74,7 +74,7 @@ public:
PinnedPoint();
PinnedPoint(const PinnedPoint &obj_tocopy);
- PinnedPoint operator=(const PinnedPoint &obj);
+ PinnedPoint &operator=(const PinnedPoint &obj);
};
private:
diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp
index 9775ecc6c6..287d760db0 100644
--- a/scene/3d/spring_arm_3d.cpp
+++ b/scene/3d/spring_arm_3d.cpp
@@ -30,7 +30,7 @@
#include "spring_arm_3d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "scene/3d/collision_object_3d.h"
#include "scene/resources/sphere_shape_3d.h"
#include "servers/physics_server_3d.h"
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 3b76cb6499..1d20a9cd3b 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -204,7 +204,6 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const {
float pixel_size = get_pixel_size();
Vector2 vertices[4] = {
-
(final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
(final_rect.position + final_rect.size) * pixel_size,
(final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
@@ -414,7 +413,6 @@ void Sprite3D::_draw() {
float pixel_size = get_pixel_size();
Vector2 vertices[4] = {
-
(final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
(final_rect.position + final_rect.size) * pixel_size,
(final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
@@ -488,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];
@@ -667,8 +665,8 @@ void Sprite3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
ADD_GROUP("Animation", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes");
ADD_PROPERTY(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes");
ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frame_coords", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_frame_coords", "get_frame_coords");
ADD_GROUP("Region", "region_");
@@ -740,7 +738,6 @@ void AnimatedSprite3D::_draw() {
float pixel_size = get_pixel_size();
Vector2 vertices[4] = {
-
(final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
(final_rect.position + final_rect.size) * pixel_size,
(final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
@@ -818,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,11 +1071,16 @@ StringName AnimatedSprite3D::get_animation() const {
}
String AnimatedSprite3D::get_configuration_warning() const {
+ String warning = SpriteBase3D::get_configuration_warning();
+
if (frames.is_null()) {
- return TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames.");
+ if (!warning.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.");
}
- return String();
+ return warning;
}
void AnimatedSprite3D::_bind_methods() {
diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp
index 4c71ab470b..e27307e75f 100644
--- a/scene/3d/vehicle_body_3d.cpp
+++ b/scene/3d/vehicle_body_3d.cpp
@@ -103,11 +103,16 @@ void VehicleWheel3D::_notification(int p_what) {
}
String VehicleWheel3D::get_configuration_warning() const {
+ String warning = Node3D::get_configuration_warning();
+
if (!Object::cast_to<VehicleBody3D>(get_parent())) {
- return TTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D.");
+ if (!warning.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.");
}
- return String();
+ return warning;
}
void VehicleWheel3D::_update(PhysicsDirectBodyState3D *s) {
@@ -629,7 +634,6 @@ VehicleBody3D::btVehicleWheelContactPoint::btVehicleWheelContactPoint(PhysicsDir
/* TODO: Why is this code unused?
if (body1) {
-
Vector3 r0 = frictionPosWorld - body1->get_global_transform().origin;
Vector3 c0 = (r0).cross(frictionDirectionWorld);
Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0);
diff --git a/scene/3d/velocity_tracker_3d.cpp b/scene/3d/velocity_tracker_3d.cpp
index db10f3273b..eba7d44c16 100644
--- a/scene/3d/velocity_tracker_3d.cpp
+++ b/scene/3d/velocity_tracker_3d.cpp
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "velocity_tracker_3d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
void VelocityTracker3D::set_track_physics_step(bool p_track_physics_step) {
physics_step = p_track_physics_step;
diff --git a/scene/3d/visibility_notifier_3d.cpp b/scene/3d/visibility_notifier_3d.cpp
index a64b0df1cc..9f5c40caf4 100644
--- a/scene/3d/visibility_notifier_3d.cpp
+++ b/scene/3d/visibility_notifier_3d.cpp
@@ -30,7 +30,7 @@
#include "visibility_notifier_3d.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/physics_body_3d.h"
#include "scene/animation/animation_player.h"
diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h
index 195674f62d..51bcb411da 100644
--- a/scene/3d/visual_instance_3d.h
+++ b/scene/3d/visual_instance_3d.h
@@ -32,7 +32,7 @@
#define VISUAL_INSTANCE_H
#include "core/math/face3.h"
-#include "core/rid.h"
+#include "core/templates/rid.h"
#include "scene/3d/node_3d.h"
#include "scene/resources/material.h"
diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp
index de5496ee35..c570fc7b7c 100644
--- a/scene/3d/voxelizer.cpp
+++ b/scene/3d/voxelizer.cpp
@@ -580,7 +580,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) {
/*if (bake_light.size()) {
for(int i=0;i<6;i++) {
-
}
}*/
diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp
index 5e73e460ed..3c12d4991e 100644
--- a/scene/3d/world_environment.cpp
+++ b/scene/3d/world_environment.cpp
@@ -110,22 +110,30 @@ Ref<CameraEffects> WorldEnvironment::get_camera_effects() const {
}
String WorldEnvironment::get_configuration_warning() const {
+ String warning = Node::get_configuration_warning();
+
if (!environment.is_valid()) {
- return TTR("WorldEnvironment requires its \"Environment\" property to contain an Environment to have a visible effect.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("WorldEnvironment requires its \"Environment\" property to contain an Environment to have a visible effect.");
}
if (!is_inside_tree()) {
- return String();
+ return warning;
}
List<Node *> nodes;
get_tree()->get_nodes_in_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), &nodes);
if (nodes.size() > 1) {
- return TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes).");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes).");
}
- return String();
+ return warning;
}
void WorldEnvironment::_bind_methods() {
diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index f4a514cdd6..763461880f 100644
--- a/scene/3d/xr_nodes.cpp
+++ b/scene/3d/xr_nodes.cpp
@@ -60,13 +60,18 @@ String XRCamera3D::get_configuration_warning() const {
return String();
}
+ String warning = Camera3D::get_configuration_warning();
+
// must be child node of XROrigin3D!
XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
if (origin == nullptr) {
- return TTR("XRCamera3D must have an XROrigin3D node as its parent.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("XRCamera3D must have an XROrigin3D node as its parent.");
};
- return String();
+ return warning;
};
Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const {
@@ -206,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;
};
@@ -252,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")));
};
@@ -277,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 {
@@ -362,17 +367,25 @@ String XRController3D::get_configuration_warning() const {
return String();
}
+ String warning = Node3D::get_configuration_warning();
+
// must be child node of XROrigin!
XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
if (origin == nullptr) {
- return TTR("XRController3D must have an XROrigin3D node as its parent.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("XRController3D must have an XROrigin3D node as its parent.");
};
if (controller_id == 0) {
- return TTR("The controller ID must not be 0 or this controller won't be bound to an actual controller.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("The controller ID must not be 0 or this controller won't be bound to an actual controller.");
};
- return String();
+ return warning;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -467,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 {
@@ -479,17 +492,25 @@ String XRAnchor3D::get_configuration_warning() const {
return String();
}
+ String warning = Node3D::get_configuration_warning();
+
// must be child node of XROrigin3D!
XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
if (origin == nullptr) {
- return TTR("XRAnchor3D must have an XROrigin3D node as its parent.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("XRAnchor3D must have an XROrigin3D node as its parent.");
};
if (anchor_id == 0) {
- return TTR("The anchor ID must not be 0 or this anchor won't be bound to an actual anchor.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("The anchor ID must not be 0 or this anchor won't be bound to an actual anchor.");
};
- return String();
+ return warning;
};
Plane XRAnchor3D::get_plane() const {
@@ -512,11 +533,16 @@ String XROrigin3D::get_configuration_warning() const {
return String();
}
+ String warning = Node3D::get_configuration_warning();
+
if (tracked_camera == nullptr) {
- return TTR("XROrigin3D requires an XRCamera3D child node.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("XROrigin3D requires an XRCamera3D child node.");
}
- return String();
+ return warning;
};
void XROrigin3D::_bind_methods() {
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 17ce05f130..36552c966d 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -565,7 +565,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 +574,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_player.cpp b/scene/animation/animation_player.cpp
index 66c587e2d4..e9e17148d6 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -30,8 +30,8 @@
#include "animation_player.h"
-#include "core/engine.h"
-#include "core/message_queue.h"
+#include "core/config/engine.h"
+#include "core/object/message_queue.h"
#include "scene/scene_string_names.h"
#include "servers/audio/audio_stream.h"
@@ -762,12 +762,10 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, f
next_pos = len;
}
- // fix delta
- delta = next_pos - cd.pos;
+ bool backwards = signbit(delta); // Negative zero means playing backwards too
+ delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here)
if (&cd == &playback.current) {
- bool backwards = delta < 0;
-
if (!backwards && cd.pos <= len && next_pos == len /*&& playback.blend.empty()*/) {
//playback finished
end_reached = true;
@@ -1294,12 +1292,12 @@ bool AnimationPlayer::is_valid() const {
}
float AnimationPlayer::get_current_animation_position() const {
- ERR_FAIL_COND_V(!playback.current.from, 0);
+ ERR_FAIL_COND_V_MSG(!playback.current.from, 0, "AnimationPlayer has no current animation");
return playback.current.pos;
}
float AnimationPlayer::get_current_animation_length() const {
- ERR_FAIL_COND_V(!playback.current.from, 0);
+ ERR_FAIL_COND_V_MSG(!playback.current.from, 0, "AnimationPlayer has no current animation");
return playback.current.from->animation->get_length();
}
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index e1b9dffb1f..dbce5643c7 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -72,7 +72,6 @@ public:
private:
enum {
-
NODE_CACHE_UPDATE_MAX = 1024,
BLEND_FROM_MAX = 3
};
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 466536db10..2ef3ba87b2 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -31,8 +31,7 @@
#include "animation_tree.h"
#include "animation_blend_tree.h"
-#include "core/engine.h"
-#include "core/method_bind_ext.gen.inc"
+#include "core/config/engine.h"
#include "scene/scene_string_names.h"
#include "servers/audio/audio_stream.h"
@@ -1288,39 +1287,31 @@ String AnimationTree::get_configuration_warning() const {
String warning = Node::get_configuration_warning();
if (!root.is_valid()) {
- if (warning != String()) {
+ if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("No root AnimationNode for the graph is set.");
}
if (!has_node(animation_player)) {
- if (warning != String()) {
+ if (!warning.empty()) {
warning += "\n\n";
}
-
warning += TTR("Path to an AnimationPlayer node containing animations is not set.");
- return warning;
- }
-
- AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player));
-
- if (!player) {
- if (warning != String()) {
- warning += "\n\n";
- }
-
- warning += TTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node.");
- return warning;
- }
+ } else {
+ AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player));
- if (!player->has_node(player->get_root())) {
- if (warning != String()) {
- warning += "\n\n";
+ if (!player) {
+ if (!warning.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()) {
+ warning += "\n\n";
+ }
+ warning += TTR("The AnimationPlayer root node is not a valid node.");
}
-
- warning += TTR("The AnimationPlayer root node is not a valid node.");
- return warning;
}
return warning;
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index bd4396d680..1a2a97ada8 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -30,8 +30,6 @@
#include "tween.h"
-#include "core/method_bind_ext.gen.inc"
-
void Tween::_add_pending_command(StringName p_key, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5, const Variant &p_arg6, const Variant &p_arg7, const Variant &p_arg8, const Variant &p_arg9, const Variant &p_arg10) {
// Add a new pending command and reference it
pending_commands.push_back(PendingCommand());
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 668870c526..822fcf0b6f 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -69,7 +69,6 @@ public:
private:
enum InterpolateType {
-
INTER_PROPERTY,
INTER_METHOD,
FOLLOW_PROPERTY,
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index 48f70e88cb..14aae9c7bf 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -30,7 +30,7 @@
#include "audio_stream_player.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
void AudioStreamPlayer::_mix_to_bus(const AudioFrame *p_frames, int p_amount) {
int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
index f57c8e58db..f848fc3e68 100644
--- a/scene/debugger/scene_debugger.cpp
+++ b/scene/debugger/scene_debugger.cpp
@@ -32,7 +32,7 @@
#include "core/debugger/engine_debugger.h"
#include "core/io/marshalls.h"
-#include "core/script_language.h"
+#include "core/object/script_language.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
#include "scene/resources/packed_scene.h"
diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h
index a2bafde039..af2d8904b5 100644
--- a/scene/debugger/scene_debugger.h
+++ b/scene/debugger/scene_debugger.h
@@ -31,10 +31,10 @@
#ifndef SCENE_DEBUGGER_H
#define SCENE_DEBUGGER_H
-#include "core/array.h"
-#include "core/object.h"
-#include "core/pair.h"
-#include "core/ustring.h"
+#include "core/object/class_db.h"
+#include "core/string/ustring.h"
+#include "core/templates/pair.h"
+#include "core/variant/array.h"
class Script;
diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp
new file mode 100644
index 0000000000..672102bf7a
--- /dev/null
+++ b/scene/gui/aspect_ratio_container.cpp
@@ -0,0 +1,172 @@
+/*************************************************************************/
+/* aspect_ratio_container.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 "aspect_ratio_container.h"
+
+Size2 AspectRatioContainer::get_minimum_size() const {
+ Size2 ms;
+ 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->is_visible()) {
+ continue;
+ }
+ Size2 minsize = c->get_combined_minimum_size();
+ ms.width = MAX(ms.width, minsize.width);
+ ms.height = MAX(ms.height, minsize.height);
+ }
+ return ms;
+}
+
+void AspectRatioContainer::set_ratio(float p_ratio) {
+ ratio = p_ratio;
+ queue_sort();
+}
+
+void AspectRatioContainer::set_stretch_mode(StretchMode p_mode) {
+ stretch_mode = p_mode;
+ queue_sort();
+}
+
+void AspectRatioContainer::set_alignment_horizontal(AlignMode p_alignment_horizontal) {
+ alignment_horizontal = p_alignment_horizontal;
+ queue_sort();
+}
+
+void AspectRatioContainer::set_alignment_vertical(AlignMode p_alignment_vertical) {
+ alignment_vertical = p_alignment_vertical;
+ queue_sort();
+}
+
+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));
+ if (!c) {
+ continue;
+ }
+ if (c->is_set_as_top_level()) {
+ continue;
+ }
+ Size2 child_minsize = c->get_combined_minimum_size();
+ Size2 child_size = Size2(ratio, 1.0);
+ float scale_factor = 1.0;
+
+ switch (stretch_mode) {
+ case STRETCH_WIDTH_CONTROLS_HEIGHT: {
+ scale_factor = size.x / child_size.x;
+ } break;
+ case STRETCH_HEIGHT_CONTROLS_WIDTH: {
+ scale_factor = size.y / child_size.y;
+ } break;
+ case STRETCH_FIT: {
+ scale_factor = MIN(size.x / child_size.x, size.y / child_size.y);
+ } break;
+ case STRETCH_COVER: {
+ scale_factor = MAX(size.x / child_size.x, size.y / child_size.y);
+ } break;
+ }
+ child_size *= scale_factor;
+ child_size.x = MAX(child_size.x, child_minsize.x);
+ child_size.y = MAX(child_size.y, child_minsize.y);
+
+ float align_x = 0.5;
+ switch (alignment_horizontal) {
+ case ALIGN_BEGIN: {
+ align_x = 0.0;
+ } break;
+ case ALIGN_CENTER: {
+ align_x = 0.5;
+ } break;
+ case ALIGN_END: {
+ align_x = 1.0;
+ } break;
+ }
+ float align_y = 0.5;
+ switch (alignment_vertical) {
+ case ALIGN_BEGIN: {
+ align_y = 0.0;
+ } break;
+ case ALIGN_CENTER: {
+ align_y = 0.5;
+ } break;
+ case ALIGN_END: {
+ align_y = 1.0;
+ } break;
+ }
+ Vector2 offset = (size - child_size) * Vector2(align_x, align_y);
+
+ 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;
+ }
+}
+
+void AspectRatioContainer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_ratio", "ratio"), &AspectRatioContainer::set_ratio);
+ ClassDB::bind_method(D_METHOD("get_ratio"), &AspectRatioContainer::get_ratio);
+
+ ClassDB::bind_method(D_METHOD("set_stretch_mode", "stretch_mode"), &AspectRatioContainer::set_stretch_mode);
+ ClassDB::bind_method(D_METHOD("get_stretch_mode"), &AspectRatioContainer::get_stretch_mode);
+
+ ClassDB::bind_method(D_METHOD("set_alignment_horizontal", "alignment_horizontal"), &AspectRatioContainer::set_alignment_horizontal);
+ ClassDB::bind_method(D_METHOD("get_alignment_horizontal"), &AspectRatioContainer::get_alignment_horizontal);
+
+ ClassDB::bind_method(D_METHOD("set_alignment_vertical", "alignment_vertical"), &AspectRatioContainer::set_alignment_vertical);
+ ClassDB::bind_method(D_METHOD("get_alignment_vertical"), &AspectRatioContainer::get_alignment_vertical);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ratio"), "set_ratio", "get_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Width controls height,Height controls width,Fit,Cover"), "set_stretch_mode", "get_stretch_mode");
+
+ ADD_GROUP("Alignment", "alignment_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment_horizontal", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment_horizontal", "get_alignment_horizontal");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment_vertical", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment_vertical", "get_alignment_vertical");
+
+ BIND_ENUM_CONSTANT(STRETCH_WIDTH_CONTROLS_HEIGHT);
+ BIND_ENUM_CONSTANT(STRETCH_HEIGHT_CONTROLS_WIDTH);
+ BIND_ENUM_CONSTANT(STRETCH_FIT);
+ BIND_ENUM_CONSTANT(STRETCH_COVER);
+
+ BIND_ENUM_CONSTANT(ALIGN_BEGIN);
+ BIND_ENUM_CONSTANT(ALIGN_CENTER);
+ BIND_ENUM_CONSTANT(ALIGN_END);
+}
diff --git a/scene/gui/aspect_ratio_container.h b/scene/gui/aspect_ratio_container.h
new file mode 100644
index 0000000000..8ffc4363c3
--- /dev/null
+++ b/scene/gui/aspect_ratio_container.h
@@ -0,0 +1,80 @@
+/*************************************************************************/
+/* aspect_ratio_container.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 ASPECT_RATIO_CONTAINER_H
+#define ASPECT_RATIO_CONTAINER_H
+
+#include "scene/gui/container.h"
+
+class AspectRatioContainer : public Container {
+ GDCLASS(AspectRatioContainer, Container);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+ virtual Size2 get_minimum_size() const override;
+
+public:
+ enum StretchMode {
+ STRETCH_WIDTH_CONTROLS_HEIGHT,
+ STRETCH_HEIGHT_CONTROLS_WIDTH,
+ STRETCH_FIT,
+ STRETCH_COVER,
+ };
+ enum AlignMode {
+ ALIGN_BEGIN,
+ ALIGN_CENTER,
+ ALIGN_END,
+ };
+
+private:
+ float ratio = 1.0;
+ StretchMode stretch_mode = STRETCH_FIT;
+ AlignMode alignment_horizontal = ALIGN_CENTER;
+ AlignMode alignment_vertical = ALIGN_CENTER;
+
+public:
+ void set_ratio(float p_ratio);
+ float get_ratio() const { return ratio; }
+
+ void set_stretch_mode(StretchMode p_mode);
+ StretchMode get_stretch_mode() const { return stretch_mode; }
+
+ void set_alignment_horizontal(AlignMode p_alignment_horizontal);
+ AlignMode get_alignment_horizontal() const { return alignment_horizontal; }
+
+ void set_alignment_vertical(AlignMode p_alignment_vertical);
+ AlignMode get_alignment_vertical() const { return alignment_vertical; }
+};
+
+VARIANT_ENUM_CAST(AspectRatioContainer::StretchMode);
+VARIANT_ENUM_CAST(AspectRatioContainer::AlignMode);
+
+#endif // ASPECT_RATIO_CONTAINER_H
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 6fef44481a..dadb1bea31 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -60,7 +60,7 @@ void BaseButton::_gui_input(Ref<InputEvent> p_event) {
Ref<InputEventMouseButton> mouse_button = p_event;
bool ui_accept = p_event->is_action("ui_accept") && !p_event->is_echo();
- bool button_masked = mouse_button.is_valid() && ((1 << (mouse_button->get_button_index() - 1)) & button_mask) > 0;
+ bool button_masked = mouse_button.is_valid() && ((1 << (mouse_button->get_button_index() - 1)) & button_mask) != 0;
if (button_masked || ui_accept) {
on_action_event(p_event);
return;
@@ -307,17 +307,6 @@ int BaseButton::get_button_mask() const {
return button_mask;
}
-void BaseButton::set_enabled_focus_mode(FocusMode p_mode) {
- enabled_focus_mode = p_mode;
- if (!status.disabled) {
- set_focus_mode(p_mode);
- }
-}
-
-Control::FocusMode BaseButton::get_enabled_focus_mode() const {
- return enabled_focus_mode;
-}
-
void BaseButton::set_keep_pressed_outside(bool p_on) {
keep_pressed_outside = p_on;
}
@@ -326,18 +315,23 @@ bool BaseButton::is_keep_pressed_outside() const {
return keep_pressed_outside;
}
-void BaseButton::set_shortcut(const Ref<ShortCut> &p_shortcut) {
+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 {
+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();
}
}
@@ -345,7 +339,7 @@ String BaseButton::get_tooltip(const Point2 &p_pos) const {
String tooltip = Control::get_tooltip(p_pos);
if (shortcut_in_tooltip && shortcut.is_valid() && shortcut->is_valid()) {
String text = shortcut->get_name() + " (" + shortcut->get_as_text() + ")";
- if (shortcut->get_name().nocasecmp_to(tooltip) != 0) {
+ if (tooltip != String() && shortcut->get_name().nocasecmp_to(tooltip) != 0) {
text += "\n" + tooltip;
}
tooltip = text;
@@ -371,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);
@@ -388,8 +407,6 @@ void BaseButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_button_mask", "mask"), &BaseButton::set_button_mask);
ClassDB::bind_method(D_METHOD("get_button_mask"), &BaseButton::get_button_mask);
ClassDB::bind_method(D_METHOD("get_draw_mode"), &BaseButton::get_draw_mode);
- ClassDB::bind_method(D_METHOD("set_enabled_focus_mode", "mode"), &BaseButton::set_enabled_focus_mode);
- ClassDB::bind_method(D_METHOD("get_enabled_focus_mode"), &BaseButton::get_enabled_focus_mode);
ClassDB::bind_method(D_METHOD("set_keep_pressed_outside", "enabled"), &BaseButton::set_keep_pressed_outside);
ClassDB::bind_method(D_METHOD("is_keep_pressed_outside"), &BaseButton::is_keep_pressed_outside);
@@ -399,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")));
@@ -412,10 +432,10 @@ void BaseButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_enabled_focus_mode", "get_enabled_focus_mode");
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, "group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group");
+ 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);
@@ -437,9 +457,9 @@ BaseButton::BaseButton() {
status.pressing_inside = false;
status.disabled = false;
set_focus_mode(FOCUS_ALL);
- enabled_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 12272446d5..661801216d 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -49,8 +49,8 @@ private:
bool toggle_mode;
bool shortcut_in_tooltip;
bool keep_pressed_outside;
- FocusMode enabled_focus_mode;
- Ref<ShortCut> shortcut;
+ Ref<Shortcut> shortcut;
+ ObjectID shortcut_context;
ActionMode action_mode;
struct Status {
@@ -76,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,
@@ -115,17 +117,17 @@ public:
void set_button_mask(int p_mask);
int get_button_mask() const;
- void set_enabled_focus_mode(FocusMode p_mode);
- FocusMode get_enabled_focus_mode() const;
-
- void set_shortcut(const Ref<ShortCut> &p_shortcut);
- Ref<ShortCut> get_shortcut() const;
+ void set_shortcut(const Ref<Shortcut> &p_shortcut);
+ Ref<Shortcut> get_shortcut() const;
virtual String get_tooltip(const Point2 &p_pos) const override;
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 f130726837..fdd88d155f 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -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;
@@ -57,7 +58,7 @@ void BoxContainer::_resort() {
if (!c || !c->is_visible_in_tree()) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
@@ -111,7 +112,7 @@ void BoxContainer::_resort() {
if (!c || !c->is_visible_in_tree()) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
@@ -152,27 +153,58 @@ 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;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
@@ -224,7 +256,7 @@ Size2 BoxContainer::get_minimum_size() const {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
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/button.cpp b/scene/gui/button.cpp
index e400801b66..711e5f9262 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -30,11 +30,11 @@
#include "button.h"
-#include "core/translation.h"
+#include "core/string/translation.h"
#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;
}
@@ -65,8 +65,19 @@ void Button::_set_internal_margin(Margin p_margin, float 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[MARGIN_RIGHT] > 0) {
+ icon_ofs_region = _internal_margin[MARGIN_RIGHT] + get_theme_constant("hseparation");
+ }
+ } else {
+ if (_internal_margin[MARGIN_LEFT] > 0) {
+ icon_ofs_region = _internal_margin[MARGIN_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,14 +228,26 @@ 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(MARGIN_RIGHT)), style->get_margin(MARGIN_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(MARGIN_RIGHT)), style->get_margin(MARGIN_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;
+ 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[MARGIN_LEFT] > 0) {
text_clip -= _internal_margin[MARGIN_LEFT] + get_theme_constant("hseparation");
}
@@ -201,14 +255,22 @@ void Button::_notification(int p_what) {
text_clip -= _internal_margin[MARGIN_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[MARGIN_RIGHT] - _internal_margin[MARGIN_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[MARGIN_RIGHT] > 0) {
+ text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - text_width - _internal_margin[MARGIN_RIGHT] - get_theme_constant("hseparation");
+ } else {
+ text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - text_width;
+ }
} else {
- text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x;
+ 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");
+ } else {
+ text_ofs.x = style->get_margin(MARGIN_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[MARGIN_LEFT] > 0) {
+ text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_theme_constant("hseparation");
+ } else {
+ text_ofs.x = style->get_margin(MARGIN_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[MARGIN_RIGHT] > 0) {
+ text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - text_width - _internal_margin[MARGIN_RIGHT] - get_theme_constant("hseparation");
+ } else {
+ text_ofs.x = size.x - style->get_margin(MARGIN_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..da89e5e6c4 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -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 _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 f8f9bec3d7..1a72f3ca4d 100644
--- a/scene/gui/center_container.cpp
+++ b/scene/gui/center_container.cpp
@@ -40,7 +40,7 @@ Size2 CenterContainer::get_minimum_size() const {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
if (!c->is_visible()) {
@@ -77,7 +77,7 @@ void CenterContainer::_notification(int p_what) {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp
index df6f38f65d..0c78369688 100644
--- a/scene/gui/check_box.cpp
+++ b/scene/gui/check_box.cpp
@@ -68,8 +68,14 @@ Size2 CheckBox::get_minimum_size() const {
}
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(MARGIN_LEFT, 0.f);
+ _set_internal_margin(MARGIN_RIGHT, get_icon_size().width);
+ } else {
+ _set_internal_margin(MARGIN_LEFT, get_icon_size().width);
+ _set_internal_margin(MARGIN_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(MARGIN_RIGHT) - get_icon_size().width;
+ } else {
+ ofs.x = sb->get_margin(MARGIN_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(MARGIN_RIGHT, get_icon_size().width);
+ } else {
+ _set_internal_margin(MARGIN_LEFT, get_icon_size().width);
+ }
}
CheckBox::~CheckBox() {
diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp
index 1ddc730dd1..e58f56a99b 100644
--- a/scene/gui/check_button.cpp
+++ b/scene/gui/check_button.cpp
@@ -30,7 +30,7 @@
#include "check_button.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include "servers/rendering_server.h"
Size2 CheckButton::get_icon_size() const {
@@ -61,19 +61,40 @@ Size2 CheckButton::get_minimum_size() const {
}
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(MARGIN_LEFT, get_icon_size().width);
+ _set_internal_margin(MARGIN_RIGHT, 0.f);
+ } else {
+ _set_internal_margin(MARGIN_LEFT, 0.f);
+ _set_internal_margin(MARGIN_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(MARGIN_LEFT);
+ } else {
+ ofs.x = get_size().width - (tex_size.width + sb->get_margin(MARGIN_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(MARGIN_LEFT, get_icon_size().width);
+ } else {
+ _set_internal_margin(MARGIN_RIGHT, get_icon_size().width);
+ }
}
CheckButton::~CheckButton() {
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
new file mode 100644
index 0000000000..59cfbccf99
--- /dev/null
+++ b/scene/gui/code_edit.cpp
@@ -0,0 +1,456 @@
+/*************************************************************************/
+/* code_edit.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 "code_edit.h"
+
+void CodeEdit::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_ENTER_TREE: {
+ 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");
+
+ bookmark_color = get_theme_color("bookmark_color");
+ bookmark_icon = get_theme_icon("bookmark");
+
+ executing_line_color = get_theme_color("executing_line_color");
+ executing_line_icon = get_theme_icon("executing_line");
+
+ line_number_color = get_theme_color("line_number_color");
+
+ folding_color = get_theme_color("code_folding_color");
+ can_fold_icon = get_theme_icon("can_fold");
+ folded_icon = get_theme_icon("folded");
+ } break;
+ case NOTIFICATION_DRAW: {
+ } break;
+ }
+}
+
+/* Main Gutter */
+void CodeEdit::_update_draw_main_gutter() {
+ set_gutter_draw(main_gutter, draw_breakpoints || draw_bookmarks || draw_executing_lines);
+}
+
+void CodeEdit::set_draw_breakpoints_gutter(bool p_draw) {
+ draw_breakpoints = p_draw;
+ set_gutter_clickable(main_gutter, p_draw);
+ _update_draw_main_gutter();
+}
+
+bool CodeEdit::is_drawing_breakpoints_gutter() const {
+ return draw_breakpoints;
+}
+
+void CodeEdit::set_draw_bookmarks_gutter(bool p_draw) {
+ draw_bookmarks = p_draw;
+ _update_draw_main_gutter();
+}
+
+bool CodeEdit::is_drawing_bookmarks_gutter() const {
+ return draw_bookmarks;
+}
+
+void CodeEdit::set_draw_executing_lines_gutter(bool p_draw) {
+ draw_executing_lines = p_draw;
+ _update_draw_main_gutter();
+}
+
+bool CodeEdit::is_drawing_executing_lines_gutter() const {
+ return draw_executing_lines;
+}
+
+void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
+ if (draw_breakpoints && is_line_breakpointed(p_line)) {
+ int padding = p_region.size.x / 6;
+
+ Rect2 breakpoint_region = p_region;
+ breakpoint_region.position += Point2(padding, padding);
+ breakpoint_region.size -= Point2(padding, padding) * 2;
+ breakpoint_icon->draw_rect(get_canvas_item(), breakpoint_region, false, breakpoint_color);
+ }
+
+ if (draw_bookmarks && is_line_bookmarked(p_line)) {
+ int horizontal_padding = p_region.size.x / 2;
+ int vertical_padding = p_region.size.y / 4;
+
+ Rect2 bookmark_region = p_region;
+ bookmark_region.position += Point2(horizontal_padding, 0);
+ bookmark_region.size -= Point2(horizontal_padding * 1.1, vertical_padding);
+ bookmark_icon->draw_rect(get_canvas_item(), bookmark_region, false, bookmark_color);
+ }
+
+ if (draw_executing_lines && is_line_executing(p_line)) {
+ int horizontal_padding = p_region.size.x / 10;
+ int vertical_padding = p_region.size.y / 4;
+
+ Rect2 executing_line_region = p_region;
+ executing_line_region.position += Point2(horizontal_padding, vertical_padding);
+ executing_line_region.size -= Point2(horizontal_padding, vertical_padding) * 2;
+ executing_line_icon->draw_rect(get_canvas_item(), executing_line_region, false, executing_line_color);
+ }
+}
+
+// Breakpoints
+void CodeEdit::set_line_as_breakpoint(int p_line, bool p_breakpointed) {
+ int mask = get_line_gutter_metadata(p_line, main_gutter);
+ set_line_gutter_metadata(p_line, main_gutter, p_breakpointed ? mask | MAIN_GUTTER_BREAKPOINT : mask & ~MAIN_GUTTER_BREAKPOINT);
+ if (p_breakpointed) {
+ breakpointed_lines[p_line] = true;
+ } else if (breakpointed_lines.has(p_line)) {
+ breakpointed_lines.erase(p_line);
+ }
+ emit_signal("breakpoint_toggled", p_line);
+ update();
+}
+
+bool CodeEdit::is_line_breakpointed(int p_line) const {
+ return (int)get_line_gutter_metadata(p_line, main_gutter) & MAIN_GUTTER_BREAKPOINT;
+}
+
+void CodeEdit::clear_breakpointed_lines() {
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_breakpointed(i)) {
+ set_line_as_breakpoint(i, false);
+ }
+ }
+}
+
+Array CodeEdit::get_breakpointed_lines() const {
+ Array ret;
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_breakpointed(i)) {
+ ret.append(i);
+ }
+ }
+ return ret;
+}
+
+// Bookmarks
+void CodeEdit::set_line_as_bookmarked(int p_line, bool p_bookmarked) {
+ int mask = get_line_gutter_metadata(p_line, main_gutter);
+ set_line_gutter_metadata(p_line, main_gutter, p_bookmarked ? mask | MAIN_GUTTER_BOOKMARK : mask & ~MAIN_GUTTER_BOOKMARK);
+ update();
+}
+
+bool CodeEdit::is_line_bookmarked(int p_line) const {
+ return (int)get_line_gutter_metadata(p_line, main_gutter) & MAIN_GUTTER_BOOKMARK;
+}
+
+void CodeEdit::clear_bookmarked_lines() {
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_bookmarked(i)) {
+ set_line_as_bookmarked(i, false);
+ }
+ }
+}
+
+Array CodeEdit::get_bookmarked_lines() const {
+ Array ret;
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_bookmarked(i)) {
+ ret.append(i);
+ }
+ }
+ return ret;
+}
+
+// executing lines
+void CodeEdit::set_line_as_executing(int p_line, bool p_executing) {
+ int mask = get_line_gutter_metadata(p_line, main_gutter);
+ set_line_gutter_metadata(p_line, main_gutter, p_executing ? mask | MAIN_GUTTER_EXECUTING : mask & ~MAIN_GUTTER_EXECUTING);
+ update();
+}
+
+bool CodeEdit::is_line_executing(int p_line) const {
+ return (int)get_line_gutter_metadata(p_line, main_gutter) & MAIN_GUTTER_EXECUTING;
+}
+
+void CodeEdit::clear_executing_lines() {
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_executing(i)) {
+ set_line_as_executing(i, false);
+ }
+ }
+}
+
+Array CodeEdit::get_executing_lines() const {
+ Array ret;
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_executing(i)) {
+ ret.append(i);
+ }
+ }
+ return ret;
+}
+
+/* Line numbers */
+void CodeEdit::set_draw_line_numbers(bool p_draw) {
+ set_gutter_draw(line_number_gutter, p_draw);
+}
+
+bool CodeEdit::is_draw_line_numbers_enabled() const {
+ return is_gutter_drawn(line_number_gutter);
+}
+
+void CodeEdit::set_line_numbers_zero_padded(bool p_zero_padded) {
+ p_zero_padded ? line_number_padding = "0" : line_number_padding = " ";
+ update();
+}
+
+bool CodeEdit::is_line_numbers_zero_padded() const {
+ return line_number_padding == "0";
+}
+
+void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
+ 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;
+ }
+ tl->draw(get_canvas_item(), Point2(p_region.position.x, yofs), number_color);
+}
+
+/* Fold Gutter */
+void CodeEdit::set_draw_fold_gutter(bool p_draw) {
+ set_gutter_draw(fold_gutter, p_draw);
+}
+
+bool CodeEdit::is_drawing_fold_gutter() const {
+ return is_gutter_drawn(fold_gutter);
+}
+
+void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_region) {
+ if (!can_fold(p_line) && !is_folded(p_line)) {
+ set_line_gutter_clickable(p_line, fold_gutter, false);
+ return;
+ }
+ set_line_gutter_clickable(p_line, fold_gutter, true);
+
+ int horizontal_padding = p_region.size.x / 10;
+ int vertical_padding = p_region.size.y / 6;
+
+ p_region.position += Point2(horizontal_padding, vertical_padding);
+ p_region.size -= Point2(horizontal_padding, vertical_padding) * 2;
+
+ if (can_fold(p_line)) {
+ can_fold_icon->draw_rect(get_canvas_item(), p_region, false, folding_color);
+ return;
+ }
+ folded_icon->draw_rect(get_canvas_item(), p_region, false, folding_color);
+}
+
+void CodeEdit::_bind_methods() {
+ /* Main Gutter */
+ ClassDB::bind_method(D_METHOD("_main_gutter_draw_callback"), &CodeEdit::_main_gutter_draw_callback);
+
+ ClassDB::bind_method(D_METHOD("set_draw_breakpoints_gutter", "enable"), &CodeEdit::set_draw_breakpoints_gutter);
+ ClassDB::bind_method(D_METHOD("is_drawing_breakpoints_gutter"), &CodeEdit::is_drawing_breakpoints_gutter);
+
+ ClassDB::bind_method(D_METHOD("set_draw_bookmarks_gutter", "enable"), &CodeEdit::set_draw_bookmarks_gutter);
+ ClassDB::bind_method(D_METHOD("is_drawing_bookmarks_gutter"), &CodeEdit::is_drawing_bookmarks_gutter);
+
+ ClassDB::bind_method(D_METHOD("set_draw_executing_lines_gutter", "enable"), &CodeEdit::set_draw_executing_lines_gutter);
+ ClassDB::bind_method(D_METHOD("is_drawing_executing_lines_gutter"), &CodeEdit::is_drawing_executing_lines_gutter);
+
+ // Breakpoints
+ ClassDB::bind_method(D_METHOD("set_line_as_breakpoint", "line", "breakpointed"), &CodeEdit::set_line_as_breakpoint);
+ ClassDB::bind_method(D_METHOD("is_line_breakpointed", "line"), &CodeEdit::is_line_breakpointed);
+ ClassDB::bind_method(D_METHOD("clear_breakpointed_lines"), &CodeEdit::clear_breakpointed_lines);
+ ClassDB::bind_method(D_METHOD("get_breakpointed_lines"), &CodeEdit::get_breakpointed_lines);
+
+ // Bookmarks
+ ClassDB::bind_method(D_METHOD("set_line_as_bookmarked", "line", "bookmarked"), &CodeEdit::set_line_as_bookmarked);
+ ClassDB::bind_method(D_METHOD("is_line_bookmarked", "line"), &CodeEdit::is_line_bookmarked);
+ ClassDB::bind_method(D_METHOD("clear_bookmarked_lines"), &CodeEdit::clear_bookmarked_lines);
+ ClassDB::bind_method(D_METHOD("get_bookmarked_lines"), &CodeEdit::get_bookmarked_lines);
+
+ // executing lines
+ ClassDB::bind_method(D_METHOD("set_line_as_executing", "line", "executing"), &CodeEdit::set_line_as_executing);
+ ClassDB::bind_method(D_METHOD("is_line_executing", "line"), &CodeEdit::is_line_executing);
+ ClassDB::bind_method(D_METHOD("clear_executing_lines"), &CodeEdit::clear_executing_lines);
+ ClassDB::bind_method(D_METHOD("get_executing_lines"), &CodeEdit::get_executing_lines);
+
+ /* Line numbers */
+ ClassDB::bind_method(D_METHOD("_line_number_draw_callback"), &CodeEdit::_line_number_draw_callback);
+
+ ClassDB::bind_method(D_METHOD("set_draw_line_numbers", "enable"), &CodeEdit::set_draw_line_numbers);
+ ClassDB::bind_method(D_METHOD("is_draw_line_numbers_enabled"), &CodeEdit::is_draw_line_numbers_enabled);
+ ClassDB::bind_method(D_METHOD("set_line_numbers_zero_padded", "enable"), &CodeEdit::set_line_numbers_zero_padded);
+ ClassDB::bind_method(D_METHOD("is_line_numbers_zero_padded"), &CodeEdit::is_line_numbers_zero_padded);
+
+ /* Fold Gutter */
+ ClassDB::bind_method(D_METHOD("_fold_gutter_draw_callback"), &CodeEdit::_fold_gutter_draw_callback);
+
+ ClassDB::bind_method(D_METHOD("set_draw_fold_gutter", "enable"), &CodeEdit::set_draw_fold_gutter);
+ ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &CodeEdit::is_drawing_fold_gutter);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_breakpoints_gutter"), "set_draw_breakpoints_gutter", "is_drawing_breakpoints_gutter");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_bookmarks"), "set_draw_bookmarks_gutter", "is_drawing_bookmarks_gutter");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_executing_lines"), "set_draw_executing_lines_gutter", "is_drawing_executing_lines_gutter");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_line_numbers"), "set_draw_line_numbers", "is_draw_line_numbers_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "zero_pad_line_numbers"), "set_line_numbers_zero_padded", "is_line_numbers_zero_padded");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter");
+
+ ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "line")));
+}
+
+void CodeEdit::_gutter_clicked(int p_line, int p_gutter) {
+ if (p_gutter == main_gutter) {
+ if (draw_breakpoints) {
+ set_line_as_breakpoint(p_line, !is_line_breakpointed(p_line));
+ }
+ return;
+ }
+
+ if (p_gutter == line_number_gutter) {
+ set_selection_mode(TextEdit::SelectionMode::SELECTION_MODE_LINE, p_line, 0);
+ select(p_line, 0, p_line + 1, 0);
+ cursor_set_line(p_line + 1);
+ cursor_set_column(0);
+ return;
+ }
+
+ if (p_gutter == fold_gutter) {
+ if (is_folded(p_line)) {
+ unfold_line(p_line);
+ } else if (can_fold(p_line)) {
+ fold_line(p_line);
+ }
+ return;
+ }
+}
+
+void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) {
+ if (p_from_line == p_to_line) {
+ return;
+ }
+
+ int lc = get_line_count();
+ line_number_digits = 1;
+ while (lc /= 10) {
+ line_number_digits++;
+ }
+ 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);
+ List<int> breakpoints;
+ breakpointed_lines.get_key_list(&breakpoints);
+ for (const List<int>::Element *E = breakpoints.front(); E; E = E->next()) {
+ int line = E->get();
+ if (line <= from_line) {
+ continue;
+ }
+ breakpointed_lines.erase(line);
+
+ emit_signal("breakpoint_toggled", line);
+ if (line_count > 0 || line >= p_from_line) {
+ emit_signal("breakpoint_toggled", line + line_count);
+ breakpointed_lines[line + line_count] = true;
+ continue;
+ }
+ }
+}
+
+void CodeEdit::_update_gutter_indexes() {
+ for (int i = 0; i < get_gutter_count(); i++) {
+ if (get_gutter_name(i) == "main_gutter") {
+ main_gutter = i;
+ continue;
+ }
+
+ if (get_gutter_name(i) == "line_numbers") {
+ line_number_gutter = i;
+ continue;
+ }
+
+ if (get_gutter_name(i) == "fold_gutter") {
+ fold_gutter = i;
+ continue;
+ }
+ }
+}
+
+CodeEdit::CodeEdit() {
+ /* Text Direction */
+ set_layout_direction(LAYOUT_DIRECTION_LTR);
+ set_text_direction(TEXT_DIRECTION_LTR);
+
+ /* Gutters */
+ int gutter_idx = 0;
+
+ /* Main Gutter */
+ add_gutter();
+ set_gutter_name(gutter_idx, "main_gutter");
+ set_gutter_draw(gutter_idx, false);
+ set_gutter_overwritable(gutter_idx, true);
+ set_gutter_type(gutter_idx, GUTTER_TPYE_CUSTOM);
+ set_gutter_custom_draw(gutter_idx, this, "_main_gutter_draw_callback");
+ gutter_idx++;
+
+ /* Line numbers */
+ add_gutter();
+ set_gutter_name(gutter_idx, "line_numbers");
+ set_gutter_draw(gutter_idx, false);
+ set_gutter_type(gutter_idx, GUTTER_TPYE_CUSTOM);
+ set_gutter_custom_draw(gutter_idx, this, "_line_number_draw_callback");
+ gutter_idx++;
+
+ /* Fold Gutter */
+ add_gutter();
+ set_gutter_name(gutter_idx, "fold_gutter");
+ set_gutter_draw(gutter_idx, false);
+ set_gutter_type(gutter_idx, GUTTER_TPYE_CUSTOM);
+ set_gutter_custom_draw(gutter_idx, this, "_fold_gutter_draw_callback");
+ gutter_idx++;
+
+ connect("lines_edited_from", callable_mp(this, &CodeEdit::_lines_edited_from));
+ connect("gutter_clicked", callable_mp(this, &CodeEdit::_gutter_clicked));
+
+ connect("gutter_added", callable_mp(this, &CodeEdit::_update_gutter_indexes));
+ connect("gutter_removed", callable_mp(this, &CodeEdit::_update_gutter_indexes));
+ _update_gutter_indexes();
+}
+
+CodeEdit::~CodeEdit() {
+}
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
new file mode 100644
index 0000000000..c989e5ed79
--- /dev/null
+++ b/scene/gui/code_edit.h
@@ -0,0 +1,135 @@
+/*************************************************************************/
+/* code_edit.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 CODEEDIT_H
+#define CODEEDIT_H
+
+#include "scene/gui/text_edit.h"
+
+class CodeEdit : public TextEdit {
+ GDCLASS(CodeEdit, TextEdit)
+
+private:
+ /* Main Gutter */
+ enum MainGutterType {
+ MAIN_GUTTER_BREAKPOINT = 0x01,
+ MAIN_GUTTER_BOOKMARK = 0x02,
+ MAIN_GUTTER_EXECUTING = 0x04
+ };
+
+ int main_gutter = -1;
+ void _update_draw_main_gutter();
+ void _main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region);
+
+ // breakpoints
+ HashMap<int, bool> breakpointed_lines;
+ bool draw_breakpoints = false;
+ Color breakpoint_color = Color(1, 1, 1);
+ Ref<Texture2D> breakpoint_icon = Ref<Texture2D>();
+
+ // bookmarks
+ bool draw_bookmarks = false;
+ Color bookmark_color = Color(1, 1, 1);
+ Ref<Texture2D> bookmark_icon = Ref<Texture2D>();
+
+ // executing lines
+ bool draw_executing_lines = false;
+ Color executing_line_color = Color(1, 1, 1);
+ Ref<Texture2D> executing_line_icon = Ref<Texture2D>();
+
+ /* Line numbers */
+ int line_number_gutter = -1;
+ int line_number_digits = 0;
+ String line_number_padding = " ";
+ Color line_number_color = Color(1, 1, 1);
+ void _line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region);
+
+ /* Fold Gutter */
+ int fold_gutter = -1;
+ bool draw_fold_gutter = false;
+ Color folding_color = Color(1, 1, 1);
+ Ref<Texture2D> can_fold_icon = Ref<Texture2D>();
+ Ref<Texture2D> folded_icon = Ref<Texture2D>();
+ void _fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_region);
+
+ void _gutter_clicked(int p_line, int p_gutter);
+ void _lines_edited_from(int p_from_line, int p_to_line);
+
+ void _update_gutter_indexes();
+
+protected:
+ void _notification(int p_what);
+
+ static void _bind_methods();
+
+public:
+ /* Main Gutter */
+ void set_draw_breakpoints_gutter(bool p_draw);
+ bool is_drawing_breakpoints_gutter() const;
+
+ void set_draw_bookmarks_gutter(bool p_draw);
+ bool is_drawing_bookmarks_gutter() const;
+
+ void set_draw_executing_lines_gutter(bool p_draw);
+ bool is_drawing_executing_lines_gutter() const;
+
+ // breakpoints
+ void set_line_as_breakpoint(int p_line, bool p_breakpointed);
+ bool is_line_breakpointed(int p_line) const;
+ void clear_breakpointed_lines();
+ Array get_breakpointed_lines() const;
+
+ // bookmarks
+ void set_line_as_bookmarked(int p_line, bool p_bookmarked);
+ bool is_line_bookmarked(int p_line) const;
+ void clear_bookmarked_lines();
+ Array get_bookmarked_lines() const;
+
+ // executing lines
+ void set_line_as_executing(int p_line, bool p_executing);
+ bool is_line_executing(int p_line) const;
+ void clear_executing_lines();
+ Array get_executing_lines() const;
+
+ /* Line numbers */
+ void set_draw_line_numbers(bool p_draw);
+ bool is_draw_line_numbers_enabled() const;
+ void set_line_numbers_zero_padded(bool p_zero_padded);
+ bool is_line_numbers_zero_padded() const;
+
+ /* Fold gutter */
+ void set_draw_fold_gutter(bool p_draw);
+ bool is_drawing_fold_gutter() const;
+
+ CodeEdit();
+ ~CodeEdit();
+};
+
+#endif // CODEEDIT_H
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index fafbb298b7..6ebd1011e9 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -610,12 +610,12 @@ void ColorPicker::_screen_pick_pressed() {
if (!screen) {
screen = memnew(Control);
r->add_child(screen);
- screen->set_as_toplevel(true);
+ screen->set_as_top_level(true);
screen->set_anchors_and_margins_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
@@ -873,6 +873,7 @@ void ColorPickerButton::_color_changed(const Color &p_color) {
void ColorPickerButton::_modal_closed() {
emit_signal("popup_closed");
+ set_pressed(false);
}
void ColorPickerButton::pressed() {
@@ -976,9 +977,8 @@ void ColorPickerButton::_update_picker() {
popup->add_child(picker);
add_child(popup);
picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed));
- popup->connect("modal_closed", callable_mp(this, &ColorPickerButton::_modal_closed));
popup->connect("about_to_popup", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(true));
- popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(false));
+ popup->connect("popup_hide", callable_mp(this, &ColorPickerButton::_modal_closed));
picker->set_pick_color(color);
picker->set_edit_alpha(edit_alpha);
emit_signal("picker_created");
diff --git a/scene/gui/color_rect.cpp b/scene/gui/color_rect.cpp
index 627e589c02..0c38b93c60 100644
--- a/scene/gui/color_rect.cpp
+++ b/scene/gui/color_rect.cpp
@@ -30,12 +30,12 @@
#include "color_rect.h"
-void ColorRect::set_frame_color(const Color &p_color) {
+void ColorRect::set_color(const Color &p_color) {
color = p_color;
update();
}
-Color ColorRect::get_frame_color() const {
+Color ColorRect::get_color() const {
return color;
}
@@ -46,12 +46,8 @@ void ColorRect::_notification(int p_what) {
}
void ColorRect::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_frame_color", "color"), &ColorRect::set_frame_color);
- ClassDB::bind_method(D_METHOD("get_frame_color"), &ColorRect::get_frame_color);
+ ClassDB::bind_method(D_METHOD("set_color", "color"), &ColorRect::set_color);
+ ClassDB::bind_method(D_METHOD("get_color"), &ColorRect::get_color);
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_frame_color", "get_frame_color");
-}
-
-ColorRect::ColorRect() {
- color = Color(1, 1, 1);
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
}
diff --git a/scene/gui/color_rect.h b/scene/gui/color_rect.h
index 3df44b9334..61d57f7cca 100644
--- a/scene/gui/color_rect.h
+++ b/scene/gui/color_rect.h
@@ -36,17 +36,15 @@
class ColorRect : public Control {
GDCLASS(ColorRect, Control);
- Color color;
+ Color color = Color(1, 1, 1);
protected:
void _notification(int p_what);
static void _bind_methods();
public:
- void set_frame_color(const Color &p_color);
- Color get_frame_color() const;
-
- ColorRect();
+ void set_color(const Color &p_color);
+ Color get_color() const;
};
#endif // COLOR_RECT_H
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index a89eef6209..f01da703c1 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "container.h"
-#include "core/message_queue.h"
+#include "core/object/message_queue.h"
#include "scene/scene_string_names.h"
void Container::_child_minsize_changed() {
@@ -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 != String()) {
+ if (!warning.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/control.cpp b/scene/gui/control.cpp
index 2cdee4641e..b471bb9d4e 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -30,18 +30,21 @@
#include "control.h"
+#include "core/config/project_settings.h"
#include "core/math/geometry_2d.h"
-#include "core/message_queue.h"
+#include "core/object/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "core/print_string.h"
-#include "core/project_settings.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"
@@ -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();
@@ -339,7 +335,6 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const {
Ref<Theme> theme = Theme::get_default();
/* Using the default theme since the properties below are meant for editor only
if (data.theme.is_valid()) {
-
theme = data.theme;
} else {
theme = Theme::get_default();
@@ -360,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));
}
}
{
@@ -424,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
@@ -493,7 +521,7 @@ void Control::_notification(int p_notification) {
}
CanvasItem *ci = Object::cast_to<CanvasItem>(parent);
- if (ci && ci->is_set_as_toplevel()) {
+ if (ci && ci->is_set_as_top_level()) {
subwindow = true;
break;
}
@@ -509,13 +537,13 @@ void Control::_notification(int p_notification) {
}
if (parent_control && !subwindow) {
- //do nothing, has a parent control and not toplevel
+ //do nothing, has a parent control and not top_level
if (data.theme.is_null() && parent_control->data.theme_owner) {
data.theme_owner = parent_control->data.theme_owner;
notification(NOTIFICATION_THEME_CHANGED);
}
} else {
- //is a regular root control or toplevel
+ //is a regular root control or top_level
data.RI = get_viewport()->_gui_add_root_control(this);
}
@@ -532,7 +560,7 @@ void Control::_notification(int p_notification) {
if (data.parent_canvas_item) {
data.parent_canvas_item->disconnect("item_rect_changed", callable_mp(this, &Control::_size_changed));
data.parent_canvas_item = nullptr;
- } else if (!is_set_as_toplevel()) {
+ } else if (!is_set_as_top_level()) {
//disconnect viewport
get_viewport()->disconnect("size_changed", callable_mp(this, &Control::_size_changed));
}
@@ -582,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();
@@ -591,7 +618,7 @@ void Control::_notification(int p_notification) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible_in_tree()) {
if (get_viewport() != nullptr) {
- get_viewport()->_gui_hid_control(this);
+ get_viewport()->_gui_hide_control(this);
}
//remove key focus
@@ -602,6 +629,10 @@ void Control::_notification(int p_notification) {
}
} break;
+ case NOTIFICATION_TRANSLATION_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ _size_changed();
+ } break;
}
}
@@ -728,13 +759,13 @@ Size2 Control::get_minimum_size() const {
}
template <class T>
-bool Control::_find_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, T &r_ret, T (Theme::*get_func)(const StringName &, const StringName &) const, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_type) {
+bool Control::_find_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, T &r_ret, T (Theme::*get_func)(const StringName &, const StringName &) const, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_node_type) {
// try with custom themes
Control *theme_owner = p_theme_owner;
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
- StringName class_name = p_type;
+ StringName class_name = p_node_type;
while (class_name != StringName()) {
if (theme_owner && (theme_owner->data.theme.operator->()->*has_func)(p_name, class_name)) {
@@ -771,13 +802,13 @@ bool Control::_find_theme_item(Control *p_theme_owner, Window *p_theme_owner_win
return false;
}
-bool Control::_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_type) {
+bool Control::_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) {
// try with custom themes
Control *theme_owner = p_theme_owner;
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
- StringName class_name = p_type;
+ StringName class_name = p_node_type;
while (class_name != StringName()) {
if (theme_owner && (theme_owner->data.theme.operator->()->*has_func)(p_name, class_name)) {
@@ -812,176 +843,176 @@ bool Control::_has_theme_item(Control *p_theme_owner, Window *p_theme_owner_wind
return false;
}
-Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
+Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_node_type) const {
+ if (p_node_type == StringName() || p_node_type == get_class_name()) {
const Ref<Texture2D> *tex = data.icon_override.getptr(p_name);
if (tex) {
return *tex;
}
}
- StringName type = p_type ? p_type : get_class_name();
+ StringName type = p_node_type ? p_node_type : get_class_name();
return get_icons(data.theme_owner, data.theme_owner_window, p_name, type);
}
-Ref<Texture2D> Control::get_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type) {
+Ref<Texture2D> Control::get_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) {
Ref<Texture2D> icon;
- if (_find_theme_item(p_theme_owner, p_theme_owner_window, icon, &Theme::get_icon, &Theme::has_icon, p_name, p_type)) {
+ if (_find_theme_item(p_theme_owner, p_theme_owner_window, icon, &Theme::get_icon, &Theme::has_icon, p_name, p_node_type)) {
return icon;
}
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_icon(p_name, p_type)) {
- return Theme::get_project_default()->get_icon(p_name, p_type);
- }
- }
-
- return Theme::get_default()->get_icon(p_name, p_type);
-}
-
-Ref<Shader> Control::get_theme_shader(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
- const Ref<Shader> *sdr = data.shader_override.getptr(p_name);
- if (sdr) {
- return *sdr;
- }
- }
-
- StringName type = p_type ? p_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_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_type)) {
- return shader;
- }
-
- if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_shader(p_name, p_type)) {
- return Theme::get_project_default()->get_shader(p_name, p_type);
+ if (Theme::get_project_default()->has_icon(p_name, p_node_type)) {
+ return Theme::get_project_default()->get_icon(p_name, p_node_type);
}
}
- return Theme::get_default()->get_shader(p_name, p_type);
+ return Theme::get_default()->get_icon(p_name, p_node_type);
}
-Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
+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);
if (style) {
return *style;
}
}
- StringName type = p_type ? p_type : get_class_name();
+ StringName type = p_node_type ? p_node_type : get_class_name();
return get_styleboxs(data.theme_owner, data.theme_owner_window, p_name, type);
}
-Ref<StyleBox> Control::get_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type) {
+Ref<StyleBox> Control::get_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) {
Ref<StyleBox> stylebox;
- if (_find_theme_item(p_theme_owner, p_theme_owner_window, stylebox, &Theme::get_stylebox, &Theme::has_stylebox, p_name, p_type)) {
+ if (_find_theme_item(p_theme_owner, p_theme_owner_window, stylebox, &Theme::get_stylebox, &Theme::has_stylebox, p_name, p_node_type)) {
return stylebox;
}
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_stylebox(p_name, p_type)) {
- return Theme::get_project_default()->get_stylebox(p_name, p_type);
+ if (Theme::get_project_default()->has_stylebox(p_name, p_node_type)) {
+ return Theme::get_project_default()->get_stylebox(p_name, p_node_type);
}
}
- return Theme::get_default()->get_stylebox(p_name, p_type);
+ return Theme::get_default()->get_stylebox(p_name, p_node_type);
}
-Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
+Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_node_type) const {
+ if (p_node_type == StringName() || p_node_type == get_class_name()) {
const Ref<Font> *font = data.font_override.getptr(p_name);
if (font) {
return *font;
}
}
- StringName type = p_type ? p_type : get_class_name();
+ StringName type = p_node_type ? p_node_type : get_class_name();
return get_fonts(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_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;
- if (_find_theme_item(p_theme_owner, p_theme_owner_window, font, &Theme::get_font, &Theme::has_font, p_name, p_type)) {
+ if (_find_theme_item(p_theme_owner, p_theme_owner_window, font, &Theme::get_font, &Theme::has_font, p_name, p_node_type)) {
return font;
}
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_font(p_name, p_type)) {
- return Theme::get_project_default()->get_font(p_name, p_type);
+ if (Theme::get_project_default()->has_font(p_name, p_node_type)) {
+ return Theme::get_project_default()->get_font(p_name, p_node_type);
+ }
+ }
+
+ 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(p_name, p_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_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
+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);
if (color) {
return *color;
}
}
- StringName type = p_type ? p_type : get_class_name();
+ StringName type = p_node_type ? p_node_type : get_class_name();
return get_colors(data.theme_owner, data.theme_owner_window, p_name, type);
}
-Color Control::get_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type) {
+Color Control::get_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) {
Color color;
- if (_find_theme_item(p_theme_owner, p_theme_owner_window, color, &Theme::get_color, &Theme::has_color, p_name, p_type)) {
+ if (_find_theme_item(p_theme_owner, p_theme_owner_window, color, &Theme::get_color, &Theme::has_color, p_name, p_node_type)) {
return color;
}
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_color(p_name, p_type)) {
- return Theme::get_project_default()->get_color(p_name, p_type);
+ if (Theme::get_project_default()->has_color(p_name, p_node_type)) {
+ return Theme::get_project_default()->get_color(p_name, p_node_type);
}
}
- return Theme::get_default()->get_color(p_name, p_type);
+ return Theme::get_default()->get_color(p_name, p_node_type);
}
-int Control::get_theme_constant(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
+int Control::get_theme_constant(const StringName &p_name, const StringName &p_node_type) const {
+ if (p_node_type == StringName() || p_node_type == get_class_name()) {
const int *constant = data.constant_override.getptr(p_name);
if (constant) {
return *constant;
}
}
- StringName type = p_type ? p_type : get_class_name();
+ StringName type = p_node_type ? p_node_type : get_class_name();
return get_constants(data.theme_owner, data.theme_owner_window, p_name, type);
}
-int Control::get_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type) {
+int Control::get_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) {
int constant;
- if (_find_theme_item(p_theme_owner, p_theme_owner_window, constant, &Theme::get_constant, &Theme::has_constant, p_name, p_type)) {
+ if (_find_theme_item(p_theme_owner, p_theme_owner_window, constant, &Theme::get_constant, &Theme::has_constant, p_name, p_node_type)) {
return constant;
}
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_constant(p_name, p_type)) {
- return Theme::get_project_default()->get_constant(p_name, p_type);
+ if (Theme::get_project_default()->has_constant(p_name, p_node_type)) {
+ return Theme::get_project_default()->get_constant(p_name, p_node_type);
}
}
- return Theme::get_default()->get_constant(p_name, p_type);
+ return Theme::get_default()->get_constant(p_name, p_node_type);
}
bool Control::has_theme_icon_override(const StringName &p_name) const {
@@ -989,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;
@@ -1004,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;
@@ -1014,154 +1045,154 @@ bool Control::has_theme_constant_override(const StringName &p_name) const {
return constant != nullptr;
}
-bool Control::has_theme_icon(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
+bool Control::has_theme_icon(const StringName &p_name, const StringName &p_node_type) const {
+ if (p_node_type == StringName() || p_node_type == get_class_name()) {
if (has_theme_icon_override(p_name)) {
return true;
}
}
- StringName type = p_type ? p_type : get_class_name();
+ StringName type = p_node_type ? p_node_type : get_class_name();
return has_icons(data.theme_owner, data.theme_owner_window, p_name, type);
}
-bool Control::has_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type) {
- if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_icon, p_name, p_type)) {
+bool Control::has_icons(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_icon, p_name, p_node_type)) {
return true;
}
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_color(p_name, p_type)) {
+ if (Theme::get_project_default()->has_color(p_name, p_node_type)) {
return true;
}
}
- return Theme::get_default()->has_icon(p_name, p_type);
+ return Theme::get_default()->has_icon(p_name, p_node_type);
}
-bool Control::has_theme_shader(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
- if (has_theme_shader_override(p_name)) {
+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_stylebox_override(p_name)) {
return true;
}
}
- StringName type = p_type ? p_type : get_class_name();
+ 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_type) {
- if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_shader, p_name, p_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_type)) {
+ if (Theme::get_project_default()->has_stylebox(p_name, p_node_type)) {
return true;
}
}
- return Theme::get_default()->has_shader(p_name, p_type);
+ return Theme::get_default()->has_stylebox(p_name, p_node_type);
}
-bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
- if (has_theme_stylebox_override(p_name)) {
+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_font_override(p_name)) {
return true;
}
}
- StringName type = p_type ? p_type : get_class_name();
+ 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_type) {
- if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_stylebox, p_name, p_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_type)) {
+ if (Theme::get_project_default()->has_font(p_name, p_node_type)) {
return true;
}
}
- return Theme::get_default()->has_stylebox(p_name, p_type);
+ return Theme::get_default()->has_font(p_name, p_node_type);
}
-bool Control::has_theme_font(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
- if (has_theme_font_override(p_name)) {
+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_size_override(p_name)) {
return true;
}
}
- StringName type = p_type ? p_type : get_class_name();
+ 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_type) {
- if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_font, p_name, p_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_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_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_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
+bool Control::has_theme_color(const StringName &p_name, const StringName &p_node_type) const {
+ if (p_node_type == StringName() || p_node_type == get_class_name()) {
if (has_theme_color_override(p_name)) {
return true;
}
}
- StringName type = p_type ? p_type : get_class_name();
+ StringName type = p_node_type ? p_node_type : get_class_name();
return has_colors(data.theme_owner, data.theme_owner_window, p_name, type);
}
-bool Control::has_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type) {
- if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_color, p_name, p_type)) {
+bool Control::has_colors(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_color, p_name, p_node_type)) {
return true;
}
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_color(p_name, p_type)) {
+ if (Theme::get_project_default()->has_color(p_name, p_node_type)) {
return true;
}
}
- return Theme::get_default()->has_color(p_name, p_type);
+ return Theme::get_default()->has_color(p_name, p_node_type);
}
-bool Control::has_theme_constant(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == get_class_name()) {
+bool Control::has_theme_constant(const StringName &p_name, const StringName &p_node_type) const {
+ if (p_node_type == StringName() || p_node_type == get_class_name()) {
if (has_theme_constant_override(p_name)) {
return true;
}
}
- StringName type = p_type ? p_type : get_class_name();
+ StringName type = p_node_type ? p_node_type : get_class_name();
- return has_constants(data.theme_owner, data.theme_owner_window, p_name, p_type);
+ return has_constants(data.theme_owner, data.theme_owner_window, p_name, p_node_type);
}
-bool Control::has_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type) {
- if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_constant, p_name, p_type)) {
+bool Control::has_constants(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_constant, p_name, p_node_type)) {
return true;
}
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_constant(p_name, p_type)) {
+ if (Theme::get_project_default()->has_constant(p_name, p_node_type)) {
return true;
}
}
- return Theme::get_default()->has_constant(p_name, p_type);
+ return Theme::get_default()->has_constant(p_name, p_node_type);
}
Rect2 Control::get_parent_anchorable_rect() const {
@@ -1218,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;
@@ -1427,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:
@@ -1437,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.margin[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.margin[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.margin[0] = x * (1.0 - data.anchor[0]) - new_size.x - p_margin + parent_rect.position.x;
break;
}
@@ -1489,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.margin[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.margin[2] = x * (0.5 - data.anchor[2]) + new_size.x / 2 + parent_rect.position.x;
break;
case PRESET_TOP_RIGHT:
@@ -1507,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.margin[2] = x * (1.0 - data.anchor[2]) - p_margin + parent_rect.position.x;
break;
}
@@ -1630,17 +1669,26 @@ void Control::_compute_anchors(Rect2 p_rect, const float p_margins[4], float (&r
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;
+ 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_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[2] = (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;
}
void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]) {
Size2 parent_rect_size = get_parent_anchorable_rect().size;
- r_margins[0] = p_rect.position.x - (p_anchors[0] * parent_rect_size.x);
+
+ float x = p_rect.position.x;
+ if (is_layout_rtl()) {
+ x = parent_rect_size.x - x - p_rect.size.x;
+ }
+ r_margins[0] = 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[2] = 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);
}
@@ -1661,6 +1709,17 @@ void Control::set_position(const Size2 &p_point, bool p_keep_margins) {
_size_changed();
}
+void Control::set_rect(const Rect2 &p_rect) {
+ for (int i = 0; i < 4; i++) {
+ data.anchor[i] = ANCHOR_BEGIN;
+ }
+
+ _compute_margins(p_rect, data.anchor, data.margin);
+ if (is_inside_tree()) {
+ _size_changed();
+ }
+}
+
void Control::_set_size(const Size2 &p_size) {
set_size(p_size);
}
@@ -1744,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));
@@ -1795,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);
@@ -1816,7 +1863,7 @@ void Control::set_focus_mode(FocusMode p_focus_mode) {
}
static Control *_next_control(Control *p_from) {
- if (p_from->is_set_as_toplevel()) {
+ if (p_from->is_set_as_top_level()) {
return nullptr; // can't go above
}
@@ -1830,7 +1877,7 @@ static Control *_next_control(Control *p_from) {
ERR_FAIL_INDEX_V(next, parent->get_child_count(), nullptr);
for (int i = (next + 1); i < parent->get_child_count(); i++) {
Control *c = Object::cast_to<Control>(parent->get_child(i));
- if (!c || !c->is_visible_in_tree() || c->is_set_as_toplevel()) {
+ if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) {
continue;
}
@@ -1867,7 +1914,7 @@ Control *Control::find_next_valid_focus() const {
for (int i = 0; i < from->get_child_count(); i++) {
Control *c = Object::cast_to<Control>(from->get_child(i));
- if (!c || !c->is_visible_in_tree() || c->is_set_as_toplevel()) {
+ if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) {
continue;
}
@@ -1879,7 +1926,7 @@ Control *Control::find_next_valid_focus() const {
next_child = _next_control(from);
if (!next_child) { //nothing else.. go up and find either window or subwindow
next_child = const_cast<Control *>(this);
- while (next_child && !next_child->is_set_as_toplevel()) {
+ while (next_child && !next_child->is_set_as_top_level()) {
next_child = cast_to<Control>(next_child->get_parent());
}
@@ -1915,7 +1962,7 @@ static Control *_prev_control(Control *p_from) {
Control *child = nullptr;
for (int i = p_from->get_child_count() - 1; i >= 0; i--) {
Control *c = Object::cast_to<Control>(p_from->get_child(i));
- if (!c || !c->is_visible_in_tree() || c->is_set_as_toplevel()) {
+ if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) {
continue;
}
@@ -1955,7 +2002,7 @@ Control *Control::find_prev_valid_focus() const {
Control *prev_child = nullptr;
- if (from->is_set_as_toplevel() || !Object::cast_to<Control>(from->get_parent())) {
+ if (from->is_set_as_top_level() || !Object::cast_to<Control>(from->get_parent())) {
//find last of the children
prev_child = _prev_control(from);
@@ -1964,7 +2011,7 @@ Control *Control::find_prev_valid_focus() const {
for (int i = (from->get_index() - 1); i >= 0; i--) {
Control *c = Object::cast_to<Control>(from->get_parent()->get_child(i));
- if (!c || !c->is_visible_in_tree() || c->is_set_as_toplevel()) {
+ if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) {
continue;
}
@@ -2023,8 +2070,8 @@ void Control::release_focus() {
update();
}
-bool Control::is_toplevel_control() const {
- return is_inside_tree() && (!data.parent_canvas_item && !data.RI && is_set_as_toplevel());
+bool Control::is_top_level_control() const {
+ return is_inside_tree() && (!data.parent_canvas_item && !data.RI && is_set_as_top_level());
}
void Control::_propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign) {
@@ -2374,7 +2421,7 @@ void Control::minimum_size_changed() {
//invalidate cache upwards
while (invalidate && invalidate->data.minimum_size_valid) {
invalidate->data.minimum_size_valid = false;
- if (invalidate->is_set_as_toplevel()) {
+ if (invalidate->is_set_as_top_level()) {
break; // do not go further up
}
if (!invalidate->data.parent && get_parent()) {
@@ -2437,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();
@@ -2499,7 +2635,7 @@ Control *Control::get_root_parent_control() const {
if (c) {
root = c;
- if (c->data.RI || c->is_toplevel_control()) {
+ if (c->data.RI || c->is_top_level_control()) {
break;
}
}
@@ -2545,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);
}
@@ -2560,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 != String()) {
+ if (!warning.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\".");
@@ -2662,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);
@@ -2729,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"));
@@ -2738,7 +2884,9 @@ void Control::_bind_methods() {
BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data")));
BIND_VMETHOD(MethodInfo("drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data")));
- BIND_VMETHOD(MethodInfo(Variant::OBJECT, "_make_custom_tooltip", PropertyInfo(Variant::STRING, "for_text")));
+ BIND_VMETHOD(MethodInfo(
+ PropertyInfo(Variant::OBJECT, "control", PROPERTY_HINT_RESOURCE_TYPE, "Control"),
+ "_make_custom_tooltip", PropertyInfo(Variant::STRING, "for_text")));
BIND_VMETHOD(MethodInfo(Variant::BOOL, "_clips_input"));
ADD_GROUP("Anchor", "anchor_");
@@ -2757,6 +2905,9 @@ void Control::_bind_methods() {
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");
@@ -2803,6 +2954,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);
@@ -2861,6 +3013,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"));
@@ -2883,6 +3053,7 @@ Control::Control() {
data.theme_owner = nullptr;
data.theme_owner_window = nullptr;
data.default_cursor = CURSOR_ARROW;
+ data.layout_dir = LAYOUT_DIRECTION_INHERITED;
data.h_size_flags = SIZE_FILL;
data.v_size_flags = SIZE_FILL;
data.expand = 1;
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 15e10df0c6..8496729f05 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -32,7 +32,7 @@
#define CONTROL_H
#include "core/math/transform_2d.h"
-#include "core/rid.h"
+#include "core/templates/rid.h"
#include "scene/gui/shortcut.h"
#include "scene/main/canvas_item.h"
#include "scene/main/node.h"
@@ -49,7 +49,6 @@ class Control : public CanvasItem {
public:
enum Anchor {
-
ANCHOR_BEGIN = 0,
ANCHOR_END = 1
};
@@ -67,7 +66,6 @@ public:
};
enum SizeFlags {
-
SIZE_FILL = 1,
SIZE_EXPAND = 2,
SIZE_EXPAND_FILL = SIZE_EXPAND | SIZE_FILL,
@@ -129,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 {
@@ -155,12 +177,12 @@ private:
GrowDirection h_grow;
GrowDirection v_grow;
+ LayoutDirection layout_dir;
+
float rotation;
Vector2 scale;
Vector2 pivot_offset;
- bool pending_resize;
-
int h_size_flags;
int v_size_flags;
float expand;
@@ -190,9 +212,9 @@ private:
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;
@@ -215,7 +237,6 @@ private:
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]);
@@ -236,23 +257,23 @@ private:
static void _propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign = true);
template <class T>
- _FORCE_INLINE_ static bool _find_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, T &, T (Theme::*get_func)(const StringName &, const StringName &) const, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_type);
+ _FORCE_INLINE_ static bool _find_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, T &, T (Theme::*get_func)(const StringName &, const StringName &) const, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_node_type);
- _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_type);
+ _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_type = StringName());
- static Ref<Shader> get_shaders(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type = StringName());
- static Ref<StyleBox> get_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type = StringName());
- static Ref<Font> get_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type = StringName());
- static Color get_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type = StringName());
- static int get_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type = StringName());
+ 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<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_type = StringName());
- static bool has_shaders(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type = StringName());
- static bool has_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type = StringName());
- static bool has_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type = StringName());
- static bool has_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type = StringName());
- static bool has_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_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_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());
protected:
virtual void add_child_notify(Node *p_child) override;
@@ -260,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;
@@ -272,7 +295,6 @@ protected:
public:
enum {
-
/* NOTIFICATION_DRAW=30,
NOTIFICATION_VISIBILITY_CHANGED=38*/
NOTIFICATION_RESIZED = 40,
@@ -283,6 +305,7 @@ public:
NOTIFICATION_THEME_CHANGED = 45,
NOTIFICATION_SCROLL_BEGIN = 47,
NOTIFICATION_SCROLL_END = 48,
+ NOTIFICATION_LAYOUT_DIRECTION_CHANGED = 49,
};
@@ -330,6 +353,10 @@ 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);
@@ -365,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;
@@ -423,32 +452,32 @@ 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_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;
- 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;
+ Ref<Texture2D> get_theme_icon(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_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_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;
+ bool has_theme_icon(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;
/* TOOLTIP */
@@ -464,7 +493,7 @@ public:
virtual Transform2D get_transform() const override;
- bool is_toplevel_control() const;
+ bool is_top_level_control() const;
Size2 get_parent_area_size() const;
Rect2 get_parent_anchorable_rect() const;
@@ -501,5 +530,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 9077bfa4ba..4f59f4a36a 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -31,8 +31,8 @@
#include "dialogs.h"
#include "core/os/keyboard.h"
-#include "core/print_string.h"
-#include "core/translation.h"
+#include "core/string/print_string.h"
+#include "core/string/translation.h"
#include "line_edit.h"
#ifdef TOOLS_ENABLED
@@ -181,7 +181,7 @@ void AcceptDialog::_update_child_rects() {
continue;
}
- if (c == hbc || c == label || c == bg || c->is_set_as_toplevel()) {
+ if (c == hbc || c == label || c == bg || c->is_set_as_top_level()) {
continue;
}
@@ -209,7 +209,7 @@ Size2 AcceptDialog::_get_contents_minimum_size() const {
continue;
}
- if (c == hbc || c == label || c->is_set_as_toplevel()) {
+ if (c == hbc || c == label || c->is_set_as_top_level()) {
continue;
}
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 4aea2928f4..eb3d5d5c6d 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -31,7 +31,7 @@
#include "file_dialog.h"
#include "core/os/keyboard.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include "scene/gui/label.h"
FileDialog::GetIconFunc FileDialog::get_icon_func = nullptr;
@@ -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);
@@ -936,7 +938,7 @@ FileDialog::FileDialog() {
filter->connect("item_selected", callable_mp(this, &FileDialog::_filter_selected));
confirm_save = memnew(ConfirmationDialog);
- // confirm_save->set_as_toplevel(true);
+ // confirm_save->set_as_top_level(true);
add_child(confirm_save);
confirm_save->connect("confirmed", callable_mp(this, &FileDialog::_save_confirm_pressed));
@@ -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/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index ecd4ad17ea..53d7ead548 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -357,7 +357,7 @@ void GradientEdit::_notification(int p_what) {
//Draw point markers
for (int i = 0; i < points.size(); i++) {
- Color col = points[i].color.contrasted();
+ Color col = points[i].color.inverted();
col.a = 0.9;
draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col);
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 11b4c9e857..96810e8707 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -262,6 +262,7 @@ void GraphEdit::remove_child_notify(Node *p_child) {
if (gn) {
gn->disconnect("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));
}
}
@@ -546,7 +547,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &pos) {
- if (p_control->is_set_as_toplevel() || !p_control->is_visible()) {
+ if (p_control->is_set_as_top_level() || !p_control->is_visible()) {
return false;
}
@@ -658,9 +659,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(2 * EDSCALE), true);
#else
- p_where->draw_polyline_colors(points, colors, 2);
+ p_where->draw_polyline_colors(points, colors, 2, true);
#endif
}
@@ -774,6 +775,11 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
}
if (mm.is_valid() && dragging) {
+ if (!moving_selection) {
+ emit_signal("begin_node_move");
+ moving_selection = true;
+ }
+
just_selected = true;
drag_accum += mm->get_relative();
for (int i = get_child_count() - 1; i >= 0; i--) {
@@ -880,16 +886,17 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
}
if (drag_accum != Vector2()) {
- emit_signal("_begin_node_move");
-
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
if (gn && gn->is_selected()) {
gn->set_drag(false);
}
}
+ }
- emit_signal("_end_node_move");
+ if (moving_selection) {
+ emit_signal("end_node_move");
+ moving_selection = false;
}
dragging = false;
@@ -1280,8 +1287,8 @@ void GraphEdit::_bind_methods() {
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"));
- ADD_SIGNAL(MethodInfo("_begin_node_move"));
- ADD_SIGNAL(MethodInfo("_end_node_move"));
+ ADD_SIGNAL(MethodInfo("begin_node_move"));
+ ADD_SIGNAL(MethodInfo("end_node_move"));
ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "ofs")));
}
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 37cb5989e9..d87bd41f27 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -98,6 +98,7 @@ private:
bool dragging;
bool just_selected;
+ bool moving_selection;
Vector2 drag_accum;
float zoom;
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 01b54ddaa8..4ce33ec8f2 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -30,15 +30,37 @@
#include "graph_node.h"
-#include "core/method_bind_ext.gen.inc"
+#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)) {
@@ -67,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)) {
@@ -99,10 +134,16 @@ 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));
- if (!c || c->is_set_as_toplevel()) {
+ if (!c || c->is_set_as_top_level()) {
continue;
}
@@ -131,7 +172,7 @@ void GraphNode::_resort() {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
@@ -156,7 +197,7 @@ void GraphNode::_resort() {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
@@ -215,7 +256,6 @@ 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");
@@ -243,7 +283,8 @@ 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(MARGIN_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);
draw_texture(close, cpos, close_color);
@@ -287,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);
@@ -370,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();
@@ -388,7 +445,7 @@ Size2 GraphNode::get_minimum_size() const {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
@@ -412,6 +469,8 @@ void GraphNode::set_title(const String &p_title) {
return;
}
title = p_title;
+ _shape();
+
update();
_change_notify("title");
minimum_size_changed();
@@ -421,6 +480,54 @@ String GraphNode::get_title() const {
return title;
}
+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();
+}
+
+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_offset(const Vector2 &p_offset) {
offset = p_offset;
emit_signal("offset_changed");
@@ -477,7 +584,7 @@ void GraphNode::_connpos_update() {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
@@ -660,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>()));
@@ -701,6 +816,8 @@ 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::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, "offset"), "set_offset", "get_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");
@@ -720,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..3cd7ae6e24 100644
--- a/scene/gui/graph_node.h
+++ b/scene/gui/graph_node.h
@@ -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,6 +66,12 @@ private:
};
String title;
+ Ref<TextLine> title_buf;
+
+ Dictionary opentype_features;
+ String language;
+ TextDirection text_direction = TEXT_DIRECTION_AUTO;
+
bool show_close;
Vector2 offset;
bool comment;
@@ -93,6 +100,7 @@ private:
void _connpos_update();
void _resort();
+ void _shape();
Vector2 drag_from;
bool selected;
@@ -124,6 +132,16 @@ public:
void set_title(const String &p_title);
String get_title() 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_offset(const Vector2 &p_offset);
Vector2 get_offset() const;
diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp
index 2f37461c4d..a08a348a18 100644
--- a/scene/gui/grid_container.cpp
+++ b/scene/gui/grid_container.cpp
@@ -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/item_list.cpp b/scene/gui/item_list.cpp
index 54150d130d..5be7804ac1 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -29,8 +29,26 @@
/*************************************************************************/
#include "item_list.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
-#include "core/project_settings.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;
@@ -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++) {
@@ -749,6 +849,14 @@ 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");
@@ -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);
}
}
@@ -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;
@@ -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);
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index 03f477940c..9684ce0a32 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -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;
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index f49acc1b96..566d77e3fd 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -30,17 +30,17 @@
#include "label.h"
-#include "core/print_string.h"
-#include "core/project_settings.h"
-#include "core/translation.h"
+#include "core/config/project_settings.h"
+#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 != from && 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(MARGIN_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(MARGIN_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) {
- CharType c = xl_text[i + pos];
- CharType 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) {
- CharType c = xl_text[i + pos];
- CharType 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,214 +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++) {
- CharType 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++) {
- CharType 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;
- }
-
- 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();
}
@@ -533,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();
@@ -557,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();
@@ -586,6 +565,7 @@ float Label::get_percent_visible() const {
void Label::set_lines_skipped(int p_lines) {
lines_skipped = p_lines;
+ _update_visible();
update();
}
@@ -595,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();
}
@@ -603,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 total_char_cache;
+ 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 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() {
@@ -617,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);
@@ -635,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);
@@ -647,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");
@@ -656,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 510a716f5d..386297f582 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -38,7 +38,6 @@ class Label : public Control {
public:
enum Align {
-
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT,
@@ -46,7 +45,6 @@ public:
};
enum VAlign {
-
VALIGN_TOP,
VALIGN_CENTER,
VALIGN_BOTTOM,
@@ -61,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;
@@ -106,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;
@@ -128,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 5afc1f438e..9f8b944f4c 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -30,26 +30,28 @@
#include "line_edit.h"
-#include "core/message_queue.h"
+#include "core/object/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "core/print_string.h"
-#include "core/translation.h"
+#include "core/string/print_string.h"
+#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(CharType 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));
@@ -70,6 +72,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (!text.empty() && is_editable() && _is_over_clear_button(b->get_position())) {
clear_button_status.press_attempt = true;
clear_button_status.pressing_inside = true;
+ update();
return;
}
@@ -199,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) {
@@ -213,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) {
@@ -236,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) {
@@ -313,7 +322,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
}
- return;
} break;
case KEY_BACKSPACE: {
@@ -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,11 +584,11 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (handled) {
accept_event();
- } else if (!k->get_command()) {
+ } else if (!k->get_command() || (k->get_command() && k->get_alt())) {
if (k->get_unicode() >= 32 && k->get_keycode() != KEY_DELETE) {
if (editable) {
selection_delete();
- CharType ucodestr[2] = { (CharType)k->get_unicode(), 0 };
+ char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 };
int prev_len = text.length();
append_at_cursor(ucodestr);
if (text.length() != prev_len) {
@@ -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;
@@ -680,13 +683,18 @@ void LineEdit::_notification(int p_what) {
} break;
#endif
case NOTIFICATION_RESIZED: {
- window_pos = 0;
+ _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));
@@ -728,39 +736,44 @@ 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;
+ 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(MARGIN_LEFT), int(size.width - style->get_margin(MARGIN_RIGHT) - (text_width)));
+ } else {
+ x_ofs = style->get_offset().x;
+ }
} break;
case ALIGN_CENTER: {
- if (window_pos != 0) {
+ 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(MARGIN_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(MARGIN_LEFT), int(size.width - style->get_margin(MARGIN_RIGHT) - (text_width)));
+ }
} break;
}
int ofs_max = width - style->get_margin(MARGIN_RIGHT);
- int char_ofs = window_pos;
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;
@@ -781,8 +794,8 @@ 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);
if (align == ALIGN_CENTER) {
- if (window_pos == 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);
+ if (scroll_offset == 0) {
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - text_width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT) * 2) / 2);
}
} else {
x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_RIGHT));
@@ -791,137 +804,135 @@ void LineEdit::_notification(int p_what) {
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;
- }
-
- CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs];
- CharType 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;
}
-
- CharType cchar = (pass && !text.empty()) ? secret_character[0] : t[char_ofs];
- CharType 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));
}
-
- CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs];
- CharType 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;
- window_pos = op.window_pos;
+ 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;
- window_pos = op.window_pos;
+ 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 = window_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(MARGIN_LEFT), int(get_size().width - style->get_margin(MARGIN_RIGHT) - (text_width)));
+ } else {
+ x_ofs = style->get_offset().x;
+ }
} break;
case ALIGN_CENTER: {
- if (window_pos != 0) {
- pixel_ofs = int(style->get_offset().x);
+ if (scroll_offset != 0) {
+ 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(MARGIN_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(MARGIN_LEFT), int(get_size().width - style->get_margin(MARGIN_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.empty() && ime_text.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(MARGIN_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT) * 2) / 2);
+ }
+ } else {
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_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 = window_pos;
+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(MARGIN_LEFT), int(get_size().width - style->get_margin(MARGIN_RIGHT) - (text_width)));
+ } else {
+ x_ofs = style->get_offset().x;
+ }
} break;
case ALIGN_CENTER: {
- if (window_pos != 0) {
- pixel_ofs = int(style->get_offset().x);
+ if (scroll_offset != 0) {
+ 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(MARGIN_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(MARGIN_LEFT), int(get_size().width - style->get_margin(MARGIN_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.empty() && ime_text.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(MARGIN_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT) * 2) / 2);
+ }
+ } else {
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_RIGHT));
+ }
+ }
+
+ 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);
}
- ofs++;
+ 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 pixel_ofs;
+ 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) {
- window_pos = CLAMP(window_pos - 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 (window_pos > cursor_pos) {
- window_pos = cursor_pos;
- }
-
- if (align == ALIGN_CENTER || align == ALIGN_RIGHT) {
- window_pos = CLAMP(window_pos - (p_to_column - p_from_column), 0, MAX(text.length() - 1, 0));
- }
if (!text_changed_dirty) {
if (is_inside_tree()) {
@@ -1283,13 +1308,100 @@ 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();
- cursor_pos = 0;
- window_pos = 0;
+}
+
+Array LineEdit::get_structured_text_bidi_override_options() const {
+ return st_args;
}
void LineEdit::clear() {
@@ -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()) {
- window_pos = 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 <= window_pos) {
- // Adjust window if cursor goes too much to the left.
- set_window_pos(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 = window_pos;
-
- if (font.is_valid()) {
- int accum_width = 0;
-
- for (int i = cursor_pos; i >= window_pos; 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(MARGIN_LEFT), int(get_size().width - style->get_margin(MARGIN_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(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(MARGIN_LEFT), int(get_size().width - style->get_margin(MARGIN_RIGHT) - (text_width)));
+ }
+ } break;
+ }
- wp = i;
+ int ofs_max = get_size().width - style->get_margin(MARGIN_RIGHT);
+ bool using_placeholder = text.empty() && ime_text.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(MARGIN_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT) * 2) / 2);
}
+ } else {
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_RIGHT));
}
+ ofs_max -= r_icon->get_width();
+ }
- if (wp != window_pos) {
- set_window_pos(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();
}
@@ -1390,19 +1513,27 @@ int LineEdit::get_cursor_position() const {
return cursor_pos;
}
-void LineEdit::set_window_pos(int p_pos) {
- window_pos = p_pos;
- if (window_pos < 0) {
- window_pos = 0;
+void LineEdit::set_scroll_offset(int p_pos) {
+ scroll_offset = p_pos;
+ if (scroll_offset < 0) {
+ scroll_offset = 0;
}
}
+int LineEdit::get_scroll_offset() const {
+ return scroll_offset;
+}
+
void LineEdit::append_at_cursor(String p_text) {
if ((max_length <= 0) || (text.length() + p_text.length() <= max_length)) {
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");
@@ -1412,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;
- window_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.empty() && ime_text.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;
@@ -1527,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();
}
@@ -1541,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();
}
@@ -1619,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));
+ }
}
}
}
@@ -1645,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_window_pos(0);
+ set_cursor_position(cursor_pos);
}
bool LineEdit::get_expand_to_text_length() const {
@@ -1657,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();
}
@@ -1702,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();
}
@@ -1711,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();
}
@@ -1725,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(MARGIN_RIGHT) - style->get_margin(MARGIN_LEFT);
+ bool using_placeholder = text.empty() && ime_text.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));
}
}
@@ -1770,9 +2029,8 @@ 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.window_pos = window_pos;
+ op.scroll_offset = scroll_offset;
undo_stack.push_back(op);
}
@@ -1796,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() {
@@ -1811,16 +2126,32 @@ 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);
ClassDB::bind_method(D_METHOD("get_placeholder_alpha"), &LineEdit::get_placeholder_alpha);
ClassDB::bind_method(D_METHOD("set_cursor_position", "position"), &LineEdit::set_cursor_position);
ClassDB::bind_method(D_METHOD("get_cursor_position"), &LineEdit::get_cursor_position);
+ ClassDB::bind_method(D_METHOD("get_scroll_offset"), &LineEdit::get_scroll_offset);
ClassDB::bind_method(D_METHOD("set_expand_to_text_length", "enabled"), &LineEdit::set_expand_to_text_length);
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);
@@ -1867,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");
@@ -1882,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");
@@ -1890,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;
- window_pos = 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 d6cc1f1f11..e7b2a34eed 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -39,7 +39,6 @@ class LineEdit : public Control {
public:
enum Align {
-
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT,
@@ -54,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;
- PopupMenu *menu;
+ bool context_menu_enabled = true;
+ PopupMenu *menu = nullptr;
+ PopupMenu *menu_dir = nullptr;
+ PopupMenu *menu_ctl = nullptr;
- int cursor_pos;
- int window_pos;
- int max_length; // 0 for no maximum.
+ bool mid_grapheme_caret_enabled = false;
- int cached_width;
- int cached_placeholder_width;
+ int cursor_pos = 0;
+ int scroll_offset = 0;
+ int max_length = 0; // 0 for no maximum.
- bool clear_button_enabled;
+ 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 shortcut_keys_enabled;
+ bool expand_to_text_length = false;
+ bool window_has_focus = true;
+
+ bool clear_button_enabled = false;
+
+ bool shortcut_keys_enabled = true;
bool virtual_keyboard_enabled = true;
@@ -106,18 +140,23 @@ private:
struct TextOperation {
int cursor_pos;
- int window_pos;
+ int scroll_offset;
int cached_width;
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();
@@ -126,28 +165,20 @@ 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);
void selection_fill_at_cursor();
- void set_window_pos(int p_pos);
+ void set_scroll_offset(int p_pos);
+ 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 f8c8bd4caf..b66ee514dc 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -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,39 +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_enabled_focus_mode(FOCUS_NONE);
+ 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..8c1daef166 100644
--- a/scene/gui/link_button.h
+++ b/scene/gui/link_button.h
@@ -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 0299065f77..b674b492d8 100644
--- a/scene/gui/margin_container.cpp
+++ b/scene/gui/margin_container.cpp
@@ -43,7 +43,7 @@ Size2 MarginContainer::get_minimum_size() const {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
if (!c->is_visible()) {
@@ -80,7 +80,7 @@ void MarginContainer::_notification(int p_what) {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index aa69fb39e7..b98b3f7094 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -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();
}
@@ -53,17 +54,9 @@ void MenuButton::_unhandled_key_input(Ref<InputEvent> p_event) {
}
void MenuButton::pressed() {
- {
- Window *w = Object::cast_to<Window>(get_viewport());
- if (w && !w->is_embedding_subwindows()) {
- print_line("windowpos: " + w->get_position());
- }
- }
Size2 size = get_size();
Point2 gp = get_screen_position();
-
- print_line("screenpos: " + gp);
gp.y += get_size().y;
popup->set_position(gp);
@@ -108,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);
@@ -130,8 +122,8 @@ MenuButton::MenuButton() {
set_flat(true);
set_toggle_mode(true);
set_disable_shortcuts(false);
- set_enabled_focus_mode(FOCUS_NONE);
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..65b46d5b69 100644
--- a/scene/gui/menu_button.h
+++ b/scene/gui/menu_button.h
@@ -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.h b/scene/gui/nine_patch_rect.h
index 487fe4c860..a539ad43c0 100644
--- a/scene/gui/nine_patch_rect.h
+++ b/scene/gui/nine_patch_rect.h
@@ -79,4 +79,5 @@ public:
};
VARIANT_ENUM_CAST(NinePatchRect::AxisStretchMode)
+
#endif // NINE_PATCH_RECT_H
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 5780cc5e71..902d2715d4 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -30,7 +30,7 @@
#include "option_button.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
Size2 OptionButton::get_minimum_size() const {
Size2 minsize = Button::get_minimum_size();
@@ -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(MARGIN_LEFT, Control::get_theme_icon("arrow")->get_width());
+ _set_internal_margin(MARGIN_RIGHT, 0.f);
+ } else {
+ _set_internal_margin(MARGIN_LEFT, 0.f);
+ _set_internal_margin(MARGIN_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(MARGIN_LEFT, Control::get_theme_icon("arrow")->get_width());
+ }
+ } else {
+ if (has_theme_icon("arrow")) {
+ _set_internal_margin(MARGIN_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/panel.cpp b/scene/gui/panel.cpp
index d8d9beca2b..acbb6d7ab5 100644
--- a/scene/gui/panel.cpp
+++ b/scene/gui/panel.cpp
@@ -30,7 +30,7 @@
#include "panel.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
void Panel::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp
index 9abdfac009..051b4de825 100644
--- a/scene/gui/panel_container.cpp
+++ b/scene/gui/panel_container.cpp
@@ -45,7 +45,7 @@ Size2 PanelContainer::get_minimum_size() const {
if (!c || !c->is_visible()) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
@@ -95,7 +95,7 @@ void PanelContainer::_notification(int p_what) {
if (!c || !c->is_visible_in_tree()) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index 5fc5f9b669..791c78e2b4 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -30,7 +30,7 @@
#include "popup.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "core/os/keyboard.h"
#include "scene/gui/panel.h"
@@ -41,62 +41,90 @@ void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
}
}
-void Popup::_parent_focused() {
- _close_pressed();
+void Popup::_initialize_visible_parents() {
+ visible_parents.clear();
+
+ Window *parent_window = this;
+ while (parent_window) {
+ parent_window = parent_window->get_parent_visible_window();
+ if (parent_window) {
+ visible_parents.push_back(parent_window);
+ parent_window->connect("focus_entered", callable_mp(this, &Popup::_parent_focused));
+ parent_window->connect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
+ }
+ }
+}
+
+void Popup::_deinitialize_visible_parents() {
+ for (uint32_t i = 0; i < visible_parents.size(); ++i) {
+ visible_parents[i]->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
+ visible_parents[i]->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
+ }
+
+ visible_parents.clear();
}
void Popup::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
- parent_visible = get_parent_visible_window();
- if (parent_visible) {
- parent_visible->connect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- }
+ _initialize_visible_parents();
} else {
- if (parent_visible) {
- parent_visible->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- parent_visible = nullptr;
- }
-
+ _deinitialize_visible_parents();
emit_signal("popup_hide");
}
} break;
- case NOTIFICATION_EXIT_TREE: {
- if (parent_visible) {
- parent_visible->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- parent_visible = nullptr;
+ case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
+ if (has_focus()) {
+ popped_up = true;
}
} break;
+ case NOTIFICATION_EXIT_TREE: {
+ _deinitialize_visible_parents();
+ } break;
case NOTIFICATION_WM_CLOSE_REQUEST: {
_close_pressed();
-
+ } break;
+ case NOTIFICATION_APPLICATION_FOCUS_OUT: {
+ _close_pressed();
} break;
}
}
-void Popup::_close_pressed() {
- Window *parent_window = parent_visible;
- if (parent_visible) {
- parent_visible->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- parent_visible = nullptr;
+void Popup::_parent_focused() {
+ if (popped_up && close_on_parent_focus) {
+ _close_pressed();
}
+}
+
+void Popup::_close_pressed() {
+ popped_up = false;
+
+ _deinitialize_visible_parents();
call_deferred("hide");
emit_signal("cancelled");
-
- if (parent_window) {
- //parent_window->grab_focus();
- }
}
void Popup::set_as_minsize() {
set_size(get_contents_minimum_size());
}
+void Popup::set_close_on_parent_focus(bool p_close) {
+ close_on_parent_focus = p_close;
+}
+
+bool Popup::get_close_on_parent_focus() {
+ return close_on_parent_focus;
+}
+
void Popup::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_close_on_parent_focus", "close"), &Popup::set_close_on_parent_focus);
+ ClassDB::bind_method(D_METHOD("get_close_on_parent_focus"), &Popup::get_close_on_parent_focus);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "close_on_parent_focus"), "set_close_on_parent_focus", "get_close_on_parent_focus");
+
ADD_SIGNAL(MethodInfo("popup_hide"));
}
@@ -126,12 +154,32 @@ Rect2i Popup::_popup_adjust_rect() const {
current.position.y = parent.position.y;
}
+ if (current.size.y > parent.size.y) {
+ current.size.y = parent.size.y;
+ }
+
+ if (current.size.x > parent.size.x) {
+ current.size.x = parent.size.x;
+ }
+
+ // Early out if max size not set.
+ Size2i max_size = get_max_size();
+ if (max_size <= Size2()) {
+ return current;
+ }
+
+ if (current.size.x > max_size.x) {
+ current.size.x = max_size.x;
+ }
+
+ if (current.size.y > max_size.y) {
+ current.size.y = max_size.y;
+ }
+
return current;
}
Popup::Popup() {
- parent_visible = nullptr;
-
set_wrap_controls(true);
set_visible(false);
set_transient(true);
@@ -155,7 +203,7 @@ Size2 PopupPanel::_get_contents_minimum_size() const {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
@@ -179,7 +227,7 @@ void PopupPanel::_update_child_rects() {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index 97c08095d3..48e7ea9452 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -33,12 +33,20 @@
#include "scene/main/window.h"
+#include "core/templates/local_vector.h"
+
class Popup : public Window {
GDCLASS(Popup, Window);
- Window *parent_visible;
+ LocalVector<Window *> visible_parents;
+ bool popped_up = false;
+ bool close_on_parent_focus = true;
void _input_from_window(const Ref<InputEvent> &p_event);
+
+ void _initialize_visible_parents();
+ void _deinitialize_visible_parents();
+
void _parent_focused();
protected:
@@ -50,6 +58,10 @@ protected:
public:
void set_as_minsize();
+
+ void set_close_on_parent_focus(bool p_close);
+ bool get_close_on_parent_focus();
+
Popup();
~Popup();
};
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 6e19b820e0..bfbb3cb3f3 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -33,17 +33,14 @@
#include "core/input/input.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "core/print_string.h"
-#include "core/translation.h"
-#include "scene/gui/control.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);
+#include "core/string/print_string.h"
+#include "core/string/translation.h"
+
+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();
}
@@ -52,25 +49,21 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
int vseparation = get_theme_constant("vseparation");
int hseparation = get_theme_constant("hseparation");
- Size2 minsize = get_theme_stylebox("panel")->get_minimum_size();
- Ref<Font> font = get_theme_font("font");
+ 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
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;
for (int i = 0; i < items.size(); i++) {
Size2 size;
- if (!items[i].icon.is_null()) {
- Size2 icon_size = items[i].icon->get_size();
- size.height = MAX(icon_size.height, font_h);
- icon_w = MAX(icon_size.width + hseparation, icon_w);
- } else {
- size.height = font_h;
- }
+
+ Size2 icon_size = items[i].get_icon_size();
+ 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;
@@ -78,15 +71,14 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
has_check = true;
}
- String text = items[i].xl_text;
- size.width += font->get_string_size(text).width;
+ size.width += items[i].text_buf->get_size().x;
if (i > 0) {
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);
}
@@ -104,39 +96,69 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
minsize.width += check_w;
}
+ if (is_inside_tree()) {
+ int height_limit = get_usable_parent_rect().size.height;
+ if (minsize.height > height_limit) {
+ minsize.height = height_limit;
+ }
+ }
+
return minsize;
}
+int PopupMenu::_get_items_total_height() const {
+ 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, items[i].text_buf->get_size().y) + vsep;
+ }
+
+ // Subtract a separator which is not needed for the last item.
+ return items_total_height - vsep;
+}
+
+void PopupMenu::_scroll_to_item(int p_item) {
+ ERR_FAIL_INDEX(p_item, items.size());
+ ERR_FAIL_COND(p_item < 0);
+
+ // Scroll item into view (upwards)
+ if (items[p_item]._ofs_cache < -control->get_position().y) {
+ int amnt_over = items[p_item]._ofs_cache + control->get_position().y;
+ scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
+ }
+
+ // Scroll item into view (downwards)
+ if (items[p_item]._ofs_cache + items[p_item]._height_cache > -control->get_position().y + scroll_container->get_size().height) {
+ int amnt_over = items[p_item]._ofs_cache + items[p_item]._height_cache + control->get_position().y - scroll_container->get_size().height;
+ scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
+ }
+}
+
int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
if (p_over.x < 0 || p_over.x >= get_size().width) {
return -1;
}
- Ref<StyleBox> style = get_theme_stylebox("panel");
+ Ref<StyleBox> style = get_theme_stylebox("panel"); // Accounts for margin in the margin container
- Point2 ofs = style->get_offset();
+ int vseparation = get_theme_constant("vseparation");
+
+ Point2 ofs = style->get_offset() + Point2(0, vseparation / 2);
if (ofs.y > p_over.y) {
return -1;
}
- Ref<Font> font = get_theme_font("font");
- int vseparation = get_theme_constant("vseparation");
- float font_h = font->get_height();
-
for (int i = 0; i < items.size(); i++) {
- ofs.y += vseparation;
- float h;
-
- if (!items[i].icon.is_null()) {
- Size2 icon_size = items[i].icon->get_size();
- h = MAX(icon_size.height, font_h);
- } else {
- h = font_h;
+ if (i > 0) {
+ ofs.y += vseparation;
}
- ofs.y += h;
- if (p_over.y < ofs.y) {
+ 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;
}
}
@@ -144,46 +166,64 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
return -1;
}
-void PopupMenu::_activate_submenu(int over) {
- Node *n = get_node(items[over].submenu);
- ERR_FAIL_COND_MSG(!n, "Item subnode does not exist: " + items[over].submenu + ".");
- Popup *pm = Object::cast_to<Popup>(n);
- ERR_FAIL_COND_MSG(!pm, "Item subnode is not a Popup: " + items[over].submenu + ".");
- if (pm->is_visible()) {
+void PopupMenu::_activate_submenu(int p_over) {
+ Node *n = get_node(items[p_over].submenu);
+ ERR_FAIL_COND_MSG(!n, "Item subnode does not exist: " + items[p_over].submenu + ".");
+ Popup *submenu_popup = Object::cast_to<Popup>(n);
+ ERR_FAIL_COND_MSG(!submenu_popup, "Item subnode is not a Popup: " + items[p_over].submenu + ".");
+ if (submenu_popup->is_visible()) {
return; //already visible!
}
- Point2 p = get_position();
- Rect2 pr(p, get_size());
Ref<StyleBox> style = get_theme_stylebox("panel");
+ int vsep = get_theme_constant("vseparation");
+
+ Point2 this_pos = get_position();
+ Rect2 this_rect(this_pos, get_size());
+
+ float scroll_offset = control->get_position().y;
- Point2 pos = p + Point2(get_size().width, items[over]._ofs_cache - style->get_offset().y);
- Size2 size = pm->get_size();
- // fix pos
- if (pos.x + size.width > get_parent_rect().size.width) {
- pos.x = p.x - size.width;
+ 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;
}
- pm->set_position(pos);
- // pm->set_scale(get_global_transform().get_scale());
- pm->popup();
-
- PopupMenu *pum = Object::cast_to<PopupMenu>(pm);
- if (pum) {
- pr.position -= pum->get_position();
- pum->clear_autohide_areas();
- pum->add_autohide_area(Rect2(pr.position.x, pr.position.y, pr.size.x, items[over]._ofs_cache));
- if (over < items.size() - 1) {
- int from = items[over + 1]._ofs_cache;
- pum->add_autohide_area(Rect2(pr.position.x, pr.position.y + from, pr.size.x, pr.size.y - from));
+ submenu_popup->set_close_on_parent_focus(false);
+ submenu_popup->set_position(submenu_pos);
+ submenu_popup->set_as_minsize(); // Shrink the popup size to it's contents.
+ submenu_popup->popup();
+
+ // Set autohide areas
+ PopupMenu *submenu_pum = Object::cast_to<PopupMenu>(submenu_popup);
+ if (submenu_pum) {
+ // Make the position of the parent popup relative to submenu popup
+ this_rect.position = this_rect.position - submenu_pum->get_position();
+
+ // Autohide area above the submenu item
+ submenu_pum->clear_autohide_areas();
+ submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2));
+
+ // If there is an area below the submenu item, add an autohide area there.
+ if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) {
+ int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height;
+ submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from));
}
}
}
void PopupMenu::_submenu_timeout() {
- //if (!has_focus()) {
- // return; //do not activate if not has focus
- //}
if (mouse_over == submenu_over) {
_activate_submenu(mouse_over);
}
@@ -191,36 +231,6 @@ void PopupMenu::_submenu_timeout() {
submenu_over = -1;
}
-void PopupMenu::_scroll(float p_factor, const Point2 &p_over) {
- int vseparation = get_theme_constant("vseparation");
- Ref<Font> font = get_theme_font("font");
-
- Rect2 visible_rect = get_usable_parent_rect();
-
- int dy = (vseparation + font->get_height()) * 3 * p_factor;
- if (dy > 0) {
- const float global_top = get_position().y;
- const float limit = global_top < visible_rect.position.y ? visible_rect.position.y - global_top : 0;
- dy = MIN(dy, limit);
- } else if (dy < 0) {
- const float global_bottom = get_position().y + get_size().y;
- const float viewport_height = visible_rect.position.y + visible_rect.size.y;
- const float limit = global_bottom > viewport_height ? global_bottom - viewport_height : 0;
- dy = -MIN(-dy, limit);
- }
-
- if (dy == 0) {
- return;
- }
-
- set_position(get_position() + Vector2(0, dy));
-
- Ref<InputEventMouseMotion> ie;
- ie.instance();
- ie->set_position(p_over - Vector2(0, dy));
- _gui_input(ie);
-}
-
void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
if (p_event->is_action("ui_down") && p_event->is_pressed()) {
int search_from = mouse_over + 1;
@@ -229,13 +239,10 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
}
for (int i = search_from; i < items.size(); i++) {
- if (i < 0 || i >= items.size()) {
- continue;
- }
-
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;
@@ -248,13 +255,10 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
}
for (int i = search_from; i >= 0; i--) {
- if (i >= items.size()) {
- continue;
- }
-
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;
@@ -282,65 +286,65 @@ 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()) {
+ 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;
if (b.is_valid()) {
- if (b->is_pressed()) {
+ if (!item_clickable_area.has_point(b->get_position())) {
return;
}
int button_idx = b->get_button_index();
- switch (button_idx) {
- case BUTTON_WHEEL_DOWN: {
- _scroll(-b->get_factor(), b->get_position());
- } break;
- case BUTTON_WHEEL_UP: {
- _scroll(b->get_factor(), b->get_position());
- } break;
- default: {
- // Allow activating item by releasing the LMB or any that was down when the popup appeared
- if (button_idx == BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) {
- bool was_during_grabbed_click = during_grabbed_click;
- during_grabbed_click = false;
- initial_button_mask = 0;
-
- int over = _get_mouse_over(b->get_position());
-
- if (invalidated_click) {
- invalidated_click = false;
- break;
- }
- if (over < 0) {
- if (!was_during_grabbed_click) {
- hide();
- }
- break; //non-activable
- }
+ if (b->is_pressed() || (!b->is_pressed() && during_grabbed_click)) {
+ // Allow activating item by releasing the LMB or any that was down when the popup appeared.
+ // However, if button was not held when opening menu, do not allow release to activate item.
+ if (button_idx == BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) {
+ bool was_during_grabbed_click = during_grabbed_click;
+ during_grabbed_click = false;
+ initial_button_mask = 0;
+
+ // Disable clicks under a time threshold to avoid selection right when opening the popup.
+ uint64_t now = OS::get_singleton()->get_ticks_msec();
+ uint64_t diff = now - popup_time_msec;
+ if (diff < 100) {
+ return;
+ }
- if (items[over].separator || items[over].disabled) {
- break;
+ int over = _get_mouse_over(b->get_position());
+ if (over < 0) {
+ if (!was_during_grabbed_click) {
+ hide();
}
+ return;
+ }
- if (items[over].submenu != "") {
- _activate_submenu(over);
- return;
- }
- activate_item(over);
+ if (items[over].separator || items[over].disabled) {
+ return;
}
+
+ if (items[over].submenu != "") {
+ _activate_submenu(over);
+ return;
+ }
+ activate_item(over);
}
}
-
- //control->update();
}
Ref<InputEventMouseMotion> m = p_event;
if (m.is_valid()) {
- if (invalidated_click) {
- moved += m->get_relative();
- if (moved.length() > 4) {
- invalidated_click = false;
- }
+ if (!item_clickable_area.has_point(m->get_position())) {
+ return;
}
for (List<Rect2>::Element *E = autohide_areas.front(); E; E = E->next()) {
@@ -370,11 +374,6 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
}
}
- Ref<InputEventPanGesture> pan_gesture = p_event;
- if (pan_gesture.is_valid()) {
- _scroll(-pan_gesture->get_delta().y, pan_gesture->get_position());
- }
-
Ref<InputEventKey> k = p_event;
if (allow_search && k.is_valid() && k->get_unicode() && k->is_pressed()) {
@@ -407,6 +406,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
if (items[i].text.findn(search_string) == 0) {
mouse_over = i;
emit_signal("id_focused", i);
+ _scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -415,38 +415,47 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
}
}
-void PopupMenu::_draw() {
+void PopupMenu::_draw_items() {
+ control->set_custom_minimum_size(Size2(0, _get_items_total_height()));
RID ci = control->get_canvas_item();
- Size2 size = get_size();
+ Size2 margin_size;
+ 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");
- style->draw(ci, Rect2(Point2(), get_size()));
- Point2 ofs = style->get_offset();
int vseparation = get_theme_constant("vseparation");
int hseparation = get_theme_constant("hseparation");
Color font_color = get_theme_color("font_color");
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;
- // Add the check and the wider icon to the offset of all items.
+ // Find the widest icon and whether any items have a checkbox, and store the offsets for each.
float icon_ofs = 0.0;
bool has_check = false;
for (int i = 0; i < items.size(); i++) {
- if (!items[i].icon.is_null()) {
- icon_ofs = MAX(items[i].icon->get_size().width, icon_ofs);
- }
+ icon_ofs = MAX(items[i].get_icon_size().width, icon_ofs);
if (items[i].checkable_type) {
has_check = true;
@@ -461,85 +470,166 @@ void PopupMenu::_draw() {
check_ofs = MAX(get_theme_icon("checked")->get_width(), get_theme_icon("radio_checked")->get_width()) + hseparation;
}
+ Point2 ofs = Point2();
+
+ // 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;
}
- Point2 item_ofs = ofs;
- Size2 icon_size;
- float h;
- if (!items[i].icon.is_null()) {
- icon_size = items[i].icon->get_size();
- h = MAX(icon_size.height, font_h);
- } else {
- h = font_h;
- }
+ _shape_item(i);
+
+ Point2 item_ofs = ofs;
+ Size2 icon_size = items[i].get_icon_size();
+ 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(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation)));
+ if (rtl) {
+ hover->draw(ci, Rect2(item_ofs + Point2(-hseparation + scroll_width, -vseparation / 2), Size2(display_width + hseparation * 2, h + vseparation)));
+ } else {
+ hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(display_width + hseparation * 2, h + vseparation)));
+ }
}
String text = items[i].xl_text;
+ // Separator
item_ofs.x += items[i].h_ofs;
if (items[i].separator) {
int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
if (text != String()) {
- int ss = font->get_string_size(text).width;
- int center = (get_size().width) / 2;
- int l = center - ss / 2;
- int r = center + ss / 2;
- if (l > item_ofs.x) {
- labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, l - item_ofs.x), sep_h)));
+ 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;
+ if (text_left > item_ofs.x) {
+ labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, text_left - item_ofs.x), sep_h)));
}
- if (r < get_size().width - style->get_margin(MARGIN_RIGHT)) {
- labeled_separator_right->draw(ci, Rect2(Point2(r, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, get_size().width - style->get_margin(MARGIN_RIGHT) - r), sep_h)));
+ if (text_right < display_width) {
+ labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, display_width - text_right), sep_h)));
}
} else {
- separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
+ separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(display_width, sep_h)));
}
}
Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1);
+ // 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(size.width - style->get_margin(MARGIN_RIGHT) - 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(MARGIN_LEFT), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
+ } else {
+ submenu->draw(ci, Point2(display_width - style->get_margin(MARGIN_RIGHT) - submenu->get_width(), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
+ }
}
- item_ofs.y += font->get_ascent();
+ // Text
if (items[i].separator) {
if (text != String()) {
- int center = (get_size().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())) {
- //accelerator
- String text2 = _get_accel_text(i);
- item_ofs.x = size.width - style->get_margin(MARGIN_RIGHT) - font->get_string_size(text2).width;
- font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text2, i == mouse_over ? font_color_hover : font_color_accel);
+ if (rtl) {
+ item_ofs.x = scroll_width + style->get_margin(MARGIN_LEFT);
+ } else {
+ item_ofs.x = display_width - style->get_margin(MARGIN_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
items.write[i]._ofs_cache = ofs.y;
+ items.write[i]._height_cache = h;
ofs.y += h;
}
}
+void PopupMenu::_draw_background() {
+ Ref<StyleBox> style = get_theme_stylebox("panel");
+ RID ci2 = margin_container->get_canvas_item();
+ style->draw(ci2, Rect2(Point2(), margin_container->get_size()));
+}
+
+void PopupMenu::_minimum_lifetime_timeout() {
+ close_allowed = true;
+ // If the mouse still isn't in this popup after timer expires, close.
+ if (!get_visible_rect().has_point(get_mouse_position())) {
+ _close_pressed();
+ }
+}
+
+void PopupMenu::_close_pressed() {
+ // Only apply minimum lifetime to submenus.
+ PopupMenu *parent_pum = Object::cast_to<PopupMenu>(get_parent());
+ if (!parent_pum) {
+ Popup::_close_pressed();
+ return;
+ }
+
+ // If the timer has expired, close. If timer is still running, do nothing.
+ if (close_allowed) {
+ close_allowed = false;
+ Popup::_close_pressed();
+ } else if (minimum_lifetime_timer->is_stopped()) {
+ minimum_lifetime_timer->start();
+ }
+}
+
+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: {
@@ -550,16 +640,19 @@ 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();
control->update();
} break;
case NOTIFICATION_WM_MOUSE_ENTER: {
- //grab_focus();
+ grab_focus();
} break;
case NOTIFICATION_WM_MOUSE_EXIT: {
if (mouse_over >= 0 && (items[mouse_over].submenu == "" || submenu_over != -1)) {
@@ -617,6 +710,13 @@ void PopupMenu::_notification(int p_what) {
if (get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
set_process_internal(true);
}
+
+ // 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));
}
} break;
}
@@ -636,6 +736,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();
}
@@ -645,6 +746,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();
}
@@ -654,6 +756,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();
}
@@ -664,6 +767,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();
}
@@ -673,6 +777,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();
}
@@ -683,6 +788,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();
}
@@ -693,12 +799,13 @@ 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();
}
#define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global) \
- ERR_FAIL_COND_MSG(p_shortcut.is_null(), "Cannot add item with invalid ShortCut."); \
+ ERR_FAIL_COND_MSG(p_shortcut.is_null(), "Cannot add item with invalid Shortcut."); \
_ref_shortcut(p_shortcut); \
item.text = p_shortcut->get_name(); \
item.xl_text = tr(item.text); \
@@ -706,57 +813,63 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
item.shortcut = p_shortcut; \
item.shortcut_is_global = p_global;
-void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
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();
}
-void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.icon = p_icon;
items.push_back(item);
+ _shape_item(items.size() - 1);
control->update();
child_controls_changed();
}
-void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
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();
}
-void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
+ _shape_item(items.size() - 1);
control->update();
child_controls_changed();
}
-void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
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();
}
-void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
+ _shape_item(items.size() - 1);
control->update();
child_controls_changed();
}
@@ -768,6 +881,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();
}
@@ -781,11 +895,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;
@@ -814,6 +965,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();
@@ -852,6 +1004,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) {
@@ -912,8 +1083,8 @@ String PopupMenu::get_item_tooltip(int p_idx) const {
return items[p_idx].tooltip;
}
-Ref<ShortCut> PopupMenu::get_item_shortcut(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, items.size(), Ref<ShortCut>());
+Ref<Shortcut> PopupMenu::get_item_shortcut(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), Ref<Shortcut>());
return items[p_idx].shortcut;
}
@@ -951,13 +1122,14 @@ void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) {
control->update();
}
-void PopupMenu::set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bool p_global) {
+void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bool p_global) {
ERR_FAIL_INDEX(p_idx, items.size());
if (items[p_idx].shortcut.is_valid()) {
_unref_shortcut(items[p_idx].shortcut);
}
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);
@@ -1190,7 +1362,7 @@ Array PopupMenu::_get_items() const {
return items;
}
-void PopupMenu::_ref_shortcut(Ref<ShortCut> p_sc) {
+void PopupMenu::_ref_shortcut(Ref<Shortcut> p_sc) {
if (!shortcut_refcount.has(p_sc)) {
shortcut_refcount[p_sc] = 1;
p_sc->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::update));
@@ -1199,7 +1371,7 @@ void PopupMenu::_ref_shortcut(Ref<ShortCut> p_sc) {
}
}
-void PopupMenu::_unref_shortcut(Ref<ShortCut> p_sc) {
+void PopupMenu::_unref_shortcut(Ref<Shortcut> p_sc) {
ERR_FAIL_COND(!shortcut_refcount.has(p_sc));
shortcut_refcount[p_sc]--;
if (shortcut_refcount[p_sc] == 0) {
@@ -1350,6 +1522,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);
@@ -1369,6 +1544,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);
@@ -1424,23 +1603,38 @@ void PopupMenu::_bind_methods() {
void PopupMenu::popup(const Rect2 &p_bounds) {
moved = Vector2();
- invalidated_click = true;
+ 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);
+ add_child(margin_container);
+ margin_container->connect("draw", callable_mp(this, &PopupMenu::_draw_background));
+
+ // Scroll Container
+ scroll_container = memnew(ScrollContainer);
+ scroll_container->set_clip_contents(true);
+ margin_container->add_child(scroll_container);
+
+ // The control which will display the items
control = memnew(Control);
- add_child(control);
-
+ control->set_clip_contents(false);
control->set_anchors_and_margins_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);
+ control->connect("draw", callable_mp(this, &PopupMenu::_draw_items));
+
connect("window_input", callable_mp(this, &PopupMenu::_gui_input));
- control->connect("draw", callable_mp(this, &PopupMenu::_draw));
mouse_over = -1;
submenu_over = -1;
initial_button_mask = 0;
during_grabbed_click = false;
- invalidated_click = false;
allow_search = true;
search_time_msec = 0;
@@ -1455,6 +1649,12 @@ PopupMenu::PopupMenu() {
submenu_timer->set_one_shot(true);
submenu_timer->connect("timeout", callable_mp(this, &PopupMenu::_submenu_timeout));
add_child(submenu_timer);
+
+ minimum_lifetime_timer = memnew(Timer);
+ minimum_lifetime_timer->set_wait_time(0.3);
+ minimum_lifetime_timer->set_one_shot(true);
+ minimum_lifetime_timer->connect("timeout", callable_mp(this, &PopupMenu::_minimum_lifetime_timeout));
+ add_child(minimum_lifetime_timer);
}
PopupMenu::~PopupMenu() {
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 43cd7a54e9..a082fcf0e7 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -31,8 +31,11 @@
#ifndef POPUP_MENU_H
#define POPUP_MENU_H
+#include "scene/gui/margin_container.h"
#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);
@@ -41,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,
@@ -51,18 +61,28 @@ class PopupMenu : public Popup {
int state;
bool separator;
bool disabled;
+ bool dirty;
int id;
Variant metadata;
String submenu;
String tooltip;
uint32_t accel;
int _ofs_cache;
+ int _height_cache;
int h_ofs;
- Ref<ShortCut> shortcut;
+ Ref<Shortcut> shortcut;
bool shortcut_is_global;
bool shortcut_is_disabled;
+ // Returns (0,0) if icon is null.
+ Size2 get_icon_size() const {
+ return icon.is_null() ? Size2() : icon->get_size();
+ }
+
Item() {
+ text_buf.instance();
+ accel_text_buf.instance();
+ dirty = true;
checked = false;
checkable_type = CHECKABLE_TYPE_NONE;
separator = false;
@@ -71,12 +91,16 @@ class PopupMenu : public Popup {
accel = 0;
disabled = false;
_ofs_cache = 0;
+ _height_cache = 0;
h_ofs = 0;
shortcut_is_global = false;
shortcut_is_disabled = false;
}
};
+ bool close_allowed = false;
+
+ Timer *minimum_lifetime_timer = nullptr;
Timer *submenu_timer;
List<Rect2> autohide_areas;
Vector<Item> items;
@@ -85,15 +109,20 @@ 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;
- void _scroll(float p_factor, const Point2 &p_over);
+
+ 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 over);
+ void _activate_submenu(int p_over);
void _submenu_timeout();
- bool invalidated_click;
+ uint64_t popup_time_msec = 0;
bool hide_on_item_selection;
bool hide_on_checkable_item_selection;
bool hide_on_multistate_item_selection;
@@ -102,18 +131,24 @@ class PopupMenu : public Popup {
Array _get_items() const;
void _set_items(const Array &p_items);
- Map<Ref<ShortCut>, int> shortcut_refcount;
+ Map<Ref<Shortcut>, int> shortcut_refcount;
- void _ref_shortcut(Ref<ShortCut> p_sc);
- void _unref_shortcut(Ref<ShortCut> p_sc);
+ void _ref_shortcut(Ref<Shortcut> p_sc);
+ void _unref_shortcut(Ref<Shortcut> p_sc);
bool allow_search;
uint64_t search_time_msec;
String search_string;
+ MarginContainer *margin_container;
+ ScrollContainer *scroll_container;
Control *control;
- void _draw();
+ void _draw_items();
+ void _draw_background();
+
+ void _minimum_lifetime_timeout();
+ void _close_pressed();
protected:
friend class MenuButton;
@@ -130,16 +165,21 @@ public:
void add_multistate_item(const String &p_label, int p_max_states, int p_default_state = 0, int p_id = -1, uint32_t p_accel = 0);
- void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
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);
@@ -151,7 +191,7 @@ public:
void set_item_as_checkable(int p_idx, bool p_checkable);
void set_item_as_radio_checkable(int p_idx, bool p_radio_checkable);
void set_item_tooltip(int p_idx, const String &p_tooltip);
- void set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bool p_global = false);
+ void set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bool p_global = false);
void set_item_h_offset(int p_idx, int p_offset);
void set_item_multistate(int p_idx, int p_state);
void toggle_item_multistate(int p_idx);
@@ -160,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;
@@ -174,7 +217,7 @@ public:
bool is_item_radio_checkable(int p_idx) const;
bool is_item_shortcut_disabled(int p_idx) const;
String get_item_tooltip(int p_idx) const;
- Ref<ShortCut> get_item_shortcut(int p_idx) const;
+ Ref<Shortcut> get_item_shortcut(int p_idx) const;
int get_item_state(int p_idx) const;
int get_current_index() const;
diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp
index 9246f1723d..1344d010ae 100644
--- a/scene/gui/progress_bar.cpp
+++ b/scene/gui/progress_bar.cpp
@@ -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, font_color);
}
}
}
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index 59e26d9e38..bdb9f408f0 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -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 != String()) {
+ if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0.");
diff --git a/scene/gui/reference_rect.cpp b/scene/gui/reference_rect.cpp
index 27c57c684a..773acb2713 100644
--- a/scene/gui/reference_rect.cpp
+++ b/scene/gui/reference_rect.cpp
@@ -30,7 +30,7 @@
#include "reference_rect.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
void ReferenceRect::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
@@ -38,7 +38,7 @@ void ReferenceRect::_notification(int p_what) {
return;
}
if (Engine::get_singleton()->is_editor_hint() || !editor_only) {
- draw_rect(Rect2(Point2(), get_size()), border_color, false);
+ draw_rect(Rect2(Point2(), get_size()), border_color, false, border_width);
}
}
}
@@ -52,6 +52,15 @@ Color ReferenceRect::get_border_color() const {
return border_color;
}
+void ReferenceRect::set_border_width(float p_width) {
+ border_width = MAX(0.0, p_width);
+ update();
+}
+
+float ReferenceRect::get_border_width() const {
+ return border_width;
+}
+
void ReferenceRect::set_editor_only(const bool &p_enabled) {
editor_only = p_enabled;
update();
@@ -65,14 +74,13 @@ void ReferenceRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_border_color"), &ReferenceRect::get_border_color);
ClassDB::bind_method(D_METHOD("set_border_color", "color"), &ReferenceRect::set_border_color);
+ ClassDB::bind_method(D_METHOD("get_border_width"), &ReferenceRect::get_border_width);
+ ClassDB::bind_method(D_METHOD("set_border_width", "width"), &ReferenceRect::set_border_width);
+
ClassDB::bind_method(D_METHOD("get_editor_only"), &ReferenceRect::get_editor_only);
ClassDB::bind_method(D_METHOD("set_editor_only", "enabled"), &ReferenceRect::set_editor_only);
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "border_width", PROPERTY_HINT_RANGE, "0.0,5.0,0.1,or_greater"), "set_border_width", "get_border_width");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "get_editor_only");
}
-
-ReferenceRect::ReferenceRect() {
- border_color = Color(1, 0, 0);
- editor_only = true;
-}
diff --git a/scene/gui/reference_rect.h b/scene/gui/reference_rect.h
index db2f4269f3..becbbf47c5 100644
--- a/scene/gui/reference_rect.h
+++ b/scene/gui/reference_rect.h
@@ -35,19 +35,22 @@
class ReferenceRect : public Control {
GDCLASS(ReferenceRect, Control);
- Color border_color;
- bool editor_only;
+
+ Color border_color = Color(1, 0, 0);
+ float border_width = 1.0;
+ bool editor_only = true;
protected:
void _notification(int p_what);
static void _bind_methods();
public:
- ReferenceRect();
-
void set_border_color(const Color &p_color);
Color get_border_color() const;
+ void set_border_width(float p_width);
+ float get_border_width() const;
+
void set_editor_only(const bool &p_enabled);
bool get_editor_only() const;
};
diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp
index 2628e5ab0f..76ca8abcc7 100644
--- a/scene/gui/rich_text_effect.cpp
+++ b/scene/gui/rich_text_effect.cpp
@@ -30,7 +30,7 @@
#include "rich_text_effect.h"
-#include "core/script_language.h"
+#include "core/object/script_language.h"
void RichTextEffect::_bind_methods() {
BIND_VMETHOD(MethodInfo(Variant::BOOL, "_process_custom_fx", PropertyInfo(Variant::OBJECT, "char_fx", PROPERTY_HINT_RESOURCE_TYPE, "CharFXTransform")));
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
index 77c82aa780..e6b9f09e4d 100644
--- a/scene/gui/rich_text_effect.h
+++ b/scene/gui/rich_text_effect.h
@@ -31,7 +31,7 @@
#ifndef RICH_TEXT_EFFECT_H
#define RICH_TEXT_EFFECT_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class RichTextEffect : public Resource {
GDCLASS(RichTextEffect, Resource);
@@ -59,7 +59,7 @@ public:
bool visibility;
Point2 offset;
Color color;
- CharType character;
+ char32_t character;
float elapsed_time;
Dictionary environment;
@@ -79,7 +79,7 @@ public:
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 = (CharType)p_char; }
+ void set_character(int p_char) { character = (char32_t)p_char; }
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 41cd67dbf1..e3b645591c 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -188,7 +188,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
wofs += line_ofs;
}
- int begin = wofs;
+ int begin = margin;
Ref<Font> cfont = _find_font(it);
if (cfont.is_null()) {
@@ -256,7 +256,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
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 ((p_mode == PROCESS_DRAW) && (align != ALIGN_FILL)) { \
+ if (align != ALIGN_FILL) { \
if (line < l.offset_caches.size()) { \
wofs = l.offset_caches[line]; \
} \
@@ -354,8 +354,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
font = p_base_font;
}
- const CharType *c = text->text.c_str();
- const CharType *cf = c;
+ const char32_t *c = text->text.get_data();
+ const char32_t *cf = c;
int ascent = font->get_ascent();
int descent = font->get_descent();
@@ -393,7 +393,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
}
rchar = 0;
- FontDrawer drawer(font, Color(1, 1, 1));
+ //FontDrawer drawer(font, Color(1, 1, 1));
while (*c) {
int end = 0;
int w = 0;
@@ -461,7 +461,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
bool selected = false;
Color fx_color = Color(color);
Point2 fx_offset;
- CharType fx_char = c[i];
+ char32_t fx_char = c[i];
if (selection.active) {
int cofs = (&c[i]) - cf;
@@ -569,19 +569,19 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
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);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs + fx_offset, fx_char, c[i + 1], -1, p_font_color_shadow);
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);
+ 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], -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], -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], -1, p_font_color_shadow);
}
}
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);
+ font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], -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);
+ cw = font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], -1, fx_color);
}
} else if (previously_visible && c[i] != '\t') {
backtrack += font->get_char_size(fx_char, c[i + 1]).x;
@@ -1424,20 +1424,6 @@ 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);
-
- Item *item = p_item;
-
- while (item) {
- if (item->type == p_type) {
- return true;
- }
- item = item->parent;
- }
- return false;
-}
-
void RichTextLabel::_fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack) {
Item *item = p_item;
while (item) {
@@ -2529,9 +2515,9 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p
return false;
}
-void RichTextLabel::selection_copy() {
+String RichTextLabel::get_selected_text() {
if (!selection.active || !selection.enabled) {
- return;
+ return "";
}
String text;
@@ -2561,6 +2547,12 @@ void RichTextLabel::selection_copy() {
item = _get_next_item(item, true);
}
+ return text;
+}
+
+void RichTextLabel::selection_copy() {
+ String text = get_selected_text();
+
if (text != "") {
DisplayServer::get_singleton()->clipboard_set(text);
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 2bda7c7fce..2c74eb741d 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -39,7 +39,6 @@ class RichTextLabel : public Control {
public:
enum Align {
-
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT,
@@ -47,14 +46,12 @@ public:
};
enum ListType {
-
LIST_NUMBERS,
LIST_LETTERS,
LIST_DOTS
};
enum ItemType {
-
ITEM_FRAME,
ITEM_TEXT,
ITEM_IMAGE,
@@ -344,7 +341,6 @@ private:
};
enum ProcessMode {
-
PROCESS_CACHE,
PROCESS_DRAW,
PROCESS_POINTER
@@ -379,7 +375,6 @@ private:
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);
@@ -475,6 +470,7 @@ public:
void set_selection_enabled(bool p_enabled);
bool is_selection_enabled() const;
+ String get_selected_text();
void selection_copy();
Error parse_bbcode(const String &p_bbcode);
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 0e9ef71892..70de63fd40 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -32,7 +32,7 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include "scene/main/window.h"
bool ScrollBar::focus_by_default = false;
@@ -449,18 +449,6 @@ double ScrollBar::get_area_offset() const {
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..358ed74965 100644
--- a/scene/gui/scroll_bar.h
+++ b/scene/gui/scroll_bar.h
@@ -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 b72b913af0..3a54ac7443 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -45,7 +45,7 @@ Size2 ScrollContainer::get_minimum_size() const {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
if (c == h_scroll || c == v_scroll) {
@@ -213,6 +213,10 @@ 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();
@@ -228,6 +232,8 @@ void ScrollContainer::_update_scrollbar_position() {
h_scroll->raise();
v_scroll->raise();
+
+ _updating_scrollbars = false;
}
void ScrollContainer::_ensure_focused_visible(Control *p_control) {
@@ -249,13 +255,18 @@ 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) {
+ 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");
};
@@ -271,6 +282,7 @@ void ScrollContainer::_notification(int p_what) {
Ref<StyleBox> sb = get_theme_stylebox("bg");
size -= sb->get_minimum_size();
ofs += sb->get_offset();
+ bool rtl = is_layout_rtl();
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;
@@ -285,7 +297,7 @@ void ScrollContainer::_notification(int p_what) {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
if (c == h_scroll || c == v_scroll) {
@@ -313,6 +325,9 @@ void ScrollContainer::_notification(int p_what) {
}
}
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;
+ }
fit_child_in_rect(c, r);
}
@@ -414,12 +429,13 @@ void ScrollContainer::update_scrollbars() {
bool hide_scroll_v = !scroll_v || min.height <= size.height;
bool hide_scroll_h = !scroll_h || min.width <= size.width;
+ v_scroll->set_max(min.height);
if (hide_scroll_v) {
+ v_scroll->set_page(size.height);
v_scroll->hide();
scroll.y = 0;
} else {
v_scroll->show();
- v_scroll->set_max(min.height);
if (hide_scroll_h) {
v_scroll->set_page(size.height);
} else {
@@ -429,12 +445,13 @@ void ScrollContainer::update_scrollbars() {
scroll.y = v_scroll->get_value();
}
+ h_scroll->set_max(min.width);
if (hide_scroll_h) {
+ h_scroll->set_page(size.width);
h_scroll->hide();
scroll.x = 0;
} else {
h_scroll->show();
- h_scroll->set_max(min.width);
if (hide_scroll_v) {
h_scroll->set_page(size.width);
} else {
@@ -520,6 +537,8 @@ void ScrollContainer::set_follow_focus(bool p_follow) {
}
String ScrollContainer::get_configuration_warning() const {
+ String warning = Container::get_configuration_warning();
+
int found = 0;
for (int i = 0; i < get_child_count(); i++) {
@@ -527,7 +546,7 @@ String ScrollContainer::get_configuration_warning() const {
if (!c) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
if (c == h_scroll || c == v_scroll) {
@@ -538,10 +557,12 @@ String ScrollContainer::get_configuration_warning() const {
}
if (found != 1) {
- return 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.");
- } else {
- return "";
+ if (!warning.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.");
}
+ return warning;
}
HScrollBar *ScrollContainer::get_h_scrollbar() {
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index b28d66ed53..4bf200009e 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -74,6 +74,7 @@ protected:
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/shortcut.cpp b/scene/gui/shortcut.cpp
index 9f5b9c40c2..f8c7bc44a7 100644
--- a/scene/gui/shortcut.cpp
+++ b/scene/gui/shortcut.cpp
@@ -32,20 +32,20 @@
#include "core/os/keyboard.h"
-void ShortCut::set_shortcut(const Ref<InputEvent> &p_shortcut) {
+void Shortcut::set_shortcut(const Ref<InputEvent> &p_shortcut) {
shortcut = p_shortcut;
emit_changed();
}
-Ref<InputEvent> ShortCut::get_shortcut() const {
+Ref<InputEvent> Shortcut::get_shortcut() const {
return shortcut;
}
-bool ShortCut::is_shortcut(const Ref<InputEvent> &p_event) const {
+bool Shortcut::is_shortcut(const Ref<InputEvent> &p_event) const {
return shortcut.is_valid() && shortcut->shortcut_match(p_event);
}
-String ShortCut::get_as_text() const {
+String Shortcut::get_as_text() const {
if (shortcut.is_valid()) {
return shortcut->as_text();
} else {
@@ -53,21 +53,21 @@ String ShortCut::get_as_text() const {
}
}
-bool ShortCut::is_valid() const {
+bool Shortcut::is_valid() const {
return shortcut.is_valid();
}
-void ShortCut::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_shortcut", "event"), &ShortCut::set_shortcut);
- ClassDB::bind_method(D_METHOD("get_shortcut"), &ShortCut::get_shortcut);
+void Shortcut::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_shortcut", "event"), &Shortcut::set_shortcut);
+ ClassDB::bind_method(D_METHOD("get_shortcut"), &Shortcut::get_shortcut);
- ClassDB::bind_method(D_METHOD("is_valid"), &ShortCut::is_valid);
+ ClassDB::bind_method(D_METHOD("is_valid"), &Shortcut::is_valid);
- ClassDB::bind_method(D_METHOD("is_shortcut", "event"), &ShortCut::is_shortcut);
- ClassDB::bind_method(D_METHOD("get_as_text"), &ShortCut::get_as_text);
+ ClassDB::bind_method(D_METHOD("is_shortcut", "event"), &Shortcut::is_shortcut);
+ ClassDB::bind_method(D_METHOD("get_as_text"), &Shortcut::get_as_text);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), "set_shortcut", "get_shortcut");
}
-ShortCut::ShortCut() {
+Shortcut::Shortcut() {
}
diff --git a/scene/gui/shortcut.h b/scene/gui/shortcut.h
index 063d4e43dc..176958b397 100644
--- a/scene/gui/shortcut.h
+++ b/scene/gui/shortcut.h
@@ -32,10 +32,10 @@
#define SHORTCUT_H
#include "core/input/input_event.h"
-#include "core/resource.h"
+#include "core/io/resource.h"
-class ShortCut : public Resource {
- GDCLASS(ShortCut, Resource);
+class Shortcut : public Resource {
+ GDCLASS(Shortcut, Resource);
Ref<InputEvent> shortcut;
@@ -50,7 +50,7 @@ public:
String get_as_text() const;
- ShortCut();
+ Shortcut();
};
#endif // SHORTCUT_H
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 3670f13705..46b24efed5 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -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;
}
@@ -62,8 +64,8 @@ void SpinBox::_text_entered(const String &p_string) {
Variant value = expr->execute(Array(), nullptr, false);
if (value.get_type() != Variant::NIL) {
set_value(value);
- _value_changed(0);
}
+ _value_changed(0);
}
LineEdit *SpinBox::get_line_edit() {
@@ -170,7 +172,8 @@ 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) {
+ if ((w != last_w)) {
+ line_edit->set_margin(MARGIN_LEFT, 0);
line_edit->set_margin(MARGIN_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();
}
}
@@ -263,6 +274,8 @@ SpinBox::SpinBox() {
line_edit->set_anchors_and_margins_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/split_container.cpp b/scene/gui/split_container.cpp
index 5c60153d91..1e85bba0e3 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -41,7 +41,7 @@ Control *SplitContainer::_getch(int p_idx) const {
if (!c || !c->is_visible_in_tree()) {
continue;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
continue;
}
@@ -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/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index 4e1ad2ae05..c5f56fe8e2 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -30,7 +30,7 @@
#include "subviewport_container.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "scene/main/viewport.h"
Size2 SubViewportContainer::get_minimum_size() const {
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 41b8fad49d..01c1a15b79 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -30,7 +30,9 @@
#include "tab_container.h"
-#include "core/message_queue.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,12 +346,10 @@ 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");
int side_margin = get_theme_constant("side_margin");
- int icon_text_distance = get_theme_constant("icon_separation");
// Find out start and width of the header area.
int header_x = side_margin;
@@ -348,101 +415,185 @@ void TabContainer::_notification(int p_what) {
break;
}
- // 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 all visible tabs.
+ // 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;
}
- Ref<StyleBox> tab_style;
- Color font_color;
- if (get_tab_disabled(i + first_tab_cache)) {
- tab_style = tab_disabled;
- font_color = font_color_disabled;
- } else if (i + first_tab_cache == current) {
- tab_style = tab_fg;
- font_color = font_color_fg;
- } else {
- tab_style = tab_bg;
- font_color = font_color_bg;
- }
- // Draw the tab background.
int tab_width = tab_widths[i];
- Rect2 tab_rect(tabs_ofs_cache + x, 0, tab_width, header_height);
- tab_style->draw(canvas, tab_rect);
-
- // Draw the tab contents.
- Control *control = Object::cast_to<Control>(tabs[i + first_tab_cache]);
- 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 + tab_style->get_margin(MARGIN_LEFT);
- int top_margin = tab_style->get_margin(MARGIN_TOP);
- int y_center = top_margin + (tab_rect.size.y - tab_style->get_minimum_size().y) / 2;
-
- // Draw the tab icon.
- if (control->has_meta("_tab_icon")) {
- Ref<Texture2D> icon = control->get_meta("_tab_icon");
- if (icon.is_valid()) {
- int y = y_center - (icon->get_height() / 2);
- icon->draw(canvas, Point2i(x_content, y));
- if (text != "") {
- x_content += icon->get_width() + icon_text_distance;
- }
+ 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 {
+ 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);
}
}
- // Draw the tab text.
- Point2i text_pos(x_content, y_center - (font->get_height() / 2) + font->get_ascent());
- font->draw(canvas, text_pos, text, font_color);
-
x += tab_width;
- last_tab_cache = i + first_tab_cache;
+ last_tab_cache = index;
+ }
+
+ 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. 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));
- }
+ 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();
- 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 (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;
}
}
+void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) {
+ Vector<Control *> tabs = _get_tabs();
+ RID canvas = get_canvas_item();
+ Ref<Font> font = get_theme_font("font");
+ int icon_text_distance = get_theme_constant("icon_separation");
+ int tab_width = _get_tab_width(p_index);
+ int header_height = _get_top_margin();
+
+ // Draw the tab background.
+ Rect2 tab_rect(p_x, 0, tab_width, header_height);
+ p_tab_style->draw(canvas, tab_rect);
+
+ // Draw the tab contents.
+ 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 y_center = top_margin + (tab_rect.size.y - p_tab_style->get_minimum_size().y) / 2;
+
+ // Draw the tab icon.
+ if (control->has_meta("_tab_icon")) {
+ Ref<Texture2D> icon = control->get_meta("_tab_icon");
+ if (icon.is_valid()) {
+ int y = y_center - (icon->get_height() / 2);
+ icon->draw(canvas, Point2i(x_content, y));
+ if (text != "") {
+ x_content += icon->get_width() + icon_text_distance;
+ }
+ }
+ }
+
+ // Draw the tab text.
+ 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::_on_theme_changed() {
+ if (!_theme_changing) {
+ return;
+ }
+
+ text_buf.clear();
+ bool rtl = is_layout_rtl();
+ Ref<Font> font = get_theme_font("font");
+ int font_size = get_theme_font_size("font_size");
+ Vector<Control *> tabs = _get_tabs();
+ 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);
+ }
+
+ minimum_size_changed();
if (get_tab_count() > 0) {
_repaint();
update();
}
+ _theme_changing = false;
}
void TabContainer::_repaint() {
@@ -478,14 +629,15 @@ void TabContainer::_on_mouse_exited() {
int TabContainer::_get_tab_width(int p_index) const {
ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0);
Control *control = Object::cast_to<Control>(_get_tabs()[p_index]);
- if (!control || control->is_set_as_toplevel() || get_tab_hidden(p_index)) {
+ if (!control || control->is_set_as_top_level() || get_tab_hidden(p_index)) {
return 0;
}
// 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")) {
@@ -517,7 +669,7 @@ Vector<Control *> TabContainer::_get_tabs() const {
Vector<Control *> controls;
for (int i = 0; i < get_child_count(); i++) {
Control *control = Object::cast_to<Control>(get_child(i));
- if (!control || control->is_toplevel_control()) {
+ if (!control || control->is_top_level_control()) {
continue;
}
@@ -527,6 +679,21 @@ Vector<Control *> TabContainer::_get_tabs() const {
}
void TabContainer::_child_renamed_callback() {
+ 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);
+ }
+
update();
}
@@ -537,13 +704,28 @@ void TabContainer::add_child_notify(Node *p_child) {
if (!c) {
return;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
return;
}
+ 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);
+ }
+
bool first = false;
- if (get_tab_count() != 1) {
+ if (tabs.size() != 1) {
c->hide();
} else {
c->show();
@@ -631,7 +813,22 @@ void TabContainer::remove_child_notify(Node *p_child) {
}
void TabContainer::_update_current_tab() {
- int tc = get_tab_count();
+ 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);
+ }
+
+ int tc = tabs.size();
if (current >= tc) {
current = tc - 1;
}
@@ -747,30 +944,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);
@@ -819,6 +1024,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);
}
@@ -943,7 +1162,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");
@@ -1010,6 +1229,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);
@@ -1036,6 +1257,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");
@@ -1055,6 +1277,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 7ea667d60f..91153e5fc3 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -33,12 +33,13 @@
#include "scene/gui/container.h"
#include "scene/gui/popup.h"
+#include "scene/resources/text_line.h"
+
class TabContainer : public Container {
GDCLASS(TabContainer, Container);
public:
enum TabAlign {
-
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT
@@ -51,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;
@@ -62,12 +64,15 @@ 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);
protected:
void _child_renamed_callback();
@@ -90,6 +95,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 8f71aa7cab..5b26428e45 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -30,7 +30,9 @@
#include "tabs.h"
-#include "core/message_queue.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,7 +333,12 @@ 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);
@@ -294,13 +346,21 @@ void Tabs::_notification(int p_what) {
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(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
+ } else {
+ icon->draw(ci, Point2i(w, sb->get_margin(MARGIN_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(MARGIN_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(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2), col);
+ }
w += tabs[i].size_text;
@@ -312,7 +372,11 @@ void Tabs::_notification(int p_what) {
Rect2 rb_rect;
rb_rect.size = style->get_minimum_size() + rb->get_size();
- rb_rect.position.x = w;
+ 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(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2;
if (rb_hover == i) {
@@ -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(MARGIN_LEFT), rb_rect.position.y + style->get_margin(MARGIN_TOP)));
+ } else {
+ rb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), rb_rect.position.y + style->get_margin(MARGIN_TOP)));
+ }
w += rb->get_width();
tabs.write[i].rb_rect = rb_rect;
}
@@ -336,7 +404,11 @@ void Tabs::_notification(int p_what) {
Rect2 cb_rect;
cb_rect.size = style->get_minimum_size() + cb->get_size();
- cb_rect.position.x = w;
+ 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(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2;
if (!tabs[i].disabled && cb_hover == i) {
@@ -347,7 +419,11 @@ 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(MARGIN_LEFT), cb_rect.position.y + style->get_margin(MARGIN_TOP)));
+ } else {
+ cb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), cb_rect.position.y + style->get_margin(MARGIN_TOP)));
+ }
w += cb->get_width();
tabs.write[i].cb_rect = cb_rect;
}
@@ -358,16 +434,30 @@ void Tabs::_notification(int p_what) {
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;
@@ -388,6 +478,7 @@ void Tabs::set_current_tab(int p_current) {
}
ERR_FAIL_INDEX(p_current, get_tab_count());
+ previous = current;
current = p_current;
_change_notify("current_tab");
@@ -401,6 +492,10 @@ int Tabs::get_current_tab() const {
return current;
}
+int Tabs::get_previous_tab() const {
+ return previous;
+}
+
int Tabs::get_hovered_tab() const {
return hover;
}
@@ -417,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();
}
@@ -426,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;
@@ -490,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
@@ -503,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();
@@ -515,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;
@@ -557,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;
}
}
@@ -573,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;
@@ -588,6 +743,7 @@ void Tabs::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) {
void Tabs::clear_tabs() {
tabs.clear();
current = 0;
+ previous = 0;
call_deferred("_update_hover");
update();
}
@@ -605,6 +761,7 @@ void Tabs::remove_tab(int p_idx) {
if (current < 0) {
current = 0;
+ previous = 0;
}
if (current >= tabs.size()) {
current = tabs.size() - 1;
@@ -764,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;
@@ -776,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;
@@ -862,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) {
@@ -917,8 +1077,16 @@ void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_count"), &Tabs::get_tab_count);
ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &Tabs::set_current_tab);
ClassDB::bind_method(D_METHOD("get_current_tab"), &Tabs::get_current_tab);
+ 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);
@@ -946,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")));
@@ -970,6 +1138,7 @@ void Tabs::_bind_methods() {
Tabs::Tabs() {
current = 0;
+ previous = 0;
tab_align = ALIGN_CENTER;
rb_hover = -1;
rb_pressing = false;
diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h
index 8d7f1aa37d..bf62ba7210 100644
--- a/scene/gui/tabs.h
+++ b/scene/gui/tabs.h
@@ -32,13 +32,13 @@
#define TABS_H
#include "scene/gui/control.h"
+#include "scene/resources/text_line.h"
class Tabs : public Control {
GDCLASS(Tabs, Control);
public:
enum TabAlign {
-
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT,
@@ -46,7 +46,6 @@ public:
};
enum CloseButtonDisplayPolicy {
-
CLOSE_BUTTON_SHOW_NEVER,
CLOSE_BUTTON_SHOW_ACTIVE_ONLY,
CLOSE_BUTTON_SHOW_ALWAYS,
@@ -57,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;
@@ -77,6 +82,7 @@ private:
bool missing_right;
Vector<Tab> tabs;
int current;
+ int previous;
int _get_top_margin() const;
TabAlign tab_align;
int rb_hover;
@@ -102,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);
@@ -118,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;
@@ -138,6 +156,7 @@ public:
int get_tab_count() const;
void set_current_tab(int p_current);
int get_current_tab() const;
+ int get_previous_tab() const;
int get_hovered_tab() const;
int get_tab_offset() const;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index e17085cafc..6b16806789 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -30,12 +30,14 @@
#include "text_edit.h"
+#include "core/config/project_settings.h"
#include "core/input/input.h"
-#include "core/message_queue.h"
+#include "core/object/message_queue.h"
+#include "core/object/script_language.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
-#include "core/script_language.h"
+#include "core/string/translation.h"
+
#include "scene/main/window.h"
#ifdef TOOLS_ENABLED
@@ -44,23 +46,23 @@
#define TAB_PIXELS
-inline bool _is_symbol(CharType c) {
+inline bool _is_symbol(char32_t c) {
return is_symbol(c);
}
-static bool _is_text_char(CharType c) {
+static bool _is_text_char(char32_t c) {
return !is_symbol(c);
}
-static bool _is_whitespace(CharType c) {
+static bool _is_whitespace(char32_t c) {
return c == '\t' || c == ' ';
}
-static bool _is_char(CharType c) {
+static bool _is_char(char32_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
-static bool _is_pair_right_symbol(CharType c) {
+static bool _is_pair_right_symbol(char32_t c) {
return c == '"' ||
c == '\'' ||
c == ')' ||
@@ -68,7 +70,7 @@ static bool _is_pair_right_symbol(CharType c) {
c == '}';
}
-static bool _is_pair_left_symbol(CharType c) {
+static bool _is_pair_left_symbol(char32_t c) {
return c == '"' ||
c == '\'' ||
c == '(' ||
@@ -76,11 +78,11 @@ static bool _is_pair_left_symbol(CharType c) {
c == '{';
}
-static bool _is_pair_symbol(CharType c) {
+static bool _is_pair_symbol(char32_t c) {
return _is_pair_left_symbol(c) || _is_pair_right_symbol(c);
}
-static CharType _get_right_pair_symbol(CharType c) {
+static char32_t _get_right_pair_symbol(char32_t c) {
if (c == '"') {
return '"';
}
@@ -107,73 +109,124 @@ static int _find_first_non_whitespace_column_of_line(const String &line) {
return left;
}
+///////////////////////////////////////////////////////////////////////////////
+
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 CharType *str = text[p_line].data.c_str();
+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;
+}
- text.write[p_line].width_cache = w;
- text.write[p_line].wrap_amount_cache = -1;
+int TextEdit::Text::get_line_height(int p_line, int p_wrap_index) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), 0);
+
+ 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;
+}
- if (text[p_line].width_cache == -1) {
- _update_line_cache(p_line);
- }
+int TextEdit::Text::get_line_wrap_amount(int p_line) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), 0);
- return text[p_line].width_cache;
+ return text[p_line].data_buf->get_line_count() - 1;
}
-void TextEdit::Text::set_line_wrap_amount(int p_line, int p_wrap_amount) const {
- ERR_FAIL_INDEX(p_line, text.size());
+Vector<Vector2i> TextEdit::Text::get_line_wrap_ranges(int p_line) const {
+ Vector<Vector2i> ret;
+ ERR_FAIL_INDEX_V(p_line, text.size(), ret);
- text.write[p_line].wrap_amount_cache = p_wrap_amount;
+ 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;
}
-int TextEdit::Text::get_line_wrap_amount(int p_line) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), -1);
+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].wrap_amount_cache;
+_FORCE_INLINE_ const String &TextEdit::Text::operator[](int p_line) const {
+ return text[p_line].data;
}
-void TextEdit::Text::clear_width_cache() {
- for (int i = 0; i < text.size(); i++) {
- text.write[i].width_cache = -1;
+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());
+
+ if (font.is_null() || font_size <= 0) {
+ return; // Not in tree?
+ }
+
+ 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.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.empty()) {
+ TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), text[p_line].bidi_override);
+ }
+ }
+
+ // 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_wrap_cache() {
+void TextEdit::Text::invalidate_all_lines() {
for (int i = 0; i < text.size(); i++) {
- text.write[i].wrap_amount_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_info_icons() {
+void TextEdit::Text::invalidate_all() {
for (int i = 0; i < text.size(); i++) {
- text.write[i].has_info = false;
+ 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 {
@@ -188,49 +241,56 @@ 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.safe = false;
- line.breakpoint = false;
- line.bookmark = false;
line.hidden = false;
- line.has_info = 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(CharType c, CharType 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;
+void TextEdit::Text::add_gutter(int p_at) {
+ for (int i = 0; i < text.size(); i++) {
+ if (p_at < 0 || p_at > gutter_count) {
+ text.write[i].gutters.push_back(Gutter());
} else {
- w = tab_w - px % tab_w; // Is right.
+ text.write[i].gutters.insert(p_at, Gutter());
}
- } else {
- w = font->get_char_size(c, next_c).width;
}
- return w;
+ gutter_count++;
}
+void TextEdit::Text::remove_gutter(int p_gutter) {
+ for (int i = 0; i < text.size(); i++) {
+ text.write[i].gutters.remove(p_gutter);
+ }
+ gutter_count--;
+}
+
+void TextEdit::Text::move_gutters(int p_from_line, int p_to_line) {
+ text.write[p_to_line].gutters = text[p_from_line].gutters;
+ text.write[p_from_line].gutters.clear();
+ text.write[p_from_line].gutters.resize(gutter_count);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
void TextEdit::_update_scrollbars() {
Size2 size = get_size();
Size2 hmin = h_scroll->get_combined_minimum_size();
@@ -249,23 +309,7 @@ void TextEdit::_update_scrollbars() {
}
int visible_width = size.width - cache.style_normal->get_minimum_size().width;
- int total_width = text.get_max_width(true) + vmin.x;
-
- if (line_numbers) {
- total_width += cache.line_number_w;
- }
-
- if (draw_breakpoint_gutter || draw_bookmark_gutter) {
- total_width += cache.breakpoint_gutter_width;
- }
-
- if (draw_info_gutter) {
- total_width += cache.info_gutter_width;
- }
-
- if (draw_fold_gutter) {
- total_width += cache.fold_gutter_width;
- }
+ int total_width = text.get_max_width(true) + vmin.x + gutters_width + gutter_padding;
if (draw_minimap) {
total_width += cache.minimap_width;
@@ -315,15 +359,15 @@ void TextEdit::_click_selection_held() {
// Warning: is_mouse_button_pressed(BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD
// and MODE_LINE. However, moving the mouse triggers _gui_input, which calls these functions too, so that's not a huge problem.
// I'm unsure if there's an actual fix that doesn't have a ton of side effects.
- if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && selection.selecting_mode != Selection::MODE_NONE) {
+ if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && selection.selecting_mode != SelectionMode::SELECTION_MODE_NONE) {
switch (selection.selecting_mode) {
- case Selection::MODE_POINTER: {
+ case SelectionMode::SELECTION_MODE_POINTER: {
_update_selection_mode_pointer();
} break;
- case Selection::MODE_WORD: {
+ case SelectionMode::SELECTION_MODE_WORD: {
_update_selection_mode_word();
} break;
- case Selection::MODE_LINE: {
+ case SelectionMode::SELECTION_MODE_LINE: {
_update_selection_mode_line();
} break;
default: {
@@ -335,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);
@@ -353,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);
@@ -410,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);
@@ -435,7 +487,7 @@ 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);
if (!dragging_minimap && (mp.x < xmargin_end - minimap_width || mp.y > xmargin_end)) {
@@ -476,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);
}
@@ -491,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();
@@ -503,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;
@@ -553,58 +608,21 @@ 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;
}
- if (draw_breakpoint_gutter || draw_bookmark_gutter) {
- breakpoint_gutter_width = (get_row_height() * 55) / 100;
- cache.breakpoint_gutter_width = breakpoint_gutter_width;
- } else {
- cache.breakpoint_gutter_width = 0;
- }
-
- if (draw_info_gutter) {
- info_gutter_width = (get_row_height());
- cache.info_gutter_width = info_gutter_width;
- } else {
- cache.info_gutter_width = 0;
- }
-
- if (draw_fold_gutter) {
- fold_gutter_width = (get_row_height() * 55) / 100;
- cache.fold_gutter_width = fold_gutter_width;
- } else {
- cache.fold_gutter_width = 0;
- }
-
cache.minimap_width = 0;
if (draw_minimap) {
cache.minimap_width = minimap_width;
}
- int line_number_char_count = 0;
-
- {
- int lc = text.size();
- cache.line_number_w = 0;
- while (lc) {
- cache.line_number_w += 1;
- lc /= 10;
- };
-
- if (line_numbers) {
- line_number_char_count = cache.line_number_w;
- cache.line_number_w = (cache.line_number_w + 1) * cache.font->get_char_size('0').width;
- } else {
- cache.line_number_w = 0;
- }
- }
_update_scrollbars();
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) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width;
+ int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + gutters_width + gutter_padding;
int xmargin_end = size.width - cache.style_normal->get_margin(MARGIN_RIGHT) - cache.minimap_width;
// Let's do it easy for now.
@@ -617,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;
@@ -628,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));
+ }
}
}
@@ -654,8 +678,8 @@ void TextEdit::_notification(int p_what) {
if (brace_matching_enabled && cursor.line >= 0 && cursor.line < text.size() && cursor.column >= 0) {
if (cursor.column < text[cursor.line].length()) {
// Check for open.
- CharType c = text[cursor.line][cursor.column];
- CharType closec = 0;
+ char32_t c = text[cursor.line][cursor.column];
+ char32_t closec = 0;
if (c == '[') {
closec = ']';
@@ -671,10 +695,10 @@ void TextEdit::_notification(int p_what) {
for (int i = cursor.line; i < text.size(); i++) {
int from = i == cursor.line ? cursor.column + 1 : 0;
for (int j = from; j < text[i].length(); j++) {
- CharType cc = text[i][j];
+ char32_t cc = text[i][j];
// Ignore any brackets inside a string.
if (cc == '"' || cc == '\'') {
- CharType quotation = cc;
+ char32_t quotation = cc;
do {
j++;
if (!(j < text[i].length())) {
@@ -720,8 +744,8 @@ void TextEdit::_notification(int p_what) {
}
if (cursor.column > 0) {
- CharType c = text[cursor.line][cursor.column - 1];
- CharType closec = 0;
+ char32_t c = text[cursor.line][cursor.column - 1];
+ char32_t closec = 0;
if (c == ']') {
closec = '[';
@@ -737,10 +761,10 @@ void TextEdit::_notification(int p_what) {
for (int i = cursor.line; i >= 0; i--) {
int from = i == cursor.line ? cursor.column - 2 : text[i].length() - 1;
for (int j = from; j >= 0; j--) {
- CharType cc = text[i][j];
+ char32_t cc = text[i][j];
// Ignore any brackets inside a string.
if (cc == '"' || cc == '\'') {
- CharType quotation = cc;
+ char32_t quotation = cc;
do {
j--;
if (!(j >= 0)) {
@@ -795,11 +819,9 @@ void TextEdit::_notification(int p_what) {
// Check if highlighted words contains only whitespaces (tabs or spaces).
bool only_whitespaces_highlighted = highlighted_text.strip_edges() == String();
- String line_num_padding = line_numbers_zero_padded ? "0" : " ";
-
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);
@@ -828,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++;
@@ -878,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;
@@ -925,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) {
@@ -944,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++;
@@ -963,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) {
@@ -985,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;
@@ -1005,444 +1026,416 @@ 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.
- if (text.is_breakpoint(line) && !draw_breakpoint_gutter) {
-#ifdef TOOLS_ENABLED
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.breakpoint_color);
-#else
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.breakpoint_color);
-#endif
- }
+ int gutter_offset = cache.style_normal->get_margin(MARGIN_LEFT);
+ for (int g = 0; g < gutters.size(); g++) {
+ const GutterInfo gutter = gutters[g];
- // Draw bookmark marker.
- if (text.is_bookmark(line)) {
- if (draw_bookmark_gutter) {
- int vertical_gap = (get_row_height() * 40) / 100;
- int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
- int marker_radius = get_row_height() - (vertical_gap * 2);
- RenderingServer::get_singleton()->canvas_item_add_circle(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2 + marker_radius / 2, ofs_y + vertical_gap + marker_radius / 2), marker_radius, Color(cache.bookmark_color.r, cache.bookmark_color.g, cache.bookmark_color.b));
+ if (!gutter.draw || gutter.width <= 0) {
+ continue;
}
- }
- // Draw breakpoint marker.
- if (text.is_breakpoint(line)) {
- if (draw_breakpoint_gutter) {
- int vertical_gap = (get_row_height() * 40) / 100;
- int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
- int marker_height = get_row_height() - (vertical_gap * 2);
- int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2);
- // No transparency on marker.
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2, ofs_y + vertical_gap, marker_width, marker_height), Color(cache.breakpoint_color.r, cache.breakpoint_color.g, cache.breakpoint_color.b));
- }
- }
+ switch (gutter.type) {
+ case GUTTER_TYPE_STRING: {
+ const String &text = get_line_gutter_text(line, g);
+ if (text == "") {
+ break;
+ }
- // Draw info icons.
- if (draw_info_gutter && text.has_info_icon(line)) {
- int vertical_gap = (get_row_height() * 40) / 100;
- int horizontal_gap = (cache.info_gutter_width * 30) / 100;
- int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width;
-
- Ref<Texture2D> info_icon = text.get_info_icon(line);
- // Ensure the icon fits the gutter size.
- Size2i icon_size = info_icon->get_size();
- if (icon_size.width > cache.info_gutter_width - horizontal_gap) {
- icon_size.width = cache.info_gutter_width - horizontal_gap;
- }
- if (icon_size.height > get_row_height() - horizontal_gap) {
- icon_size.height = get_row_height() - horizontal_gap;
- }
+ Ref<TextLine> tl;
+ tl.instance();
+ tl->add_string(text, cache.font, cache.font_size);
- Size2i icon_pos;
- int xofs = horizontal_gap - (info_icon->get_width() / 4);
- int yofs = vertical_gap - (info_icon->get_height() / 4);
- icon_pos.x = gutter_left + xofs + ofs_x;
- icon_pos.y = ofs_y + yofs;
+ 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);
+ if (icon.is_null()) {
+ break;
+ }
- draw_texture_rect(info_icon, Rect2(icon_pos, icon_size));
- }
+ Rect2 gutter_rect = Rect2(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, row_height));
- // Draw execution marker.
- if (executing_line == line) {
- if (draw_breakpoint_gutter) {
- int icon_extra_size = 4;
- int vertical_gap = (get_row_height() * 40) / 100;
- int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
- int marker_height = get_row_height() - (vertical_gap * 2) + icon_extra_size;
- int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2) + icon_extra_size;
- cache.executing_icon->draw_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2 - icon_extra_size / 2, ofs_y + vertical_gap - icon_extra_size / 2, marker_width, marker_height), false, Color(cache.executing_line_color.r, cache.executing_line_color.g, cache.executing_line_color.b));
- } else {
-#ifdef TOOLS_ENABLED
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.executing_line_color);
-#else
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.executing_line_color);
-#endif
- }
- }
+ int horizontal_padding = gutter_rect.size.x / 6;
+ int vertical_padding = gutter_rect.size.y / 6;
- // Draw fold markers.
- if (draw_fold_gutter) {
- int horizontal_gap = (cache.fold_gutter_width * 30) / 100;
- int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w + cache.info_gutter_width;
- if (is_folded(line)) {
- int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2;
- int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2;
- cache.folded_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
- } else if (can_fold(line)) {
- int xofs = -cache.can_fold_icon->get_width() / 2 - horizontal_gap + 3;
- int yofs = (get_row_height() - cache.can_fold_icon->get_height()) / 2;
- cache.can_fold_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
- }
- }
+ 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;
+ }
- // Draw line numbers.
- if (cache.line_number_w) {
- int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
- String fc = String::num(line + 1);
- while (fc.length() < line_number_char_count) {
- fc = line_num_padding + fc;
+ 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, 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));
+ }
+ }
+ } break;
}
- cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.info_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, text.is_safe(line) ? cache.safe_line_number_color : cache.line_number_color);
+ gutter_offset += gutter.width;
}
}
- // 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.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.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);
- }
+ ofs_y += (row_height - text_height) / 2;
- bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col + highlighted_text.length());
+ const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid);
+ const TextServer::Glyph *glyphs = visual.ptr();
+ int gl_size = visual.size();
- // 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;
- }
-
- 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);
+ float char_ofs = 0.f;
+ 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);
+ }
+ 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;
- }
-
- CharType cchar = ime_text[ofs];
- CharType 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) {
-#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(caret_w, caret_h)), cache.caret_color);
- } else {
+ // Carets
#ifdef TOOLS_ENABLED
- caret_w = (block_caret) ? caret_w : 2 * EDSCALE;
+ int caret_width = Math::round(EDSCALE);
#else
- caret_w = (block_caret) ? caret_w : 2;
+ int caret_width = 1;
#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);
- CharType cchar = ime_text[ofs];
- CharType 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);
}
}
@@ -1451,19 +1444,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;
}
@@ -1474,15 +1467,26 @@ 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);
+
+ for (int i = 0; i < lines; i++) {
+ int l = line_from + i;
+ ERR_CONTINUE(l < 0 || l >= completion_options_size);
+ if (completion_options[l].default_value.get_type() == Variant::COLOR) {
+ w += icon_area_size.width;
+ break;
+ }
+ }
+
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;
}
@@ -1503,19 +1507,24 @@ void TextEdit::_notification(int p_what) {
if (cache.completion_background_color.a > 0.01) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(completion_rect.position, completion_rect.size + Size2(scrollw, 0)), cache.completion_background_color);
}
- int line_from = CLAMP(completion_index - lines / 2, 0, completion_options_size - lines);
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;
@@ -1525,7 +1534,21 @@ void TextEdit::_notification(int p_what) {
}
title_pos.x = icon_area.position.x + icon_area.size.width + 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));
+
+ tl->set_width(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) {
@@ -1563,16 +1586,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) {
@@ -1582,7 +1605,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;
}
@@ -1596,15 +1619,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;
@@ -1614,7 +1637,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;
@@ -1627,12 +1650,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(), get_viewport()->get_window_id());
}
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
- DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true);
+ String text = _base_get_text(0, 0, selection.selecting_line, selection.selecting_column);
+ int cursor_start = text.length();
+ 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 (selected_text.length() > 0) {
+ cursor_end = cursor_start + selected_text.length();
+ }
+ }
+
+ DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true, -1, cursor_start, cursor_end);
}
} break;
case NOTIFICATION_FOCUS_EXIT: {
@@ -1646,6 +1680,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();
@@ -1655,18 +1690,27 @@ 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;
}
}
-void TextEdit::_consume_pair_symbol(CharType ch) {
+void TextEdit::_consume_pair_symbol(char32_t ch) {
int cursor_position_to_move = cursor_get_column() + 1;
- CharType ch_single[2] = { ch, 0 };
- CharType ch_single_pair[2] = { _get_right_pair_symbol(ch), 0 };
- CharType ch_pair[3] = { ch, _get_right_pair_symbol(ch), 0 };
+ char32_t ch_single[2] = { ch, 0 };
+ char32_t ch_single_pair[2] = { _get_right_pair_symbol(ch), 0 };
+ char32_t ch_pair[3] = { ch, _get_right_pair_symbol(ch), 0 };
if (is_selection_active()) {
int new_column, new_line;
@@ -1771,8 +1815,8 @@ void TextEdit::_consume_backspace_for_pair_symbol(int prev_line, int prev_column
bool remove_right_symbol = false;
if (cursor.column < text[cursor.line].length() && cursor.column > 0) {
- CharType left_char = text[cursor.line][cursor.column - 1];
- CharType right_char = text[cursor.line][cursor.column];
+ char32_t left_char = text[cursor.line][cursor.column - 1];
+ char32_t right_char = text[cursor.line][cursor.column];
if (right_char == _get_right_pair_symbol(left_char)) {
remove_right_symbol = true;
@@ -1797,18 +1841,34 @@ void TextEdit::backspace_at_cursor() {
int prev_line = cursor.column ? cursor.line : cursor.line - 1;
int prev_column = cursor.column ? (cursor.column - 1) : (text[cursor.line - 1].length());
- if (is_line_hidden(cursor.line)) {
- set_line_as_hidden(prev_line, true);
- }
- if (is_line_set_as_breakpoint(cursor.line)) {
- if (!text.is_breakpoint(prev_line)) {
- emit_signal("breakpoint_toggled", prev_line);
+ if (cursor.line != prev_line) {
+ for (int i = 0; i < gutters.size(); i++) {
+ if (!gutters[i].overwritable) {
+ continue;
+ }
+
+ if (text.get_line_gutter_text(cursor.line, i) != "") {
+ text.set_line_gutter_text(prev_line, i, text.get_line_gutter_text(cursor.line, i));
+ text.set_line_gutter_item_color(prev_line, i, text.get_line_gutter_item_color(cursor.line, i));
+ }
+
+ if (text.get_line_gutter_icon(cursor.line, i).is_valid()) {
+ text.set_line_gutter_icon(prev_line, i, text.get_line_gutter_icon(cursor.line, i));
+ text.set_line_gutter_item_color(prev_line, i, text.get_line_gutter_item_color(cursor.line, i));
+ }
+
+ if (text.get_line_gutter_metadata(cursor.line, i) != "") {
+ text.set_line_gutter_metadata(prev_line, i, text.get_line_gutter_metadata(cursor.line, i));
+ }
+
+ if (text.is_line_gutter_clickable(cursor.line, i)) {
+ text.set_line_gutter_clickable(prev_line, i, true);
+ }
}
- set_line_as_breakpoint(prev_line, true);
}
- if (text.has_info_icon(cursor.line)) {
- set_line_info_icon(prev_line, text.get_info_icon(cursor.line), text.get_info(cursor.line));
+ if (is_line_hidden(cursor.line)) {
+ set_line_as_hidden(prev_line, true);
}
if (auto_brace_completion_enabled &&
@@ -1872,6 +1932,9 @@ void TextEdit::indent_right() {
for (int i = start_line; i <= end_line; i++) {
String line_text = get_line(i);
+ if (line_text.size() == 0 && is_selection_active()) {
+ continue;
+ }
if (indent_using_spaces) {
// We don't really care where selection is - we just need to know indentation level at the beginning of the line.
int left = _find_first_non_whitespace_column_of_line(line_text);
@@ -1986,7 +2049,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;
@@ -1995,20 +2058,14 @@ 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) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width);
+ int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_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;
@@ -2017,38 +2074,27 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
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
+ int row = 1;
for (int i = get_first_visible_line(); i < cursor.line; i++) {
- if (is_line_hidden(i)) {
- row -= 1;
- continue;
- }
- row += times_line_wraps(i);
- }
- // 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;
+ 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) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width - 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(MARGIN_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);
}
@@ -2113,7 +2159,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;
}
@@ -2134,7 +2188,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();
@@ -2173,47 +2227,26 @@ 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);
-
- // Toggle breakpoint on gutter click.
- if (draw_breakpoint_gutter) {
- int gutter = cache.style_normal->get_margin(MARGIN_LEFT);
- if (mb->get_position().x > gutter - 6 && mb->get_position().x <= gutter + cache.breakpoint_gutter_width - 3) {
- set_line_as_breakpoint(row, !is_line_set_as_breakpoint(row));
- emit_signal("breakpoint_toggled", row);
- return;
- }
- }
+ _get_mouse_pos(Point2i(mpos.x, mpos.y), row, col);
- // Emit info clicked.
- if (draw_info_gutter && text.has_info_icon(row)) {
- int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
- int gutter_left = left_margin + cache.breakpoint_gutter_width;
- if (mb->get_position().x > gutter_left - 6 && mb->get_position().x <= gutter_left + cache.info_gutter_width - 3) {
- emit_signal("info_clicked", row, text.get_info(row));
- return;
+ int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
+ for (int i = 0; i < gutters.size(); i++) {
+ if (!gutters[i].draw || gutters[i].width <= 0) {
+ continue;
}
- }
- // Toggle fold on gutter click if can.
- if (draw_fold_gutter) {
- int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
- int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w + cache.info_gutter_width;
- if (mb->get_position().x > gutter_left - 6 && mb->get_position().x <= gutter_left + cache.fold_gutter_width - 3) {
- if (is_folded(row)) {
- unfold_line(row);
- } else if (can_fold(row)) {
- fold_line(row);
- }
+ if (mpos.x > left_margin && mpos.x <= (left_margin + gutters[i].width) - 3) {
+ emit_signal("gutter_clicked", row, i);
return;
}
+
+ left_margin += gutters[i].width;
}
// Unfold on folded icon click.
if (is_folded(row)) {
- int line_width = text.get_line_width(row);
- line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.info_gutter_width + cache.fold_gutter_width - cursor.x_ofs;
- if (mb->get_position().x > line_width - 3 && mb->get_position().x <= line_width + cache.folded_eol_icon->get_width() + 3) {
+ left_margin += gutter_padding + text.get_line_width(row) - cursor.x_ofs;
+ if (mpos.x > left_margin && mpos.x <= left_margin + cache.folded_eol_icon->get_width() + 3) {
unfold_line(row);
return;
}
@@ -2236,7 +2269,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->get_shift() && (cursor.column != prev_col || cursor.line != prev_line)) {
if (!selection.active) {
selection.active = true;
- selection.selecting_mode = Selection::MODE_POINTER;
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_POINTER;
selection.from_column = prev_col;
selection.from_line = prev_line;
selection.to_column = cursor.column;
@@ -2280,19 +2313,19 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
} else {
selection.active = false;
- selection.selecting_mode = Selection::MODE_POINTER;
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_POINTER;
selection.selecting_line = row;
selection.selecting_column = col;
}
if (!mb->is_doubleclick() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < 600 && cursor.line == prev_line) {
// Triple-click select line.
- selection.selecting_mode = Selection::MODE_LINE;
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_LINE;
_update_selection_mode_line();
last_dblclk = 0;
} else if (mb->is_doubleclick() && text[cursor.line].length()) {
// Double-click select word.
- selection.selecting_mode = Selection::MODE_WORD;
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_WORD;
_update_selection_mode_word();
last_dblclk = OS::get_singleton()->get_ticks_msec();
}
@@ -2304,7 +2337,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()) {
@@ -2324,7 +2357,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();
@@ -2334,7 +2367,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;
@@ -2370,9 +2403,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);
}
@@ -2392,13 +2429,13 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (!dragging_minimap) {
switch (selection.selecting_mode) {
- case Selection::MODE_POINTER: {
+ case SelectionMode::SELECTION_MODE_POINTER: {
_update_selection_mode_pointer();
} break;
- case Selection::MODE_WORD: {
+ case SelectionMode::SELECTION_MODE_WORD: {
_update_selection_mode_word();
} break;
- case Selection::MODE_LINE: {
+ case SelectionMode::SELECTION_MODE_LINE: {
_update_selection_mode_line();
} break;
default: {
@@ -2426,7 +2463,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());
}
@@ -2537,7 +2575,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->get_unicode() > 32) {
_reset_caret_blink_timer();
- const CharType chr[2] = { (CharType)k->get_unicode(), 0 };
+ const char32_t chr[2] = { (char32_t)k->get_unicode(), 0 };
if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) {
_consume_pair_symbol(chr[0]);
} else {
@@ -2675,7 +2713,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (unselect) {
selection.active = false;
- selection.selecting_mode = Selection::MODE_NONE;
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
update();
}
if (clear) {
@@ -2764,7 +2802,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)) {
@@ -2781,7 +2819,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
// No need to move the brace below if we are not taking the text with us.
- char closing_char = _get_right_pair_symbol(indent_char);
+ char32_t closing_char = _get_right_pair_symbol(indent_char);
if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column]) && !k->get_command()) {
brace_indent = true;
ins += "\n" + ins.substr(1, ins.length() - 2);
@@ -2907,34 +2945,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);
@@ -3004,17 +3020,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);
}
@@ -3025,7 +3036,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()) {
@@ -3067,16 +3082,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);
}
@@ -3087,7 +3098,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()) {
@@ -3226,34 +3241,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;
@@ -3264,7 +3257,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);
@@ -3309,7 +3306,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
// Compute whitespace symbols seq length.
int current_line_whitespace_len = 0;
while (current_line_whitespace_len < text[cursor.line].length()) {
- CharType c = text[cursor.line][current_line_whitespace_len];
+ char32_t c = text[cursor.line][current_line_whitespace_len];
if (c != '\t' && c != ' ') {
break;
}
@@ -3455,7 +3452,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
int current_line_whitespace_len = 0;
while (current_line_whitespace_len < text[cursor.line].length()) {
- CharType c = text[cursor.line][current_line_whitespace_len];
+ char32_t c = text[cursor.line][current_line_whitespace_len];
if (c != '\t' && c != ' ')
break;
current_line_whitespace_len++;
@@ -3494,6 +3491,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;
@@ -3581,9 +3592,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();
}
@@ -3604,7 +3614,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
- if (!keycode_handled && !k->get_command()) { // For German keyboards.
+ if (!keycode_handled && (!k->get_command() || (k->get_command() && k->get_alt()))) { // For German keyboards.
if (k->get_unicode() >= 32) {
if (readonly) {
@@ -3621,7 +3631,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- const CharType chr[2] = { (CharType)k->get_unicode(), 0 };
+ const char32_t chr[2] = { (char32_t)k->get_unicode(), 0 };
if (completion_hint != "" && k->get_unicode() == ')') {
completion_hint = "";
@@ -3703,17 +3713,17 @@ void TextEdit::_scroll_down(real_t p_delta) {
}
void TextEdit::_pre_shift_selection() {
- if (!selection.active || selection.selecting_mode == Selection::MODE_NONE) {
+ if (!selection.active || selection.selecting_mode == SelectionMode::SELECTION_MODE_NONE) {
selection.selecting_line = cursor.line;
selection.selecting_column = cursor.column;
selection.active = true;
}
- selection.selecting_mode = Selection::MODE_SHIFT;
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_SHIFT;
}
void TextEdit::_post_shift_selection() {
- if (selection.active && selection.selecting_mode == Selection::MODE_SHIFT) {
+ if (selection.active && selection.selecting_mode == SelectionMode::SELECTION_MODE_SHIFT) {
select(selection.selecting_line, selection.selecting_column, cursor.line, cursor.column);
update();
}
@@ -3772,30 +3782,15 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
Vector<String> substrings = p_text.replace("\r", "").split("\n");
- /* STEP 2: Fire breakpoint_toggled signals. */
-
// Is this just a new empty line?
bool shift_first_line = p_char == 0 && p_text.replace("\r", "") == "\n";
- int i = p_line + !shift_first_line;
- int lines = substrings.size() - 1;
- for (; i < text.size(); i++) {
- if (text.is_breakpoint(i)) {
- if ((i - lines < p_line || !text.is_breakpoint(i - lines)) || (i - lines == p_line && !shift_first_line)) {
- emit_signal("breakpoint_toggled", i);
- }
- if (i + lines >= text.size() || !text.is_breakpoint(i + lines)) {
- emit_signal("breakpoint_toggled", i + lines);
- }
- }
- }
-
- /* STEP 3: Add spaces if the char is greater than the end of the line. */
+ /* 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 4: Separate dest string in pre and post text. */
+ /* STEP 3: Separate dest string in pre and post text. */
String preinsert_text = text[p_line].substr(0, p_char);
String postinsert_text = text[p_line].substr(p_char, text[p_line].size());
@@ -3804,40 +3799,40 @@ 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));
}
}
if (shift_first_line) {
- text.set_breakpoint(p_line + 1, text.is_breakpoint(p_line));
+ text.move_gutters(p_line, p_line + 1);
text.set_hidden(p_line + 1, text.is_hidden(p_line));
- if (text.has_info_icon(p_line)) {
- text.set_info_icon(p_line + 1, text.get_info_icon(p_line), text.get_info(p_line));
- }
- text.set_breakpoint(p_line, false);
text.set_hidden(p_line, false);
- text.set_info_icon(p_line, nullptr, "");
}
- 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");
}
text_changed_dirty = true;
}
- emit_signal("line_edited_from", p_line);
+ emit_signal("lines_edited_from", p_line, r_end_line);
}
String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const {
@@ -3874,25 +3869,13 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
String pre_text = text[p_from_line].substr(0, p_from_column);
String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length());
- int lines = p_to_line - p_from_line;
-
- for (int i = p_from_line + 1; i < text.size(); i++) {
- if (text.is_breakpoint(i)) {
- if (i + lines >= text.size() || !text.is_breakpoint(i + lines)) {
- emit_signal("breakpoint_toggled", i);
- }
- if (i > p_to_line && (i - lines < 0 || !text.is_breakpoint(i - lines))) {
- emit_signal("breakpoint_toggled", i - lines);
- }
- }
- }
-
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()) {
@@ -3900,7 +3883,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
}
text_changed_dirty = true;
}
- emit_signal("line_edited_from", p_from_line);
+ emit_signal("lines_edited_from", p_to_line, p_from_line);
}
void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r_end_line, int *r_end_char) {
@@ -4066,6 +4049,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 {
@@ -4093,19 +4083,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 - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width - 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() {
@@ -4129,7 +4127,7 @@ void TextEdit::adjust_viewport_to_cursor() {
set_line_as_last_visible(cur_line, cur_wrap);
}
- int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width - cache.minimap_width;
+ int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding - cache.minimap_width;
if (v_scroll->is_visible_in_tree()) {
visible_width -= v_scroll->get_combined_minimum_size().width;
}
@@ -4137,14 +4135,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;
- if (cursor_x > (cursor.x_ofs + visible_width)) {
- cursor.x_ofs = cursor_x - visible_width + 1;
+ // 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) {
- cursor.x_ofs = cursor_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;
@@ -4164,7 +4180,7 @@ void TextEdit::center_viewport_to_cursor() {
}
set_line_as_center_visible(cursor.line, get_cursor_wrap_index());
- int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width - cache.minimap_width;
+ int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding - cache.minimap_width;
if (v_scroll->is_visible_in_tree()) {
visible_width -= v_scroll->get_combined_minimum_size().width;
}
@@ -4172,14 +4188,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;
@@ -4204,24 +4239,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 {
@@ -4233,66 +4261,12 @@ 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;
+ 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));
}
- while (col < line_text.length()) {
- CharType 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++;
- }
- // 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;
}
@@ -4322,6 +4296,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;
@@ -4456,6 +4438,30 @@ bool TextEdit::is_right_click_moving_caret() const {
return right_click_moves_caret;
}
+TextEdit::SelectionMode TextEdit::get_selection_mode() const {
+ return selection.selecting_mode;
+}
+
+void TextEdit::set_selection_mode(SelectionMode p_mode, int p_line, int p_column) {
+ selection.selecting_mode = p_mode;
+ if (p_line >= 0) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ selection.selecting_line = p_line;
+ }
+ if (p_column >= 0) {
+ ERR_FAIL_INDEX(p_column, text[selection.selecting_line].length());
+ selection.selecting_column = p_column;
+ }
+}
+
+int TextEdit::get_selection_line() const {
+ return selection.selecting_line;
+};
+
+int TextEdit::get_selection_column() const {
+ return selection.selecting_column;
+};
+
void TextEdit::_v_scroll_input() {
scrolling = false;
minimap_clicked = false;
@@ -4495,101 +4501,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) {
@@ -4599,7 +4551,7 @@ void TextEdit::insert_text_at_cursor(const String &p_text) {
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
selection.active = false;
- selection.selecting_mode = Selection::MODE_NONE;
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
}
_insert_text_at_cursor(p_text);
@@ -4611,54 +4563,41 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
return CURSOR_POINTING_HAND;
}
- int gutter = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width;
if ((completion_active && completion_rect.has_point(p_pos))) {
return CURSOR_ARROW;
}
- if (p_pos.x < gutter) {
- int row, col;
- _get_mouse_pos(p_pos, row, col);
- int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
- // Breakpoint icon.
- if (draw_breakpoint_gutter && p_pos.x > left_margin - 6 && p_pos.x <= left_margin + cache.breakpoint_gutter_width - 3) {
- return CURSOR_POINTING_HAND;
- }
+ int row, col;
+ _get_mouse_pos(p_pos, row, col);
- // Info icons.
- int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.info_gutter_width;
- if (draw_info_gutter && p_pos.x > left_margin + cache.breakpoint_gutter_width - 6 && p_pos.x <= gutter_left - 3) {
- if (text.has_info_icon(row)) {
- return CURSOR_POINTING_HAND;
+ int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
+ int gutter = left_margin + gutters_width;
+ if (p_pos.x < gutter) {
+ for (int i = 0; i < gutters.size(); i++) {
+ if (!gutters[i].draw) {
+ continue;
}
- return CURSOR_ARROW;
- }
- // Fold icon.
- if (draw_fold_gutter && p_pos.x > gutter_left + cache.line_number_w - 6 && p_pos.x <= gutter_left + cache.line_number_w + cache.fold_gutter_width - 3) {
- if (is_folded(row) || can_fold(row)) {
- return CURSOR_POINTING_HAND;
- } else {
- return CURSOR_ARROW;
+ if (p_pos.x > left_margin && p_pos.x <= (left_margin + gutters[i].width) - 3) {
+ if (gutters[i].clickable || is_line_gutter_clickable(row, i)) {
+ return CURSOR_POINTING_HAND;
+ }
}
+ left_margin += gutters[i].width;
}
+ return CURSOR_ARROW;
+ }
+ int xmargin_end = get_size().width - cache.style_normal->get_margin(MARGIN_RIGHT);
+ if (draw_minimap && p_pos.x > xmargin_end - minimap_width && p_pos.x <= xmargin_end) {
return CURSOR_ARROW;
- } else {
- int xmargin_end = get_size().width - cache.style_normal->get_margin(MARGIN_RIGHT);
- if (draw_minimap && p_pos.x > xmargin_end - minimap_width && p_pos.x <= xmargin_end) {
- return CURSOR_ARROW;
- }
-
- int row, col;
- _get_mouse_pos(p_pos, row, col);
- // EOL fold icon.
- if (is_folded(row)) {
- int line_width = text.get_line_width(row);
- line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width - cursor.x_ofs;
- if (p_pos.x > line_width - 3 && p_pos.x <= line_width + cache.folded_eol_icon->get_width() + 3) {
- return CURSOR_POINTING_HAND;
- }
+ }
+
+ // EOL fold icon.
+ if (is_folded(row)) {
+ gutter += gutter_padding + text.get_line_width(row) - cursor.x_ofs;
+ if (p_pos.x > gutter - 3 && p_pos.x <= gutter + cache.folded_eol_icon->get_width() + 3) {
+ return CURSOR_POINTING_HAND;
}
}
@@ -4688,7 +4627,7 @@ void TextEdit::set_text(String p_text) {
update();
setting_text = false;
-};
+}
String TextEdit::get_text() {
String longthing;
@@ -4701,11 +4640,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();
@@ -4780,32 +4832,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();
}
@@ -4814,7 +4840,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 {
@@ -4856,10 +4885,9 @@ 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.line_number_color = get_theme_color("line_number_color");
- cache.safe_line_number_color = get_theme_color("safe_line_number_color");
cache.font_color = get_theme_color("font_color");
cache.font_color_selected = get_theme_color("font_color_selected");
cache.font_color_readonly = get_theme_color("font_color_readonly");
@@ -4867,9 +4895,6 @@ void TextEdit::_update_caches() {
cache.mark_color = get_theme_color("mark_color");
cache.current_line_color = get_theme_color("current_line_color");
cache.line_length_guideline_color = get_theme_color("line_length_guideline_color");
- cache.bookmark_color = get_theme_color("bookmark_color");
- cache.breakpoint_color = get_theme_color("breakpoint_color");
- cache.executing_line_color = get_theme_color("executing_line_color");
cache.code_folding_color = get_theme_color("code_folding_color");
cache.brace_mismatch_color = get_theme_color("brace_mismatch_color");
cache.word_highlighted_color = get_theme_color("word_highlighted_color");
@@ -4881,21 +4906,29 @@ 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_icon = get_theme_icon("folded");
- cache.can_fold_icon = get_theme_icon("fold");
cache.folded_eol_icon = get_theme_icon("GuiEllipsis", "EditorIcons");
- cache.executing_icon = get_theme_icon("MainPlay", "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);
}
}
+/* Syntax Highlighting. */
Ref<SyntaxHighlighter> TextEdit::get_syntax_highlighter() {
return syntax_highlighter;
}
@@ -4908,6 +4941,187 @@ void TextEdit::set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighte
update();
}
+/* Gutters. */
+void TextEdit::_update_gutter_width() {
+ gutters_width = 0;
+ for (int i = 0; i < gutters.size(); i++) {
+ if (gutters[i].draw) {
+ gutters_width += gutters[i].width;
+ }
+ }
+ if (gutters_width > 0) {
+ gutter_padding = 2;
+ }
+ update();
+}
+
+void TextEdit::add_gutter(int p_at) {
+ if (p_at < 0 || p_at > gutters.size()) {
+ gutters.push_back(GutterInfo());
+ } else {
+ gutters.insert(p_at, GutterInfo());
+ }
+
+ for (int i = 0; i < text.size() + 1; i++) {
+ text.add_gutter(p_at);
+ }
+ emit_signal("gutter_added");
+ update();
+}
+
+void TextEdit::remove_gutter(int p_gutter) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+
+ gutters.remove(p_gutter);
+
+ for (int i = 0; i < text.size() + 1; i++) {
+ text.remove_gutter(p_gutter);
+ }
+ emit_signal("gutter_removed");
+ update();
+}
+
+int TextEdit::get_gutter_count() const {
+ return gutters.size();
+}
+
+void TextEdit::set_gutter_name(int p_gutter, const String &p_name) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].name = p_name;
+}
+
+String TextEdit::get_gutter_name(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), "");
+ return gutters[p_gutter].name;
+}
+
+void TextEdit::set_gutter_type(int p_gutter, GutterType p_type) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].type = p_type;
+ update();
+}
+
+TextEdit::GutterType TextEdit::get_gutter_type(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), GUTTER_TYPE_STRING);
+ return gutters[p_gutter].type;
+}
+
+void TextEdit::set_gutter_width(int p_gutter, int p_width) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].width = p_width;
+ _update_gutter_width();
+}
+
+int TextEdit::get_gutter_width(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), -1);
+ return gutters[p_gutter].width;
+}
+
+void TextEdit::set_gutter_draw(int p_gutter, bool p_draw) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].draw = p_draw;
+ _update_gutter_width();
+}
+
+bool TextEdit::is_gutter_drawn(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false);
+ return gutters[p_gutter].draw;
+}
+
+void TextEdit::set_gutter_clickable(int p_gutter, bool p_clickable) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].clickable = p_clickable;
+ update();
+}
+
+bool TextEdit::is_gutter_clickable(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false);
+ return gutters[p_gutter].clickable;
+}
+
+void TextEdit::set_gutter_overwritable(int p_gutter, bool p_overwritable) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].overwritable = p_overwritable;
+}
+
+bool TextEdit::is_gutter_overwritable(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false);
+ return gutters[p_gutter].overwritable;
+}
+
+void TextEdit::set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ ERR_FAIL_NULL(p_object);
+
+ gutters.write[p_gutter].custom_draw_obj = p_object->get_instance_id();
+ gutters.write[p_gutter].custom_draw_callback = p_callback;
+ update();
+}
+
+// Line gutters.
+void TextEdit::set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ text.set_line_gutter_metadata(p_line, p_gutter, p_metadata);
+}
+
+Variant TextEdit::get_line_gutter_metadata(int p_line, int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), "");
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), "");
+ return text.get_line_gutter_metadata(p_line, p_gutter);
+}
+
+void TextEdit::set_line_gutter_text(int p_line, int p_gutter, const String &p_text) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ text.set_line_gutter_text(p_line, p_gutter, p_text);
+ update();
+}
+
+String TextEdit::get_line_gutter_text(int p_line, int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), "");
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), "");
+ return text.get_line_gutter_text(p_line, p_gutter);
+}
+
+void TextEdit::set_line_gutter_icon(int p_line, int p_gutter, Ref<Texture2D> p_icon) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ text.set_line_gutter_icon(p_line, p_gutter, p_icon);
+ update();
+}
+
+Ref<Texture2D> TextEdit::get_line_gutter_icon(int p_line, int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), Ref<Texture2D>());
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), Ref<Texture2D>());
+ return text.get_line_gutter_icon(p_line, p_gutter);
+}
+
+void TextEdit::set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ text.set_line_gutter_item_color(p_line, p_gutter, p_color);
+ update();
+}
+
+Color TextEdit::get_line_gutter_item_color(int p_line, int p_gutter) {
+ ERR_FAIL_INDEX_V(p_line, text.size(), Color());
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), Color());
+ return text.get_line_gutter_item_color(p_line, p_gutter);
+}
+
+void TextEdit::set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ text.set_line_gutter_clickable(p_line, p_gutter, p_clickable);
+}
+
+bool TextEdit::is_line_gutter_clickable(int p_line, int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), false);
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false);
+ return text.is_line_gutter_clickable(p_line, p_gutter);
+}
+
void TextEdit::add_keyword(const String &p_keyword) {
keywords.insert(p_keyword);
}
@@ -4947,7 +5161,7 @@ void TextEdit::cut() {
cursor_set_column(selection.from_column);
selection.active = false;
- selection.selecting_mode = Selection::MODE_NONE;
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
update();
cut_copy_line = "";
}
@@ -4973,7 +5187,7 @@ void TextEdit::paste() {
begin_complex_operation();
if (selection.active) {
selection.active = false;
- selection.selecting_mode = Selection::MODE_NONE;
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
cursor_set_line(selection.from_line);
cursor_set_column(selection.from_column);
@@ -5005,7 +5219,7 @@ void TextEdit::select_all() {
selection.selecting_column = 0;
selection.to_line = text.size() - 1;
selection.to_column = text[selection.to_line].length();
- selection.selecting_mode = Selection::MODE_SHIFT;
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_SHIFT;
selection.shiftclick_left = true;
cursor_set_line(selection.to_line, false);
cursor_set_column(selection.to_column, false);
@@ -5114,27 +5328,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) {
@@ -5338,106 +5538,6 @@ void TextEdit::set_line_as_marked(int p_line, bool p_marked) {
update();
}
-void TextEdit::set_line_as_safe(int p_line, bool p_safe) {
- ERR_FAIL_INDEX(p_line, text.size());
- text.set_safe(p_line, p_safe);
- update();
-}
-
-bool TextEdit::is_line_set_as_safe(int p_line) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), false);
- return text.is_safe(p_line);
-}
-
-void TextEdit::set_executing_line(int p_line) {
- ERR_FAIL_INDEX(p_line, text.size());
- executing_line = p_line;
- update();
-}
-
-void TextEdit::clear_executing_line() {
- executing_line = -1;
- update();
-}
-
-bool TextEdit::is_line_set_as_bookmark(int p_line) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), false);
- return text.is_bookmark(p_line);
-}
-
-void TextEdit::set_line_as_bookmark(int p_line, bool p_bookmark) {
- ERR_FAIL_INDEX(p_line, text.size());
- text.set_bookmark(p_line, p_bookmark);
- update();
-}
-
-void TextEdit::get_bookmarks(List<int> *p_bookmarks) const {
- for (int i = 0; i < text.size(); i++) {
- if (text.is_bookmark(i)) {
- p_bookmarks->push_back(i);
- }
- }
-}
-
-Array TextEdit::get_bookmarks_array() const {
- Array arr;
- for (int i = 0; i < text.size(); i++) {
- if (text.is_bookmark(i)) {
- arr.append(i);
- }
- }
- return arr;
-}
-
-bool TextEdit::is_line_set_as_breakpoint(int p_line) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), false);
- return text.is_breakpoint(p_line);
-}
-
-void TextEdit::set_line_as_breakpoint(int p_line, bool p_breakpoint) {
- ERR_FAIL_INDEX(p_line, text.size());
- text.set_breakpoint(p_line, p_breakpoint);
- update();
-}
-
-void TextEdit::get_breakpoints(List<int> *p_breakpoints) const {
- for (int i = 0; i < text.size(); i++) {
- if (text.is_breakpoint(i)) {
- p_breakpoints->push_back(i);
- }
- }
-}
-
-Array TextEdit::get_breakpoints_array() const {
- Array arr;
- for (int i = 0; i < text.size(); i++) {
- if (text.is_breakpoint(i)) {
- arr.append(i);
- }
- }
- return arr;
-}
-
-void TextEdit::remove_breakpoints() {
- for (int i = 0; i < text.size(); i++) {
- if (text.is_breakpoint(i)) {
- /* Should "breakpoint_toggled" be fired when breakpoints are removed this way? */
- text.set_breakpoint(i, false);
- }
- }
-}
-
-void TextEdit::set_line_info_icon(int p_line, Ref<Texture2D> p_icon, String p_info) {
- ERR_FAIL_INDEX(p_line, text.size());
- text.set_info_icon(p_line, p_icon, p_info);
- update();
-}
-
-void TextEdit::clear_info_icons() {
- text.clear_info_icons();
- update();
-}
-
void TextEdit::set_line_as_hidden(int p_line, bool p_hidden) {
ERR_FAIL_INDEX(p_line, text.size());
if (is_hiding_enabled() || !p_hidden) {
@@ -5530,7 +5630,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;
@@ -5545,7 +5645,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;
@@ -5920,8 +6020,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++) {
@@ -5946,6 +6049,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 {
@@ -6112,9 +6216,9 @@ void TextEdit::_confirm_completion() {
// When inserted into the middle of an existing string/method, don't add an unnecessary quote/bracket.
String line = text[cursor.line];
- CharType next_char = line[cursor.column];
- CharType last_completion_char = completion_current.insert_text[completion_current.insert_text.length() - 1];
- CharType last_completion_char_display = completion_current.display[completion_current.display.length() - 1];
+ char32_t next_char = line[cursor.column];
+ char32_t last_completion_char = completion_current.insert_text[completion_current.insert_text.length() - 1];
+ char32_t last_completion_char_display = completion_current.display[completion_current.display.length() - 1];
if ((last_completion_char == '"' || last_completion_char == '\'') && (last_completion_char == next_char || last_completion_char_display == next_char)) {
_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
@@ -6158,7 +6262,7 @@ void TextEdit::_cancel_completion() {
update();
}
-static bool _is_completable(CharType c) {
+static bool _is_completable(char32_t c) {
return !_is_symbol(c) || c == '"' || c == '\'';
}
@@ -6289,14 +6393,14 @@ void TextEdit::_update_completion_candidates() {
String display_lower = option.display.to_lower();
- const CharType *ssq = &s[0];
- const CharType *ssq_lower = &s_lower[0];
+ const char32_t *ssq = &s[0];
+ const char32_t *ssq_lower = &s_lower[0];
- const CharType *tgt = &option.display[0];
- const CharType *tgt_lower = &display_lower[0];
+ const char32_t *tgt = &option.display[0];
+ const char32_t *tgt_lower = &display_lower[0];
- const CharType *ssq_last_tgt = nullptr;
- const CharType *ssq_lower_last_tgt = nullptr;
+ const char32_t *ssq_last_tgt = nullptr;
+ const char32_t *ssq_lower_last_tgt = nullptr;
for (; *tgt; tgt++, tgt_lower++) {
if (*ssq == *tgt) {
@@ -6413,7 +6517,7 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
int beg, end;
if (select_word(s, col, beg, end)) {
bool inside_quotes = false;
- CharType selected_quote = '\0';
+ char32_t selected_quote = '\0';
int qbegin = 0, qend = 0;
for (int i = 0; i < s.length(); i++) {
if (s[i] == '"' || s[i] == '\'') {
@@ -6468,7 +6572,7 @@ void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_funct
}
void TextEdit::set_line(int line, String new_text) {
- if (line < 0 || line > text.size()) {
+ if (line < 0 || line >= text.size()) {
return;
}
_remove_text(line, 0, line, text[line].length());
@@ -6499,20 +6603,6 @@ void TextEdit::insert_at(const String &p_text, int at) {
}
}
-void TextEdit::set_show_line_numbers(bool p_show) {
- line_numbers = p_show;
- update();
-}
-
-void TextEdit::set_line_numbers_zero_padded(bool p_zero_padded) {
- line_numbers_zero_padded = p_zero_padded;
- update();
-}
-
-bool TextEdit::is_show_line_numbers_enabled() const {
- return line_numbers;
-}
-
void TextEdit::set_show_line_length_guidelines(bool p_show) {
line_length_guidelines = p_show;
update();
@@ -6528,71 +6618,11 @@ void TextEdit::set_line_length_guideline_hard_column(int p_column) {
update();
}
-void TextEdit::set_bookmark_gutter_enabled(bool p_draw) {
- draw_bookmark_gutter = p_draw;
- update();
-}
-
-bool TextEdit::is_bookmark_gutter_enabled() const {
- return draw_bookmark_gutter;
-}
-
-void TextEdit::set_breakpoint_gutter_enabled(bool p_draw) {
- draw_breakpoint_gutter = p_draw;
- update();
-}
-
-bool TextEdit::is_breakpoint_gutter_enabled() const {
- return draw_breakpoint_gutter;
-}
-
-void TextEdit::set_breakpoint_gutter_width(int p_gutter_width) {
- breakpoint_gutter_width = p_gutter_width;
- update();
-}
-
-int TextEdit::get_breakpoint_gutter_width() const {
- return cache.breakpoint_gutter_width;
-}
-
-void TextEdit::set_draw_fold_gutter(bool p_draw) {
- draw_fold_gutter = p_draw;
- update();
-}
-
-bool TextEdit::is_drawing_fold_gutter() const {
- return draw_fold_gutter;
-}
-
-void TextEdit::set_fold_gutter_width(int p_gutter_width) {
- fold_gutter_width = p_gutter_width;
- update();
-}
-
-int TextEdit::get_fold_gutter_width() const {
- return cache.fold_gutter_width;
-}
-
-void TextEdit::set_draw_info_gutter(bool p_draw) {
- draw_info_gutter = p_draw;
- update();
-}
-
-bool TextEdit::is_drawing_info_gutter() const {
- return draw_info_gutter;
-}
-
-void TextEdit::set_info_gutter_width(int p_gutter_width) {
- info_gutter_width = p_gutter_width;
- update();
-}
-
-int TextEdit::get_info_gutter_width() const {
- return info_gutter_width;
-}
-
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();
}
@@ -6601,7 +6631,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();
}
@@ -6662,6 +6695,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));
+ }
}
}
}
@@ -6723,21 +6851,90 @@ 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);
BIND_ENUM_CONSTANT(SEARCH_BACKWARDS);
+ BIND_ENUM_CONSTANT(SELECTION_MODE_NONE);
+ BIND_ENUM_CONSTANT(SELECTION_MODE_SHIFT);
+ BIND_ENUM_CONSTANT(SELECTION_MODE_POINTER);
+ BIND_ENUM_CONSTANT(SELECTION_MODE_WORD);
+ BIND_ENUM_CONSTANT(SELECTION_MODE_LINE);
+
/*
ClassDB::bind_method(D_METHOD("delete_char"),&TextEdit::delete_char);
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);
@@ -6746,6 +6943,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));
@@ -6759,9 +6961,17 @@ 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);
+ ClassDB::bind_method(D_METHOD("get_selection_mode"), &TextEdit::get_selection_mode);
+ ClassDB::bind_method(D_METHOD("set_selection_mode", "mode", "line", "column"), &TextEdit::set_selection_mode, DEFVAL(-1), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_selection_line"), &TextEdit::get_selection_line);
+ ClassDB::bind_method(D_METHOD("get_selection_column"), &TextEdit::get_selection_column);
+
ClassDB::bind_method(D_METHOD("set_readonly", "enable"), &TextEdit::set_readonly);
ClassDB::bind_method(D_METHOD("is_readonly"), &TextEdit::is_readonly);
@@ -6797,16 +7007,10 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("redo"), &TextEdit::redo);
ClassDB::bind_method(D_METHOD("clear_undo_history"), &TextEdit::clear_undo_history);
- ClassDB::bind_method(D_METHOD("set_show_line_numbers", "enable"), &TextEdit::set_show_line_numbers);
- ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled);
ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs);
ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs);
ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces);
ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces);
- ClassDB::bind_method(D_METHOD("set_breakpoint_gutter_enabled", "enable"), &TextEdit::set_breakpoint_gutter_enabled);
- ClassDB::bind_method(D_METHOD("is_breakpoint_gutter_enabled"), &TextEdit::is_breakpoint_gutter_enabled);
- ClassDB::bind_method(D_METHOD("set_draw_fold_gutter"), &TextEdit::set_draw_fold_gutter);
- ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &TextEdit::is_drawing_fold_gutter);
ClassDB::bind_method(D_METHOD("set_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled);
ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled);
@@ -6829,6 +7033,40 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_syntax_highlighter", "syntax_highlighter"), &TextEdit::set_syntax_highlighter);
ClassDB::bind_method(D_METHOD("get_syntax_highlighter"), &TextEdit::get_syntax_highlighter);
+ /* Gutters. */
+ BIND_ENUM_CONSTANT(GUTTER_TYPE_STRING);
+ BIND_ENUM_CONSTANT(GUTTER_TPYE_ICON);
+ BIND_ENUM_CONSTANT(GUTTER_TPYE_CUSTOM);
+
+ ClassDB::bind_method(D_METHOD("add_gutter", "at"), &TextEdit::add_gutter, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("remove_gutter", "gutter"), &TextEdit::remove_gutter);
+ ClassDB::bind_method(D_METHOD("get_gutter_count"), &TextEdit::get_gutter_count);
+ ClassDB::bind_method(D_METHOD("set_gutter_name", "gutter", "name"), &TextEdit::set_gutter_name);
+ ClassDB::bind_method(D_METHOD("get_gutter_name", "gutter"), &TextEdit::get_gutter_name);
+ ClassDB::bind_method(D_METHOD("set_gutter_type", "gutter", "type"), &TextEdit::set_gutter_type);
+ ClassDB::bind_method(D_METHOD("get_gutter_type", "gutter"), &TextEdit::get_gutter_type);
+ ClassDB::bind_method(D_METHOD("set_gutter_width", "gutter", "width"), &TextEdit::set_gutter_width);
+ ClassDB::bind_method(D_METHOD("get_gutter_width", "gutter"), &TextEdit::get_gutter_width);
+ ClassDB::bind_method(D_METHOD("set_gutter_draw", "gutter", "draw"), &TextEdit::set_gutter_draw);
+ ClassDB::bind_method(D_METHOD("is_gutter_drawn", "gutter"), &TextEdit::is_gutter_drawn);
+ ClassDB::bind_method(D_METHOD("set_gutter_clickable", "gutter", "clickable"), &TextEdit::set_gutter_clickable);
+ ClassDB::bind_method(D_METHOD("is_gutter_clickable", "gutter"), &TextEdit::is_gutter_clickable);
+ ClassDB::bind_method(D_METHOD("set_gutter_overwritable", "gutter", "overwritable"), &TextEdit::set_gutter_overwritable);
+ ClassDB::bind_method(D_METHOD("is_gutter_overwritable", "gutter"), &TextEdit::is_gutter_overwritable);
+ ClassDB::bind_method(D_METHOD("set_gutter_custom_draw", "column", "object", "callback"), &TextEdit::set_gutter_custom_draw);
+
+ // Line gutters.
+ ClassDB::bind_method(D_METHOD("set_line_gutter_metadata", "line", "gutter", "metadata"), &TextEdit::set_line_gutter_metadata);
+ ClassDB::bind_method(D_METHOD("get_line_gutter_metadata", "line", "gutter"), &TextEdit::get_line_gutter_metadata);
+ ClassDB::bind_method(D_METHOD("set_line_gutter_text", "line", "gutter", "text"), &TextEdit::set_line_gutter_text);
+ ClassDB::bind_method(D_METHOD("get_line_gutter_text", "line", "gutter"), &TextEdit::get_line_gutter_text);
+ ClassDB::bind_method(D_METHOD("set_line_gutter_icon", "line", "gutter", "icon"), &TextEdit::set_line_gutter_icon);
+ ClassDB::bind_method(D_METHOD("get_line_gutter_icon", "line", "gutter"), &TextEdit::get_line_gutter_icon);
+ ClassDB::bind_method(D_METHOD("set_line_gutter_item_color", "line", "gutter", "color"), &TextEdit::set_line_gutter_item_color);
+ ClassDB::bind_method(D_METHOD("get_line_gutter_item_color", "line", "gutter"), &TextEdit::get_line_gutter_item_color);
+ ClassDB::bind_method(D_METHOD("set_line_gutter_clickable", "line", "gutter", "clickable"), &TextEdit::set_line_gutter_clickable);
+ ClassDB::bind_method(D_METHOD("is_line_gutter_clickable", "line", "gutter"), &TextEdit::is_line_gutter_clickable);
+
ClassDB::bind_method(D_METHOD("set_highlight_current_line", "enabled"), &TextEdit::set_highlight_current_line);
ClassDB::bind_method(D_METHOD("is_highlight_current_line_enabled"), &TextEdit::is_highlight_current_line_enabled);
@@ -6844,22 +7082,19 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option);
ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu);
- ClassDB::bind_method(D_METHOD("get_breakpoints"), &TextEdit::get_breakpoints_array);
- ClassDB::bind_method(D_METHOD("remove_breakpoints"), &TextEdit::remove_breakpoints);
-
ClassDB::bind_method(D_METHOD("draw_minimap", "draw"), &TextEdit::set_draw_minimap);
ClassDB::bind_method(D_METHOD("is_drawing_minimap"), &TextEdit::is_drawing_minimap);
ClassDB::bind_method(D_METHOD("set_minimap_width", "width"), &TextEdit::set_minimap_width);
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, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "breakpoint_gutter"), "set_breakpoint_gutter_enabled", "is_breakpoint_gutter_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
@@ -6884,14 +7119,20 @@ 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"));
- ADD_SIGNAL(MethodInfo("line_edited_from", PropertyInfo(Variant::INT, "line")));
+ ADD_SIGNAL(MethodInfo("lines_edited_from", PropertyInfo(Variant::INT, "from_line"), PropertyInfo(Variant::INT, "to_line")));
ADD_SIGNAL(MethodInfo("request_completion"));
- ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "row")));
+ ADD_SIGNAL(MethodInfo("gutter_clicked", PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::INT, "gutter")));
+ ADD_SIGNAL(MethodInfo("gutter_added"));
+ ADD_SIGNAL(MethodInfo("gutter_removed"));
ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::INT, "column")));
- ADD_SIGNAL(MethodInfo("info_clicked", PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::STRING, "info")));
ADD_SIGNAL(MethodInfo("symbol_validate", PropertyInfo(Variant::STRING, "symbol")));
BIND_ENUM_CONSTANT(MENU_CUT);
@@ -6901,6 +7142,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);
@@ -6922,15 +7184,8 @@ TextEdit::TextEdit() {
wrap_right_offset = 10;
set_focus_mode(FOCUS_ALL);
_update_caches();
- cache.row_height = 1;
cache.line_spacing = 1;
- cache.line_number_w = 1;
- cache.breakpoint_gutter_width = 0;
- breakpoint_gutter_width = 0;
- cache.fold_gutter_width = 0;
- fold_gutter_width = 0;
- info_gutter_width = 0;
- cache.info_gutter_width = 0;
+ cache.font_size = 16;
set_default_cursor_shape(CURSOR_IBEAM);
indent_size = 4;
@@ -6954,7 +7209,7 @@ TextEdit::TextEdit() {
cursor_changed_dirty = false;
text_changed_dirty = false;
- selection.selecting_mode = Selection::MODE_NONE;
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
selection.selecting_line = 0;
selection.selecting_column = 0;
selection.selecting_text = false;
@@ -6994,15 +7249,9 @@ TextEdit::TextEdit() {
completion_active = false;
completion_line_ofs = 0;
tooltip_obj = nullptr;
- line_numbers = false;
- line_numbers_zero_padded = false;
line_length_guidelines = false;
line_length_guideline_soft_col = 80;
line_length_guideline_hard_col = 100;
- draw_bookmark_gutter = false;
- draw_breakpoint_gutter = false;
- draw_fold_gutter = false;
- draw_info_gutter = false;
hiding_enabled = false;
next_operation_is_complex = false;
scroll_past_end_of_file_enabled = false;
@@ -7036,12 +7285,44 @@ 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;
-
- executing_line = -1;
}
TextEdit::~TextEdit() {
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index a6bc9963cc..c2fe4afec5 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -36,87 +36,142 @@
#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);
public:
+ enum GutterType {
+ GUTTER_TYPE_STRING,
+ GUTTER_TPYE_ICON,
+ GUTTER_TPYE_CUSTOM
+ };
+
+ enum SelectionMode {
+ SELECTION_MODE_NONE,
+ SELECTION_MODE_SHIFT,
+ SELECTION_MODE_POINTER,
+ SELECTION_MODE_WORD,
+ SELECTION_MODE_LINE
+ };
+
+private:
+ struct GutterInfo {
+ GutterType type = GutterType::GUTTER_TYPE_STRING;
+ String name = "";
+ int width = 24;
+ bool draw = true;
+ bool clickable = false;
+ bool overwritable = false;
+
+ ObjectID custom_draw_obj = ObjectID();
+ StringName custom_draw_callback;
+ };
+ Vector<GutterInfo> gutters;
+ int gutters_width = 0;
+ int gutter_padding = 0;
+
+ void _update_gutter_width();
+
class Text {
public:
+ struct Gutter {
+ Variant metadata;
+ bool clickable = false;
+
+ Ref<Texture2D> icon = Ref<Texture2D>();
+ String text = "";
+ Color color = Color(1, 1, 1);
+ };
+
struct Line {
- int width_cache : 24;
- bool marked : 1;
- bool breakpoint : 1;
- bool bookmark : 1;
- bool hidden : 1;
- bool safe : 1;
- bool has_info : 1;
- int wrap_amount_cache : 24;
- Ref<Texture2D> info_icon;
- String info;
+ Vector<Gutter> gutters;
+
String data;
+ Vector<Vector2i> bidi_override;
+ Ref<TextParagraph> data_buf;
+
+ bool marked;
+ bool hidden;
+
Line() {
- width_cache = 0;
+ data_buf.instance();
+
marked = false;
- breakpoint = false;
- bookmark = false;
hidden = false;
- safe = false;
- has_info = false;
- wrap_amount_cache = 0;
}
};
private:
mutable Vector<Line> text;
Ref<Font> font;
- int indent_size;
+ int font_size = -1;
+
+ Dictionary opentype_features;
+ String language;
+ TextServer::Direction direction = TextServer::DIRECTION_AUTO;
+ bool draw_control_chars = false;
- void _update_line_cache(int p_line) const;
+ int width = -1;
+
+ int indent_size = 4;
+ int gutter_count = 0;
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(CharType c, CharType 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_bookmark(int p_line, bool p_bookmark) { text.write[p_line].bookmark = p_bookmark; }
- bool is_bookmark(int p_line) const { return text[p_line].bookmark; }
- void set_breakpoint(int p_line, bool p_breakpoint) { text.write[p_line].breakpoint = p_breakpoint; }
- bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
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 set_safe(int p_line, bool p_safe) { text.write[p_line].safe = p_safe; }
- bool is_safe(int p_line) const { return text[p_line].safe; }
- void set_info_icon(int p_line, Ref<Texture2D> p_icon, String p_info) {
- if (p_icon.is_null()) {
- text.write[p_line].has_info = false;
- return;
- }
- text.write[p_line].info_icon = p_icon;
- text.write[p_line].info = p_info;
- text.write[p_line].has_info = true;
- }
- bool has_info_icon(int p_line) const { return text[p_line].has_info; }
- const Ref<Texture2D> &get_info_icon(int p_line) const { return text[p_line].info_icon; }
- const String &get_info(int p_line) const { return text[p_line].info; }
- 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();
- void clear_info_icons();
- _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
- Text() { indent_size = 4; }
+
+ 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);
+ void remove_gutter(int p_gutter);
+ void move_gutters(int p_from_line, int p_to_line);
+
+ void set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata) { text.write[p_line].gutters.write[p_gutter].metadata = p_metadata; }
+ const Variant &get_line_gutter_metadata(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].metadata; }
+
+ void set_line_gutter_text(int p_line, int p_gutter, const String &p_text) { text.write[p_line].gutters.write[p_gutter].text = p_text; }
+ const String &get_line_gutter_text(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].text; }
+
+ void set_line_gutter_icon(int p_line, int p_gutter, Ref<Texture2D> p_icon) { text.write[p_line].gutters.write[p_gutter].icon = p_icon; }
+ const Ref<Texture2D> &get_line_gutter_icon(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].icon; }
+
+ void set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color) { text.write[p_line].gutters.write[p_gutter].color = p_color; }
+ const Color &get_line_gutter_item_color(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].color; }
+
+ void set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable) { text.write[p_line].gutters.write[p_gutter].clickable = p_clickable; }
+ bool is_line_gutter_clickable(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].clickable; }
};
-private:
struct Cursor {
int last_fit_x;
int line, column; ///< cursor
@@ -132,16 +187,7 @@ private:
} cursor;
struct Selection {
- enum Mode {
-
- MODE_NONE,
- MODE_SHIFT,
- MODE_POINTER,
- MODE_WORD,
- MODE_LINE
- };
-
- Mode selecting_mode;
+ SelectionMode selecting_mode;
int selecting_line, selecting_column;
int selected_word_beg, selected_word_end, selected_word_origin;
bool selecting_text;
@@ -153,7 +199,7 @@ private:
bool shiftclick_left;
Selection() {
- selecting_mode = MODE_NONE;
+ selecting_mode = SelectionMode::SELECTION_MODE_NONE;
selecting_line = 0;
selecting_column = 0;
selected_word_beg = 0;
@@ -169,60 +215,6 @@ private:
}
} selection;
- struct Cache {
- Ref<Texture2D> tab_icon;
- Ref<Texture2D> space_icon;
- Ref<Texture2D> can_fold_icon;
- Ref<Texture2D> folded_icon;
- Ref<Texture2D> folded_eol_icon;
- Ref<Texture2D> executing_icon;
- Ref<StyleBox> style_normal;
- Ref<StyleBox> style_focus;
- Ref<StyleBox> style_readonly;
- Ref<Font> font;
- Color completion_background_color;
- Color completion_selected_color;
- Color completion_existing_color;
- Color completion_font_color;
- Color caret_color;
- Color caret_background_color;
- Color line_number_color;
- Color safe_line_number_color;
- Color font_color;
- Color font_color_selected;
- Color font_color_readonly;
- Color selection_color;
- Color mark_color;
- Color bookmark_color;
- Color breakpoint_color;
- Color executing_line_color;
- Color code_folding_color;
- Color current_line_color;
- Color line_length_guideline_color;
- Color brace_mismatch_color;
- Color word_highlighted_color;
- Color search_result_color;
- Color search_result_border_color;
- Color background_color;
-
- int row_height;
- int line_spacing;
- int line_number_w;
- int breakpoint_gutter_width;
- int fold_gutter_width;
- int info_gutter_width;
- int minimap_width;
- Cache() {
- row_height = 0;
- line_spacing = 0;
- line_number_w = 0;
- breakpoint_gutter_width = 0;
- fold_gutter_width = 0;
- info_gutter_width = 0;
- minimap_width = 0;
- }
- } cache;
-
Map<int, Dictionary> syntax_highlighting_cache;
struct TextOperation {
@@ -290,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;
@@ -305,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;
@@ -318,19 +319,10 @@ private:
bool cursor_changed_dirty;
bool text_changed_dirty;
bool undo_enabled;
- bool line_numbers;
- bool line_numbers_zero_padded;
bool line_length_guidelines;
int line_length_guideline_soft_col;
int line_length_guideline_hard_col;
- bool draw_bookmark_gutter;
- bool draw_breakpoint_gutter;
- int breakpoint_gutter_width;
- bool draw_fold_gutter;
- int fold_gutter_width;
bool hiding_enabled;
- bool draw_info_gutter;
- int info_gutter_width;
bool draw_minimap;
int minimap_width;
Point2 minimap_char_size;
@@ -385,11 +377,8 @@ private:
bool context_menu_enabled;
bool shortcut_keys_enabled;
-
bool virtual_keyboard_enabled = true;
- int executing_line;
-
void _generate_context_menu();
int get_visible_rows() const;
@@ -398,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;
@@ -418,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;
@@ -447,7 +434,7 @@ private:
Size2 get_minimum_size() const override;
int _get_control_height() const;
- int get_row_height() const;
+ Point2 _get_local_mouse_pos() const;
void _reset_caret_blink_timer();
void _toggle_draw_caret();
@@ -469,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();
@@ -480,6 +469,43 @@ private:
int _calculate_spaces_till_next_right_indent(int column);
protected:
+ struct Cache {
+ Ref<Texture2D> tab_icon;
+ Ref<Texture2D> space_icon;
+ Ref<Texture2D> folded_eol_icon;
+ Ref<StyleBox> style_normal;
+ 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;
+ Color completion_font_color;
+ Color caret_color;
+ Color caret_background_color;
+ Color font_color;
+ Color font_color_selected;
+ Color font_color_readonly;
+ Color selection_color;
+ Color mark_color;
+ Color code_folding_color;
+ Color current_line_color;
+ Color line_length_guideline_color;
+ Color brace_mismatch_color;
+ Color word_highlighted_color;
+ Color search_result_color;
+ Color search_result_border_color;
+ Color background_color;
+
+ int line_spacing;
+ int minimap_width;
+ Cache() {
+ line_spacing = 0;
+ minimap_width = 0;
+ }
+ } cache;
+
virtual String get_tooltip(const Point2 &p_pos) const override;
void _insert_text(int p_line, int p_char, const String &p_text, int *r_end_line = nullptr, int *r_end_char = nullptr);
@@ -488,15 +514,61 @@ protected:
void _gui_input(const Ref<InputEvent> &p_gui_input);
void _notification(int p_what);
- void _consume_pair_symbol(CharType ch);
+ void _consume_pair_symbol(char32_t ch);
void _consume_backspace_for_pair_symbol(int prev_line, int prev_column);
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();
void set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter);
+ /* Gutters. */
+ void add_gutter(int p_at = -1);
+ void remove_gutter(int p_gutter);
+ int get_gutter_count() const;
+
+ void set_gutter_name(int p_gutter, const String &p_name);
+ String get_gutter_name(int p_gutter) const;
+
+ void set_gutter_type(int p_gutter, GutterType p_type);
+ GutterType get_gutter_type(int p_gutter) const;
+
+ void set_gutter_width(int p_gutter, int p_width);
+ int get_gutter_width(int p_gutter) const;
+
+ void set_gutter_draw(int p_gutter, bool p_draw);
+ bool is_gutter_drawn(int p_gutter) const;
+
+ void set_gutter_clickable(int p_gutter, bool p_clickable);
+ bool is_gutter_clickable(int p_gutter) const;
+
+ void set_gutter_overwritable(int p_gutter, bool p_overwritable);
+ bool is_gutter_overwritable(int p_gutter) const;
+
+ void set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback);
+
+ // Line gutters.
+ void set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata);
+ Variant get_line_gutter_metadata(int p_line, int p_gutter) const;
+
+ void set_line_gutter_text(int p_line, int p_gutter, const String &p_text);
+ String get_line_gutter_text(int p_line, int p_gutter) const;
+
+ void set_line_gutter_icon(int p_line, int p_gutter, Ref<Texture2D> p_icon);
+ Ref<Texture2D> get_line_gutter_icon(int p_line, int p_gutter) const;
+
+ void set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color);
+ Color get_line_gutter_item_color(int p_line, int p_gutter);
+
+ void set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable);
+ bool is_line_gutter_clickable(int p_line, int p_gutter) const;
+
enum MenuItems {
MENU_CUT,
MENU_COPY,
@@ -505,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
};
@@ -528,28 +621,31 @@ 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);
void insert_at(const String &p_text, int at);
int get_line_count() const;
void set_line_as_marked(int p_line, bool p_marked);
- void set_line_as_bookmark(int p_line, bool p_bookmark);
- bool is_line_set_as_bookmark(int p_line) const;
- void get_bookmarks(List<int> *p_bookmarks) const;
- Array get_bookmarks_array() const;
- void set_line_as_breakpoint(int p_line, bool p_breakpoint);
- bool is_line_set_as_breakpoint(int p_line) const;
- void set_executing_line(int p_line);
- void clear_executing_line();
- void set_line_as_safe(int p_line, bool p_safe);
- bool is_line_set_as_safe(int p_line) const;
- void get_breakpoints(List<int> *p_breakpoints) const;
- Array get_breakpoints_array() const;
- void remove_breakpoints();
-
- void set_line_info_icon(int p_line, Ref<Texture2D> p_icon, String p_info = "");
- void clear_info_icons();
void set_line_as_hidden(int p_line, bool p_hidden);
bool is_line_hidden(int p_line) const;
@@ -569,6 +665,7 @@ public:
String get_text();
String get_line(int line) const;
void set_line(int line, String new_text);
+ int get_row_height() const;
void backspace_at_cursor();
void indent_left();
@@ -595,6 +692,9 @@ 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);
@@ -614,6 +714,11 @@ public:
void set_right_click_moves_caret(bool p_enable);
bool is_right_click_moving_caret() const;
+ SelectionMode get_selection_mode() const;
+ void set_selection_mode(SelectionMode p_mode, int p_line = -1, int p_column = -1);
+ int get_selection_line() const;
+ int get_selection_column() const;
+
void set_readonly(bool p_readonly);
bool is_readonly() const;
@@ -690,39 +795,13 @@ public:
void menu_option(int p_option);
- void set_show_line_numbers(bool p_show);
- bool is_show_line_numbers_enabled() const;
-
void set_highlight_current_line(bool p_enabled);
bool is_highlight_current_line_enabled() const;
- void set_line_numbers_zero_padded(bool p_zero_padded);
-
void set_show_line_length_guidelines(bool p_show);
void set_line_length_guideline_soft_column(int p_column);
void set_line_length_guideline_hard_column(int p_column);
- void set_bookmark_gutter_enabled(bool p_draw);
- bool is_bookmark_gutter_enabled() const;
-
- void set_breakpoint_gutter_enabled(bool p_draw);
- bool is_breakpoint_gutter_enabled() const;
-
- void set_breakpoint_gutter_width(int p_gutter_width);
- int get_breakpoint_gutter_width() const;
-
- void set_draw_fold_gutter(bool p_draw);
- bool is_drawing_fold_gutter() const;
-
- void set_fold_gutter_width(int p_gutter_width);
- int get_fold_gutter_width() const;
-
- void set_draw_info_gutter(bool p_draw);
- bool is_drawing_info_gutter() const;
-
- void set_info_gutter_width(int p_gutter_width);
- int get_info_gutter_width() const;
-
void set_draw_minimap(bool p_draw);
bool is_drawing_minimap() const;
@@ -764,6 +843,8 @@ public:
~TextEdit();
};
+VARIANT_ENUM_CAST(TextEdit::GutterType);
+VARIANT_ENUM_CAST(TextEdit::SelectionMode);
VARIANT_ENUM_CAST(TextEdit::MenuItems);
VARIANT_ENUM_CAST(TextEdit::SearchFlags);
diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp
index 484b14d11c..e0d98d1c22 100644
--- a/scene/gui/texture_progress.cpp
+++ b/scene/gui/texture_progress.cpp
@@ -30,7 +30,7 @@
#include "texture_progress.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
void TextureProgress::set_under_texture(const Ref<Texture2D> &p_texture) {
under = p_texture;
diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h
index efd3f0698a..e39545f679 100644
--- a/scene/gui/texture_rect.h
+++ b/scene/gui/texture_rect.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef TEXTURE_FRAME_H
-#define TEXTURE_FRAME_H
+#ifndef TEXTURE_RECT_H
+#define TEXTURE_RECT_H
#include "scene/gui/control.h"
@@ -83,4 +83,5 @@ public:
};
VARIANT_ENUM_CAST(TextureRect::StretchMode);
-#endif // TEXTURE_FRAME_H
+
+#endif // TEXTURE_RECT_H
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 5057f84192..063a5e7ac1 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -30,12 +30,13 @@
#include "tree.h"
+#include "core/config/project_settings.h"
#include "core/input/input.h"
#include "core/math/math_funcs.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "core/print_string.h"
-#include "core/project_settings.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(",");
@@ -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;
@@ -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).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,7 +1349,11 @@ 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) {
@@ -1160,6 +1361,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
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));
//Rect2 r = Rect2i(row_rect.pos,row_rect.size);
//r.grow(cache.selected->get_margin(MARGIN_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,13 +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;
- 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);
@@ -1196,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);
@@ -1208,8 +1414,12 @@ 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 || drop_mode_section == 0) {
+ 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);
}
@@ -1218,7 +1428,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x + r.size.x - 1, r.position.y, 1, r.size.y), cache.drop_position_color);
}
- if (drop_mode_section == 1 || drop_mode_section == 0) {
+ 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.y, r.size.x, 1), cache.drop_position_color);
}
}
@@ -1226,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;
@@ -1252,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: {
@@ -1261,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();
@@ -1292,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;
}
@@ -1337,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;
}
@@ -1365,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);
@@ -1379,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 {
@@ -1397,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);
}
}
@@ -1433,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);
}
@@ -1809,7 +2028,6 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
case TreeItem::CELL_MODE_RANGE: {
if (c.text != "") {
//if (x >= (get_column_width(col)-item_h/2)) {
-
popup_menu->clear();
for (int i = 0; i < c.text.get_slice_count(","); i++) {
String s = c.text.get_slicec(',', i);
@@ -1980,7 +2198,6 @@ void Tree::_text_editor_enter(String p_text) {
} else if (c.val > c.max) {
c.val = c.max;
}
-
//popup_edited_item->edited_signal.call( popup_edited_item_col );
} break;
default: {
@@ -2346,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;
@@ -2372,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) {
@@ -2403,11 +2628,16 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
cache.hover_cell = col;
if (it != old_it || col != old_col) {
- // Only need to update if mouse enters/exits a button
- bool was_over_button = old_it && old_it->cells[old_col].custom_button;
- bool is_over_button = it && it->cells[col].custom_button;
- if (was_over_button || is_over_button) {
+ if (old_it && old_col >= old_it->cells.size()) {
+ // Columns may have changed since last update().
update();
+ } else {
+ // Only need to update if mouse enters/exits a button
+ bool was_over_button = old_it && old_it->cells[old_col].custom_button;
+ bool is_over_button = it && it->cells[col].custom_button;
+ if (was_over_button || is_over_button) {
+ update();
+ }
}
}
}
@@ -2423,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;
@@ -2454,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();
@@ -2487,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");
}
@@ -2532,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();
@@ -2572,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) {
@@ -2635,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();
@@ -2782,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) {
@@ -2911,17 +3171,22 @@ void Tree::_notification(int p_what) {
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);
+ 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) {
@@ -2939,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);
}
@@ -3014,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 {
@@ -3022,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();
}
@@ -3223,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;
}
}
@@ -3379,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();
}
@@ -3388,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()) {
@@ -3553,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) {
@@ -3580,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) {
@@ -3607,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) {
@@ -3727,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();
@@ -3818,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);
@@ -3886,7 +4241,7 @@ Tree::Tree() {
popup_menu = memnew(PopupMenu);
popup_menu->hide();
add_child(popup_menu);
- // popup_menu->set_as_toplevel(true);
+ // popup_menu->set_as_top_level(true);
popup_editor = memnew(Popup);
popup_editor->set_wrap_controls(true);
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index c0910a8fe0..82422b8be3 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -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;
@@ -44,7 +45,6 @@ class TreeItem : public Object {
public:
enum TreeCellMode {
-
CELL_MODE_STRING, ///< just a string
CELL_MODE_CHECK, ///< string + check
CELL_MODE_RANGE, ///< Contains a range
@@ -68,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;
@@ -109,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;
@@ -183,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;
@@ -202,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);
@@ -349,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;
}
@@ -375,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);
@@ -401,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;
@@ -564,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;
@@ -587,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/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index d6d1134cc9..43350fae37 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -31,8 +31,8 @@
#include "canvas_item.h"
#include "core/input/input.h"
-#include "core/message_queue.h"
-#include "core/method_bind_ext.gen.inc"
+#include "core/object/message_queue.h"
+#include "scene/2d/canvas_group.h"
#include "scene/main/canvas_layer.h"
#include "scene/main/viewport.h"
#include "scene/main/window.h"
@@ -364,14 +364,14 @@ 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();
for (int i = 0; i < get_child_count(); i++) {
CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i));
- if (c && c->visible) { //should the toplevels stop propagation? i think so but..
+ if (c && c->visible) { //should the top_levels stop propagation? i think so but..
c->_propagate_visibility_changed(p_visible);
}
}
@@ -486,7 +486,7 @@ Transform2D CanvasItem::get_global_transform() const {
return global_transform;
}
-void CanvasItem::_toplevel_raise_self() {
+void CanvasItem::_top_level_raise_self() {
if (!is_inside_tree()) {
return;
}
@@ -499,7 +499,7 @@ void CanvasItem::_toplevel_raise_self() {
}
void CanvasItem::_enter_canvas() {
- if ((!Object::cast_to<CanvasItem>(get_parent())) || toplevel) {
+ if ((!Object::cast_to<CanvasItem>(get_parent())) || top_level) {
Node *n = this;
canvas_layer = nullptr;
@@ -533,7 +533,7 @@ void CanvasItem::_enter_canvas() {
get_viewport()->gui_reset_canvas_sort_index();
}
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE, group, "_toplevel_raise_self");
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE, group, "_top_level_raise_self");
} else {
CanvasItem *parent = get_parent_item();
@@ -599,7 +599,7 @@ void CanvasItem::_notification(int p_what) {
}
if (group != "") {
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE, group, "_toplevel_raise_self");
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE, group, "_top_level_raise_self");
} else {
CanvasItem *p = get_parent_item();
ERR_FAIL_COND(!p);
@@ -674,27 +674,29 @@ Color CanvasItem::get_modulate() const {
return modulate;
}
-void CanvasItem::set_as_toplevel(bool p_toplevel) {
- if (toplevel == p_toplevel) {
+void CanvasItem::set_as_top_level(bool p_top_level) {
+ if (top_level == p_top_level) {
return;
}
if (!is_inside_tree()) {
- toplevel = p_toplevel;
+ top_level = p_top_level;
return;
}
_exit_canvas();
- toplevel = p_toplevel;
+ top_level = p_top_level;
_enter_canvas();
+
+ _notify_transform();
}
-bool CanvasItem::is_set_as_toplevel() const {
- return toplevel;
+bool CanvasItem::is_set_as_top_level() const {
+ return top_level;
}
CanvasItem *CanvasItem::get_parent_item() const {
- if (toplevel) {
+ if (top_level) {
return nullptr;
}
@@ -740,21 +742,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;
@@ -763,7 +765,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) {
@@ -832,25 +834,25 @@ void CanvasItem::draw_circle(const Point2 &p_pos, float p_radius, const Color &p
RenderingServer::get_singleton()->canvas_item_add_circle(canvas_item, p_pos, p_radius, p_color);
}
-void CanvasItem::draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) {
+void CanvasItem::draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_texture.is_null());
- p_texture->draw(canvas_item, p_pos, p_modulate, false, p_normal_map, p_specular_map, p_specular_color_shininess, RS::CanvasItemTextureFilter(p_texture_filter), RS::CanvasItemTextureRepeat(p_texture_repeat));
+ p_texture->draw(canvas_item, p_pos, p_modulate, false);
}
-void CanvasItem::draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) {
+void CanvasItem::draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_texture.is_null());
- p_texture->draw_rect(canvas_item, p_rect, p_tile, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, RS::CanvasItemTextureFilter(p_texture_filter), RS::CanvasItemTextureRepeat(p_texture_repeat));
+ p_texture->draw_rect(canvas_item, p_rect, p_tile, p_modulate, p_transpose);
}
-void CanvasItem::draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, bool p_clip_uv, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) {
+void CanvasItem::draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_texture.is_null());
- p_texture->draw_rect_region(canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, RS::CanvasItemTextureFilter(p_texture_filter), RS::CanvasItemTextureRepeat(p_texture_repeat), p_clip_uv);
+ p_texture->draw_rect_region(canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect) {
@@ -861,14 +863,11 @@ void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p
p_style_box->draw(canvas_item, p_rect);
}
-void CanvasItem::draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture, float p_width, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) {
+void CanvasItem::draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture, float p_width) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
- RID rid_normal = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID rid_specular = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
-
- RenderingServer::get_singleton()->canvas_item_add_primitive(canvas_item, p_points, p_colors, p_uvs, rid, p_width, rid_normal, rid_specular, p_specular_color_shininess, RS::CanvasItemTextureFilter(p_texture_filter), RS::CanvasItemTextureRepeat(p_texture_repeat));
+ RenderingServer::get_singleton()->canvas_item_add_primitive(canvas_item, p_points, p_colors, p_uvs, rid, p_width);
}
void CanvasItem::draw_set_transform(const Point2 &p_offset, float p_rot, const Size2 &p_scale) {
@@ -885,63 +884,54 @@ void CanvasItem::draw_set_transform_matrix(const Transform2D &p_matrix) {
RenderingServer::get_singleton()->canvas_item_add_set_transform(canvas_item, p_matrix);
}
-void CanvasItem::draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) {
+void CanvasItem::draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture) {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
- RID rid_normal = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID rid_specular = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, p_colors, p_uvs, rid, rid_normal, rid_specular, p_specular_color_shininess, RS::CanvasItemTextureFilter(p_texture_filter), RS::CanvasItemTextureRepeat(p_texture_repeat));
+ RenderingServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, p_colors, p_uvs, rid);
}
-void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) {
+void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture) {
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);
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
- RID rid_normal = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID rid_specular = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
-
- RenderingServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid, rid_normal, rid_specular, p_specular_color_shininess, RS::CanvasItemTextureFilter(p_texture_filter), RS::CanvasItemTextureRepeat(p_texture_repeat));
+ RenderingServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid);
}
-void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, const Transform2D &p_transform, const Color &p_modulate, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) {
+void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform, const Color &p_modulate) {
ERR_FAIL_COND(p_mesh.is_null());
RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
- RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_map_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), p_transform, p_modulate, texture_rid, normal_map_rid, specular_map_rid, p_specular_color_shininess, RS::CanvasItemTextureFilter(p_texture_filter), RS::CanvasItemTextureRepeat(p_texture_repeat));
+ RenderingServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), p_transform, p_modulate, texture_rid);
}
-void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, TextureFilter p_texture_filter, TextureRepeat p_texture_repeat) {
+void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture) {
ERR_FAIL_COND(p_multimesh.is_null());
RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
- RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_map_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
-
- RenderingServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid, normal_map_rid, specular_map_rid, p_specular_color_shininess, RS::CanvasItemTextureFilter(p_texture_filter), RS::CanvasItemTextureRepeat(p_texture_repeat));
+ 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.c_str()[0], Color(1, 1, 1), true);
- }
- return p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.c_str()[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) {
@@ -967,7 +957,7 @@ void CanvasItem::_notify_transform(CanvasItem *p_node) {
for (List<CanvasItem *>::Element *E = p_node->children_items.front(); E; E = E->next()) {
CanvasItem *ci = E->get();
- if (ci->toplevel) {
+ if (ci->top_level) {
continue;
}
_notify_transform(ci);
@@ -997,9 +987,9 @@ ObjectID CanvasItem::get_canvas_layer_instance_id() const {
}
}
-CanvasItem *CanvasItem::get_toplevel() const {
+CanvasItem *CanvasItem::get_top_level() const {
CanvasItem *ci = const_cast<CanvasItem *>(this);
- while (!ci->toplevel && Object::cast_to<CanvasItem>(ci->get_parent())) {
+ while (!ci->top_level && Object::cast_to<CanvasItem>(ci->get_parent())) {
ci = Object::cast_to<CanvasItem>(ci->get_parent());
}
@@ -1009,7 +999,7 @@ CanvasItem *CanvasItem::get_toplevel() const {
Ref<World2D> CanvasItem::get_world_2d() const {
ERR_FAIL_COND_V(!is_inside_tree(), Ref<World2D>());
- CanvasItem *tl = get_toplevel();
+ CanvasItem *tl = get_top_level();
if (tl->get_viewport()) {
return tl->get_viewport()->find_world_2d();
@@ -1104,7 +1094,7 @@ void CanvasItem::force_update_transform() {
}
void CanvasItem::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_toplevel_raise_self"), &CanvasItem::_toplevel_raise_self);
+ ClassDB::bind_method(D_METHOD("_top_level_raise_self"), &CanvasItem::_top_level_raise_self);
ClassDB::bind_method(D_METHOD("_update_callback"), &CanvasItem::_update_callback);
#ifdef TOOLS_ENABLED
@@ -1136,8 +1126,8 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("update"), &CanvasItem::update);
- ClassDB::bind_method(D_METHOD("set_as_toplevel", "enable"), &CanvasItem::set_as_toplevel);
- ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &CanvasItem::is_set_as_toplevel);
+ ClassDB::bind_method(D_METHOD("set_as_top_level", "enable"), &CanvasItem::set_as_top_level);
+ ClassDB::bind_method(D_METHOD("is_set_as_top_level"), &CanvasItem::is_set_as_top_level);
ClassDB::bind_method(D_METHOD("set_light_mask", "light_mask"), &CanvasItem::set_light_mask);
ClassDB::bind_method(D_METHOD("get_light_mask"), &CanvasItem::get_light_mask);
@@ -1155,25 +1145,25 @@ 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));
ClassDB::bind_method(D_METHOD("draw_circle", "position", "radius", "color"), &CanvasItem::draw_circle);
- ClassDB::bind_method(D_METHOD("draw_texture", "texture", "position", "modulate", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE));
- ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture", "rect", "tile", "modulate", "transpose", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE));
- ClassDB::bind_method(D_METHOD("draw_texture_rect_region", "texture", "rect", "src_rect", "modulate", "transpose", "normal_map", "specular_map", "specular_shininess", "clip_uv", "texture_filter", "texture_repeat"), &CanvasItem::draw_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(true), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE));
+ ClassDB::bind_method(D_METHOD("draw_texture", "texture", "position", "modulate"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)));
+ ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture", "rect", "tile", "modulate", "transpose"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("draw_texture_rect_region", "texture", "rect", "src_rect", "modulate", "transpose", "clip_uv"), &CanvasItem::draw_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("draw_style_box", "style_box", "rect"), &CanvasItem::draw_style_box);
- ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0), DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE));
- ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE));
- ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_colored_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE));
- 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_mesh", "mesh", "texture", "normal_map", "specular_map", "specular_shininess", "transform", "modulate", "texture_filter", "texture_repeat"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE));
- ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture", "normal_map", "specular_map", "specular_shininess", "texture_filter", "texture_repeat"), &CanvasItem::draw_multimesh, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(TEXTURE_FILTER_PARENT_NODE), DEFVAL(TEXTURE_REPEAT_PARENT_NODE));
-
+ 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", "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);
@@ -1211,6 +1201,9 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture_repeat", "mode"), &CanvasItem::set_texture_repeat);
ClassDB::bind_method(D_METHOD("get_texture_repeat"), &CanvasItem::get_texture_repeat);
+ ClassDB::bind_method(D_METHOD("set_clip_children", "enable"), &CanvasItem::set_clip_children);
+ ClassDB::bind_method(D_METHOD("is_clipping_children"), &CanvasItem::is_clipping_children);
+
BIND_VMETHOD(MethodInfo("_draw"));
ADD_GROUP("Visibility", "");
@@ -1218,7 +1211,9 @@ void CanvasItem::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_on_top", PROPERTY_HINT_NONE, "", 0), "_set_on_top", "_is_on_top"); //compatibility
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_children"), "set_clip_children", "is_clipping_children");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
ADD_GROUP("Texture", "texture_");
@@ -1228,13 +1223,11 @@ void CanvasItem::_bind_methods() {
ADD_GROUP("Material", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material");
- //exporting these things doesn't really make much sense i think
- // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toplevel", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_as_toplevel", "is_set_as_toplevel");
// ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),"set_transform_notify","is_transform_notify_enabled");
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);
@@ -1329,23 +1322,7 @@ void CanvasItem::_update_texture_filter_changed(bool p_propagate) {
if (parent_item) {
texture_filter_cache = parent_item->texture_filter_cache;
} else {
- //from viewport
- switch (get_viewport()->get_default_canvas_item_texture_filter()) {
- case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST:
- texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST;
- break;
- case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR:
- texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
- break;
- case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS:
- texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS;
- break;
- case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS:
- texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS;
- break;
- default: {
- }
- }
+ texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
}
} else {
texture_filter_cache = RS::CanvasItemTextureFilter(texture_filter);
@@ -1355,7 +1332,7 @@ void CanvasItem::_update_texture_filter_changed(bool p_propagate) {
if (p_propagate) {
for (List<CanvasItem *>::Element *E = children_items.front(); E; E = E->next()) {
- if (!E->get()->toplevel && E->get()->texture_filter == TEXTURE_FILTER_PARENT_NODE) {
+ if (!E->get()->top_level && E->get()->texture_filter == TEXTURE_FILTER_PARENT_NODE) {
E->get()->_update_texture_filter_changed(true);
}
}
@@ -1369,6 +1346,7 @@ void CanvasItem::set_texture_filter(TextureFilter p_texture_filter) {
}
texture_filter = p_texture_filter;
_update_texture_filter_changed(true);
+ _change_notify();
}
CanvasItem::TextureFilter CanvasItem::get_texture_filter() const {
@@ -1385,20 +1363,7 @@ void CanvasItem::_update_texture_repeat_changed(bool p_propagate) {
if (parent_item) {
texture_repeat_cache = parent_item->texture_repeat_cache;
} else {
- //from viewport
- switch (get_viewport()->get_default_canvas_item_texture_repeat()) {
- case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED:
- texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
- break;
- case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED:
- texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED;
- break;
- case Viewport::DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR:
- texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR;
- break;
- default: {
- }
- }
+ texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
}
} else {
texture_repeat_cache = RS::CanvasItemTextureRepeat(texture_repeat);
@@ -1407,7 +1372,7 @@ void CanvasItem::_update_texture_repeat_changed(bool p_propagate) {
update();
if (p_propagate) {
for (List<CanvasItem *>::Element *E = children_items.front(); E; E = E->next()) {
- if (!E->get()->toplevel && E->get()->texture_repeat == TEXTURE_REPEAT_PARENT_NODE) {
+ if (!E->get()->top_level && E->get()->texture_repeat == TEXTURE_REPEAT_PARENT_NODE) {
E->get()->_update_texture_repeat_changed(true);
}
}
@@ -1421,6 +1386,23 @@ void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) {
}
texture_repeat = p_texture_repeat;
_update_texture_repeat_changed(true);
+ _change_notify();
+}
+
+void CanvasItem::set_clip_children(bool p_enabled) {
+ if (clip_children == p_enabled) {
+ return;
+ }
+ clip_children = p_enabled;
+
+ if (Object::cast_to<CanvasGroup>(this) != nullptr) {
+ //avoid accidental bugs, make this not work on CanvasGroup
+ return;
+ }
+ RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), clip_children ? RS::CANVAS_GROUP_MODE_OPAQUE : RS::CANVAS_GROUP_MODE_DISABLED);
+}
+bool CanvasItem::is_clipping_children() const {
+ return clip_children;
}
CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const {
@@ -1435,10 +1417,11 @@ CanvasItem::CanvasItem() :
pending_update = false;
modulate = Color(1, 1, 1, 1);
self_modulate = Color(1, 1, 1, 1);
- toplevel = false;
+ top_level = false;
first_draw = false;
drawing = false;
behind = false;
+ clip_children = false;
block_transform_notify = false;
canvas_layer = nullptr;
use_parent_material = false;
@@ -1457,3 +1440,154 @@ CanvasItem::CanvasItem() :
CanvasItem::~CanvasItem() {
RenderingServer::get_singleton()->free(canvas_item);
}
+
+///////////////////////////////////////////////////////////////////
+
+void CanvasTexture::set_diffuse_texture(const Ref<Texture2D> &p_diffuse) {
+ ERR_FAIL_COND_MSG(Object::cast_to<CanvasTexture>(p_diffuse.ptr()) != nullptr, "Cant self-assign a CanvasTexture");
+ diffuse_texture = p_diffuse;
+
+ RID tex_rid = diffuse_texture.is_valid() ? diffuse_texture->get_rid() : RID();
+ RS::get_singleton()->canvas_texture_set_channel(canvas_texture, RS::CANVAS_TEXTURE_CHANNEL_DIFFUSE, tex_rid);
+ emit_changed();
+}
+Ref<Texture2D> CanvasTexture::get_diffuse_texture() const {
+ return diffuse_texture;
+}
+
+void CanvasTexture::set_normal_texture(const Ref<Texture2D> &p_normal) {
+ ERR_FAIL_COND_MSG(Object::cast_to<CanvasTexture>(p_normal.ptr()) != nullptr, "Cant self-assign a CanvasTexture");
+ normal_texture = p_normal;
+ RID tex_rid = normal_texture.is_valid() ? normal_texture->get_rid() : RID();
+ RS::get_singleton()->canvas_texture_set_channel(canvas_texture, RS::CANVAS_TEXTURE_CHANNEL_NORMAL, tex_rid);
+}
+Ref<Texture2D> CanvasTexture::get_normal_texture() const {
+ return normal_texture;
+}
+
+void CanvasTexture::set_specular_texture(const Ref<Texture2D> &p_specular) {
+ ERR_FAIL_COND_MSG(Object::cast_to<CanvasTexture>(p_specular.ptr()) != nullptr, "Cant self-assign a CanvasTexture");
+ specular_texture = p_specular;
+ RID tex_rid = specular_texture.is_valid() ? specular_texture->get_rid() : RID();
+ RS::get_singleton()->canvas_texture_set_channel(canvas_texture, RS::CANVAS_TEXTURE_CHANNEL_SPECULAR, tex_rid);
+}
+Ref<Texture2D> CanvasTexture::get_specular_texture() const {
+ return specular_texture;
+}
+
+void CanvasTexture::set_specular_color(const Color &p_color) {
+ specular = p_color;
+ RS::get_singleton()->canvas_texture_set_shading_parameters(canvas_texture, specular, shininess);
+}
+Color CanvasTexture::get_specular_color() const {
+ return specular;
+}
+
+void CanvasTexture::set_specular_shininess(float p_shininess) {
+ shininess = p_shininess;
+ RS::get_singleton()->canvas_texture_set_shading_parameters(canvas_texture, specular, shininess);
+}
+float CanvasTexture::get_specular_shininess() const {
+ return shininess;
+}
+
+void CanvasTexture::set_texture_filter(CanvasItem::TextureFilter p_filter) {
+ texture_filter = p_filter;
+ RS::get_singleton()->canvas_texture_set_texture_filter(canvas_texture, RS::CanvasItemTextureFilter(p_filter));
+}
+CanvasItem::TextureFilter CanvasTexture::get_texture_filter() const {
+ return texture_filter;
+}
+
+void CanvasTexture::set_texture_repeat(CanvasItem::TextureRepeat p_repeat) {
+ texture_repeat = p_repeat;
+ RS::get_singleton()->canvas_texture_set_texture_repeat(canvas_texture, RS::CanvasItemTextureRepeat(p_repeat));
+}
+CanvasItem::TextureRepeat CanvasTexture::get_texture_repeat() const {
+ return texture_repeat;
+}
+
+int CanvasTexture::get_width() const {
+ if (diffuse_texture.is_valid()) {
+ return diffuse_texture->get_width();
+ } else {
+ return 1;
+ }
+}
+int CanvasTexture::get_height() const {
+ if (diffuse_texture.is_valid()) {
+ return diffuse_texture->get_height();
+ } else {
+ return 1;
+ }
+}
+
+bool CanvasTexture::is_pixel_opaque(int p_x, int p_y) const {
+ if (diffuse_texture.is_valid()) {
+ return diffuse_texture->is_pixel_opaque(p_x, p_y);
+ } else {
+ return false;
+ }
+}
+
+bool CanvasTexture::has_alpha() const {
+ if (diffuse_texture.is_valid()) {
+ return diffuse_texture->has_alpha();
+ } else {
+ return false;
+ }
+}
+
+Ref<Image> CanvasTexture::get_data() const {
+ if (diffuse_texture.is_valid()) {
+ return diffuse_texture->get_data();
+ } else {
+ return Ref<Image>();
+ }
+}
+
+RID CanvasTexture::get_rid() const {
+ return canvas_texture;
+}
+
+void CanvasTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_diffuse_texture", "texture"), &CanvasTexture::set_diffuse_texture);
+ ClassDB::bind_method(D_METHOD("get_diffuse_texture"), &CanvasTexture::get_diffuse_texture);
+
+ ClassDB::bind_method(D_METHOD("set_normal_texture", "texture"), &CanvasTexture::set_normal_texture);
+ ClassDB::bind_method(D_METHOD("get_normal_texture"), &CanvasTexture::get_normal_texture);
+
+ ClassDB::bind_method(D_METHOD("set_specular_texture", "texture"), &CanvasTexture::set_specular_texture);
+ ClassDB::bind_method(D_METHOD("get_specular_texture"), &CanvasTexture::get_specular_texture);
+
+ ClassDB::bind_method(D_METHOD("set_specular_color", "color"), &CanvasTexture::set_specular_color);
+ ClassDB::bind_method(D_METHOD("get_specular_color"), &CanvasTexture::get_specular_color);
+
+ ClassDB::bind_method(D_METHOD("set_specular_shininess", "shininess"), &CanvasTexture::set_specular_shininess);
+ ClassDB::bind_method(D_METHOD("get_specular_shininess"), &CanvasTexture::get_specular_shininess);
+
+ ClassDB::bind_method(D_METHOD("set_texture_filter", "filter"), &CanvasTexture::set_texture_filter);
+ ClassDB::bind_method(D_METHOD("get_texture_filter"), &CanvasTexture::get_texture_filter);
+
+ ClassDB::bind_method(D_METHOD("set_texture_repeat", "repeat"), &CanvasTexture::set_texture_repeat);
+ ClassDB::bind_method(D_METHOD("get_texture_repeat"), &CanvasTexture::get_texture_repeat);
+
+ ADD_GROUP("Diffuse", "diffuse_");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "diffuse_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_diffuse_texture", "get_diffuse_texture");
+ ADD_GROUP("Normalmap", "normal_");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_texture", "get_normal_texture");
+ ADD_GROUP("Specular", "specular_");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "specular_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_specular_texture", "get_specular_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "specular_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_specular_color", "get_specular_color");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "specular_shininess", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_specular_shininess", "get_specular_shininess");
+ ADD_GROUP("Texture", "texture_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,MipmapNearest,MipmapLinear,MipmapNearestAniso,MipmapLinearAniso"), "set_texture_filter", "get_texture_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat");
+}
+
+CanvasTexture::CanvasTexture() {
+ canvas_texture = RS::get_singleton()->canvas_texture_create();
+}
+CanvasTexture::~CanvasTexture() {
+ RS::get_singleton()->free(canvas_texture);
+}
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index d9ffe770ff..3cde6b69c1 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -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;
@@ -200,8 +201,9 @@ private:
Window *window;
bool first_draw;
bool visible;
+ bool clip_children;
bool pending_update;
- bool toplevel;
+ bool top_level;
bool drawing;
bool block_transform_notify;
bool behind;
@@ -220,7 +222,7 @@ private:
mutable Transform2D global_transform;
mutable bool global_invalid;
- void _toplevel_raise_self();
+ void _top_level_raise_self();
void _propagate_visibility_changed(bool p_visible);
@@ -315,6 +317,9 @@ public:
void update();
+ void set_clip_children(bool p_enabled);
+ bool is_clipping_children() const;
+
virtual void set_light_mask(int p_light_mask);
int get_light_mask() const;
@@ -327,26 +332,27 @@ 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);
void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color);
- void draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1), const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE);
- void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE);
- void draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), bool p_clip_uv = false, TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE);
+ void draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1));
+ void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false);
+ void draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false);
void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect);
- void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>(), float p_width = 1, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE);
- void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>(), const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE);
- void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>(), const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE);
+ void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>(), float p_width = 1);
+ void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());
+ void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());
- void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE);
- void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), TextureFilter p_texture_filter = TEXTURE_FILTER_PARENT_NODE, TextureRepeat p_texture_repeat = TEXTURE_REPEAT_PARENT_NODE);
+ 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);
@@ -355,8 +361,8 @@ public:
/* RECT / TRANSFORM */
- void set_as_toplevel(bool p_toplevel);
- bool is_set_as_toplevel() const;
+ void set_as_top_level(bool p_top_level);
+ bool is_set_as_top_level() const;
void set_draw_behind_parent(bool p_enable);
bool is_draw_behind_parent_enabled() const;
@@ -369,7 +375,7 @@ public:
virtual Transform2D get_global_transform_with_canvas() const;
virtual Transform2D get_screen_transform() const;
- CanvasItem *get_toplevel() const;
+ CanvasItem *get_top_level() const;
_FORCE_INLINE_ RID get_canvas_item() const {
return canvas_item;
}
@@ -405,10 +411,10 @@ public:
void force_update_transform();
- void set_texture_filter(TextureFilter p_texture_filter);
+ virtual void set_texture_filter(TextureFilter p_texture_filter);
TextureFilter get_texture_filter() const;
- void set_texture_repeat(TextureRepeat p_texture_repeat);
+ virtual void set_texture_repeat(TextureRepeat p_texture_repeat);
TextureRepeat get_texture_repeat() const;
// Used by control nodes to retrieve the parent's anchorable area
@@ -423,4 +429,58 @@ public:
VARIANT_ENUM_CAST(CanvasItem::TextureFilter)
VARIANT_ENUM_CAST(CanvasItem::TextureRepeat)
+class CanvasTexture : public Texture2D {
+ GDCLASS(CanvasTexture, Texture2D);
+ OBJ_SAVE_TYPE(Texture2D); // Saves derived classes with common type so they can be interchanged.
+
+ Ref<Texture2D> diffuse_texture;
+ Ref<Texture2D> normal_texture;
+ Ref<Texture2D> specular_texture;
+ Color specular = Color(1, 1, 1, 1);
+ float shininess = 1.0;
+
+ RID canvas_texture;
+
+ CanvasItem::TextureFilter texture_filter = CanvasItem::TEXTURE_FILTER_PARENT_NODE;
+ CanvasItem::TextureRepeat texture_repeat = CanvasItem::TEXTURE_REPEAT_PARENT_NODE;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_diffuse_texture(const Ref<Texture2D> &p_diffuse);
+ Ref<Texture2D> get_diffuse_texture() const;
+
+ void set_normal_texture(const Ref<Texture2D> &p_normal);
+ Ref<Texture2D> get_normal_texture() const;
+
+ void set_specular_texture(const Ref<Texture2D> &p_specular);
+ Ref<Texture2D> get_specular_texture() const;
+
+ void set_specular_color(const Color &p_color);
+ Color get_specular_color() const;
+
+ void set_specular_shininess(float p_shininess);
+ float get_specular_shininess() const;
+
+ void set_texture_filter(CanvasItem::TextureFilter p_filter);
+ CanvasItem::TextureFilter get_texture_filter() const;
+
+ void set_texture_repeat(CanvasItem::TextureRepeat p_repeat);
+ CanvasItem::TextureRepeat get_texture_repeat() const;
+
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+
+ virtual bool is_pixel_opaque(int p_x, int p_y) const override;
+ virtual bool has_alpha() const override;
+
+ virtual Ref<Image> get_data() const override;
+
+ virtual RID get_rid() const override;
+
+ CanvasTexture();
+ ~CanvasTexture();
+};
+
#endif // CANVAS_ITEM_H
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 82ee4dde50..f484d25dc1 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -29,6 +29,8 @@
/*************************************************************************/
#include "http_request.h"
+#include "core/io/compression.h"
+#include "core/string/ustring.h"
void HTTPRequest::_redirect_request(const String &p_new_url) {
}
@@ -82,7 +84,51 @@ Error HTTPRequest::_parse_url(const String &p_url) {
return OK;
}
+bool HTTPRequest::has_header(const PackedStringArray &p_headers, const String &p_header_name) {
+ bool exists = false;
+
+ String lower_case_header_name = p_header_name.to_lower();
+ for (int i = 0; i < p_headers.size() && !exists; i++) {
+ String sanitized = p_headers[i].strip_edges().to_lower();
+ if (sanitized.begins_with(lower_case_header_name)) {
+ exists = true;
+ }
+ }
+
+ return exists;
+}
+
+String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const String &p_header_name) {
+ String value = "";
+
+ String lowwer_case_header_name = p_header_name.to_lower();
+ for (int i = 0; i < p_headers.size(); i++) {
+ if (p_headers[i].find(":", 0) >= 0) {
+ Vector<String> parts = p_headers[i].split(":", false, 1);
+ if (parts[0].strip_edges().to_lower() == lowwer_case_header_name) {
+ value = parts[1].strip_edges();
+ break;
+ }
+ }
+ }
+
+ return value;
+}
+
Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const String &p_request_data) {
+ // Copy the string into a raw buffer
+ Vector<uint8_t> raw_data;
+
+ CharString charstr = p_request_data.utf8();
+ size_t len = charstr.length();
+ raw_data.resize(len);
+ uint8_t *w = raw_data.ptrw();
+ copymem(w, charstr.ptr(), len);
+
+ return request_raw(p_url, p_custom_headers, p_ssl_validate_domain, p_method, raw_data);
+}
+
+Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const Vector<uint8_t> &p_request_data_raw) {
ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V_MSG(requesting, ERR_BUSY, "HTTPRequest is processing a request. Wait for completion or cancel it before attempting a new one.");
@@ -102,7 +148,14 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h
headers = p_custom_headers;
- request_data = p_request_data;
+ if (accept_gzip) {
+ // If the user has specified a different Accept-Encoding, don't overwrite it
+ if (!has_header(headers, "Accept-Encoding")) {
+ headers.push_back("Accept-Encoding: gzip, deflate");
+ }
+ }
+
+ request_data = p_request_data_raw;
requesting = true;
@@ -288,7 +341,7 @@ bool HTTPRequest::_update_connection() {
} else {
// Did not request yet, do request
- Error err = client->request(method, request_string, headers, request_data);
+ Error err = client->request_raw(method, request_string, headers, request_data);
if (err != OK) {
call_deferred("_request_done", RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
return true;
@@ -382,9 +435,47 @@ bool HTTPRequest::_update_connection() {
ERR_FAIL_V(false);
}
-void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data) {
+void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) {
cancel_request();
- emit_signal("request_completed", p_status, p_code, headers, p_data);
+
+ // Determine if the request body is compressed
+ bool is_compressed;
+ String content_encoding = get_header_value(p_headers, "Content-Encoding").to_lower();
+ Compression::Mode mode;
+ if (content_encoding == "gzip") {
+ mode = Compression::Mode::MODE_GZIP;
+ is_compressed = true;
+ } else if (content_encoding == "deflate") {
+ mode = Compression::Mode::MODE_DEFLATE;
+ is_compressed = true;
+ } else {
+ is_compressed = false;
+ }
+
+ const PackedByteArray *data = NULL;
+
+ if (accept_gzip && is_compressed && p_data.size() > 0) {
+ // Decompress request body
+ PackedByteArray *decompressed = memnew(PackedByteArray);
+ int result = Compression::decompress_dynamic(decompressed, body_size_limit, p_data.ptr(), p_data.size(), mode);
+ if (result == OK) {
+ data = decompressed;
+ } else if (result == -5) {
+ WARN_PRINT("Decompressed size of HTTP response body exceeded body_size_limit");
+ p_status = RESULT_BODY_SIZE_LIMIT_EXCEEDED;
+ // Just return the raw data if we failed to decompress it
+ data = &p_data;
+ } else {
+ WARN_PRINT("Failed to decompress HTTP response body");
+ p_status = RESULT_BODY_DECOMPRESS_FAILED;
+ // Just return the raw data if we failed to decompress it
+ data = &p_data;
+ }
+ } else {
+ data = &p_data;
+ }
+
+ emit_signal("request_completed", p_status, p_code, p_headers, *data);
}
void HTTPRequest::_notification(int p_what) {
@@ -415,6 +506,14 @@ bool HTTPRequest::is_using_threads() const {
return use_threads;
}
+void HTTPRequest::set_accept_gzip(bool p_gzip) {
+ accept_gzip = p_gzip;
+}
+
+bool HTTPRequest::is_accepting_gzip() const {
+ return accept_gzip;
+}
+
void HTTPRequest::set_body_size_limit(int p_bytes) {
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
@@ -481,6 +580,7 @@ void HTTPRequest::_timeout() {
void HTTPRequest::_bind_methods() {
ClassDB::bind_method(D_METHOD("request", "url", "custom_headers", "ssl_validate_domain", "method", "request_data"), &HTTPRequest::request, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(String()));
+ ClassDB::bind_method(D_METHOD("request_raw", "url", "custom_headers", "ssl_validate_domain", "method", "request_data_raw"), &HTTPRequest::request_raw, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(PackedByteArray()));
ClassDB::bind_method(D_METHOD("cancel_request"), &HTTPRequest::cancel_request);
ClassDB::bind_method(D_METHOD("get_http_client_status"), &HTTPRequest::get_http_client_status);
@@ -488,6 +588,9 @@ void HTTPRequest::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_threads", "enable"), &HTTPRequest::set_use_threads);
ClassDB::bind_method(D_METHOD("is_using_threads"), &HTTPRequest::is_using_threads);
+ ClassDB::bind_method(D_METHOD("set_accept_gzip", "enable"), &HTTPRequest::set_accept_gzip);
+ ClassDB::bind_method(D_METHOD("is_accepting_gzip"), &HTTPRequest::is_accepting_gzip);
+
ClassDB::bind_method(D_METHOD("set_body_size_limit", "bytes"), &HTTPRequest::set_body_size_limit);
ClassDB::bind_method(D_METHOD("get_body_size_limit"), &HTTPRequest::get_body_size_limit);
@@ -512,6 +615,7 @@ void HTTPRequest::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "download_file", PROPERTY_HINT_FILE), "set_download_file", "get_download_file");
ADD_PROPERTY(PropertyInfo(Variant::INT, "download_chunk_size", PROPERTY_HINT_RANGE, "256,16777216"), "set_download_chunk_size", "get_download_chunk_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_threads"), "set_use_threads", "is_using_threads");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "accept_gzip"), "set_accept_gzip", "is_accepting_gzip");
ADD_PROPERTY(PropertyInfo(Variant::INT, "body_size_limit", PROPERTY_HINT_RANGE, "-1,2000000000"), "set_body_size_limit", "get_body_size_limit");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_redirects", PROPERTY_HINT_RANGE, "-1,64"), "set_max_redirects", "get_max_redirects");
ADD_PROPERTY(PropertyInfo(Variant::INT, "timeout", PROPERTY_HINT_RANGE, "0,86400"), "set_timeout", "get_timeout");
@@ -527,6 +631,7 @@ void HTTPRequest::_bind_methods() {
BIND_ENUM_CONSTANT(RESULT_SSL_HANDSHAKE_ERROR);
BIND_ENUM_CONSTANT(RESULT_NO_RESPONSE);
BIND_ENUM_CONSTANT(RESULT_BODY_SIZE_LIMIT_EXCEEDED);
+ BIND_ENUM_CONSTANT(RESULT_BODY_DECOMPRESS_FAILED);
BIND_ENUM_CONSTANT(RESULT_REQUEST_FAILED);
BIND_ENUM_CONSTANT(RESULT_DOWNLOAD_FILE_CANT_OPEN);
BIND_ENUM_CONSTANT(RESULT_DOWNLOAD_FILE_WRITE_ERROR);
@@ -544,6 +649,7 @@ HTTPRequest::HTTPRequest() {
got_response = false;
validate_ssl = false;
use_ssl = false;
+ accept_gzip = true;
response_code = 0;
request_sent = false;
requesting = false;
diff --git a/scene/main/http_request.h b/scene/main/http_request.h
index 1409965d45..2e8931120b 100644
--- a/scene/main/http_request.h
+++ b/scene/main/http_request.h
@@ -50,6 +50,7 @@ public:
RESULT_SSL_HANDSHAKE_ERROR,
RESULT_NO_RESPONSE,
RESULT_BODY_SIZE_LIMIT_EXCEEDED,
+ RESULT_BODY_DECOMPRESS_FAILED,
RESULT_REQUEST_FAILED,
RESULT_DOWNLOAD_FILE_CANT_OPEN,
RESULT_DOWNLOAD_FILE_WRITE_ERROR,
@@ -68,12 +69,13 @@ private:
bool validate_ssl;
bool use_ssl;
HTTPClient::Method method;
- String request_data;
+ Vector<uint8_t> request_data;
bool request_sent;
Ref<HTTPClient> client;
PackedByteArray body;
volatile bool use_threads;
+ bool accept_gzip;
bool got_response;
int response_code;
@@ -102,12 +104,15 @@ private:
Error _parse_url(const String &p_url);
Error _request();
+ bool has_header(const PackedStringArray &p_headers, const String &p_header_name);
+ String get_header_value(const PackedStringArray &p_headers, const String &header_name);
+
volatile bool thread_done;
volatile bool thread_request_quit;
Thread *thread;
- void _request_done(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
+ void _request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data);
static void _thread_func(void *p_userdata);
protected:
@@ -116,12 +121,16 @@ protected:
public:
Error request(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_ssl_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const String &p_request_data = ""); //connects to a full url and perform request
+ Error request_raw(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_ssl_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const Vector<uint8_t> &p_request_data_raw = Vector<uint8_t>()); //connects to a full url and perform request
void cancel_request();
HTTPClient::Status get_http_client_status() const;
void set_use_threads(bool p_use);
bool is_using_threads() const;
+ void set_accept_gzip(bool p_gzip);
+ bool is_accepting_gzip() const;
+
void set_download_file(const String &p_file);
String get_download_file() const;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 4dcfcd9d96..47440f8c60 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -32,8 +32,8 @@
#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
-#include "core/message_queue.h"
-#include "core/print_string.h"
+#include "core/object/message_queue.h"
+#include "core/string/print_string.h"
#include "instance_placeholder.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/resources/packed_scene.h"
@@ -1094,7 +1094,7 @@ String increase_numeric_string(const String &s) {
if (!carry) {
break;
}
- CharType n = s[i];
+ char32_t n = s[i];
if (n == '9') { // keep carry as true: 9 + 1
res[i] = '0';
} else {
@@ -1155,7 +1155,7 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co
String name_string = name;
String nums;
for (int i = name_string.length() - 1; i >= 0; i--) {
- CharType n = name_string[i];
+ char32_t n = name_string[i];
if (n >= '0' && n <= '9') {
nums = String::chr(name_string[i]) + nums;
} else {
@@ -1327,6 +1327,9 @@ int Node::get_child_count() const {
}
Node *Node::get_child(int p_index) const {
+ if (p_index < 0) {
+ p_index += data.children.size();
+ }
ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr);
return data.children[p_index];
@@ -2852,6 +2855,7 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_PATH_CHANGED);
BIND_CONSTANT(NOTIFICATION_INTERNAL_PROCESS);
BIND_CONSTANT(NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
+ BIND_CONSTANT(NOTIFICATION_POST_ENTER_TREE);
BIND_CONSTANT(NOTIFICATION_WM_MOUSE_ENTER);
BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT);
@@ -2869,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);
@@ -2885,7 +2890,6 @@ void Node::_bind_methods() {
ADD_SIGNAL(MethodInfo("tree_exiting"));
ADD_SIGNAL(MethodInfo("tree_exited"));
- ADD_GROUP("Pause", "pause_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name");
diff --git a/scene/main/node.h b/scene/main/node.h
index 2928466cd0..024d036fd3 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -31,13 +31,12 @@
#ifndef NODE_H
#define NODE_H
-#include "core/class_db.h"
-#include "core/map.h"
-#include "core/node_path.h"
-#include "core/object.h"
-#include "core/project_settings.h"
-#include "core/script_language.h"
-#include "core/typed_array.h"
+#include "core/config/project_settings.h"
+#include "core/object/class_db.h"
+#include "core/object/script_language.h"
+#include "core/string/node_path.h"
+#include "core/templates/map.h"
+#include "core/variant/typed_array.h"
#include "scene/main/scene_tree.h"
class Viewport;
@@ -48,14 +47,12 @@ class Node : public Object {
public:
enum PauseMode {
-
PAUSE_MODE_INHERIT,
PAUSE_MODE_STOP,
PAUSE_MODE_PROCESS
};
enum DuplicateFlags {
-
DUPLICATE_SIGNALS = 1,
DUPLICATE_GROUPS = 2,
DUPLICATE_SCRIPTS = 4,
@@ -77,9 +74,8 @@ public:
private:
struct GroupData {
- bool persistent;
- SceneTree::Group *group;
- GroupData() { persistent = false; }
+ bool persistent = false;
+ SceneTree::Group *group = nullptr;
};
struct NetData {
@@ -219,7 +215,6 @@ protected:
public:
enum {
-
// you can make your own, but don't use the same numbers as other notifications in other nodes
NOTIFICATION_ENTER_TREE = 10,
NOTIFICATION_EXIT_TREE = 11,
@@ -258,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 */
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index adefb53862..9e396d4030 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -30,19 +30,19 @@
#include "scene_tree.h"
+#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/input/input.h"
#include "core/io/marshalls.h"
#include "core/io/resource_loader.h"
-#include "core/message_queue.h"
+#include "core/object/message_queue.h"
#include "core/os/dir_access.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "core/print_string.h"
-#include "core/project_settings.h"
+#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"
@@ -827,12 +827,9 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio
/*
void SceneMainLoop::_update_listener_2d() {
-
if (listener_2d.is_valid()) {
-
SpatialSound2DServer::get_singleton()->listener_set_space( listener_2d, world_2d->get_sound_space() );
}
-
}
*/
@@ -1383,14 +1380,31 @@ SceneTree::SceneTree() {
root->set_as_audio_listener_2d(true);
current_scene = nullptr;
- int msaa_mode = GLOBAL_DEF("rendering/quality/screen_filters/msaa", 0);
+ const int msaa_mode = GLOBAL_DEF("rendering/quality/screen_filters/msaa", 0);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/screen_filters/msaa", PropertyInfo(Variant::INT, "rendering/quality/screen_filters/msaa", PROPERTY_HINT_ENUM, "Disabled (Fastest),2x (Fast),4x (Average),8x (Slow),16x (Slower)"));
root->set_msaa(Viewport::MSAA(msaa_mode));
- int ssaa_mode = GLOBAL_DEF("rendering/quality/screen_filters/screen_space_aa", 0);
+ const int ssaa_mode = GLOBAL_DEF("rendering/quality/screen_filters/screen_space_aa", 0);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/screen_filters/screen_space_aa", PropertyInfo(Variant::INT, "rendering/quality/screen_filters/screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"));
root->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode));
+ const bool use_debanding = GLOBAL_DEF("rendering/quality/screen_filters/use_debanding", false);
+ root->set_use_debanding(use_debanding);
+
+ 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);
+
+ bool snap_2d_vertices = GLOBAL_DEF("rendering/quality/2d/snap_2d_vertices_to_pixel", false);
+ root->set_snap_2d_vertices_to_pixel(snap_2d_vertices);
+
+ Viewport::SDFOversize sdf_oversize = Viewport::SDFOversize(int(GLOBAL_DEF("rendering/quality/2d_sdf/oversize", 1)));
+ root->set_sdf_oversize(sdf_oversize);
+ Viewport::SDFScale sdf_scale = Viewport::SDFScale(int(GLOBAL_DEF("rendering/quality/2d_sdf/scale", 1)));
+ root->set_sdf_scale(sdf_scale);
+
+ 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
List<String> exts;
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 0f74f2e973..3e5802ce2e 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SCENE_MAIN_LOOP_H
-#define SCENE_MAIN_LOOP_H
+#ifndef SCENE_TREE_H
+#define SCENE_TREE_H
#include "core/io/multiplayer_api.h"
#include "core/os/main_loop.h"
#include "core/os/thread_safe.h"
-#include "core/self_list.h"
+#include "core/templates/self_list.h"
#include "scene/resources/mesh.h"
#include "scene/resources/world_2d.h"
#include "scene/resources/world_3d.h"
@@ -354,4 +354,4 @@ public:
VARIANT_ENUM_CAST(SceneTree::GroupCallFlags);
-#endif
+#endif // SCENE_TREE_H
diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp
index 726dcb58de..432fb5b4fb 100644
--- a/scene/main/shader_globals_override.cpp
+++ b/scene/main/shader_globals_override.cpp
@@ -202,7 +202,7 @@ void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const
Override o;
o.in_use = false;
Callable::CallError ce;
- o.override = Variant::construct(pinfo.type, nullptr, 0, ce);
+ Variant::construct(pinfo.type, o.override, nullptr, 0, ce);
overrides[variables[i]] = o;
}
@@ -261,11 +261,16 @@ void ShaderGlobalsOverride::_notification(int p_what) {
}
String ShaderGlobalsOverride::get_configuration_warning() const {
+ String warning = Node::get_configuration_warning();
+
if (!active) {
- return TTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene.");
}
- return String();
+ return warning;
}
void ShaderGlobalsOverride::_bind_methods() {
diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp
index fb55892b54..1c6037d26e 100644
--- a/scene/main/timer.cpp
+++ b/scene/main/timer.cpp
@@ -30,7 +30,7 @@
#include "timer.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
void Timer::_notification(int p_what) {
switch (p_what) {
@@ -46,7 +46,7 @@ void Timer::_notification(int p_what) {
}
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
- if (timer_process_mode == TIMER_PROCESS_PHYSICS || !is_processing_internal()) {
+ if (!processing || timer_process_mode == TIMER_PROCESS_PHYSICS || !is_processing_internal()) {
return;
}
time_left -= get_process_delta_time();
@@ -63,7 +63,7 @@ void Timer::_notification(int p_what) {
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (timer_process_mode == TIMER_PROCESS_IDLE || !is_physics_processing_internal()) {
+ if (!processing || timer_process_mode == TIMER_PROCESS_IDLE || !is_physics_processing_internal()) {
return;
}
time_left -= get_physics_process_delta_time();
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index d962171555..6350777a3d 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -30,11 +30,13 @@
#include "viewport.h"
+#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/debugger/engine_debugger.h"
#include "core/input/input.h"
#include "core/os/os.h"
-#include "core/project_settings.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"
@@ -161,6 +163,11 @@ ViewportTexture::~ViewportTexture() {
/////////////////////////////////////
+// Aliases used to provide custom styles to tooltips in the default
+// theme and editor theme.
+// TooltipPanel is also used for custom tooltips, while TooltipLabel
+// is only relevant for default tooltips.
+
class TooltipPanel : public PopupPanel {
GDCLASS(TooltipPanel, PopupPanel);
@@ -175,6 +182,8 @@ public:
TooltipLabel() {}
};
+/////////////////////////////////////
+
Viewport::GUI::GUI() {
embed_subwindows_hint = false;
embedding_subwindows = false;
@@ -188,7 +197,7 @@ Viewport::GUI::GUI() {
mouse_over = nullptr;
drag_mouse_over = nullptr;
- tooltip = nullptr;
+ tooltip_control = nullptr;
tooltip_popup = nullptr;
tooltip_label = nullptr;
}
@@ -278,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;
@@ -814,7 +826,14 @@ void Viewport::_notification(int p_what) {
}
} break;
- case NOTIFICATION_WM_MOUSE_EXIT:
+ case NOTIFICATION_WM_MOUSE_EXIT: {
+ _drop_physics_mouseover();
+
+ // Unlike on loss of focus (NOTIFICATION_WM_WINDOW_FOCUS_OUT), do not
+ // drop the gui mouseover here, as a scrollbar may be dragged while the
+ // mouse is outside the window (without the window having lost focus).
+ // See bug #39634
+ } break;
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
_drop_physics_mouseover();
@@ -1465,7 +1484,7 @@ void Viewport::_gui_sort_roots() {
}
void Viewport::_gui_cancel_tooltip() {
- gui.tooltip = nullptr;
+ gui.tooltip_control = nullptr;
gui.tooltip_timer = -1;
if (gui.tooltip_popup) {
gui.tooltip_popup->queue_delete();
@@ -1474,29 +1493,34 @@ void Viewport::_gui_cancel_tooltip() {
}
}
-String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_which) {
+String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_tooltip_owner) {
Vector2 pos = p_pos;
String tooltip;
while (p_control) {
tooltip = p_control->get_tooltip(pos);
- if (r_which) {
- *r_which = p_control;
+ if (r_tooltip_owner) {
+ *r_tooltip_owner = p_control;
}
- if (tooltip != String()) {
+ // If we found a tooltip, we stop here.
+ if (!tooltip.empty()) {
break;
}
- pos = p_control->get_transform().xform(pos);
+
+ // Otherwise, we check parent controls unless some conditions prevent it.
if (p_control->data.mouse_filter == Control::MOUSE_FILTER_STOP) {
break;
}
- if (p_control->is_set_as_toplevel()) {
+ if (p_control->is_set_as_top_level()) {
break;
}
+ // Transform cursor pos for parent control.
+ pos = p_control->get_transform().xform(pos);
+
p_control = p_control->get_parent_control();
}
@@ -1504,34 +1528,40 @@ String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Cont
}
void Viewport::_gui_show_tooltip() {
- if (!gui.tooltip) {
+ if (!gui.tooltip_control) {
return;
}
- Control *which = nullptr;
- String tooltip = _gui_get_tooltip(gui.tooltip, gui.tooltip->get_global_transform().xform_inv(gui.tooltip_pos), &which);
- tooltip = tooltip.strip_edges();
- if (tooltip.length() == 0) {
- return; // bye
+ // Get the Control under cursor and the relevant tooltip text, if any.
+ Control *tooltip_owner = nullptr;
+ String tooltip_text = _gui_get_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()) {
+ return; // Nothing to show.
}
+ // Remove previous popup if we change something.
if (gui.tooltip_popup) {
memdelete(gui.tooltip_popup);
gui.tooltip_popup = nullptr;
gui.tooltip_label = nullptr;
}
- if (!which) {
+ if (!tooltip_owner) {
return;
}
- Control *rp = which;
-
- Control *base_tooltip = which->make_custom_tooltip(tooltip);
+ // Controls can implement `make_custom_tooltip` to provide their own tooltip.
+ // This should be a Control node which will be added as child to a TooltipPanel.
+ Control *base_tooltip = tooltip_owner->make_custom_tooltip(tooltip_text);
+ // If no custom tooltip is given, use a default implementation.
if (!base_tooltip) {
gui.tooltip_label = memnew(TooltipLabel);
- gui.tooltip_label->set_text(tooltip);
+ gui.tooltip_label->set_text(tooltip_text);
base_tooltip = gui.tooltip_label;
}
@@ -1545,10 +1575,7 @@ void Viewport::_gui_show_tooltip() {
gui.tooltip_popup = panel;
- rp->add_child(gui.tooltip_popup);
-
- //if (gui.tooltip) // Avoids crash when rapidly switching controls.
- // gui.tooltip_popup->set_scale(gui.tooltip->get_global_transform().get_scale());
+ tooltip_owner->add_child(gui.tooltip_popup);
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());
@@ -1620,7 +1647,7 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
}
}
- if (!control->is_inside_tree() || control->is_set_as_toplevel()) {
+ if (!control->is_inside_tree() || control->is_set_as_top_level()) {
break;
}
if (gui.key_event_accepted) {
@@ -1631,7 +1658,7 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
}
}
- if (ci->is_set_as_toplevel()) {
+ if (ci->is_set_as_top_level()) {
break;
}
@@ -1655,7 +1682,7 @@ void Viewport::_gui_call_notification(Control *p_control, int p_what) {
break;
}
- if (!control->is_inside_tree() || control->is_set_as_toplevel()) {
+ if (!control->is_inside_tree() || control->is_set_as_top_level()) {
break;
}
if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP) {
@@ -1663,7 +1690,7 @@ void Viewport::_gui_call_notification(Control *p_control, int p_what) {
}
}
- if (ci->is_set_as_toplevel()) {
+ if (ci->is_set_as_top_level()) {
break;
}
@@ -1721,7 +1748,7 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_
if (!c || !c->clips_input() || c->has_point(matrix.affine_inverse().xform(p_global))) {
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
CanvasItem *ci = Object::cast_to<CanvasItem>(p_node->get_child(i));
- if (!ci || ci->is_set_as_toplevel()) {
+ if (!ci || ci->is_set_as_top_level()) {
continue;
}
@@ -1768,7 +1795,7 @@ bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_che
p_at_pos = ci->get_transform().xform(p_at_pos);
- if (ci->is_set_as_toplevel()) {
+ if (ci->is_set_as_top_level()) {
break;
}
@@ -1782,18 +1809,12 @@ 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;
+ Control *over = nullptr;
+
Point2 mpos = mb->get_position();
if (mb->is_pressed()) {
Size2 pos = mpos;
@@ -1865,7 +1886,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
- if (ci->is_set_as_toplevel()) {
+ if (ci->is_set_as_top_level()) {
break;
}
@@ -1897,8 +1918,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
_gui_cancel_tooltip();
- //gui.tooltip_popup->hide();
-
} else {
if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == BUTTON_LEFT) {
if (gui.drag_mouse_over) {
@@ -1948,12 +1967,36 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.drag_data=Variant(); //always clear
}*/
+ // In case the mouse was released after for example dragging a scrollbar,
+ // check whether the current control is different from the stored one. If
+ // it is different, rather than wait for it to be updated the next time the
+ // mouse is moved, notify the control so that it can e.g. drop the highlight.
+ // This code is duplicated from the mm.is_valid()-case further below.
+ if (gui.mouse_focus) {
+ over = gui.mouse_focus;
+ } else {
+ over = _gui_find_control(mpos);
+ }
+
+ if (gui.mouse_focus_mask == 0 && over != gui.mouse_over) {
+ if (gui.mouse_over) {
+ _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
+ }
+
+ _gui_cancel_tooltip();
+
+ if (over) {
+ _gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER);
+ }
+ }
+
+ gui.mouse_over = over;
+
set_input_as_handled();
}
}
Ref<InputEventMouseMotion> mm = p_event;
-
if (mm.is_valid()) {
gui.key_event_accepted = false;
Point2 mpos = mm->get_position();
@@ -1993,7 +2036,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
- if (ci->is_set_as_toplevel()) {
+ if (ci->is_set_as_top_level()) {
break;
}
@@ -2008,10 +2051,11 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
+ // These sections of code are reused in the mb.is_valid() case further up
+ // for the purpose of notifying controls about potential changes in focus
+ // when the mousebutton is released.
if (gui.mouse_focus) {
over = gui.mouse_focus;
- //recompute focus_inv_xform again here
-
} else {
over = _gui_find_control(mpos);
}
@@ -2052,8 +2096,8 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
bool is_tooltip_shown = false;
if (gui.tooltip_popup) {
- if (can_tooltip && gui.tooltip) {
- String tooltip = _gui_get_tooltip(over, gui.tooltip->get_global_transform().xform_inv(mpos));
+ if (can_tooltip && gui.tooltip_control) {
+ String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform().xform_inv(mpos));
if (tooltip.length() == 0) {
_gui_cancel_tooltip();
@@ -2078,14 +2122,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (can_tooltip && !is_tooltip_shown) {
- gui.tooltip = over;
- gui.tooltip_pos = over->get_screen_transform().xform(pos); //(parent_xform * get_transform()).affine_inverse().xform(pos);
+ gui.tooltip_control = over;
+ gui.tooltip_pos = over->get_screen_transform().xform(pos);
gui.tooltip_timer = gui.tooltip_delay;
}
}
- //pos = gui.focus_inv_xform.xform(pos);
-
mm->set_position(pos);
Control::CursorShape cursor_shape = Control::CURSOR_ARROW;
@@ -2105,7 +2147,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (c->data.mouse_filter == Control::MOUSE_FILTER_STOP) {
break;
}
- if (c->is_set_as_toplevel()) {
+ if (c->is_set_as_top_level()) {
break;
}
c = c->get_parent_control();
@@ -2332,7 +2374,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
//keyboard focus
//if (from && p_event->is_pressed() && !p_event->get_alt() && !p_event->get_metakey() && !p_event->key->get_command()) {
-
Ref<InputEventKey> k = p_event;
//need to check for mods, otherwise any combination of alt/ctrl/shift+<up/down/left/righ/etc> is handled here when it shouldn't be.
bool mods = k.is_valid() && (k->get_control() || k->get_alt() || k->get_shift() || k->get_metakey());
@@ -2404,7 +2445,7 @@ void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) {
if (gui.drag_preview) {
memdelete(gui.drag_preview);
}
- p_control->set_as_toplevel(true);
+ p_control->set_as_top_level(true);
p_control->set_position(gui.last_mouse_pos);
p_base->get_root_parent_control()->add_child(p_control); //add as child of viewport
p_control->raise();
@@ -2422,7 +2463,7 @@ void Viewport::_gui_unfocus_control(Control *p_control) {
}
}
-void Viewport::_gui_hid_control(Control *p_control) {
+void Viewport::_gui_hide_control(Control *p_control) {
if (gui.mouse_focus == p_control) {
_drop_mouse_focus();
}
@@ -2436,7 +2477,7 @@ void Viewport::_gui_hid_control(Control *p_control) {
if (gui.drag_mouse_over == p_control) {
gui.drag_mouse_over = nullptr;
}
- if (gui.tooltip == p_control) {
+ if (gui.tooltip_control == p_control) {
_gui_cancel_tooltip();
}
}
@@ -2459,8 +2500,8 @@ void Viewport::_gui_remove_control(Control *p_control) {
if (gui.drag_mouse_over == p_control) {
gui.drag_mouse_over = nullptr;
}
- if (gui.tooltip == p_control) {
- gui.tooltip = nullptr;
+ if (gui.tooltip_control == p_control) {
+ gui.tooltip_control = nullptr;
}
}
@@ -2998,7 +3039,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);
}
@@ -3089,14 +3133,18 @@ Variant Viewport::gui_get_drag_data() const {
String Viewport::get_configuration_warning() const {
/*if (get_parent() && !Object::cast_to<Control>(get_parent()) && !render_target) {
-
return TTR("This viewport is not set as render target. If you intend for it to display its contents directly to the screen, make it a child of a Control so it can obtain a size. Otherwise, make it a RenderTarget and assign its internal texture to some node for display.");
}*/
+ String warning = Node::get_configuration_warning();
+
if (size.x == 0 || size.y == 0) {
- return TTR("Viewport size must be greater than 0 to render anything.");
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("Viewport size must be greater than 0 to render anything.");
}
- return String();
+ return warning;
}
void Viewport::gui_reset_canvas_sort_index() {
@@ -3133,6 +3181,17 @@ Viewport::ScreenSpaceAA Viewport::get_screen_space_aa() const {
return screen_space_aa;
}
+void Viewport::set_use_debanding(bool p_use_debanding) {
+ if (use_debanding == p_use_debanding)
+ return;
+ use_debanding = p_use_debanding;
+ RS::get_singleton()->viewport_set_use_debanding(viewport, p_use_debanding);
+}
+
+bool Viewport::is_using_debanding() const {
+ return use_debanding;
+}
+
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));
@@ -3154,6 +3213,24 @@ bool Viewport::is_snap_controls_to_pixels_enabled() const {
return snap_controls_to_pixels;
}
+void Viewport::set_snap_2d_transforms_to_pixel(bool p_enable) {
+ snap_2d_transforms_to_pixel = p_enable;
+ RS::get_singleton()->viewport_set_snap_2d_transforms_to_pixel(viewport, snap_2d_transforms_to_pixel);
+}
+
+bool Viewport::is_snap_2d_transforms_to_pixel_enabled() const {
+ return snap_2d_transforms_to_pixel;
+}
+
+void Viewport::set_snap_2d_vertices_to_pixel(bool p_enable) {
+ snap_2d_vertices_to_pixel = p_enable;
+ RS::get_singleton()->viewport_set_snap_2d_vertices_to_pixel(viewport, snap_2d_vertices_to_pixel);
+}
+
+bool Viewport::is_snap_2d_vertices_to_pixel_enabled() const {
+ return snap_2d_vertices_to_pixel;
+}
+
bool Viewport::gui_is_dragging() const {
return gui.dragging;
}
@@ -3208,11 +3285,28 @@ void Viewport::_validate_property(PropertyInfo &property) const {
}
void Viewport::set_default_canvas_item_texture_filter(DefaultCanvasItemTextureFilter p_filter) {
+ ERR_FAIL_INDEX(p_filter, DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX);
+
if (default_canvas_item_texture_filter == p_filter) {
return;
}
default_canvas_item_texture_filter = p_filter;
- _propagate_update_default_filter(this);
+ switch (default_canvas_item_texture_filter) {
+ case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST:
+ RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
+ break;
+ case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR:
+ RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR);
+ break;
+ case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS:
+ RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS);
+ break;
+ case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS:
+ RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
+ break;
+ default: {
+ }
+ }
}
Viewport::DefaultCanvasItemTextureFilter Viewport::get_default_canvas_item_texture_filter() const {
@@ -3220,37 +3314,31 @@ Viewport::DefaultCanvasItemTextureFilter Viewport::get_default_canvas_item_textu
}
void Viewport::set_default_canvas_item_texture_repeat(DefaultCanvasItemTextureRepeat p_repeat) {
+ ERR_FAIL_INDEX(p_repeat, DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX);
+
if (default_canvas_item_texture_repeat == p_repeat) {
return;
}
- default_canvas_item_texture_repeat = p_repeat;
- _propagate_update_default_repeat(this);
-}
-
-Viewport::DefaultCanvasItemTextureRepeat Viewport::get_default_canvas_item_texture_repeat() const {
- return default_canvas_item_texture_repeat;
-}
-void Viewport::_propagate_update_default_filter(Node *p_node) {
- CanvasItem *ci = Object::cast_to<CanvasItem>(p_node);
- if (ci) {
- ci->_update_texture_filter_changed(false);
- }
+ default_canvas_item_texture_repeat = p_repeat;
- for (int i = 0; i < p_node->get_child_count(); i++) {
- _propagate_update_default_filter(p_node->get_child(i));
+ switch (default_canvas_item_texture_repeat) {
+ case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED:
+ RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+ break;
+ case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED:
+ RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
+ break;
+ case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR:
+ RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
+ break;
+ default: {
+ }
}
}
-void Viewport::_propagate_update_default_repeat(Node *p_node) {
- CanvasItem *ci = Object::cast_to<CanvasItem>(p_node);
- if (ci) {
- ci->_update_texture_repeat_changed(false);
- }
-
- for (int i = 0; i < p_node->get_child_count(); i++) {
- _propagate_update_default_repeat(p_node->get_child(i));
- }
+Viewport::DefaultCanvasItemTextureRepeat Viewport::get_default_canvas_item_texture_repeat() const {
+ return default_canvas_item_texture_repeat;
}
DisplayServer::WindowID Viewport::get_window_id() const {
@@ -3294,6 +3382,24 @@ void Viewport::pass_mouse_focus_to(Viewport *p_viewport, Control *p_control) {
}
}
+void Viewport::set_sdf_oversize(SDFOversize p_sdf_oversize) {
+ ERR_FAIL_INDEX(p_sdf_oversize, SDF_OVERSIZE_MAX);
+ sdf_oversize = p_sdf_oversize;
+ RS::get_singleton()->viewport_set_sdf_oversize_and_scale(viewport, RS::ViewportSDFOversize(sdf_oversize), RS::ViewportSDFScale(sdf_scale));
+}
+Viewport::SDFOversize Viewport::get_sdf_oversize() const {
+ return sdf_oversize;
+}
+
+void Viewport::set_sdf_scale(SDFScale p_sdf_scale) {
+ ERR_FAIL_INDEX(p_sdf_scale, SDF_SCALE_MAX);
+ sdf_scale = p_sdf_scale;
+ RS::get_singleton()->viewport_set_sdf_oversize_and_scale(viewport, RS::ViewportSDFOversize(sdf_oversize), RS::ViewportSDFScale(sdf_scale));
+}
+Viewport::SDFScale Viewport::get_sdf_scale() const {
+ return sdf_scale;
+}
+
void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_world_2d", "world_2d"), &Viewport::set_world_2d);
ClassDB::bind_method(D_METHOD("get_world_2d"), &Viewport::get_world_2d);
@@ -3319,6 +3425,9 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_screen_space_aa", "screen_space_aa"), &Viewport::set_screen_space_aa);
ClassDB::bind_method(D_METHOD("get_screen_space_aa"), &Viewport::get_screen_space_aa);
+ ClassDB::bind_method(D_METHOD("set_use_debanding", "enable"), &Viewport::set_use_debanding);
+ ClassDB::bind_method(D_METHOD("is_using_debanding"), &Viewport::is_using_debanding);
+
ClassDB::bind_method(D_METHOD("set_debug_draw", "debug_draw"), &Viewport::set_debug_draw);
ClassDB::bind_method(D_METHOD("get_debug_draw"), &Viewport::get_debug_draw);
@@ -3365,6 +3474,12 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_snap_controls_to_pixels", "enabled"), &Viewport::set_snap_controls_to_pixels);
ClassDB::bind_method(D_METHOD("is_snap_controls_to_pixels_enabled"), &Viewport::is_snap_controls_to_pixels_enabled);
+ ClassDB::bind_method(D_METHOD("set_snap_2d_transforms_to_pixel", "enabled"), &Viewport::set_snap_2d_transforms_to_pixel);
+ ClassDB::bind_method(D_METHOD("is_snap_2d_transforms_to_pixel_enabled"), &Viewport::is_snap_2d_transforms_to_pixel_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_snap_2d_vertices_to_pixel", "enabled"), &Viewport::set_snap_2d_vertices_to_pixel);
+ ClassDB::bind_method(D_METHOD("is_snap_2d_vertices_to_pixel_enabled"), &Viewport::is_snap_2d_vertices_to_pixel_enabled);
+
ClassDB::bind_method(D_METHOD("set_shadow_atlas_quadrant_subdiv", "quadrant", "subdiv"), &Viewport::set_shadow_atlas_quadrant_subdiv);
ClassDB::bind_method(D_METHOD("get_shadow_atlas_quadrant_subdiv", "quadrant"), &Viewport::get_shadow_atlas_quadrant_subdiv);
@@ -3384,14 +3499,23 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_repeat", "mode"), &Viewport::set_default_canvas_item_texture_repeat);
ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_repeat"), &Viewport::get_default_canvas_item_texture_repeat);
+ ClassDB::bind_method(D_METHOD("set_sdf_oversize", "oversize"), &Viewport::set_sdf_oversize);
+ ClassDB::bind_method(D_METHOD("get_sdf_oversize"), &Viewport::get_sdf_oversize);
+
+ 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);
+
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");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transparent_bg"), "set_transparent_background", "has_transparent_background");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handle_input_locally"), "set_handle_input_locally", "is_handling_input_locally");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_transforms_to_pixel"), "set_snap_2d_transforms_to_pixel", "is_snap_2d_transforms_to_pixel_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_vertices_to_pixel"), "set_snap_2d_vertices_to_pixel", "is_snap_2d_vertices_to_pixel_enabled");
ADD_GROUP("Rendering", "");
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::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");
@@ -3405,6 +3529,9 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_disable_input"), "set_disable_input", "is_input_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_snap_controls_to_pixels"), "set_snap_controls_to_pixels", "is_snap_controls_to_pixels_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_embed_subwindows"), "set_embed_subwindows_hint", "get_embed_subwindows_hint");
+ ADD_GROUP("SDF", "sdf_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_oversize", PROPERTY_HINT_ENUM, "100%,120%,150%,200%"), "set_sdf_oversize", "get_sdf_oversize");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_scale", PROPERTY_HINT_ENUM, "100%,50%,25%"), "set_sdf_scale", "get_sdf_scale");
ADD_GROUP("Shadow Atlas", "shadow_atlas_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_atlas_size"), "set_shadow_atlas_size", "get_shadow_atlas_size");
ADD_PROPERTYI(PropertyInfo(Variant::INT, "shadow_atlas_quad_0", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_shadow_atlas_quadrant_subdiv", "get_shadow_atlas_quadrant_subdiv", 0);
@@ -3474,6 +3601,17 @@ void Viewport::_bind_methods() {
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX);
+
+ BIND_ENUM_CONSTANT(SDF_OVERSIZE_100_PERCENT);
+ BIND_ENUM_CONSTANT(SDF_OVERSIZE_120_PERCENT);
+ BIND_ENUM_CONSTANT(SDF_OVERSIZE_150_PERCENT);
+ BIND_ENUM_CONSTANT(SDF_OVERSIZE_200_PERCENT);
+ BIND_ENUM_CONSTANT(SDF_OVERSIZE_MAX);
+
+ BIND_ENUM_CONSTANT(SDF_SCALE_100_PERCENT);
+ BIND_ENUM_CONSTANT(SDF_SCALE_50_PERCENT);
+ BIND_ENUM_CONSTANT(SDF_SCALE_25_PERCENT);
+ BIND_ENUM_CONSTANT(SDF_SCALE_MAX);
}
Viewport::Viewport() {
@@ -3522,14 +3660,13 @@ Viewport::Viewport() {
disable_input = false;
- //window tooltip
+ // Window tooltip.
gui.tooltip_timer = -1;
- //gui.tooltip_timer->force_parent_owned();
gui.tooltip_delay = GLOBAL_DEF("gui/timers/tooltip_delay_sec", 0.5);
ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::FLOAT, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers
- gui.tooltip = nullptr;
+ gui.tooltip_control = nullptr;
gui.tooltip_label = nullptr;
gui.drag_preview = nullptr;
gui.drag_attempted = false;
@@ -3546,6 +3683,9 @@ Viewport::Viewport() {
debug_draw = DEBUG_DRAW_DISABLED;
snap_controls_to_pixels = true;
+ snap_2d_transforms_to_pixel = false;
+ snap_2d_vertices_to_pixel = false;
+
physics_last_mouse_state.alt = false;
physics_last_mouse_state.control = false;
physics_last_mouse_state.shift = false;
@@ -3558,6 +3698,10 @@ Viewport::Viewport() {
default_canvas_item_texture_filter = DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
default_canvas_item_texture_repeat = DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
+
+ sdf_oversize = SDF_OVERSIZE_120_PERCENT;
+ sdf_scale = SDF_SCALE_50_PERCENT;
+ set_sdf_oversize(SDF_OVERSIZE_120_PERCENT); //set to server
}
Viewport::~Viewport() {
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 52145a7761..7ce202d27c 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -159,6 +159,21 @@ public:
DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX,
};
+ enum SDFOversize {
+ SDF_OVERSIZE_100_PERCENT,
+ SDF_OVERSIZE_120_PERCENT,
+ SDF_OVERSIZE_150_PERCENT,
+ SDF_OVERSIZE_200_PERCENT,
+ SDF_OVERSIZE_MAX
+ };
+
+ enum SDFScale {
+ SDF_SCALE_100_PERCENT,
+ SDF_SCALE_50_PERCENT,
+ SDF_SCALE_25_PERCENT,
+ SDF_SCALE_MAX
+ };
+
enum {
SUBWINDOW_CANVAS_LAYER = 1024
};
@@ -225,6 +240,8 @@ private:
bool gen_mipmaps;
bool snap_controls_to_pixels;
+ bool snap_2d_transforms_to_pixel;
+ bool snap_2d_vertices_to_pixel;
bool physics_object_picking;
List<Ref<InputEvent>> physics_picking_events;
@@ -279,9 +296,13 @@ private:
MSAA msaa;
ScreenSpaceAA screen_space_aa;
+ bool use_debanding = false;
Ref<ViewportTexture> default_texture;
Set<ViewportTexture *> viewport_textures;
+ SDFOversize sdf_oversize;
+ SDFScale sdf_scale;
+
enum SubWindowDrag {
SUB_WINDOW_DRAG_DISABLED,
SUB_WINDOW_DRAG_MOVE,
@@ -320,7 +341,7 @@ private:
Control *mouse_over;
Control *drag_mouse_over;
Vector2 drag_mouse_over_pos;
- Control *tooltip;
+ Control *tooltip_control;
Window *tooltip_popup;
Label *tooltip_label;
Point2 tooltip_pos;
@@ -356,9 +377,6 @@ private:
DefaultCanvasItemTextureFilter default_canvas_item_texture_filter;
DefaultCanvasItemTextureRepeat default_canvas_item_texture_repeat;
- void _propagate_update_default_filter(Node *p_node);
- void _propagate_update_default_repeat(Node *p_node);
-
bool disable_input;
void _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input);
@@ -382,12 +400,12 @@ private:
void _gui_remove_root_control(List<Control *>::Element *RI);
- String _gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_which = nullptr);
+ String _gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_tooltip_owner = nullptr);
void _gui_cancel_tooltip();
void _gui_show_tooltip();
void _gui_remove_control(Control *p_control);
- void _gui_hid_control(Control *p_control);
+ void _gui_hide_control(Control *p_control);
void _gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control);
void _gui_set_drag_preview(Control *p_base, Control *p_control);
@@ -521,6 +539,9 @@ public:
void set_screen_space_aa(ScreenSpaceAA p_screen_space_aa);
ScreenSpaceAA get_screen_space_aa() const;
+ void set_use_debanding(bool p_use_debanding);
+ bool is_using_debanding() const;
+
Vector2 get_camera_coords(const Vector2 &p_viewport_coords) const;
Vector2 get_camera_rect_size() const;
@@ -555,6 +576,12 @@ public:
void set_snap_controls_to_pixels(bool p_enable);
bool is_snap_controls_to_pixels_enabled() const;
+ void set_snap_2d_transforms_to_pixel(bool p_enable);
+ bool is_snap_2d_transforms_to_pixel_enabled() const;
+
+ void set_snap_2d_vertices_to_pixel(bool p_enable);
+ bool is_snap_2d_vertices_to_pixel_enabled() const;
+
void set_input_as_handled();
bool is_input_handled() const;
@@ -563,6 +590,12 @@ public:
bool gui_is_dragging() const;
+ void set_sdf_oversize(SDFOversize p_sdf_oversize);
+ SDFOversize get_sdf_oversize() const;
+
+ void set_sdf_scale(SDFScale p_sdf_scale);
+ SDFScale get_sdf_scale() const;
+
void set_default_canvas_item_texture_filter(DefaultCanvasItemTextureFilter p_filter);
DefaultCanvasItemTextureFilter get_default_canvas_item_texture_filter() const;
@@ -589,7 +622,6 @@ class SubViewport : public Viewport {
public:
enum ClearMode {
-
CLEAR_MODE_ALWAYS,
CLEAR_MODE_NEVER,
CLEAR_MODE_ONLY_NEXT_FRAME
@@ -642,6 +674,8 @@ VARIANT_ENUM_CAST(Viewport::ShadowAtlasQuadrantSubdiv);
VARIANT_ENUM_CAST(Viewport::MSAA);
VARIANT_ENUM_CAST(Viewport::ScreenSpaceAA);
VARIANT_ENUM_CAST(Viewport::DebugDraw);
+VARIANT_ENUM_CAST(Viewport::SDFScale);
+VARIANT_ENUM_CAST(Viewport::SDFOversize);
VARIANT_ENUM_CAST(SubViewport::ClearMode);
VARIANT_ENUM_CAST(Viewport::RenderInfo);
VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureFilter);
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index a5c5be8a44..ad87139332 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -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) {
@@ -246,6 +247,8 @@ void Window::_make_window() {
}
}
+ _update_window_callbacks();
+
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_VISIBLE);
DisplayServer::get_singleton()->show_window(window_id);
}
@@ -379,7 +382,6 @@ void Window::set_visible(bool p_visible) {
}
if (p_visible && window_id == DisplayServer::INVALID_WINDOW_ID) {
_make_window();
- _update_window_callbacks();
}
} else {
if (visible) {
@@ -658,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);
}
}
@@ -738,7 +739,6 @@ void Window::_notification(int p_what) {
//create
if (visible) {
_make_window();
- _update_window_callbacks();
}
}
}
@@ -895,11 +895,11 @@ void Window::_window_input(const Ref<InputEvent> &p_ev) {
if (exclusive_child != nullptr) {
Window *focus_target = exclusive_child;
+ focus_target->grab_focus();
while (focus_target->exclusive_child != nullptr) {
- focus_target->grab_focus();
focus_target = focus_target->exclusive_child;
+ focus_target->grab_focus();
}
- focus_target->grab_focus();
if (!is_embedding_subwindows()) { //not embedding, no need for event
return;
@@ -983,7 +983,7 @@ void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio
Rect2i popup_rect;
popup_rect.size = Vector2i(MIN(size_ratio.x, p_size.x), MIN(size_ratio.y, p_size.y));
- popup_rect.position = (parent_rect.size - popup_rect.size) / 2;
+ popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
popup(popup_rect);
}
@@ -1009,7 +1009,7 @@ void Window::popup_centered(const Size2i &p_minsize) {
} else {
popup_rect.size = p_minsize;
}
- popup_rect.position = (parent_rect.size - popup_rect.size) / 2;
+ popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
popup(popup_rect);
}
@@ -1031,7 +1031,7 @@ void Window::popup_centered_ratio(float p_ratio) {
Rect2i popup_rect;
popup_rect.size = parent_rect.size * p_ratio;
- popup_rect.position = (parent_rect.size - popup_rect.size) / 2;
+ popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
popup(popup_rect);
}
@@ -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 09c52b30a3..20f8309952 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -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;
@@ -147,10 +156,9 @@ protected:
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);
@@ -238,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;
@@ -265,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 47b8b6073b..73507d36fc 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -30,14 +30,15 @@
#include "register_scene_types.h"
-#include "core/class_db.h"
+#include "core/config/project_settings.h"
+#include "core/object/class_db.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "scene/2d/animated_sprite_2d.h"
#include "scene/2d/area_2d.h"
#include "scene/2d/audio_stream_player_2d.h"
#include "scene/2d/back_buffer_copy.h"
#include "scene/2d/camera_2d.h"
+#include "scene/2d/canvas_group.h"
#include "scene/2d/canvas_modulate.h"
#include "scene/2d/collision_polygon_2d.h"
#include "scene/2d/collision_shape_2d.h"
@@ -76,11 +77,13 @@
#include "scene/animation/tween.h"
#include "scene/audio/audio_stream_player.h"
#include "scene/debugger/scene_debugger.h"
+#include "scene/gui/aspect_ratio_container.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/center_container.h"
#include "scene/gui/check_box.h"
#include "scene/gui/check_button.h"
+#include "scene/gui/code_edit.h"
#include "scene/gui/color_picker.h"
#include "scene/gui/color_rect.h"
#include "scene/gui/control.h"
@@ -141,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"
@@ -165,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"
@@ -193,6 +198,7 @@
#include "scene/3d/decal.h"
#include "scene/3d/gi_probe.h"
#include "scene/3d/gpu_particles_3d.h"
+#include "scene/3d/gpu_particles_collision_3d.h"
#include "scene/3d/immediate_geometry_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/lightmap_probe.h"
@@ -226,12 +232,15 @@
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<ResourceFormatLoaderBMFont> resource_loader_bmfont;
+static Ref<ResourceFormatLoaderStreamTexture3D> resource_loader_texture_3d;
static Ref<ResourceFormatSaverShader> resource_saver_shader;
static Ref<ResourceFormatLoaderShader> resource_loader_shader;
@@ -243,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);
@@ -252,6 +266,9 @@ void register_scene_types() {
resource_loader_texture_layered.instance();
ResourceLoader::add_resource_format_loader(resource_loader_texture_layered);
+ resource_loader_texture_3d.instance();
+ ResourceLoader::add_resource_format_loader(resource_loader_texture_3d);
+
resource_saver_text.instance();
ResourceSaver::add_resource_format_saver(resource_saver_text, true);
@@ -264,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>();
@@ -291,7 +305,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
- ClassDB::register_class<ShortCut>();
+ ClassDB::register_class<Shortcut>();
ClassDB::register_class<Control>();
ClassDB::register_class<Button>();
ClassDB::register_class<Label>();
@@ -317,6 +331,7 @@ void register_scene_types() {
ClassDB::register_class<ColorRect>();
ClassDB::register_class<NinePatchRect>();
ClassDB::register_class<ReferenceRect>();
+ ClassDB::register_class<AspectRatioContainer>();
ClassDB::register_class<TabContainer>();
ClassDB::register_class<Tabs>();
ClassDB::register_virtual_class<Separator>();
@@ -347,6 +362,7 @@ void register_scene_types() {
ClassDB::register_class<Tree>();
ClassDB::register_class<TextEdit>();
+ ClassDB::register_class<CodeEdit>();
ClassDB::register_class<SyntaxHighlighter>();
ClassDB::register_class<CodeHighlighter>();
@@ -372,7 +388,11 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
- AcceptDialog::set_swap_cancel_ok(GLOBAL_DEF_NOVAL("gui/common/swap_cancel_ok", bool(DisplayServer::get_singleton()->get_swap_cancel_ok())));
+ bool swap_cancel_ok = false;
+ if (DisplayServer::get_singleton()) {
+ swap_cancel_ok = GLOBAL_DEF_NOVAL("gui/common/swap_cancel_ok", bool(DisplayServer::get_singleton()->get_swap_cancel_ok()));
+ }
+ AcceptDialog::set_swap_cancel_ok(swap_cancel_ok);
#endif
/* REGISTER 3D */
@@ -440,6 +460,15 @@ void register_scene_types() {
ClassDB::register_class<LightmapProbe>();
ClassDB::register_virtual_class<Lightmapper>();
ClassDB::register_class<GPUParticles3D>();
+ ClassDB::register_virtual_class<GPUParticlesCollision3D>();
+ ClassDB::register_class<GPUParticlesCollisionBox>();
+ ClassDB::register_class<GPUParticlesCollisionSphere>();
+ ClassDB::register_class<GPUParticlesCollisionSDF>();
+ ClassDB::register_class<GPUParticlesCollisionHeightField>();
+ ClassDB::register_virtual_class<GPUParticlesAttractor3D>();
+ ClassDB::register_class<GPUParticlesAttractorBox>();
+ ClassDB::register_class<GPUParticlesAttractorSphere>();
+ ClassDB::register_class<GPUParticlesAttractorVectorField>();
ClassDB::register_class<CPUParticles3D>();
ClassDB::register_class<Position3D>();
@@ -502,7 +531,8 @@ void register_scene_types() {
ClassDB::register_class<VisualShaderNodeCustom>();
ClassDB::register_class<VisualShaderNodeInput>();
ClassDB::register_virtual_class<VisualShaderNodeOutput>();
- ClassDB::register_class<VisualShaderNodeGroupBase>();
+ ClassDB::register_virtual_class<VisualShaderNodeResizableBase>();
+ ClassDB::register_virtual_class<VisualShaderNodeGroupBase>();
ClassDB::register_class<VisualShaderNodeFloatConstant>();
ClassDB::register_class<VisualShaderNodeIntConstant>();
ClassDB::register_class<VisualShaderNodeBooleanConstant>();
@@ -543,8 +573,10 @@ void register_scene_types() {
ClassDB::register_class<VisualShaderNodeVectorDecompose>();
ClassDB::register_class<VisualShaderNodeTransformDecompose>();
ClassDB::register_class<VisualShaderNodeTexture>();
+ ClassDB::register_class<VisualShaderNodeCurveTexture>();
ClassDB::register_virtual_class<VisualShaderNodeSample3D>();
ClassDB::register_class<VisualShaderNodeTexture2DArray>();
+ ClassDB::register_class<VisualShaderNodeTexture3D>();
ClassDB::register_class<VisualShaderNodeCubemap>();
ClassDB::register_virtual_class<VisualShaderNodeUniform>();
ClassDB::register_class<VisualShaderNodeUniformRef>();
@@ -557,6 +589,7 @@ void register_scene_types() {
ClassDB::register_class<VisualShaderNodeTextureUniform>();
ClassDB::register_class<VisualShaderNodeTextureUniformTriplanar>();
ClassDB::register_class<VisualShaderNodeTexture2DArrayUniform>();
+ ClassDB::register_class<VisualShaderNodeTexture3DUniform>();
ClassDB::register_class<VisualShaderNodeCubemapUniform>();
ClassDB::register_class<VisualShaderNodeIf>();
ClassDB::register_class<VisualShaderNodeSwitch>();
@@ -570,6 +603,7 @@ void register_scene_types() {
ClassDB::register_class<ShaderMaterial>();
ClassDB::register_virtual_class<CanvasItem>();
+ ClassDB::register_class<CanvasTexture>();
ClassDB::register_class<CanvasItemMaterial>();
SceneTree::add_idle_callback(CanvasItemMaterial::flush_changes);
CanvasItemMaterial::init_shaders();
@@ -577,6 +611,7 @@ void register_scene_types() {
/* REGISTER 2D */
ClassDB::register_class<Node2D>();
+ ClassDB::register_class<CanvasGroup>();
ClassDB::register_class<CPUParticles2D>();
ClassDB::register_class<GPUParticles2D>();
ClassDB::register_class<Sprite2D>();
@@ -601,7 +636,9 @@ void register_scene_types() {
ClassDB::register_class<Polygon2D>();
ClassDB::register_class<Skeleton2D>();
ClassDB::register_class<Bone2D>();
- ClassDB::register_class<Light2D>();
+ ClassDB::register_virtual_class<Light2D>();
+ ClassDB::register_class<PointLight2D>();
+ ClassDB::register_class<DirectionalLight2D>();
ClassDB::register_class<LightOccluder2D>();
ClassDB::register_class<OccluderPolygon2D>();
ClassDB::register_class<YSort>();
@@ -642,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>();
@@ -697,6 +734,9 @@ void register_scene_types() {
ClassDB::register_class<CameraTexture>();
ClassDB::register_virtual_class<TextureLayered>();
ClassDB::register_virtual_class<ImageTextureLayered>();
+ ClassDB::register_virtual_class<Texture3D>();
+ ClassDB::register_class<ImageTexture3D>();
+ ClassDB::register_class<StreamTexture3D>();
ClassDB::register_class<Cubemap>();
ClassDB::register_class<CubemapArray>();
ClassDB::register_class<Texture2DArray>();
@@ -706,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>();
@@ -771,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.
@@ -807,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");
@@ -860,6 +901,7 @@ void register_scene_types() {
ClassDB::add_compatibility_class("RemoteTransform", "RemoteTransform3D");
ClassDB::add_compatibility_class("RigidBody", "RigidBody3D");
ClassDB::add_compatibility_class("Shape", "Shape3D");
+ ClassDB::add_compatibility_class("ShortCut", "Shortcut");
ClassDB::add_compatibility_class("Skeleton", "Skeleton3D");
ClassDB::add_compatibility_class("SkeletonIK", "SkeletonIK3D");
ClassDB::add_compatibility_class("SliderJoint", "SliderJoint3D");
@@ -876,6 +918,7 @@ void register_scene_types() {
ClassDB::add_compatibility_class("VehicleBody", "VehicleBody3D");
ClassDB::add_compatibility_class("VehicleWheel", "VehicleWheel3D");
ClassDB::add_compatibility_class("ViewportContainer", "SubViewportContainer");
+ ClassDB::add_compatibility_class("Viewport", "SubViewport");
ClassDB::add_compatibility_class("VisibilityEnabler", "VisibilityEnabler3D");
ClassDB::add_compatibility_class("VisibilityNotifier", "VisibilityNotifier3D");
ClassDB::add_compatibility_class("VisualServer", "RenderingServer");
@@ -885,8 +928,9 @@ void register_scene_types() {
ClassDB::add_compatibility_class("VisualShaderNodeScalarUniform", "VisualShaderNodeFloatUniform");
ClassDB::add_compatibility_class("World", "World3D");
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
@@ -912,8 +956,10 @@ void register_scene_types() {
}
}
- // Always make the default theme to avoid invalid default font/icon/style in the given theme
- make_default_theme(default_theme_hidpi, font);
+ // Always make the default theme to avoid invalid default font/icon/style in the given theme.
+ if (RenderingServer::get_singleton()) {
+ make_default_theme(default_theme_hidpi, font);
+ }
if (theme_path != String()) {
Ref<Theme> theme = ResourceLoader::load(theme_path);
@@ -933,17 +979,23 @@ 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();
+ ResourceLoader::remove_resource_format_loader(resource_loader_texture_3d);
+ resource_loader_texture_3d.unref();
+
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();
@@ -956,9 +1008,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/resources/animation.cpp b/scene/resources/animation.cpp
index b8edd70712..b2aad97d3b 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -721,7 +721,6 @@ bool Animation::track_get_interpolation_loop_wrap(int p_track) const {
/*
template<class T>
int Animation::_insert_pos(float p_time, T& p_keys) {
-
// simple, linear time inset that should be fast enough in reality.
int idx=p_keys.size();
@@ -734,14 +733,12 @@ int Animation::_insert_pos(float p_time, T& p_keys) {
p_keys.insert(idx,T());
return idx;
} else if (p_keys[idx-1].time == p_time) {
-
// condition for replacing.
return idx-1;
}
idx--;
}
-
}
*/
@@ -2633,6 +2630,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode);
ClassDB::bind_method(D_METHOD("value_track_get_key_indices", "track_idx", "time_sec", "delta"), &Animation::_value_track_get_key_indices);
+ ClassDB::bind_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec"), &Animation::value_track_interpolate);
ClassDB::bind_method(D_METHOD("method_track_get_key_indices", "track_idx", "time_sec", "delta"), &Animation::_method_track_get_key_indices);
ClassDB::bind_method(D_METHOD("method_track_get_name", "track_idx", "key_idx"), &Animation::method_track_get_name);
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 722a400fd6..c52431f5f6 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -31,7 +31,7 @@
#ifndef ANIMATION_H
#define ANIMATION_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class Animation : public Resource {
GDCLASS(Animation, Resource);
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index f02e7987a9..600a859894 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -261,7 +261,8 @@ void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, in
sign = -1;
}
- float base_rate = AudioServer::get_singleton()->get_mix_rate();
+ float global_rate_scale = AudioServer::get_singleton()->get_global_rate_scale();
+ float base_rate = AudioServer::get_singleton()->get_mix_rate() * global_rate_scale;
float srate = base->mix_rate;
srate *= p_rate_scale;
float fincrement = srate / base_rate;
diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h
index 59f3b4dc3c..56ff72c094 100644
--- a/scene/resources/bit_map.h
+++ b/scene/resources/bit_map.h
@@ -31,9 +31,9 @@
#ifndef BIT_MAP_H
#define BIT_MAP_H
-#include "core/image.h"
+#include "core/io/image.h"
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
-#include "core/resource.h"
class BitMap : public Resource {
GDCLASS(BitMap, Resource);
diff --git a/scene/resources/box_shape_3d.cpp b/scene/resources/box_shape_3d.cpp
index 69339faf76..e1c8a377c0 100644
--- a/scene/resources/box_shape_3d.cpp
+++ b/scene/resources/box_shape_3d.cpp
@@ -31,7 +31,7 @@
#include "box_shape_3d.h"
#include "servers/physics_server_3d.h"
-Vector<Vector3> BoxShape3D::get_debug_mesh_lines() {
+Vector<Vector3> BoxShape3D::get_debug_mesh_lines() const {
Vector<Vector3> lines;
AABB aabb;
aabb.position = -get_extents();
diff --git a/scene/resources/box_shape_3d.h b/scene/resources/box_shape_3d.h
index 99b6410799..fe634ce568 100644
--- a/scene/resources/box_shape_3d.h
+++ b/scene/resources/box_shape_3d.h
@@ -46,7 +46,7 @@ public:
void set_extents(const Vector3 &p_extents);
Vector3 get_extents() const;
- virtual Vector<Vector3> get_debug_mesh_lines() override;
+ virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
BoxShape3D();
diff --git a/scene/resources/camera_effects.h b/scene/resources/camera_effects.h
index 99fd4eb37c..9a26f3d0b2 100644
--- a/scene/resources/camera_effects.h
+++ b/scene/resources/camera_effects.h
@@ -31,8 +31,8 @@
#ifndef CAMERA_EFFECTS_H
#define CAMERA_EFFECTS_H
-#include "core/resource.h"
-#include "core/rid.h"
+#include "core/io/resource.h"
+#include "core/templates/rid.h"
class CameraEffects : public Resource {
GDCLASS(CameraEffects, Resource);
diff --git a/scene/resources/capsule_shape_3d.cpp b/scene/resources/capsule_shape_3d.cpp
index 28fc0d470c..5da7f682e5 100644
--- a/scene/resources/capsule_shape_3d.cpp
+++ b/scene/resources/capsule_shape_3d.cpp
@@ -31,7 +31,7 @@
#include "capsule_shape_3d.h"
#include "servers/physics_server_3d.h"
-Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() {
+Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const {
float radius = get_radius();
float height = get_height();
@@ -106,8 +106,8 @@ void CapsuleShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_height", "height"), &CapsuleShape3D::set_height);
ClassDB::bind_method(D_METHOD("get_height"), &CapsuleShape3D::get_height);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_height", "get_height");
}
CapsuleShape3D::CapsuleShape3D() :
diff --git a/scene/resources/capsule_shape_3d.h b/scene/resources/capsule_shape_3d.h
index a638618c48..432ca5654e 100644
--- a/scene/resources/capsule_shape_3d.h
+++ b/scene/resources/capsule_shape_3d.h
@@ -49,7 +49,7 @@ public:
void set_height(float p_height);
float get_height() const;
- virtual Vector<Vector3> get_debug_mesh_lines() override;
+ virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
CapsuleShape3D();
diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp
index 7315945c03..7cbafcbc4d 100644
--- a/scene/resources/concave_polygon_shape_3d.cpp
+++ b/scene/resources/concave_polygon_shape_3d.cpp
@@ -32,7 +32,7 @@
#include "servers/physics_server_3d.h"
-Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() {
+Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const {
Set<DrawEdge> edges;
Vector<Vector3> data = get_faces();
diff --git a/scene/resources/concave_polygon_shape_3d.h b/scene/resources/concave_polygon_shape_3d.h
index a3c10adce2..c17765b9ef 100644
--- a/scene/resources/concave_polygon_shape_3d.h
+++ b/scene/resources/concave_polygon_shape_3d.h
@@ -65,7 +65,7 @@ public:
void set_faces(const Vector<Vector3> &p_faces);
Vector<Vector3> get_faces() const;
- virtual Vector<Vector3> get_debug_mesh_lines() override;
+ virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
ConcavePolygonShape3D();
diff --git a/scene/resources/convex_polygon_shape_3d.cpp b/scene/resources/convex_polygon_shape_3d.cpp
index affeb05a8f..29549e1114 100644
--- a/scene/resources/convex_polygon_shape_3d.cpp
+++ b/scene/resources/convex_polygon_shape_3d.cpp
@@ -32,7 +32,7 @@
#include "core/math/quick_hull.h"
#include "servers/physics_server_3d.h"
-Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() {
+Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const {
Vector<Vector3> points = get_points();
if (points.size() > 3) {
diff --git a/scene/resources/convex_polygon_shape_3d.h b/scene/resources/convex_polygon_shape_3d.h
index 43d8b90740..f436d2f5d4 100644
--- a/scene/resources/convex_polygon_shape_3d.h
+++ b/scene/resources/convex_polygon_shape_3d.h
@@ -46,7 +46,7 @@ public:
void set_points(const Vector<Vector3> &p_points);
Vector<Vector3> get_points() const;
- virtual Vector<Vector3> get_debug_mesh_lines() override;
+ virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
ConvexPolygonShape3D();
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 57ddaf897d..6c5f4b7057 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -31,7 +31,7 @@
#ifndef CURVE_H
#define CURVE_H
-#include "core/resource.h"
+#include "core/io/resource.h"
// y(x) curve
class Curve : public Resource {
diff --git a/scene/resources/cylinder_shape_3d.cpp b/scene/resources/cylinder_shape_3d.cpp
index 44786d6025..bb8c27a60d 100644
--- a/scene/resources/cylinder_shape_3d.cpp
+++ b/scene/resources/cylinder_shape_3d.cpp
@@ -31,7 +31,7 @@
#include "cylinder_shape_3d.h"
#include "servers/physics_server_3d.h"
-Vector<Vector3> CylinderShape3D::get_debug_mesh_lines() {
+Vector<Vector3> CylinderShape3D::get_debug_mesh_lines() const {
float radius = get_radius();
float height = get_height();
@@ -99,8 +99,8 @@ void CylinderShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_height", "height"), &CylinderShape3D::set_height);
ClassDB::bind_method(D_METHOD("get_height"), &CylinderShape3D::get_height);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_height", "get_height");
}
CylinderShape3D::CylinderShape3D() :
diff --git a/scene/resources/cylinder_shape_3d.h b/scene/resources/cylinder_shape_3d.h
index 2564a04a97..e579e1f7cf 100644
--- a/scene/resources/cylinder_shape_3d.h
+++ b/scene/resources/cylinder_shape_3d.h
@@ -48,7 +48,7 @@ public:
void set_height(float p_height);
float get_height() const;
- virtual Vector<Vector3> get_debug_mesh_lines() override;
+ virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
CylinderShape3D();
diff --git a/scene/resources/default_theme/arrow_left.png b/scene/resources/default_theme/arrow_left.png
new file mode 100644
index 0000000000..4163059dd3
--- /dev/null
+++ b/scene/resources/default_theme/arrow_left.png
Binary files differ
diff --git a/scene/resources/default_theme/bookmark.png b/scene/resources/default_theme/bookmark.png
new file mode 100644
index 0000000000..9718cf53b6
--- /dev/null
+++ b/scene/resources/default_theme/bookmark.png
Binary files differ
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 9008f6d5b9..85cd0f9bb4 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -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;
@@ -95,40 +97,6 @@ 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];
-
- 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];
-
- font->add_char(chr, 0, frect, align, advance);
- }
-
- 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]);
- }
-
- font->set_height(p_height);
- font->set_ascent(p_ascent);
-
- return font;
-}
-
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));
@@ -182,11 +150,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 +166,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 +183,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 +194,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 +239,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);
@@ -288,6 +274,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);
@@ -318,7 +305,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 +326,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 +334,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 +345,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 +365,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));
@@ -382,10 +379,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("tab", "TextEdit", make_icon(tab_png));
theme->set_icon("space", "TextEdit", make_icon(space_png));
- theme->set_icon("folded", "TextEdit", make_icon(arrow_right_png));
- theme->set_icon("fold", "TextEdit", make_icon(arrow_down_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));
@@ -398,16 +394,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color_readonly", "TextEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
theme->set_color("selection_color", "TextEdit", font_color_selection);
theme->set_color("mark_color", "TextEdit", Color(1.0, 0.4, 0.4, 0.4));
- theme->set_color("bookmark_color", "TextEdit", Color(0.08, 0.49, 0.98));
- theme->set_color("breakpoint_color", "TextEdit", Color(0.8, 0.8, 0.4, 0.2));
- theme->set_color("executing_line_color", "TextEdit", Color(0.2, 0.8, 0.2, 0.4));
theme->set_color("code_folding_color", "TextEdit", Color(0.8, 0.8, 0.8, 0.8));
theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8));
theme->set_color("caret_color", "TextEdit", control_font_color);
theme->set_color("caret_background_color", "TextEdit", Color(0, 0, 0));
theme->set_color("brace_mismatch_color", "TextEdit", Color(1, 0.2, 0.2));
- theme->set_color("line_number_color", "TextEdit", Color(0.67, 0.67, 0.67, 0.4));
- theme->set_color("safe_line_number_color", "TextEdit", Color(0.67, 0.78, 0.67, 0.6));
theme->set_color("word_highlighted_color", "TextEdit", Color(0.8, 0.9, 0.9, 0.15));
theme->set_constant("completion_lines", "TextEdit", 7);
@@ -415,6 +406,51 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("completion_scroll_width", "TextEdit", 3);
theme->set_constant("line_spacing", "TextEdit", 4 * scale);
+ // CodeEdit
+ theme->set_stylebox("normal", "CodeEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
+ theme->set_stylebox("focus", "CodeEdit", focus);
+ theme->set_stylebox("read_only", "CodeEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4, 0, 0, 0, 0));
+ theme->set_stylebox("completion", "CodeEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
+
+ theme->set_icon("tab", "CodeEdit", make_icon(tab_png));
+ theme->set_icon("space", "CodeEdit", make_icon(space_png));
+ theme->set_icon("breakpoint", "CodeEdit", make_icon(graph_port_png));
+ theme->set_icon("bookmark", "CodeEdit", make_icon(bookmark_png));
+ theme->set_icon("executing_line", "CodeEdit", make_icon(arrow_right_png));
+ theme->set_icon("can_fold", "CodeEdit", make_icon(arrow_down_png));
+ 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));
+ theme->set_color("completion_selected_color", "CodeEdit", Color(0.26, 0.26, 0.27));
+ theme->set_color("completion_existing_color", "CodeEdit", Color(0.87, 0.87, 0.87, 0.13));
+ theme->set_color("completion_scroll_color", "CodeEdit", control_font_color_pressed);
+ theme->set_color("completion_font_color", "CodeEdit", Color(0.67, 0.67, 0.67));
+ theme->set_color("font_color", "CodeEdit", control_font_color);
+ theme->set_color("font_color_selected", "CodeEdit", Color(0, 0, 0));
+ theme->set_color("font_color_readonly", "CodeEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+ theme->set_color("selection_color", "CodeEdit", font_color_selection);
+ theme->set_color("mark_color", "CodeEdit", Color(1.0, 0.4, 0.4, 0.4));
+ theme->set_color("bookmark_color", "CodeEdit", Color(0.5, 0.64, 1, 0.8));
+ theme->set_color("breakpoint_color", "CodeEdit", Color(0.9, 0.29, 0.3));
+ theme->set_color("executing_line_color", "CodeEdit", Color(0.98, 0.89, 0.27));
+ theme->set_color("code_folding_color", "CodeEdit", Color(0.8, 0.8, 0.8, 0.8));
+ theme->set_color("current_line_color", "CodeEdit", Color(0.25, 0.25, 0.26, 0.8));
+ theme->set_color("caret_color", "CodeEdit", control_font_color);
+ theme->set_color("caret_background_color", "CodeEdit", Color(0, 0, 0));
+ theme->set_color("brace_mismatch_color", "CodeEdit", Color(1, 0.2, 0.2));
+ theme->set_color("line_number_color", "CodeEdit", Color(0.67, 0.67, 0.67, 0.4));
+ theme->set_color("safe_line_number_color", "CodeEdit", Color(0.67, 0.78, 0.67, 0.6));
+ theme->set_color("word_highlighted_color", "CodeEdit", Color(0.8, 0.9, 0.9, 0.15));
+
+ theme->set_constant("completion_lines", "CodeEdit", 7);
+ theme->set_constant("completion_max_width", "CodeEdit", 50);
+ theme->set_constant("completion_scroll_width", "CodeEdit", 3);
+ theme->set_constant("line_spacing", "CodeEdit", 4 * scale);
+
Ref<Texture2D> empty_icon = memnew(ImageTexture);
// HScrollBar
@@ -526,13 +562,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);
@@ -595,9 +634,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);
@@ -626,7 +667,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));
@@ -655,6 +699,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);
@@ -679,6 +724,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);
@@ -738,6 +784,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
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));
@@ -826,12 +873,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);
@@ -840,6 +922,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/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/option_button_disabled_mirrored.png b/scene/resources/default_theme/option_button_disabled_mirrored.png
new file mode 100644
index 0000000000..9d149a35ca
--- /dev/null
+++ b/scene/resources/default_theme/option_button_disabled_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_hover_mirrored.png b/scene/resources/default_theme/option_button_hover_mirrored.png
new file mode 100644
index 0000000000..d49c165645
--- /dev/null
+++ b/scene/resources/default_theme/option_button_hover_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_normal_mirrored.png b/scene/resources/default_theme/option_button_normal_mirrored.png
new file mode 100644
index 0000000000..feec848f33
--- /dev/null
+++ b/scene/resources/default_theme/option_button_normal_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/option_button_pressed_mirrored.png b/scene/resources/default_theme/option_button_pressed_mirrored.png
new file mode 100644
index 0000000000..94cabb18d6
--- /dev/null
+++ b/scene/resources/default_theme/option_button_pressed_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/submenu_mirrored.png b/scene/resources/default_theme/submenu_mirrored.png
new file mode 100644
index 0000000000..1142b9ba9f
--- /dev/null
+++ b/scene/resources/default_theme/submenu_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
index edcdb90db9..7765348f80 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -6,10 +6,18 @@ 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
};
+static const unsigned char bookmark_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, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xed, 0x93, 0x31, 0xa, 0xc0, 0x30, 0xc, 0x3, 0xa5, 0xd0, 0xff, 0x7f, 0x59, 0x1d, 0x8a, 0x42, 0x8, 0x9, 0x95, 0xc9, 0xd2, 0xa1, 0x9a, 0x8c, 0xf1, 0xdd, 0x62, 0x1b, 0x38, 0xc, 0x87, 0x5a, 0x5, 0xae, 0x79, 0xde, 0x2, 0x1, 0x80, 0x94, 0x39, 0x48, 0x76, 0x49, 0x17, 0xa4, 0xf0, 0x24, 0x61, 0x2b, 0x51, 0x8b, 0xfc, 0x82, 0xcf, 0xb, 0x48, 0x7a, 0xdf, 0x75, 0x81, 0xf, 0xe5, 0x29, 0xf7, 0x92, 0x6b, 0x3, 0x1a, 0x1e, 0xda, 0x7c, 0x3d, 0x77, 0x21, 0x7b, 0xa8, 0x74, 0x2e, 0xcb, 0xd, 0xc8, 0x75, 0x13, 0x28, 0x9, 0xed, 0xc2, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char button_disabled_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, 0xc7, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xd0, 0x81, 0x66, 0x43, 0x31, 0x14, 0x87, 0xf1, 0xf, 0x5, 0x17, 0xb8, 0x28, 0x2e, 0x8, 0x71, 0xf3, 0x6, 0x19, 0xb6, 0xb9, 0xcb, 0xac, 0x95, 0xa4, 0xb7, 0xad, 0x6a, 0xd5, 0x68, 0x5f, 0xe4, 0x3e, 0x76, 0x1e, 0xe1, 0xbf, 0x21, 0xa6, 0xab, 0xf8, 0x1, 0x7c, 0x9c, 0x73, 0xe, 0xac, 0xe8, 0xe8, 0x19, 0x30, 0x58, 0xc6, 0xca, 0x62, 0x18, 0xe8, 0xe9, 0x58, 0x41, 0xc7, 0x1a, 0x87, 0x27, 0x10, 0x49, 0xe4, 0x5f, 0x89, 0x48, 0xc0, 0xe3, 0x58, 0xd3, 0x41, 0x8f, 0xb, 0xcb, 0xbd, 0x7c, 0xeb, 0xbf, 0x7b, 0x9, 0xb, 0x8e, 0x1e, 0x6, 0xfc, 0xad, 0x64, 0x6d, 0xb5, 0x79, 0xb0, 0x55, 0xd6, 0xad, 0xe0, 0x19, 0xc0, 0x10, 0xae, 0xda, 0x34, 0x5c, 0x45, 0xc0, 0x80, 0x25, 0x5e, 0xf4, 0xd5, 0x70, 0x11, 0x11, 0xb, 0x23, 0xe9, 0xac, 0xcf, 0x86, 0xb3, 0x48, 0x8c, 0x30, 0x92, 0x4f, 0xa, 0xd, 0x27, 0x91, 0x6b, 0x70, 0xd4, 0x47, 0xc3, 0xf1, 0x2f, 0x48, 0x7, 0x4d, 0xd, 0x87, 0x3a, 0xc2, 0x12, 0x67, 0xbd, 0x37, 0xcc, 0x75, 0x49, 0x43, 0xd8, 0xe9, 0xad, 0x61, 0x57, 0xcf, 0x1c, 0xf0, 0xfb, 0x32, 0xe9, 0xf5, 0xc9, 0xa4, 0x7d, 0x7d, 0x54, 0x8f, 0x7b, 0x59, 0xe6, 0x92, 0x14, 0x1f, 0x24, 0xcd, 0x3f, 0x7b, 0x6b, 0xa, 0xe, 0x6a, 0x82, 0x91, 0x45, 0x30, 0xba, 0x1, 0x4a, 0x51, 0xc4, 0x35, 0x1f, 0xe5, 0xa1, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -210,18 +218,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
};
@@ -310,6 +334,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
};
@@ -350,6 +378,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
};
@@ -358,6 +394,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
};
@@ -422,4 +466,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
new file mode 100644
index 0000000000..799b63c098
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off_disabled_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off_mirrored.png b/scene/resources/default_theme/toggle_off_mirrored.png
new file mode 100644
index 0000000000..3487605d58
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on_disabled_mirrored.png b/scene/resources/default_theme/toggle_on_disabled_mirrored.png
new file mode 100644
index 0000000000..0758babd4f
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on_disabled_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on_mirrored.png b/scene/resources/default_theme/toggle_on_mirrored.png
new file mode 100644
index 0000000000..3fd953c8e2
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on_mirrored.png
Binary files differ
diff --git a/scene/resources/default_theme/window_resizer_mirrored.png b/scene/resources/default_theme/window_resizer_mirrored.png
new file mode 100644
index 0000000000..bbce5f1406
--- /dev/null
+++ b/scene/resources/default_theme/window_resizer_mirrored.png
Binary files differ
diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp
deleted file mode 100644
index 99f87dd6ed..0000000000
--- a/scene/resources/dynamic_font.cpp
+++ /dev/null
@@ -1,1143 +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(CharType 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(CharType p_char, CharType 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 += CharType(charcode);
- }
- charcode = FT_Get_Next_Char(face, charcode, &gindex);
- }
-
- return chars;
-}
-
-float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType 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, RID(), RID(), Color(1, 1, 1, 1), 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);
- for (int i = 0; i < texsize * texsize * p_color_size; i++) {
- w[i] = 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(CharType 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(CharType 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(CharType p_char, CharType 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, CharType p_char, CharType 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 e8637e7e34..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/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(CharType p_char, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const;
- Character _make_outline_char(CharType 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<CharType, Character> char_map;
-
- _FORCE_INLINE_ void _update_char(CharType 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(CharType p_char, CharType 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, CharType p_char, CharType 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(CharType p_char, CharType 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, CharType p_char, CharType 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 94629a77b9..1eb78a3679 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -30,7 +30,8 @@
#include "environment.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
#include "servers/rendering_server.h"
#include "texture.h"
@@ -44,6 +45,9 @@ void Environment::set_background(BGMode p_bg) {
bg_mode = p_bg;
RS::get_singleton()->environment_set_background(environment, RS::EnvironmentBG(p_bg));
_change_notify();
+ if (bg_mode != BG_SKY) {
+ set_fog_aerial_perspective(0.0);
+ }
}
Environment::BGMode Environment::get_background() const {
@@ -134,6 +138,7 @@ Color Environment::get_ambient_light_color() const {
void Environment::set_ambient_source(AmbientSource p_source) {
ambient_source = p_source;
_update_ambient_light();
+ _change_notify();
}
Environment::AmbientSource Environment::get_ambient_source() const {
@@ -161,6 +166,7 @@ float Environment::get_ambient_light_sky_contribution() const {
void Environment::set_reflection_source(ReflectionSource p_source) {
reflection_source = p_source;
_update_ambient_light();
+ _change_notify();
}
Environment::ReflectionSource Environment::get_reflection_source() const {
@@ -576,22 +582,28 @@ bool Environment::is_glow_enabled() const {
return glow_enabled;
}
-void Environment::set_glow_level_enabled(int p_level, bool p_enabled) {
+void Environment::set_glow_level(int p_level, float p_intensity) {
ERR_FAIL_INDEX(p_level, RS::MAX_GLOW_LEVELS);
- if (p_enabled) {
- glow_levels |= (1 << p_level);
- } else {
- glow_levels &= ~(1 << p_level);
- }
+ glow_levels.write[p_level] = p_intensity;
_update_glow();
}
-bool Environment::is_glow_level_enabled(int p_level) const {
- ERR_FAIL_INDEX_V(p_level, RS::MAX_GLOW_LEVELS, false);
+float Environment::get_glow_level(int p_level) const {
+ ERR_FAIL_INDEX_V(p_level, RS::MAX_GLOW_LEVELS, 0.0);
+
+ return glow_levels[p_level];
+}
+
+void Environment::set_glow_normalized(bool p_normalized) {
+ glow_normalize_levels = p_normalized;
+
+ _update_glow();
+}
- return glow_levels & (1 << p_level);
+bool Environment::is_glow_normalized() const {
+ return glow_normalize_levels;
}
void Environment::set_glow_intensity(float p_intensity) {
@@ -668,10 +680,24 @@ float Environment::get_glow_hdr_luminance_cap() const {
}
void Environment::_update_glow() {
+ Vector<float> normalized_levels;
+ if (glow_normalize_levels) {
+ normalized_levels.resize(7);
+ float size = 0.0;
+ for (int i = 0; i < glow_levels.size(); i++) {
+ size += glow_levels[i];
+ }
+ for (int i = 0; i < glow_levels.size(); i++) {
+ normalized_levels.write[i] = glow_levels[i] / size;
+ }
+ } else {
+ normalized_levels = glow_levels;
+ }
+
RS::get_singleton()->environment_set_glow(
environment,
glow_enabled,
- glow_levels,
+ normalized_levels,
glow_intensity,
glow_strength,
glow_mix,
@@ -694,150 +720,137 @@ bool Environment::is_fog_enabled() const {
return fog_enabled;
}
-void Environment::set_fog_color(const Color &p_color) {
- fog_color = p_color;
+void Environment::set_fog_light_color(const Color &p_light_color) {
+ fog_light_color = p_light_color;
_update_fog();
}
-
-Color Environment::get_fog_color() const {
- return fog_color;
+Color Environment::get_fog_light_color() const {
+ return fog_light_color;
}
-
-void Environment::set_fog_sun_color(const Color &p_color) {
- fog_sun_color = p_color;
+void Environment::set_fog_light_energy(float p_amount) {
+ fog_light_energy = p_amount;
_update_fog();
}
-
-Color Environment::get_fog_sun_color() const {
- return fog_sun_color;
+float Environment::get_fog_light_energy() const {
+ return fog_light_energy;
}
-
-void Environment::set_fog_sun_amount(float p_amount) {
- fog_sun_amount = p_amount;
+void Environment::set_fog_sun_scatter(float p_amount) {
+ fog_sun_scatter = p_amount;
_update_fog();
}
-
-float Environment::get_fog_sun_amount() const {
- return fog_sun_amount;
+float Environment::get_fog_sun_scatter() const {
+ return fog_sun_scatter;
}
-
-void Environment::_update_fog() {
- RS::get_singleton()->environment_set_fog(
- environment,
- fog_enabled,
- fog_color,
- fog_sun_color,
- fog_sun_amount);
+void Environment::set_fog_density(float p_amount) {
+ fog_density = p_amount;
+ _update_fog();
}
-
-void Environment::set_fog_depth_enabled(bool p_enabled) {
- fog_depth_enabled = p_enabled;
- _update_fog_depth();
+float Environment::get_fog_density() const {
+ return fog_density;
}
-
-bool Environment::is_fog_depth_enabled() const {
- return fog_depth_enabled;
+void Environment::set_fog_height(float p_amount) {
+ fog_height = p_amount;
+ _update_fog();
}
-
-void Environment::set_fog_depth_begin(float p_distance) {
- fog_depth_begin = p_distance;
- _update_fog_depth();
+float Environment::get_fog_height() const {
+ return fog_height;
}
-
-float Environment::get_fog_depth_begin() const {
- return fog_depth_begin;
+void Environment::set_fog_height_density(float p_amount) {
+ fog_height_density = p_amount;
+ _update_fog();
}
-
-void Environment::set_fog_depth_end(float p_distance) {
- fog_depth_end = p_distance;
- _update_fog_depth();
+float Environment::get_fog_height_density() const {
+ return fog_height_density;
}
-float Environment::get_fog_depth_end() const {
- return fog_depth_end;
+void Environment::set_fog_aerial_perspective(float p_aerial_perspective) {
+ fog_aerial_perspective = p_aerial_perspective;
+ _update_fog();
}
-
-void Environment::set_fog_depth_curve(float p_curve) {
- fog_depth_curve = p_curve;
- _update_fog_depth();
+float Environment::get_fog_aerial_perspective() const {
+ return fog_aerial_perspective;
}
-float Environment::get_fog_depth_curve() const {
- return fog_depth_curve;
+void Environment::_update_fog() {
+ RS::get_singleton()->environment_set_fog(
+ environment,
+ fog_enabled,
+ fog_light_color,
+ fog_light_energy,
+ fog_sun_scatter,
+ fog_density,
+ fog_height,
+ fog_height_density,
+ fog_aerial_perspective);
}
-void Environment::set_fog_transmit_enabled(bool p_enabled) {
- fog_transmit_enabled = p_enabled;
- _update_fog_depth();
-}
+// Volumetric Fog
-bool Environment::is_fog_transmit_enabled() const {
- return fog_transmit_enabled;
+void Environment::_update_volumetric_fog() {
+ RS::get_singleton()->environment_set_volumetric_fog(environment, volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_light, volumetric_fog_light_energy, volumetric_fog_length, volumetric_fog_detail_spread, volumetric_fog_gi_inject, RS::EnvVolumetricFogShadowFilter(volumetric_fog_shadow_filter));
}
-void Environment::set_fog_transmit_curve(float p_curve) {
- fog_transmit_curve = p_curve;
- _update_fog_depth();
+void Environment::set_volumetric_fog_enabled(bool p_enable) {
+ volumetric_fog_enabled = p_enable;
+ _update_volumetric_fog();
+ _change_notify();
}
-float Environment::get_fog_transmit_curve() const {
- return fog_transmit_curve;
+bool Environment::is_volumetric_fog_enabled() const {
+ return volumetric_fog_enabled;
}
-
-void Environment::_update_fog_depth() {
- RS::get_singleton()->environment_set_fog_depth(
- environment,
- fog_depth_enabled,
- fog_depth_begin,
- fog_depth_end,
- fog_depth_curve,
- fog_transmit_enabled,
- fog_transmit_curve);
+void Environment::set_volumetric_fog_density(float p_density) {
+ p_density = CLAMP(p_density, 0.0000001, 1.0);
+ volumetric_fog_density = p_density;
+ _update_volumetric_fog();
}
-
-void Environment::set_fog_height_enabled(bool p_enabled) {
- fog_height_enabled = p_enabled;
- _update_fog_height();
+float Environment::get_volumetric_fog_density() const {
+ return volumetric_fog_density;
}
-
-bool Environment::is_fog_height_enabled() const {
- return fog_height_enabled;
+void Environment::set_volumetric_fog_light(Color p_color) {
+ volumetric_fog_light = p_color;
+ _update_volumetric_fog();
}
-
-void Environment::set_fog_height_min(float p_distance) {
- fog_height_min = p_distance;
- _update_fog_height();
+Color Environment::get_volumetric_fog_light() const {
+ return volumetric_fog_light;
}
-
-float Environment::get_fog_height_min() const {
- return fog_height_min;
+void Environment::set_volumetric_fog_light_energy(float p_begin) {
+ volumetric_fog_light_energy = p_begin;
+ _update_volumetric_fog();
}
-
-void Environment::set_fog_height_max(float p_distance) {
- fog_height_max = p_distance;
- _update_fog_height();
+float Environment::get_volumetric_fog_light_energy() const {
+ return volumetric_fog_light_energy;
}
-
-float Environment::get_fog_height_max() const {
- return fog_height_max;
+void Environment::set_volumetric_fog_length(float p_length) {
+ volumetric_fog_length = p_length;
+ _update_volumetric_fog();
+}
+float Environment::get_volumetric_fog_length() const {
+ return volumetric_fog_length;
+}
+void Environment::set_volumetric_fog_detail_spread(float p_detail_spread) {
+ volumetric_fog_detail_spread = p_detail_spread;
+ _update_volumetric_fog();
+}
+float Environment::get_volumetric_fog_detail_spread() const {
+ return volumetric_fog_detail_spread;
}
-void Environment::set_fog_height_curve(float p_distance) {
- fog_height_curve = p_distance;
- _update_fog_height();
+void Environment::set_volumetric_fog_gi_inject(float p_gi_inject) {
+ volumetric_fog_gi_inject = p_gi_inject;
+ _update_volumetric_fog();
+}
+float Environment::get_volumetric_fog_gi_inject() const {
+ return volumetric_fog_gi_inject;
}
-float Environment::get_fog_height_curve() const {
- return fog_height_curve;
+void Environment::set_volumetric_fog_shadow_filter(VolumetricFogShadowFilter p_filter) {
+ volumetric_fog_shadow_filter = p_filter;
+ _update_volumetric_fog();
}
-void Environment::_update_fog_height() {
- RS::get_singleton()->environment_set_fog_height(
- environment,
- fog_height_enabled,
- fog_height_min,
- fog_height_max,
- fog_height_curve);
+Environment::VolumetricFogShadowFilter Environment::get_volumetric_fog_shadow_filter() const {
+ return volumetric_fog_shadow_filter;
}
// Adjustment
@@ -879,23 +892,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
@@ -907,6 +935,12 @@ void Environment::_validate_property(PropertyInfo &property) const {
}
}
+ if (property.name == "fog_aerial_perspective") {
+ if (bg_mode != BG_SKY) {
+ property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+ }
+
if (property.name == "glow_intensity" && glow_blend_mode == GLOW_BLEND_MODE_MIX) {
property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
}
@@ -935,6 +969,7 @@ void Environment::_validate_property(PropertyInfo &property) const {
static const char *hide_prefixes[] = {
"fog_",
+ "volumetric_fog_",
"auto_exposure_",
"ss_reflections_",
"ssao_",
@@ -1181,8 +1216,10 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_glow_enabled", "enabled"), &Environment::set_glow_enabled);
ClassDB::bind_method(D_METHOD("is_glow_enabled"), &Environment::is_glow_enabled);
- ClassDB::bind_method(D_METHOD("set_glow_level_enabled", "idx", "enabled"), &Environment::set_glow_level_enabled);
- ClassDB::bind_method(D_METHOD("is_glow_level_enabled", "idx"), &Environment::is_glow_level_enabled);
+ ClassDB::bind_method(D_METHOD("set_glow_level", "idx", "intensity"), &Environment::set_glow_level);
+ ClassDB::bind_method(D_METHOD("get_glow_level", "idx"), &Environment::get_glow_level);
+ ClassDB::bind_method(D_METHOD("set_glow_normalized", "normalize"), &Environment::set_glow_normalized);
+ ClassDB::bind_method(D_METHOD("is_glow_normalized"), &Environment::is_glow_normalized);
ClassDB::bind_method(D_METHOD("set_glow_intensity", "intensity"), &Environment::set_glow_intensity);
ClassDB::bind_method(D_METHOD("get_glow_intensity"), &Environment::get_glow_intensity);
ClassDB::bind_method(D_METHOD("set_glow_strength", "strength"), &Environment::set_glow_strength);
@@ -1202,13 +1239,14 @@ void Environment::_bind_methods() {
ADD_GROUP("Glow", "glow_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "glow_enabled"), "set_glow_enabled", "is_glow_enabled");
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/1"), "set_glow_level_enabled", "is_glow_level_enabled", 0);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/2"), "set_glow_level_enabled", "is_glow_level_enabled", 1);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/3"), "set_glow_level_enabled", "is_glow_level_enabled", 2);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/4"), "set_glow_level_enabled", "is_glow_level_enabled", 3);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/5"), "set_glow_level_enabled", "is_glow_level_enabled", 4);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/6"), "set_glow_level_enabled", "is_glow_level_enabled", 5);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "glow_levels/7"), "set_glow_level_enabled", "is_glow_level_enabled", 6);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/1", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 0);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/2", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 1);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/3", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 2);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/4", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 3);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/5", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 4);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/6", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 5);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "glow_levels/7", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_glow_level", "get_glow_level", 6);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "glow_normalized"), "set_glow_normalized", "is_glow_normalized");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_intensity", PROPERTY_HINT_RANGE, "0.0,8.0,0.01"), "set_glow_intensity", "get_glow_intensity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_strength", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"), "set_glow_strength", "get_glow_strength");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_mix", PROPERTY_HINT_RANGE, "0.0,1.0,0.001"), "set_glow_mix", "get_glow_mix");
@@ -1222,52 +1260,62 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fog_enabled", "enabled"), &Environment::set_fog_enabled);
ClassDB::bind_method(D_METHOD("is_fog_enabled"), &Environment::is_fog_enabled);
- ClassDB::bind_method(D_METHOD("set_fog_color", "color"), &Environment::set_fog_color);
- ClassDB::bind_method(D_METHOD("get_fog_color"), &Environment::get_fog_color);
- ClassDB::bind_method(D_METHOD("set_fog_sun_color", "color"), &Environment::set_fog_sun_color);
- ClassDB::bind_method(D_METHOD("get_fog_sun_color"), &Environment::get_fog_sun_color);
- ClassDB::bind_method(D_METHOD("set_fog_sun_amount", "amount"), &Environment::set_fog_sun_amount);
- ClassDB::bind_method(D_METHOD("get_fog_sun_amount"), &Environment::get_fog_sun_amount);
-
- ClassDB::bind_method(D_METHOD("set_fog_depth_enabled", "enabled"), &Environment::set_fog_depth_enabled);
- ClassDB::bind_method(D_METHOD("is_fog_depth_enabled"), &Environment::is_fog_depth_enabled);
- ClassDB::bind_method(D_METHOD("set_fog_depth_begin", "distance"), &Environment::set_fog_depth_begin);
- ClassDB::bind_method(D_METHOD("get_fog_depth_begin"), &Environment::get_fog_depth_begin);
- ClassDB::bind_method(D_METHOD("set_fog_depth_end", "distance"), &Environment::set_fog_depth_end);
- ClassDB::bind_method(D_METHOD("get_fog_depth_end"), &Environment::get_fog_depth_end);
- ClassDB::bind_method(D_METHOD("set_fog_depth_curve", "curve"), &Environment::set_fog_depth_curve);
- ClassDB::bind_method(D_METHOD("get_fog_depth_curve"), &Environment::get_fog_depth_curve);
- ClassDB::bind_method(D_METHOD("set_fog_transmit_enabled", "enabled"), &Environment::set_fog_transmit_enabled);
- ClassDB::bind_method(D_METHOD("is_fog_transmit_enabled"), &Environment::is_fog_transmit_enabled);
- ClassDB::bind_method(D_METHOD("set_fog_transmit_curve", "curve"), &Environment::set_fog_transmit_curve);
- ClassDB::bind_method(D_METHOD("get_fog_transmit_curve"), &Environment::get_fog_transmit_curve);
-
- ClassDB::bind_method(D_METHOD("set_fog_height_enabled", "enabled"), &Environment::set_fog_height_enabled);
- ClassDB::bind_method(D_METHOD("is_fog_height_enabled"), &Environment::is_fog_height_enabled);
- ClassDB::bind_method(D_METHOD("set_fog_height_min", "height"), &Environment::set_fog_height_min);
- ClassDB::bind_method(D_METHOD("get_fog_height_min"), &Environment::get_fog_height_min);
- ClassDB::bind_method(D_METHOD("set_fog_height_max", "height"), &Environment::set_fog_height_max);
- ClassDB::bind_method(D_METHOD("get_fog_height_max"), &Environment::get_fog_height_max);
- ClassDB::bind_method(D_METHOD("set_fog_height_curve", "curve"), &Environment::set_fog_height_curve);
- ClassDB::bind_method(D_METHOD("get_fog_height_curve"), &Environment::get_fog_height_curve);
+ ClassDB::bind_method(D_METHOD("set_fog_light_color", "light_color"), &Environment::set_fog_light_color);
+ ClassDB::bind_method(D_METHOD("get_fog_light_color"), &Environment::get_fog_light_color);
+ ClassDB::bind_method(D_METHOD("set_fog_light_energy", "light_energy"), &Environment::set_fog_light_energy);
+ ClassDB::bind_method(D_METHOD("get_fog_light_energy"), &Environment::get_fog_light_energy);
+ ClassDB::bind_method(D_METHOD("set_fog_sun_scatter", "sun_scatter"), &Environment::set_fog_sun_scatter);
+ ClassDB::bind_method(D_METHOD("get_fog_sun_scatter"), &Environment::get_fog_sun_scatter);
+
+ ClassDB::bind_method(D_METHOD("set_fog_density", "density"), &Environment::set_fog_density);
+ ClassDB::bind_method(D_METHOD("get_fog_density"), &Environment::get_fog_density);
+
+ ClassDB::bind_method(D_METHOD("set_fog_height", "height"), &Environment::set_fog_height);
+ ClassDB::bind_method(D_METHOD("get_fog_height"), &Environment::get_fog_height);
+
+ ClassDB::bind_method(D_METHOD("set_fog_height_density", "height_density"), &Environment::set_fog_height_density);
+ ClassDB::bind_method(D_METHOD("get_fog_height_density"), &Environment::get_fog_height_density);
+
+ ClassDB::bind_method(D_METHOD("set_fog_aerial_perspective", "aerial_perspective"), &Environment::set_fog_aerial_perspective);
+ ClassDB::bind_method(D_METHOD("get_fog_aerial_perspective"), &Environment::get_fog_aerial_perspective);
ADD_GROUP("Fog", "fog_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_enabled"), "set_fog_enabled", "is_fog_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_color"), "set_fog_color", "get_fog_color");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_sun_color"), "set_fog_sun_color", "get_fog_sun_color");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_sun_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_fog_sun_amount", "get_fog_sun_amount");
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_depth_enabled"), "set_fog_depth_enabled", "is_fog_depth_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_depth_begin", PROPERTY_HINT_RANGE, "0,4000,0.1"), "set_fog_depth_begin", "get_fog_depth_begin");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_depth_end", PROPERTY_HINT_RANGE, "0,4000,0.1,or_greater"), "set_fog_depth_end", "get_fog_depth_end");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_depth_curve", PROPERTY_HINT_EXP_EASING), "set_fog_depth_curve", "get_fog_depth_curve");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_transmit_enabled"), "set_fog_transmit_enabled", "is_fog_transmit_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_transmit_curve", PROPERTY_HINT_EXP_EASING), "set_fog_transmit_curve", "get_fog_transmit_curve");
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_height_enabled"), "set_fog_height_enabled", "is_fog_height_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_min", PROPERTY_HINT_RANGE, "-4000,4000,0.1,or_lesser,or_greater"), "set_fog_height_min", "get_fog_height_min");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_max", PROPERTY_HINT_RANGE, "-4000,4000,0.1,or_lesser,or_greater"), "set_fog_height_max", "get_fog_height_max");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_curve", PROPERTY_HINT_EXP_EASING), "set_fog_height_curve", "get_fog_height_curve");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_fog_light_color", "get_fog_light_color");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_fog_light_energy", "get_fog_light_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_sun_scatter", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_fog_sun_scatter", "get_fog_sun_scatter");
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,16,0.0001"), "set_fog_density", "get_fog_density");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_aerial_perspective", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_aerial_perspective", "get_fog_aerial_perspective");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater"), "set_fog_height", "get_fog_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "0,128,0.001,or_greater"), "set_fog_height_density", "get_fog_height_density");
+
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_enabled", "enabled"), &Environment::set_volumetric_fog_enabled);
+ ClassDB::bind_method(D_METHOD("is_volumetric_fog_enabled"), &Environment::is_volumetric_fog_enabled);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_light", "color"), &Environment::set_volumetric_fog_light);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_light"), &Environment::get_volumetric_fog_light);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_density", "density"), &Environment::set_volumetric_fog_density);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_density"), &Environment::get_volumetric_fog_density);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_light_energy", "begin"), &Environment::set_volumetric_fog_light_energy);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_light_energy"), &Environment::get_volumetric_fog_light_energy);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_length", "length"), &Environment::set_volumetric_fog_length);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_length"), &Environment::get_volumetric_fog_length);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_detail_spread", "detail_spread"), &Environment::set_volumetric_fog_detail_spread);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_detail_spread"), &Environment::get_volumetric_fog_detail_spread);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_gi_inject", "gi_inject"), &Environment::set_volumetric_fog_gi_inject);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_gi_inject"), &Environment::get_volumetric_fog_gi_inject);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_shadow_filter", "shadow_filter"), &Environment::set_volumetric_fog_shadow_filter);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_shadow_filter"), &Environment::get_volumetric_fog_shadow_filter);
+
+ ADD_GROUP("Volumetric Fog", "volumetric_fog_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_enabled"), "set_volumetric_fog_enabled", "is_volumetric_fog_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_density", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater"), "set_volumetric_fog_density", "get_volumetric_fog_density");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "volumetric_fog_light", PROPERTY_HINT_COLOR_NO_ALPHA), "set_volumetric_fog_light", "get_volumetric_fog_light");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_light_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_light_energy", "get_volumetric_fog_light_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_EXP_RANGE, "0.00,16,0.01"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "0.01,16,0.01"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "volumetric_fog_shadow_filter", PROPERTY_HINT_ENUM, "Disabled,Low,Medium,High"), "set_volumetric_fog_shadow_filter", "get_volumetric_fog_shadow_filter");
// Adjustment
@@ -1287,7 +1335,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
@@ -1331,12 +1379,27 @@ void Environment::_bind_methods() {
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_DISABLED);
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_75_PERCENT);
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_50_PERCENT);
+
+ BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED);
+ BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_LOW);
+ BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM);
+ BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_HIGH);
}
Environment::Environment() {
environment = RS::get_singleton()->environment_create();
set_camera_feed_id(bg_camera_feed_id);
+
+ glow_levels.resize(7);
+ glow_levels.write[0] = 0.0;
+ glow_levels.write[1] = 0.0;
+ glow_levels.write[2] = 1.0;
+ glow_levels.write[3] = 0.0;
+ glow_levels.write[4] = 1.0;
+ glow_levels.write[5] = 0.0;
+ glow_levels.write[6] = 0.0;
+
_update_ambient_light();
_update_tonemap();
_update_ssr();
@@ -1344,10 +1407,8 @@ Environment::Environment() {
_update_sdfgi();
_update_glow();
_update_fog();
- _update_fog_depth();
- _update_fog_height();
_update_adjustment();
-
+ _update_volumetric_fog();
_change_notify();
}
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index f334d22115..106ba92bfe 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -31,7 +31,7 @@
#ifndef ENVIRONMENT_H
#define ENVIRONMENT_H
-#include "core/resource.h"
+#include "core/io/resource.h"
#include "scene/resources/sky.h"
#include "scene/resources/texture.h"
#include "servers/rendering_server.h"
@@ -97,6 +97,13 @@ public:
GLOW_BLEND_MODE_MIX,
};
+ enum VolumetricFogShadowFilter {
+ VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED,
+ VOLUMETRIC_FOG_SHADOW_FILTER_LOW,
+ VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM,
+ VOLUMETRIC_FOG_SHADOW_FILTER_HIGH,
+ };
+
private:
RID environment;
@@ -164,7 +171,8 @@ private:
// Glow
bool glow_enabled = false;
- int glow_levels = (1 << 2) | (1 << 4);
+ Vector<float> glow_levels;
+ bool glow_normalize_levels = false;
float glow_intensity = 0.8;
float glow_strength = 1.0;
float glow_mix = 0.05;
@@ -177,31 +185,34 @@ private:
// Fog
bool fog_enabled = false;
- Color fog_color = Color(0.5, 0.6, 0.7);
- Color fog_sun_color = Color(1.0, 0.9, 0.7);
- float fog_sun_amount = 0.0;
- void _update_fog();
+ Color fog_light_color = Color(0.5, 0.6, 0.7);
+ float fog_light_energy = 1.0;
+ float fog_sun_scatter = 0.0;
+ float fog_density = 0.001;
+ float fog_height = 0.0;
+ float fog_height_density = 0.0; //can be negative to invert effect
+ float fog_aerial_perspective = 0.0;
- bool fog_depth_enabled = true;
- float fog_depth_begin = 10.0;
- float fog_depth_end = 100.0;
- float fog_depth_curve = 1.0;
- bool fog_transmit_enabled = false;
- float fog_transmit_curve = 1.0;
- void _update_fog_depth();
+ void _update_fog();
- bool fog_height_enabled = false;
- float fog_height_min = 10.0;
- float fog_height_max = 0.0;
- float fog_height_curve = 1.0;
- void _update_fog_height();
+ // Volumetric Fog
+ bool volumetric_fog_enabled = false;
+ float volumetric_fog_density = 0.01;
+ Color volumetric_fog_light = Color(0.0, 0.0, 0.0);
+ float volumetric_fog_light_energy = 1.0;
+ float volumetric_fog_length = 64.0;
+ float volumetric_fog_detail_spread = 2.0;
+ VolumetricFogShadowFilter volumetric_fog_shadow_filter = VOLUMETRIC_FOG_SHADOW_FILTER_LOW;
+ float volumetric_fog_gi_inject = 0.0;
+ void _update_volumetric_fog();
// Adjustment
bool adjustment_enabled = false;
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:
@@ -324,8 +335,10 @@ public:
// Glow
void set_glow_enabled(bool p_enabled);
bool is_glow_enabled() const;
- void set_glow_level_enabled(int p_level, bool p_enabled);
- bool is_glow_level_enabled(int p_level) const;
+ void set_glow_level(int p_level, float p_intensity);
+ float get_glow_level(int p_level) const;
+ void set_glow_normalized(bool p_normalized);
+ bool is_glow_normalized() const;
void set_glow_intensity(float p_intensity);
float get_glow_intensity() const;
void set_glow_strength(float p_strength);
@@ -344,36 +357,42 @@ public:
float get_glow_hdr_luminance_cap() const;
// Fog
+
void set_fog_enabled(bool p_enabled);
bool is_fog_enabled() const;
- void set_fog_color(const Color &p_color);
- Color get_fog_color() const;
- void set_fog_sun_color(const Color &p_color);
- Color get_fog_sun_color() const;
- void set_fog_sun_amount(float p_amount);
- float get_fog_sun_amount() const;
-
- void set_fog_depth_enabled(bool p_enabled);
- bool is_fog_depth_enabled() const;
- void set_fog_depth_begin(float p_distance);
- float get_fog_depth_begin() const;
- void set_fog_depth_end(float p_distance);
- float get_fog_depth_end() const;
- void set_fog_depth_curve(float p_curve);
- float get_fog_depth_curve() const;
- void set_fog_transmit_enabled(bool p_enabled);
- bool is_fog_transmit_enabled() const;
- void set_fog_transmit_curve(float p_curve);
- float get_fog_transmit_curve() const;
-
- void set_fog_height_enabled(bool p_enabled);
- bool is_fog_height_enabled() const;
- void set_fog_height_min(float p_distance);
- float get_fog_height_min() const;
- void set_fog_height_max(float p_distance);
- float get_fog_height_max() const;
- void set_fog_height_curve(float p_distance);
- float get_fog_height_curve() const;
+ void set_fog_light_color(const Color &p_light_color);
+ Color get_fog_light_color() const;
+ void set_fog_light_energy(float p_amount);
+ float get_fog_light_energy() const;
+ void set_fog_sun_scatter(float p_amount);
+ float get_fog_sun_scatter() const;
+
+ void set_fog_density(float p_amount);
+ float get_fog_density() const;
+ void set_fog_height(float p_amount);
+ float get_fog_height() const;
+ void set_fog_height_density(float p_amount);
+ float get_fog_height_density() const;
+ void set_fog_aerial_perspective(float p_aerial_perspective);
+ float get_fog_aerial_perspective() const;
+
+ // Volumetric Fog
+ void set_volumetric_fog_enabled(bool p_enable);
+ bool is_volumetric_fog_enabled() const;
+ void set_volumetric_fog_density(float p_density);
+ float get_volumetric_fog_density() const;
+ void set_volumetric_fog_light(Color p_color);
+ Color get_volumetric_fog_light() const;
+ void set_volumetric_fog_light_energy(float p_begin);
+ float get_volumetric_fog_light_energy() const;
+ void set_volumetric_fog_length(float p_length);
+ float get_volumetric_fog_length() const;
+ void set_volumetric_fog_detail_spread(float p_detail_spread);
+ float get_volumetric_fog_detail_spread() const;
+ void set_volumetric_fog_shadow_filter(VolumetricFogShadowFilter p_filter);
+ VolumetricFogShadowFilter get_volumetric_fog_shadow_filter() const;
+ void set_volumetric_fog_gi_inject(float p_gi_inject);
+ float get_volumetric_fog_gi_inject() const;
// Adjustment
void set_adjustment_enabled(bool p_enabled);
@@ -384,8 +403,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();
@@ -399,5 +418,6 @@ VARIANT_ENUM_CAST(Environment::SSAOBlur)
VARIANT_ENUM_CAST(Environment::SDFGICascades)
VARIANT_ENUM_CAST(Environment::SDFGIYScale)
VARIANT_ENUM_CAST(Environment::GlowBlendMode)
+VARIANT_ENUM_CAST(Environment::VolumetricFogShadowFilter)
#endif // ENVIRONMENT_H
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index ccab88a153..7c17610df7 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -31,640 +31,975 @@
#include "font.h"
#include "core/io/resource_loader.h"
-#include "core/method_bind_ext.gen.inc"
-#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));
+
+ 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);
+
+ 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);
+
+ 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);
+
+ ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
+ ClassDB::bind_method(D_METHOD("get_antialiased"), &FontData::get_antialiased);
+
+ 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");
+
+ 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;
+ }
+
+ return false;
+}
- 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;
+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;
}
- draw(p_canvas_item, p_pos + Point2(ofs, 0), p_text, p_modulate, p_width, p_outline_modulate);
+
+ return false;
}
-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;
+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));
- 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;
+ 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));
+ }
+ p_list->push_back(PropertyInfo(Variant::NIL, "script_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+}
- if (p_clip_w >= 0 && (ofs.x + width) > p_clip_w) {
- break; //clip
- }
+RID FontData::get_rid() const {
+ return rid;
+}
- 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;
+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();
+}
- 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);
- }
+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();
}
-void Font::update_changes() {
+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();
}
-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);
+void FontData::set_data_path(const String &p_path) {
+ load_resource(p_path, base_size);
}
-Font::Font() {
+String FontData::get_data_path() const {
+ return path;
}
-/////////////////////////////////////////////////////////////////
+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);
+}
-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
+float FontData::get_ascent(int p_size) const {
+ if (rid == RID()) {
+ return 0.f;
}
- int chars = len / 9;
+ return TS->font_get_ascent(rid, (p_size < 0) ? base_size : p_size);
+}
- 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]);
+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);
}
-Vector<int> BitmapFont::_get_chars() const {
- Vector<int> chars;
+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);
+}
- const CharType *key = nullptr;
+Dictionary FontData::get_feature_list() const {
+ if (rid == RID()) {
+ return Dictionary();
+ }
+ return TS->font_get_feature_list(rid);
+}
- 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);
+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);
+}
+
+void FontData::set_antialiased(bool p_antialiased) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_set_antialiased(rid, p_antialiased);
+ 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);
+bool FontData::get_antialiased() const {
+ if (rid == RID()) {
+ return false;
}
+ return TS->font_get_antialiased(rid);
+}
- return chars;
+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();
}
-void BitmapFont::_set_kernings(const Vector<int> &p_kernings) {
- int len = p_kernings.size();
- ERR_FAIL_COND(len % 3);
- if (!len) {
- return;
+bool FontData::get_distance_field_hint() const {
+ if (rid == RID()) {
+ return false;
}
- const int *r = p_kernings.ptr();
+ return TS->font_get_distance_field_hint(rid);
+}
+
+void FontData::set_hinting(TextServer::Hinting p_hinting) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_set_hinting(rid, p_hinting);
+ emit_changed();
+}
- for (int i = 0; i < len / 3; i++) {
- const int *data = &r[i * 3];
- add_kerning_pair(data[0], data[1], data[2]);
+TextServer::Hinting FontData::get_hinting() const {
+ if (rid == RID()) {
+ return TextServer::HINTING_NONE;
}
+ return TS->font_get_hinting(rid);
+}
+
+void FontData::set_force_autohinter(bool p_enabeld) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_set_force_autohinter(rid, p_enabeld);
+ emit_changed();
}
-Vector<int> BitmapFont::_get_kernings() const {
- Vector<int> kernings;
+bool FontData::get_force_autohinter() const {
+ if (rid == RID()) {
+ return false;
+ }
+ return TS->font_get_force_autohinter(rid);
+}
- 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());
+bool FontData::has_char(char32_t p_char) const {
+ if (rid == RID()) {
+ return false;
}
+ return TS->font_has_char(rid, p_char);
+}
+
+String FontData::get_supported_chars() const {
+ ERR_FAIL_COND_V(rid == RID(), String());
+ return TS->font_get_supported_chars(rid);
+}
- return kernings;
+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);
}
-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);
+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);
+}
+
+bool FontData::has_outline() const {
+ if (rid == RID()) {
+ return false;
}
+ return TS->font_has_outline(rid);
+}
+
+float FontData::get_base_size() const {
+ return base_size;
}
-Vector<Variant> BitmapFont::_get_textures() const {
- Vector<Variant> rtextures;
- for (int i = 0; i < textures.size(); i++) {
- rtextures.push_back(textures[i]);
+bool FontData::is_language_supported(const String &p_language) const {
+ if (rid == RID()) {
+ return false;
}
- return rtextures;
+ return TS->font_is_language_supported(rid, p_language);
}
-Error BitmapFont::create_from_fnt(const String &p_file) {
- //fnt format used by angelcode bmfont
- //http://www.angelcode.com/products/bmfont/
+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();
+}
- FileAccess *f = FileAccess::open(p_file, FileAccess::READ);
+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);
+}
- ERR_FAIL_COND_V_MSG(!f, ERR_FILE_NOT_FOUND, "Can't open font: " + p_file + ".");
+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();
+}
- clear();
+Vector<String> FontData::get_language_support_overrides() const {
+ if (rid == RID()) {
+ return Vector<String>();
+ }
+ return TS->font_get_language_support_overrides(rid);
+}
- while (true) {
- String line = f->get_line();
+bool FontData::is_script_supported(const String &p_script) const {
+ if (rid == RID()) {
+ return false;
+ }
+ return TS->font_is_script_supported(rid, p_script);
+}
- int delimiter = line.find(" ");
- String type = line.substr(0, delimiter);
- int pos = delimiter + 1;
- Map<String, String> keys;
+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();
+}
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
+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);
+}
- 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();
- }
+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();
+}
- value = line.substr(eq + 1, end - eq);
+Vector<String> FontData::get_script_support_overrides() const {
+ if (rid == RID()) {
+ return Vector<String>();
+ }
+ return TS->font_get_script_support_overrides(rid);
+}
- pos = end;
- }
+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);
+}
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
+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);
+}
- keys[key] = value;
- }
+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);
+}
- 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());
- }
+FontData::FontData() {}
- } 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") {
- CharType idx = 0;
- if (keys.has("id")) {
- idx = keys["id"].to_int();
- }
+FontData::FontData(const String &p_filename, int p_base_size) {
+ load_resource(p_filename, p_base_size);
+}
- Rect2 rect;
+FontData::FontData(const PackedByteArray &p_data, const String &p_type, int p_base_size) {
+ _load_memory(p_data, p_type, p_base_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();
- }
+FontData::~FontData() {
+ if (rid != RID()) {
+ TS->free(rid);
+ }
+}
- Point2 ofs;
+/*************************************************************************/
- if (keys.has("xoffset")) {
- ofs.x = keys["xoffset"].to_int();
- }
- if (keys.has("yoffset")) {
- ofs.y = keys["yoffset"].to_int();
- }
+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 texture = 0;
- if (keys.has("page")) {
- texture = keys["page"].to_int();
- }
- int advance = -1;
- if (keys.has("xadvance")) {
- advance = keys["xadvance"].to_int();
- }
+ 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));
- add_char(idx, texture, rect, ofs, advance);
+ 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));
- } else if (type == "kerning") {
- CharType first = 0, second = 0;
- int k = 0;
+ ClassDB::bind_method(D_METHOD("get_spacing", "type"), &Font::get_spacing);
+ ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &Font::set_spacing);
- 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();
- }
+ 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));
- add_kerning_pair(first, second, -k);
- }
+ 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));
- if (f->eof_reached()) {
- break;
- }
- }
+ ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char);
+ ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars);
- memdelete(f);
+ 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)));
- return OK;
-}
+ ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
-void BitmapFont::set_height(float p_height) {
- height = p_height;
-}
+ 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);
-float BitmapFont::get_height() const {
- return height;
+ BIND_ENUM_CONSTANT(SPACING_TOP);
+ BIND_ENUM_CONSTANT(SPACING_BOTTOM);
}
-void BitmapFont::set_ascent(float p_ascent) {
- ascent = p_ascent;
-}
+void Font::_data_changed() {
+ cache.clear();
+ cache_wrap.clear();
-float BitmapFont::get_ascent() const {
- return ascent;
+ emit_changed();
+ _change_notify();
}
-float BitmapFont::get_descent() const {
- return height - ascent;
+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;
}
-float BitmapFont::get_underline_position() const {
- return 2;
+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;
+ }
+ }
+
+ return false;
}
-float BitmapFont::get_underline_thickness() const {
- return 1;
+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"));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(data.size()), PROPERTY_HINT_RESOURCE_TYPE, "FontData"));
}
-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);
+void Font::add_data(const Ref<FontData> &p_data) {
+ ERR_FAIL_COND(p_data.is_null());
+ data.push_back(p_data);
+
+ if (data[data.size() - 1].is_valid()) {
+ data.write[data.size() - 1]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+
+ cache.clear();
+ cache_wrap.clear();
+
+ emit_changed();
+ _change_notify();
}
-int BitmapFont::get_texture_count() const {
- return textures.size();
-};
+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());
-Ref<Texture2D> BitmapFont::get_texture(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, textures.size(), Ref<Texture2D>());
- return textures[p_idx];
-};
+ if (data[p_idx].is_valid()) {
+ data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed));
+ }
+
+ data.write[p_idx] = p_data;
-int BitmapFont::get_character_count() const {
- return char_map.size();
-};
+ if (data[p_idx].is_valid()) {
+ data.write[p_idx]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
-Vector<CharType> BitmapFont::get_char_keys() const {
- Vector<CharType> chars;
- chars.resize(char_map.size());
- const CharType *ct = nullptr;
- int count = 0;
- while ((ct = char_map.next(ct))) {
- chars.write[count++] = *ct;
- };
+ cache.clear();
+ cache_wrap.clear();
- return chars;
-};
+ emit_changed();
+ _change_notify();
+}
+
+int Font::get_data_count() const {
+ return data.size();
+}
-BitmapFont::Character BitmapFont::get_character(CharType p_char) const {
- if (!char_map.has(p_char)) {
- ERR_FAIL_V(Character());
- };
+Ref<FontData> Font::get_data(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, data.size(), Ref<FontData>());
+ return data[p_idx];
+}
- return char_map[p_char];
-};
+void Font::remove_data(int p_idx) {
+ ERR_FAIL_INDEX(p_idx, data.size());
-void BitmapFont::add_char(CharType 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;
+ if (data[p_idx].is_valid()) {
+ data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed));
}
- 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;
+ data.remove(p_idx);
+
+ cache.clear();
+ cache_wrap.clear();
- char_map[p_char] = c;
+ emit_changed();
+ _change_notify();
}
-void BitmapFont::add_kerning_pair(CharType p_A, CharType p_B, int p_kerning) {
- KerningPairKey kpk;
- kpk.A = p_A;
- kpk.B = p_B;
+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;
+}
- if (p_kerning == 0 && kerning_map.has(kpk)) {
- kerning_map.erase(kpk);
- } else {
- kerning_map[kpk] = p_kerning;
+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));
}
+ return ret + spacing_top + spacing_bottom;
}
-Vector<BitmapFont::KerningPairKey> BitmapFont::get_kerning_pair_keys() const {
- Vector<BitmapFont::KerningPairKey> ret;
- ret.resize(kerning_map.size());
- int i = 0;
+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;
+}
- for (Map<KerningPairKey, int>::Element *E = kerning_map.front(); E; E = E->next()) {
- ret.write[i++] = E->key();
+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;
+}
+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 BitmapFont::get_kerning_pair(CharType p_A, CharType p_B) const {
- KerningPairKey kpk;
- kpk.A = p_A;
- kpk.B = p_B;
+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;
+}
- const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk);
- if (E) {
- return E->get();
+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 0;
}
-void BitmapFont::set_distance_field_hint(bool p_distance_field) {
- distance_field_hint = p_distance_field;
+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;
+ }
+
emit_changed();
+ _change_notify();
}
-bool BitmapFont::is_distance_field_hint() const {
- return distance_field_hint;
-}
+// Drawing string and string sizes, cached.
-void BitmapFont::clear() {
- height = 1;
- ascent = 0;
- char_map.clear();
- textures.clear();
- kerning_map.clear();
- distance_field_hint = false;
-}
+Size2 Font::get_string_size(const String &p_text, int p_size) const {
+ ERR_FAIL_COND_V(data.empty(), Size2());
-Size2 Font::get_string_size(const String &p_string) const {
- float w = 0;
+ uint64_t hash = p_text.hash64();
+ hash = hash_djb2_one_64(p_size, hash);
- int l = p_string.length();
- if (l == 0) {
- return Size2(0, get_height());
+ 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);
}
- const CharType *sptr = &p_string[0];
-
- for (int i = 0; i < l; i++) {
- w += get_char_size(sptr[i], sptr[i + 1]).width;
+ 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);
}
-
- return Size2(w, get_height());
}
-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()));
+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.empty(), Size2());
+
+ uint64_t hash = p_text.hash64();
+ hash = hash_djb2_one_64(p_size, hash);
- int l = p_string.length();
- if (l == 0) {
- return Size2(p_width, get_height());
+ 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);
}
- 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;
- }
+ 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 Size2(p_width, h);
+ return ret;
}
-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::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);
}
- fallback = p_fallback;
-}
+ 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();
+ }
+
+ buffer->set_width(p_width);
+ buffer->set_align(p_align);
-Ref<BitmapFont> BitmapFont::get_fallback() const {
- return fallback;
+ 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);
}
-float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) 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->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, p_outline);
- }
- return 0;
- }
+ 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);
- 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, RID(), RID(), Color(1, 1, 1, 1), false);
+ 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);
}
- return get_char_size(p_char, p_next).width;
-}
+ lines_buffer->set_align(p_align);
-Size2 BitmapFont::get_char_size(CharType p_char, CharType p_next) const {
- const Character *c = char_map.getptr(p_char);
-
- if (!c) {
- if (fallback.is_valid()) {
- return fallback->get_char_size(p_char, p_next);
+ 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);
}
- return Size2();
- }
- Size2 ret(c->advance, c->rect.size.y);
+ 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();
+}
+
+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_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);
+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("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));
+void Font::update_changes() {
+ emit_changed();
+}
- ClassDB::bind_method(D_METHOD("get_texture_count"), &BitmapFont::get_texture_count);
- ClassDB::bind_method(D_METHOD("get_texture", "idx"), &BitmapFont::get_texture);
+Font::Font() {
+ cache.set_capacity(128);
+ cache_wrap.set_capacity(32);
+}
- ClassDB::bind_method(D_METHOD("set_distance_field_hint", "enable"), &BitmapFont::set_distance_field_hint);
+Font::~Font() {
+ cache.clear();
+ cache_wrap.clear();
+}
- ClassDB::bind_method(D_METHOD("clear"), &BitmapFont::clear);
+/*************************************************************************/
- ClassDB::bind_method(D_METHOD("_set_chars"), &BitmapFont::_set_chars);
- ClassDB::bind_method(D_METHOD("_get_chars"), &BitmapFont::_get_chars);
+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_kernings"), &BitmapFont::_set_kernings);
- ClassDB::bind_method(D_METHOD("_get_kernings"), &BitmapFont::_get_kernings);
+ Ref<FontData> dfont;
+ dfont.instance();
+ dfont->load_resource(p_path);
- ClassDB::bind_method(D_METHOD("_set_textures"), &BitmapFont::_set_textures);
- ClassDB::bind_method(D_METHOD("_get_textures"), &BitmapFont::_get_textures);
+ if (r_error) {
+ *r_error = OK;
+ }
- ClassDB::bind_method(D_METHOD("set_fallback", "fallback"), &BitmapFont::set_fallback);
- ClassDB::bind_method(D_METHOD("get_fallback"), &BitmapFont::get_fallback);
+ 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 == "DynacmicFontData") {
+ 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 e6b296800b..bc82a6fabf 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -31,179 +31,190 @@
#ifndef FONT_H
#define FONT_H
-#include "core/map.h"
-#include "core/resource.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(CharType p_char, CharType 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;
- virtual bool has_outline() const { return false; }
- virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const = 0;
+ float get_underline_position(int p_size) const;
+ float get_underline_thickness(int p_size) const;
- void update_changes();
- Font();
-};
+ void set_antialiased(bool p_antialiased);
+ bool get_antialiased() 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;
- CharType chr;
- CharType next;
- Color modulate;
- };
+ void set_distance_field_hint(bool p_distance_field);
+ bool get_distance_field_hint() const;
- Vector<PendingDraw> pending_draws;
+ void set_force_autohinter(bool p_enabeld);
+ bool get_force_autohinter() 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, CharType p_char, CharType 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_hinting(TextServer::Hinting p_hinting);
+ TextServer::Hinting get_hinting() const;
-class BitmapFont : public Font {
- GDCLASS(BitmapFont, Font);
- RES_BASE_EXTENSION("font");
+ bool has_char(char32_t p_char) const;
+ String get_supported_chars() const;
- Vector<Ref<Texture2D>> textures;
+ 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;
-public:
- struct Character {
- int texture_idx;
- Rect2 rect;
- float v_align;
- float h_align;
- float advance;
-
- Character() {
- texture_idx = 0;
- v_align = 0;
- }
- };
+ 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;
+
+ uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector = 0x0000) const;
+
+ 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();
+};
- struct KerningPairKey {
- union {
- struct {
- uint32_t A, B;
- };
+/*************************************************************************/
+
+class TextLine;
+class TextParagraph;
- uint64_t pair;
- };
+class Font : public Resource {
+ GDCLASS(Font, Resource);
- _FORCE_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; }
+public:
+ enum SpacingType {
+ SPACING_TOP,
+ SPACING_BOTTOM
};
private:
- HashMap<CharType, 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(CharType 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<CharType> get_char_keys() const;
- Character get_character(CharType 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(CharType p_A, CharType p_B, int p_kerning);
- int get_kerning_pair(CharType p_A, CharType 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(CharType p_char, CharType 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, CharType p_char, CharType 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 d271c906ff..6b41b97e45 100644
--- a/scene/resources/gradient.cpp
+++ b/scene/resources/gradient.cpp
@@ -32,14 +32,8 @@
#include "core/core_string_names.h"
-//setter and getter names for property serialization
-#define COLOR_RAMP_GET_OFFSETS "get_offsets"
-#define COLOR_RAMP_GET_COLORS "get_colors"
-#define COLOR_RAMP_SET_OFFSETS "set_offsets"
-#define COLOR_RAMP_SET_COLORS "set_colors"
-
Gradient::Gradient() {
- //Set initial color ramp transition from black to white
+ //Set initial gradient transition from black to white
points.resize(2);
points.write[0].color = Color(0, 0, 0, 1);
points.write[0].offset = 0;
@@ -65,14 +59,14 @@ void Gradient::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_point_count"), &Gradient::get_points_count);
- ClassDB::bind_method(D_METHOD(COLOR_RAMP_SET_OFFSETS, "offsets"), &Gradient::set_offsets);
- ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_OFFSETS), &Gradient::get_offsets);
+ ClassDB::bind_method(D_METHOD("set_offsets", "offsets"), &Gradient::set_offsets);
+ ClassDB::bind_method(D_METHOD("get_offsets"), &Gradient::get_offsets);
- ClassDB::bind_method(D_METHOD(COLOR_RAMP_SET_COLORS, "colors"), &Gradient::set_colors);
- ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_COLORS), &Gradient::get_colors);
+ ClassDB::bind_method(D_METHOD("set_colors", "colors"), &Gradient::set_colors);
+ ClassDB::bind_method(D_METHOD("get_colors"), &Gradient::get_colors);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), COLOR_RAMP_SET_OFFSETS, COLOR_RAMP_GET_OFFSETS);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), COLOR_RAMP_SET_COLORS, COLOR_RAMP_GET_COLORS);
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), "set_offsets", "get_offsets");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), "set_colors", "get_colors");
}
Vector<float> Gradient::get_offsets() const {
@@ -129,7 +123,7 @@ void Gradient::add_point(float p_offset, const Color &p_color) {
void Gradient::remove_point(int p_index) {
ERR_FAIL_INDEX(p_index, points.size());
- ERR_FAIL_COND(points.size() <= 2);
+ ERR_FAIL_COND(points.size() <= 1);
points.remove(p_index);
emit_signal(CoreStringNames::get_singleton()->changed);
}
@@ -141,32 +135,29 @@ void Gradient::set_points(Vector<Gradient::Point> &p_points) {
}
void Gradient::set_offset(int pos, const float offset) {
- ERR_FAIL_COND(pos < 0);
- if (points.size() <= pos) {
- points.resize(pos + 1);
- }
+ ERR_FAIL_INDEX(pos, points.size());
+ _update_sorting();
points.write[pos].offset = offset;
is_sorted = false;
emit_signal(CoreStringNames::get_singleton()->changed);
}
-float Gradient::get_offset(int pos) const {
+float Gradient::get_offset(int pos) {
ERR_FAIL_INDEX_V(pos, points.size(), 0.0);
+ _update_sorting();
return points[pos].offset;
}
void Gradient::set_color(int pos, const Color &color) {
- ERR_FAIL_COND(pos < 0);
- if (points.size() <= pos) {
- points.resize(pos + 1);
- is_sorted = false;
- }
+ ERR_FAIL_INDEX(pos, points.size());
+ _update_sorting();
points.write[pos].color = color;
emit_signal(CoreStringNames::get_singleton()->changed);
}
-Color Gradient::get_color(int pos) const {
+Color Gradient::get_color(int pos) {
ERR_FAIL_INDEX_V(pos, points.size(), Color());
+ _update_sorting();
return points[pos].color;
}
diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h
index d40dcc8d44..e839909770 100644
--- a/scene/resources/gradient.h
+++ b/scene/resources/gradient.h
@@ -31,7 +31,7 @@
#ifndef GRADIENT_H
#define GRADIENT_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class Gradient : public Resource {
GDCLASS(Gradient, Resource);
@@ -49,6 +49,12 @@ public:
private:
Vector<Point> points;
bool is_sorted;
+ _FORCE_INLINE_ void _update_sorting() {
+ if (!is_sorted) {
+ points.sort();
+ is_sorted = true;
+ }
+ }
protected:
static void _bind_methods();
@@ -64,10 +70,10 @@ public:
Vector<Point> &get_points();
void set_offset(int pos, const float offset);
- float get_offset(int pos) const;
+ float get_offset(int pos);
void set_color(int pos, const Color &color);
- Color get_color(int pos) const;
+ Color get_color(int pos);
void set_offsets(const Vector<float> &p_offsets);
Vector<float> get_offsets() const;
@@ -80,10 +86,7 @@ public:
return Color(0, 0, 0, 1);
}
- if (!is_sorted) {
- points.sort();
- is_sorted = true;
- }
+ _update_sorting();
//binary search
int low = 0;
diff --git a/scene/resources/height_map_shape_3d.cpp b/scene/resources/height_map_shape_3d.cpp
index e112c6b436..2ae47bcf3c 100644
--- a/scene/resources/height_map_shape_3d.cpp
+++ b/scene/resources/height_map_shape_3d.cpp
@@ -31,7 +31,7 @@
#include "height_map_shape_3d.h"
#include "servers/physics_server_3d.h"
-Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() {
+Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const {
Vector<Vector3> points;
if ((map_width != 0) && (map_depth != 0)) {
diff --git a/scene/resources/height_map_shape_3d.h b/scene/resources/height_map_shape_3d.h
index c5715a57b1..9ee8b49689 100644
--- a/scene/resources/height_map_shape_3d.h
+++ b/scene/resources/height_map_shape_3d.h
@@ -54,7 +54,7 @@ public:
void set_map_data(PackedFloat32Array p_new);
PackedFloat32Array get_map_data() const;
- virtual Vector<Vector3> get_debug_mesh_lines() override;
+ virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
HeightMapShape3D();
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 1e95a35726..6e08af23f5 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -30,7 +30,7 @@
#include "material.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
@@ -324,7 +324,6 @@ void BaseMaterial3D::init_shaders() {
shader_names->rim_texture_channel = "rim_texture_channel";
shader_names->heightmap_texture_channel = "heightmap_texture_channel";
shader_names->refraction_texture_channel = "refraction_texture_channel";
- shader_names->alpha_scissor_threshold = "alpha_scissor_threshold";
shader_names->transmittance_color = "transmittance_color";
shader_names->transmittance_curve = "transmittance_curve";
@@ -349,6 +348,12 @@ void BaseMaterial3D::init_shaders() {
shader_names->texture_names[TEXTURE_DETAIL_ALBEDO] = "texture_detail_albedo";
shader_names->texture_names[TEXTURE_DETAIL_NORMAL] = "texture_detail_normal";
shader_names->texture_names[TEXTURE_ORM] = "texture_orm";
+
+ shader_names->alpha_scissor_threshold = "alpha_scissor_threshold";
+ shader_names->alpha_hash_scale = "alpha_hash_scale";
+
+ shader_names->alpha_antialiasing_edge = "alpha_antialiasing_edge";
+ shader_names->albedo_texture_size = "albedo_texture_size";
}
Ref<StandardMaterial3D> BaseMaterial3D::materials_for_2d[BaseMaterial3D::MAX_MATERIALS_FOR_2D];
@@ -435,6 +440,8 @@ void BaseMaterial3D::_update_shader() {
case BLEND_MODE_MUL:
code += "blend_mul";
break;
+ case BLEND_MODE_MAX:
+ break; // Internal value, skip.
}
DepthDrawMode ddm = depth_draw_mode;
@@ -452,10 +459,8 @@ void BaseMaterial3D::_update_shader() {
case DEPTH_DRAW_DISABLED:
code += ",depth_draw_never";
break;
- }
-
- if (transparency == TRANSPARENCY_ALPHA_DEPTH_PRE_PASS) {
- code += ",depth_prepass_alpha";
+ case DEPTH_DRAW_MAX:
+ break; // Internal value, skip.
}
switch (cull_mode) {
@@ -468,6 +473,8 @@ void BaseMaterial3D::_update_shader() {
case CULL_DISABLED:
code += ",cull_disabled";
break;
+ case CULL_MAX:
+ break; // Internal value, skip.
}
switch (diffuse_mode) {
case DIFFUSE_BURLEY:
@@ -485,6 +492,8 @@ void BaseMaterial3D::_update_shader() {
case DIFFUSE_TOON:
code += ",diffuse_toon";
break;
+ case DIFFUSE_MAX:
+ break; // Internal value, skip.
}
switch (specular_mode) {
case SPECULAR_SCHLICK_GGX:
@@ -502,6 +511,8 @@ void BaseMaterial3D::_update_shader() {
case SPECULAR_DISABLED:
code += ",specular_disabled";
break;
+ case SPECULAR_MAX:
+ break; // Internal value, skip.
}
if (features[FEATURE_SUBSURFACE_SCATTERING] && flags[FLAG_SUBSURFACE_MODE_SKIN]) {
code += ",sss_mode_skin";
@@ -525,6 +536,23 @@ void BaseMaterial3D::_update_shader() {
if (flags[FLAG_USE_SHADOW_TO_OPACITY]) {
code += ",shadow_to_opacity";
}
+
+ if (transparency == TRANSPARENCY_ALPHA_DEPTH_PRE_PASS) {
+ code += ",depth_prepass_alpha";
+ }
+
+ // Although its technically possible to do alpha antialiasing without using alpha hash or alpha scissor,
+ // it is restricted in the base material because it has no use, and abusing it with regular Alpha blending can
+ // saturate the MSAA mask
+ if (transparency == TRANSPARENCY_ALPHA_HASH || transparency == TRANSPARENCY_ALPHA_SCISSOR) {
+ // alpha antialiasing is only useful in ALPHA_HASH or ALPHA_SCISSOR
+ if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE) {
+ code += ",alpha_to_coverage";
+ } else if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE) {
+ code += ",alpha_to_coverage_and_one";
+ }
+ }
+
code += ";\n";
code += "uniform vec4 albedo : hint_color;\n";
@@ -541,8 +569,18 @@ void BaseMaterial3D::_update_shader() {
code += "uniform float distance_fade_max;\n";
}
+ // alpha scissor is only valid if there is not antialiasing edge
+ // alpha hash is valid whenever, but not with alpha scissor
if (transparency == TRANSPARENCY_ALPHA_SCISSOR) {
code += "uniform float alpha_scissor_threshold;\n";
+ } else if (transparency == TRANSPARENCY_ALPHA_HASH) {
+ code += "uniform float alpha_hash_scale;\n";
+ }
+ // if alpha antialiasing isn't off, add in the edge variable
+ if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF &&
+ (transparency == TRANSPARENCY_ALPHA_SCISSOR || transparency == TRANSPARENCY_ALPHA_HASH)) {
+ code += "uniform float alpha_antialiasing_edge;\n";
+ code += "uniform ivec2 albedo_texture_size;\n";
}
code += "uniform float point_size : hint_range(0,128);\n";
@@ -568,6 +606,8 @@ void BaseMaterial3D::_update_shader() {
case TEXTURE_CHANNEL_GRAYSCALE: {
code += "uniform sampler2D texture_roughness : hint_roughness_gray," + texfilter_str + ";\n";
} break;
+ case TEXTURE_CHANNEL_MAX:
+ break; // Internal value, skip.
}
code += "uniform float specular;\n";
@@ -731,6 +771,8 @@ void BaseMaterial3D::_update_shader() {
code += "\tUV /= vec2(h_frames, v_frames);\n";
code += "\tUV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n";
} break;
+ case BILLBOARD_MAX:
+ break; // Internal value, skip.
}
if (flags[FLAG_FIXED_SIZE]) {
@@ -903,6 +945,8 @@ void BaseMaterial3D::_update_shader() {
case TEXTURE_CHANNEL_GRAYSCALE: {
code += "\tvec4 roughness_texture_channel = vec4(0.333333,0.333333,0.333333,0.0);\n";
} break;
+ case TEXTURE_CHANNEL_MAX:
+ break; // Internal value, skip.
}
if (flags[FLAG_UV1_USE_TRIPLANAR]) {
@@ -970,10 +1014,17 @@ void BaseMaterial3D::_update_shader() {
code += "\tALBEDO *= 1.0 - ref_amount;\n";
code += "\tALPHA = 1.0;\n";
- } else if (transparency == TRANSPARENCY_ALPHA || transparency == TRANSPARENCY_ALPHA_DEPTH_PRE_PASS || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) {
+ } else if (transparency != TRANSPARENCY_DISABLED || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) {
code += "\tALPHA = albedo.a * albedo_tex.a;\n";
+ }
+ if (transparency == TRANSPARENCY_ALPHA_HASH) {
+ code += "\tALPHA_HASH_SCALE = alpha_hash_scale;\n";
} else if (transparency == TRANSPARENCY_ALPHA_SCISSOR) {
- code += "\tif (albedo.a * albedo_tex.a < alpha_scissor_threshold) discard;\n";
+ code += "\tALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold;\n";
+ }
+ if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF && (transparency == TRANSPARENCY_ALPHA_HASH || transparency == TRANSPARENCY_ALPHA_SCISSOR)) {
+ code += "\tALPHA_ANTIALIASING_EDGE = alpha_antialiasing_edge;\n";
+ code += "\tALPHA_TEXTURE_COORDINATE = UV * vec2(albedo_texture_size);\n";
}
if (proximity_fade_enabled) {
@@ -1143,6 +1194,8 @@ void BaseMaterial3D::_update_shader() {
case BLEND_MODE_MUL: {
code += "\tvec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb*detail_tex.rgb,detail_tex.a);\n";
} break;
+ case BLEND_MODE_MAX:
+ break; // Internal value, skip.
}
code += "\tvec3 detail_norm = mix(NORMALMAP,detail_norm_tex.rgb,detail_tex.a);\n";
@@ -1424,6 +1477,20 @@ BaseMaterial3D::Transparency BaseMaterial3D::get_transparency() const {
return transparency;
}
+void BaseMaterial3D::set_alpha_antialiasing(AlphaAntiAliasing p_alpha_aa) {
+ if (alpha_antialiasing_mode == p_alpha_aa) {
+ return;
+ }
+
+ alpha_antialiasing_mode = p_alpha_aa;
+ _queue_shader_change();
+ _change_notify();
+}
+
+BaseMaterial3D::AlphaAntiAliasing BaseMaterial3D::get_alpha_antialiasing() const {
+ return alpha_antialiasing_mode;
+}
+
void BaseMaterial3D::set_shading_mode(ShadingMode p_shading_mode) {
if (shading_mode == p_shading_mode) {
return;
@@ -1530,6 +1597,10 @@ void BaseMaterial3D::set_texture(TextureParam p_param, const Ref<Texture2D> &p_t
textures[p_param] = p_texture;
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
RS::get_singleton()->material_set_param(_get_material(), shader_names->texture_names[p_param], rid);
+ if (p_texture.is_valid() && p_param == TEXTURE_ALBEDO) {
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->albedo_texture_size,
+ Vector2i(p_texture->get_width(), p_texture->get_height()));
+ }
_change_notify();
_queue_shader_change();
}
@@ -1605,10 +1676,34 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}
+ // you can only enable anti-aliasing (in mataerials) on alpha scissor and alpha hash
+ const bool can_select_aa = (transparency == TRANSPARENCY_ALPHA_SCISSOR || transparency == TRANSPARENCY_ALPHA_HASH);
+ // alpha anti aliasiasing is only enabled when you can select aa
+ const bool alpha_aa_enabled = (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF) && can_select_aa;
+
+ // alpha scissor slider isn't needed when alpha antialiasing is enabled
if (property.name == "alpha_scissor_threshold" && transparency != TRANSPARENCY_ALPHA_SCISSOR) {
property.usage = 0;
}
+ // alpha hash scale slider is only needed if transparency is alpha hash
+ if (property.name == "alpha_hash_scale" && transparency != TRANSPARENCY_ALPHA_HASH) {
+ property.usage = 0;
+ }
+
+ if (property.name == "alpha_antialiasing_mode" && !can_select_aa) {
+ property.usage = 0;
+ }
+
+ // we cant choose an antialiasing mode if alpha isnt possible
+ if (property.name == "alpha_antialiasing_edge" && !alpha_aa_enabled) {
+ property.usage = 0;
+ }
+
+ if (property.name == "blend_mode" && alpha_aa_enabled) {
+ property.usage = 0;
+ }
+
if ((property.name == "heightmap_min_layers" || property.name == "heightmap_max_layers") && !deep_parallax) {
property.usage = 0;
}
@@ -1845,6 +1940,24 @@ float BaseMaterial3D::get_alpha_scissor_threshold() const {
return alpha_scissor_threshold;
}
+void BaseMaterial3D::set_alpha_hash_scale(float p_scale) {
+ alpha_hash_scale = p_scale;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_hash_scale, p_scale);
+}
+
+float BaseMaterial3D::get_alpha_hash_scale() const {
+ return alpha_hash_scale;
+}
+
+void BaseMaterial3D::set_alpha_antialiasing_edge(float p_edge) {
+ alpha_antialiasing_edge = p_edge;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_antialiasing_edge, p_edge);
+}
+
+float BaseMaterial3D::get_alpha_antialiasing_edge() const {
+ return alpha_antialiasing_edge;
+}
+
void BaseMaterial3D::set_grow(float p_grow) {
grow = p_grow;
RS::get_singleton()->material_set_param(_get_material(), shader_names->grow, p_grow);
@@ -2033,6 +2146,12 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transparency", "transparency"), &BaseMaterial3D::set_transparency);
ClassDB::bind_method(D_METHOD("get_transparency"), &BaseMaterial3D::get_transparency);
+ ClassDB::bind_method(D_METHOD("set_alpha_antialiasing", "alpha_aa"), &BaseMaterial3D::set_alpha_antialiasing);
+ ClassDB::bind_method(D_METHOD("get_alpha_antialiasing"), &BaseMaterial3D::get_alpha_antialiasing);
+
+ ClassDB::bind_method(D_METHOD("set_alpha_antialiasing_edge", "edge"), &BaseMaterial3D::set_alpha_antialiasing_edge);
+ ClassDB::bind_method(D_METHOD("get_alpha_antialiasing_edge"), &BaseMaterial3D::get_alpha_antialiasing_edge);
+
ClassDB::bind_method(D_METHOD("set_shading_mode", "shading_mode"), &BaseMaterial3D::set_shading_mode);
ClassDB::bind_method(D_METHOD("get_shading_mode"), &BaseMaterial3D::get_shading_mode);
@@ -2186,6 +2305,9 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_alpha_scissor_threshold", "threshold"), &BaseMaterial3D::set_alpha_scissor_threshold);
ClassDB::bind_method(D_METHOD("get_alpha_scissor_threshold"), &BaseMaterial3D::get_alpha_scissor_threshold);
+ ClassDB::bind_method(D_METHOD("set_alpha_hash_scale", "threshold"), &BaseMaterial3D::set_alpha_hash_scale);
+ ClassDB::bind_method(D_METHOD("get_alpha_hash_scale"), &BaseMaterial3D::get_alpha_hash_scale);
+
ClassDB::bind_method(D_METHOD("set_grow_enabled", "enable"), &BaseMaterial3D::set_grow_enabled);
ClassDB::bind_method(D_METHOD("is_grow_enabled"), &BaseMaterial3D::is_grow_enabled);
@@ -2217,8 +2339,11 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_distance_fade_min_distance"), &BaseMaterial3D::get_distance_fade_min_distance);
ADD_GROUP("Transparency", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,AlphaScissor,DepthPrePass"), "set_transparency", "get_transparency");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,Alpha Scissor,Alpha Hash,Depth PrePass"), "set_transparency", "get_transparency");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_alpha_hash_scale", "get_alpha_hash_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode", PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip"), "set_alpha_antialiasing", "get_alpha_antialiasing");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_antialiasing_edge", "get_alpha_antialiasing_edge");
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul"), "set_blend_mode", "get_blend_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mode", PROPERTY_HINT_ENUM, "Back,Front,Disabled"), "set_cull_mode", "get_cull_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_draw_mode", PROPERTY_HINT_ENUM, "Opaque Only,Always,Never"), "set_depth_draw_mode", "get_depth_draw_mode");
@@ -2292,10 +2417,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);
@@ -2414,6 +2539,7 @@ void BaseMaterial3D::_bind_methods() {
BIND_ENUM_CONSTANT(TRANSPARENCY_DISABLED);
BIND_ENUM_CONSTANT(TRANSPARENCY_ALPHA);
BIND_ENUM_CONSTANT(TRANSPARENCY_ALPHA_SCISSOR);
+ BIND_ENUM_CONSTANT(TRANSPARENCY_ALPHA_HASH);
BIND_ENUM_CONSTANT(TRANSPARENCY_ALPHA_DEPTH_PRE_PASS);
BIND_ENUM_CONSTANT(TRANSPARENCY_MAX);
@@ -2441,6 +2567,10 @@ void BaseMaterial3D::_bind_methods() {
BIND_ENUM_CONSTANT(BLEND_MODE_SUB);
BIND_ENUM_CONSTANT(BLEND_MODE_MUL);
+ BIND_ENUM_CONSTANT(ALPHA_ANTIALIASING_OFF);
+ BIND_ENUM_CONSTANT(ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
+ BIND_ENUM_CONSTANT(ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
+
BIND_ENUM_CONSTANT(DEPTH_DRAW_OPAQUE_ONLY);
BIND_ENUM_CONSTANT(DEPTH_DRAW_ALWAYS);
BIND_ENUM_CONSTANT(DEPTH_DRAW_DISABLED);
@@ -2506,8 +2636,9 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
element(this) {
orm = p_orm;
// Initialize to the same values as the shader
- transparency = TRANSPARENCY_DISABLED;
shading_mode = SHADING_MODE_PER_PIXEL;
+ transparency = TRANSPARENCY_DISABLED;
+ alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
set_albedo(Color(1.0, 1.0, 1.0, 1.0));
set_specular(0.5);
set_roughness(1.0);
@@ -2539,9 +2670,14 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
set_particles_anim_h_frames(1);
set_particles_anim_v_frames(1);
set_particles_anim_loop(false);
- set_alpha_scissor_threshold(0.98);
emission_op = EMISSION_OP_ADD;
+ set_transparency(TRANSPARENCY_DISABLED);
+ set_alpha_antialiasing(ALPHA_ANTIALIASING_OFF);
+ set_alpha_scissor_threshold(0.05);
+ set_alpha_hash_scale(1.0);
+ set_alpha_antialiasing_edge(0.3);
+
proximity_fade_enabled = false;
distance_fade = DISTANCE_FADE_DISABLED;
set_proximity_fade_distance(1);
@@ -2582,10 +2718,8 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
features[i] = false;
}
- current_key.key0 = 0;
- current_key.key1 = 0;
- current_key.invalid_key = 1;
texture_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS;
+
_queue_shader_change();
}
@@ -2633,6 +2767,12 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value)
set_transparency(TRANSPARENCY_ALPHA_SCISSOR);
}
return true;
+ } else if (p_name == "params_use_alpha_hash") {
+ bool use_hash = p_value;
+ if (use_hash) {
+ set_transparency(TRANSPARENCY_ALPHA_HASH);
+ }
+ return true;
} else if (p_name == "params_depth_draw_mode") {
int mode = p_value;
if (mode == 3) {
@@ -2667,6 +2807,8 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value)
{ "params_grow", "grow" },
{ "params_grow_amount", "grow_amount" },
{ "params_alpha_scissor_threshold", "alpha_scissor_threshold" },
+ { "params_alpha_hash_scale", "alpha_hash_scale" },
+ { "params_alpha_antialiasing_edge", "alpha_antialiasing_edge" },
{ "depth_scale", "heightmap_scale" },
{ "depth_deep_parallax", "heightmap_deep_parallax" },
diff --git a/scene/resources/material.h b/scene/resources/material.h
index b5bdd77eb5..caf28eea18 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -31,8 +31,8 @@
#ifndef MATERIAL_H
#define MATERIAL_H
-#include "core/resource.h"
-#include "core/self_list.h"
+#include "core/io/resource.h"
+#include "core/templates/self_list.h"
#include "scene/resources/shader.h"
#include "scene/resources/texture.h"
#include "servers/rendering/shader_language.h"
@@ -145,17 +145,26 @@ public:
enum DetailUV {
DETAIL_UV_1,
- DETAIL_UV_2
+ DETAIL_UV_2,
+ DETAIL_UV_MAX
};
enum Transparency {
TRANSPARENCY_DISABLED,
TRANSPARENCY_ALPHA,
TRANSPARENCY_ALPHA_SCISSOR,
+ TRANSPARENCY_ALPHA_HASH,
TRANSPARENCY_ALPHA_DEPTH_PRE_PASS,
TRANSPARENCY_MAX,
};
+ enum AlphaAntiAliasing {
+ ALPHA_ANTIALIASING_OFF,
+ ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
+ ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE,
+ ALPHA_ANTIALIASING_MAX
+ };
+
enum ShadingMode {
SHADING_MODE_UNSHADED,
SHADING_MODE_PER_PIXEL,
@@ -184,18 +193,21 @@ public:
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
+ BLEND_MODE_MAX
};
enum DepthDrawMode {
DEPTH_DRAW_OPAQUE_ONLY,
DEPTH_DRAW_ALWAYS,
DEPTH_DRAW_DISABLED,
+ DEPTH_DRAW_MAX
};
enum CullMode {
CULL_BACK,
CULL_FRONT,
- CULL_DISABLED
+ CULL_DISABLED,
+ CULL_MAX
};
enum Flags {
@@ -227,6 +239,7 @@ public:
DIFFUSE_LAMBERT_WRAP,
DIFFUSE_OREN_NAYAR,
DIFFUSE_TOON,
+ DIFFUSE_MAX
};
enum SpecularMode {
@@ -235,6 +248,7 @@ public:
SPECULAR_PHONG,
SPECULAR_TOON,
SPECULAR_DISABLED,
+ SPECULAR_MAX
};
enum BillboardMode {
@@ -242,6 +256,7 @@ public:
BILLBOARD_ENABLED,
BILLBOARD_FIXED_Y,
BILLBOARD_PARTICLES,
+ BILLBOARD_MAX
};
enum TextureChannel {
@@ -249,12 +264,14 @@ public:
TEXTURE_CHANNEL_GREEN,
TEXTURE_CHANNEL_BLUE,
TEXTURE_CHANNEL_ALPHA,
- TEXTURE_CHANNEL_GRAYSCALE
+ TEXTURE_CHANNEL_GRAYSCALE,
+ TEXTURE_CHANNEL_MAX
};
enum EmissionOperator {
EMISSION_OP_ADD,
- EMISSION_OP_MULTIPLY
+ EMISSION_OP_MULTIPLY,
+ EMISSION_OP_MAX
};
enum DistanceFadeMode {
@@ -262,43 +279,47 @@ public:
DISTANCE_FADE_PIXEL_ALPHA,
DISTANCE_FADE_PIXEL_DITHER,
DISTANCE_FADE_OBJECT_DITHER,
+ DISTANCE_FADE_MAX
};
private:
- union MaterialKey {
- struct {
- uint64_t feature_mask : FEATURE_MAX;
- uint64_t detail_uv : 1;
- uint64_t blend_mode : 2;
- uint64_t depth_draw_mode : 2;
- uint64_t cull_mode : 2;
- uint64_t flags : FLAG_MAX;
- uint64_t detail_blend_mode : 2;
- uint64_t diffuse_mode : 3;
- uint64_t specular_mode : 3;
- uint64_t invalid_key : 1;
- uint64_t deep_parallax : 1;
- uint64_t billboard_mode : 2;
- uint64_t grow : 1;
- uint64_t proximity_fade : 1;
- uint64_t distance_fade : 2;
- uint64_t emission_op : 1;
- uint64_t texture_filter : 3;
- uint64_t transparency : 2;
- uint64_t shading_mode : 2;
- uint64_t roughness_channel : 3;
- };
-
- struct {
- uint64_t key0;
- uint64_t key1;
- };
+ struct MaterialKey {
+ // enum values
+ uint64_t texture_filter : get_num_bits(TEXTURE_FILTER_MAX - 1);
+ uint64_t detail_uv : get_num_bits(DETAIL_UV_MAX - 1);
+ uint64_t transparency : get_num_bits(TRANSPARENCY_MAX - 1);
+ uint64_t alpha_antialiasing_mode : get_num_bits(ALPHA_ANTIALIASING_MAX - 1);
+ uint64_t shading_mode : get_num_bits(SHADING_MODE_MAX - 1);
+ uint64_t blend_mode : get_num_bits(BLEND_MODE_MAX - 1);
+ uint64_t depth_draw_mode : get_num_bits(DEPTH_DRAW_MAX - 1);
+ uint64_t cull_mode : get_num_bits(CULL_MAX - 1);
+ uint64_t diffuse_mode : get_num_bits(DIFFUSE_MAX - 1);
+ uint64_t specular_mode : get_num_bits(SPECULAR_MAX - 1);
+ uint64_t billboard_mode : get_num_bits(BILLBOARD_MAX - 1);
+ uint64_t detail_blend_mode : get_num_bits(BLEND_MODE_MAX - 1);
+ uint64_t roughness_channel : get_num_bits(TEXTURE_CHANNEL_MAX - 1);
+ uint64_t emission_op : get_num_bits(EMISSION_OP_MAX - 1);
+ uint64_t distance_fade : get_num_bits(DISTANCE_FADE_MAX - 1);
+
+ // flag bitfield
+ uint64_t feature_mask : FEATURE_MAX - 1;
+ uint64_t flags : FLAG_MAX - 1;
+
+ // booleans
+ uint64_t deep_parallax : 1;
+ uint64_t grow : 1;
+ uint64_t proximity_fade : 1;
+
+ MaterialKey() {
+ memset(this, 0, sizeof(MaterialKey));
+ }
bool operator==(const MaterialKey &p_key) const {
- return (key0 == p_key.key0) && (key1 == p_key.key1);
+ return memcmp(this, &p_key, sizeof(MaterialKey)) == 0;
}
+
bool operator<(const MaterialKey &p_key) const {
- return (key0 == p_key.key0) ? (key1 < p_key.key1) : (key0 < p_key.key0);
+ return memcmp(this, &p_key, sizeof(MaterialKey)) < 0;
}
};
@@ -313,13 +334,7 @@ private:
_FORCE_INLINE_ MaterialKey _compute_key() const {
MaterialKey mk;
- mk.key0 = 0;
- mk.key1 = 0;
- for (int i = 0; i < FEATURE_MAX; i++) {
- if (features[i]) {
- mk.feature_mask |= ((uint64_t)1 << i);
- }
- }
+
mk.detail_uv = detail_uv;
mk.blend_mode = blend_mode;
mk.depth_draw_mode = depth_draw_mode;
@@ -328,20 +343,28 @@ private:
mk.transparency = transparency;
mk.shading_mode = shading_mode;
mk.roughness_channel = roughness_texture_channel;
- for (int i = 0; i < FLAG_MAX; i++) {
- if (flags[i]) {
- mk.flags |= ((uint64_t)1 << i);
- }
- }
mk.detail_blend_mode = detail_blend_mode;
mk.diffuse_mode = diffuse_mode;
mk.specular_mode = specular_mode;
mk.billboard_mode = billboard_mode;
- mk.deep_parallax = deep_parallax ? 1 : 0;
+ mk.deep_parallax = deep_parallax;
mk.grow = grow_enabled;
mk.proximity_fade = proximity_fade_enabled;
mk.distance_fade = distance_fade;
mk.emission_op = emission_op;
+ mk.alpha_antialiasing_mode = alpha_antialiasing_mode;
+
+ for (int i = 0; i < FEATURE_MAX; i++) {
+ if (features[i]) {
+ mk.feature_mask |= ((uint64_t)1 << i);
+ }
+ }
+
+ for (int i = 0; i < FLAG_MAX; i++) {
+ if (flags[i]) {
+ mk.flags |= ((uint64_t)1 << i);
+ }
+ }
return mk;
}
@@ -392,9 +415,14 @@ private:
StringName rim_texture_channel;
StringName heightmap_texture_channel;
StringName refraction_texture_channel;
- StringName alpha_scissor_threshold;
StringName texture_names[TEXTURE_MAX];
+
+ StringName alpha_scissor_threshold;
+ StringName alpha_hash_scale;
+
+ StringName alpha_antialiasing_edge;
+ StringName albedo_texture_size;
};
static Mutex material_mutex;
@@ -433,6 +461,8 @@ private:
float refraction;
float point_size;
float alpha_scissor_threshold;
+ float alpha_hash_scale;
+ float alpha_antialiasing_edge;
bool grow_enabled;
float ao_light_affect;
float grow;
@@ -482,6 +512,8 @@ private:
TextureChannel ao_texture_channel;
TextureChannel refraction_texture_channel;
+ AlphaAntiAliasing alpha_antialiasing_mode;
+
bool features[FEATURE_MAX];
Ref<Texture2D> textures[TEXTURE_MAX];
@@ -584,6 +616,12 @@ public:
void set_transparency(Transparency p_transparency);
Transparency get_transparency() const;
+ void set_alpha_antialiasing(AlphaAntiAliasing p_alpha_aa);
+ AlphaAntiAliasing get_alpha_antialiasing() const;
+
+ void set_alpha_antialiasing_edge(float p_edge);
+ float get_alpha_antialiasing_edge() const;
+
void set_shading_mode(ShadingMode p_shading_mode);
ShadingMode get_shading_mode() const;
@@ -660,6 +698,9 @@ public:
void set_alpha_scissor_threshold(float p_threshold);
float get_alpha_scissor_threshold() const;
+ void set_alpha_hash_scale(float p_scale);
+ float get_alpha_hash_scale() const;
+
void set_on_top_of_alpha();
void set_proximity_fade(bool p_enable);
@@ -707,6 +748,7 @@ VARIANT_ENUM_CAST(BaseMaterial3D::TextureParam)
VARIANT_ENUM_CAST(BaseMaterial3D::TextureFilter)
VARIANT_ENUM_CAST(BaseMaterial3D::ShadingMode)
VARIANT_ENUM_CAST(BaseMaterial3D::Transparency)
+VARIANT_ENUM_CAST(BaseMaterial3D::AlphaAntiAliasing)
VARIANT_ENUM_CAST(BaseMaterial3D::DetailUV)
VARIANT_ENUM_CAST(BaseMaterial3D::Feature)
VARIANT_ENUM_CAST(BaseMaterial3D::BlendMode)
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 10f0a040d0..c6815c8ecc 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -30,7 +30,7 @@
#include "mesh.h"
-#include "core/pair.h"
+#include "core/templates/pair.h"
#include "scene/resources/concave_polygon_shape_3d.h"
#include "scene/resources/convex_polygon_shape_3d.h"
#include "surface_tool.h"
@@ -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 {
@@ -168,7 +168,6 @@ Vector<Face3> Mesh::get_faces() const {
return Vector<Face3>();
/*
for (int i=0;i<surfaces.size();i++) {
-
if (RenderingServer::get_singleton()->mesh_surface_get_primitive_type( mesh, i ) != RenderingServer::PRIMITIVE_TRIANGLES )
continue;
@@ -181,12 +180,10 @@ Vector<Face3> Mesh::get_faces() const {
bool has_indices;
if (len>0) {
-
indices=RenderingServer::get_singleton()->mesh_surface_get_array(mesh, i,RenderingServer::ARRAY_INDEX);
has_indices=true;
} else {
-
len=vertices.size();
has_indices=false;
}
@@ -210,11 +207,9 @@ Vector<Face3> Mesh::get_faces() const {
for (int i=0;i<len/3;i++) {
-
Face3 face;
for (int j=0;j<3;j++) {
-
int idx=i*3+j;
face.vertex[j] = has_indices ? verticesptr[ indicesptr[ idx ] ] : verticesptr[idx];
}
@@ -485,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);
@@ -494,31 +511,28 @@ 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);
}
void Mesh::clear_cache() const {
@@ -565,11 +579,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));
@@ -577,7 +630,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);
@@ -676,6 +729,7 @@ 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;
@@ -732,6 +786,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"];
@@ -798,6 +853,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);
}
@@ -829,6 +885,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;
@@ -853,9 +915,9 @@ 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;
+ data["blend_shapes_count"] = surface.blend_shape_count;
}
if (surfaces[i].material.is_valid()) {
@@ -872,7 +934,6 @@ Array ArrayMesh::_get_surfaces() const {
ret.push_back(data);
}
- print_line("Saving surfaces: " + itos(ret.size()));
return ret;
}
@@ -902,6 +963,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")) {
@@ -928,11 +995,9 @@ 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]);
- }
+ if (d.has("blend_shapes") && d.has("blend_shape_count")) {
+ surface.blend_shape_data = d["blend_shapes"];
+ surface.blend_shape_count = d["blend_shape_count"];
}
Ref<Material> material;
@@ -988,7 +1053,7 @@ 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());
+ blend_shapes.resize(surface_data[i].blend_shape_count);
} else {
aabb.merge_with(s.aabb);
}
@@ -1076,7 +1141,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, uint32_t p_blend_shape_count, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods) {
_create_if_empty();
Surface s;
@@ -1096,10 +1161,13 @@ 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.blend_shape_count = p_blend_shape_count;
+ sd.bone_aabbs = p_bone_aabbs;
sd.lods = p_lods;
RenderingServer::get_singleton()->mesh_add_surface(mesh, sd);
@@ -1117,15 +1185,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.blend_shape_count, surface.bone_aabbs, surface.lods);
}
Array ArrayMesh::surface_get_arrays(int p_surface) const {
@@ -1458,29 +1528,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);
}
@@ -1513,7 +1583,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);
@@ -1543,29 +1613,8 @@ void ArrayMesh::_bind_methods() {
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);
+ BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED);
+ BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE);
}
void ArrayMesh::reload_from_file() {
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index fd1fa1b48f..ae2139a0cf 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -31,9 +31,9 @@
#ifndef MESH_H
#define MESH_H
+#include "core/io/resource.h"
#include "core/math/face3.h"
#include "core/math/triangle_mesh.h"
-#include "core/resource.h"
#include "scene/resources/material.h"
#include "scene/resources/shape_3d.h"
#include "servers/rendering_server.h"
@@ -50,19 +50,21 @@ protected:
public:
enum {
-
NO_INDEX_ARRAY = RenderingServer::NO_INDEX_ARRAY,
ARRAY_WEIGHTS_SIZE = RenderingServer::ARRAY_WEIGHTS_SIZE
};
enum ArrayType {
-
ARRAY_VERTEX = RenderingServer::ARRAY_VERTEX,
ARRAY_NORMAL = RenderingServer::ARRAY_NORMAL,
ARRAY_TANGENT = RenderingServer::ARRAY_TANGENT,
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,
@@ -70,30 +72,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,
};
@@ -106,12 +125,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;
@@ -158,6 +171,12 @@ class ArrayMesh : public Mesh {
Array _get_surfaces() const;
void _set_surfaces(const Array &p_data);
+public:
+ enum BlendShapeMode {
+ BLEND_SHAPE_MODE_NORMALIZED = RS::BLEND_SHAPE_MODE_NORMALIZED,
+ BLEND_SHAPE_MODE_RELATIVE = RS::BLEND_SHAPE_MODE_RELATIVE,
+ };
+
private:
struct Surface {
uint32_t format;
@@ -190,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>(), uint32_t p_blend_shape_count = 0, 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;
@@ -209,7 +228,6 @@ public:
void surface_update_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data);
int get_surface_count() const override;
- void surface_remove(int p_idx);
void clear_surfaces();
@@ -248,7 +266,8 @@ 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);
+VARIANT_ENUM_CAST(ArrayMesh::BlendShapeMode);
#endif
diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h
index 7b78398669..0d5fb3005b 100644
--- a/scene/resources/mesh_library.h
+++ b/scene/resources/mesh_library.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GRID_THEME_H
-#define GRID_THEME_H
+#ifndef MESH_LIBRARY_H
+#define MESH_LIBRARY_H
-#include "core/map.h"
-#include "core/resource.h"
+#include "core/io/resource.h"
+#include "core/templates/map.h"
#include "mesh.h"
#include "scene/3d/navigation_region_3d.h"
#include "shape_3d.h"
@@ -96,4 +96,4 @@ public:
~MeshLibrary();
};
-#endif // CUBE_GRID_THEME_H
+#endif // MESH_LIBRARY_H
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index cb201bc539..09674f3465 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -30,10 +30,10 @@
#include "packed_scene.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "core/core_string_names.h"
-#include "core/engine.h"
#include "core/io/resource_loader.h"
-#include "core/project_settings.h"
#include "scene/2d/node_2d.h"
#include "scene/3d/node_3d.h"
#include "scene/gui/control.h"
@@ -848,7 +848,7 @@ Error SceneState::pack(Node *p_scene) {
Map<Node *, int> node_map;
Map<Node *, int> nodepath_map;
- //if using scene inheritance, pack the scene it inherits from
+ // If using scene inheritance, pack the scene it inherits from.
if (scene->get_scene_inherited_state().is_valid()) {
String path = scene->get_scene_inherited_state()->get_path();
Ref<PackedScene> instance = ResourceLoader::load(path);
@@ -856,8 +856,8 @@ Error SceneState::pack(Node *p_scene) {
base_scene_idx = _vm_get_variant(instance, variant_map);
}
}
- //instanced, only direct sub-scnes are supported of course
+ // Instanced, only direct sub-scenes are supported of course.
Error err = _parse_node(scene, scene, -1, name_map, variant_map, node_map, nodepath_map);
if (err) {
clear();
@@ -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 004758afa5..fce3891507 100644
--- a/scene/resources/packed_scene.h
+++ b/scene/resources/packed_scene.h
@@ -31,7 +31,7 @@
#ifndef PACKED_SCENE_H
#define PACKED_SCENE_H
-#include "core/resource.h"
+#include "core/io/resource.h"
#include "scene/main/node.h"
class SceneState : public Reference {
@@ -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 fc92a721db..e2f96c54cb 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -91,13 +91,16 @@ void ParticlesMaterial::init_shaders() {
shader_names->emission_texture_normal = "emission_texture_normal";
shader_names->emission_texture_color = "emission_texture_color";
- shader_names->trail_divisor = "trail_divisor";
- shader_names->trail_size_modifier = "trail_size_modifier";
- shader_names->trail_color_modifier = "trail_color_modifier";
-
shader_names->gravity = "gravity";
shader_names->lifetime_randomness = "lifetime_randomness";
+
+ shader_names->sub_emitter_frequency = "sub_emitter_frequency";
+ shader_names->sub_emitter_amount_at_end = "sub_emitter_amount_at_end";
+ shader_names->sub_emitter_keep_velocity = "sub_emitter_keep_velocity";
+
+ shader_names->collision_friction = "collision_friction";
+ shader_names->collision_bounce = "collision_bounce";
}
void ParticlesMaterial::finish_shaders() {
@@ -136,6 +139,10 @@ void ParticlesMaterial::_update_shader() {
String code = "shader_type particles;\n";
+ if (collision_scale) {
+ code += "render_mode collision_use_scale;\n";
+ }
+
code += "uniform vec3 direction;\n";
code += "uniform float spread;\n";
code += "uniform float flatness;\n";
@@ -192,9 +199,17 @@ void ParticlesMaterial::_update_shader() {
}
}
- code += "uniform vec4 color_value : hint_color;\n";
+ if (sub_emitter_mode != SUB_EMITTER_DISABLED) {
+ if (sub_emitter_mode == SUB_EMITTER_CONSTANT) {
+ code += "uniform float sub_emitter_frequency;\n";
+ }
+ if (sub_emitter_mode == SUB_EMITTER_AT_END) {
+ code += "uniform int sub_emitter_amount_at_end;\n";
+ }
+ code += "uniform bool sub_emitter_keep_velocity;\n";
+ }
- code += "uniform int trail_divisor;\n";
+ code += "uniform vec4 color_value : hint_color;\n";
code += "uniform vec3 gravity;\n";
@@ -239,12 +254,9 @@ void ParticlesMaterial::_update_shader() {
code += "uniform sampler2D anim_offset_texture;\n";
}
- if (trail_size_modifier.is_valid()) {
- code += "uniform sampler2D trail_size_modifier;\n";
- }
-
- if (trail_color_modifier.is_valid()) {
- code += "uniform sampler2D trail_color_modifier;\n";
+ if (collision_enabled) {
+ code += "uniform float collision_friction;\n";
+ code += "uniform float collision_bounce;\n";
}
//need a random function
@@ -277,8 +289,8 @@ void ParticlesMaterial::_update_shader() {
code += "}\n";
code += "\n";
- code += "void vertex() {\n";
- code += " uint base_number = NUMBER / uint(trail_divisor);\n";
+ code += "void compute() {\n";
+ code += " uint base_number = NUMBER;\n";
code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n";
code += " float angle_rand = rand_from_seed(alt_seed);\n";
code += " float scale_rand = rand_from_seed(alt_seed);\n";
@@ -293,17 +305,7 @@ void ParticlesMaterial::_update_shader() {
code += " ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n";
code += " ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n";
}
- code += " bool restart = false;\n";
- code += " if (CUSTOM.y > CUSTOM.w) {\n";
- code += " restart = true;\n";
- code += " }\n\n";
- code += " if (RESTART || restart) {\n";
-
- if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
- code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n";
- } else {
- code += " float tex_linear_velocity = 0.0;\n";
- }
+ code += " if (RESTART) {\n";
if (tex_parameters[PARAM_ANGLE].is_valid()) {
code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n";
@@ -319,25 +321,34 @@ void ParticlesMaterial::_update_shader() {
code += " float spread_rad = spread * degree_to_rad;\n";
- if (flags[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";
- code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
+ code += " if (RESTART_VELOCITY) {\n";
+
+ if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
+ code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n";
+ } else {
+ code += " float tex_linear_velocity = 0.0;\n";
+ }
+
+ 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";
+ code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
} else {
//initiate velocity spread in 3D
- code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
- code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n";
- code += " angle1_rad += direction.z != 0.0 ? atan(direction.x, direction.z) : sign(direction.x) * (pi / 2.0);\n";
- code += " angle2_rad += direction.z != 0.0 ? atan(direction.y, abs(direction.z)) : (direction.x != 0.0 ? atan(direction.y, abs(direction.x)) : sign(direction.y) * (pi / 2.0));\n";
- code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n";
- code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n";
- code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n";
- code += " vec3 vec_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
- code += " vec_direction = normalize(vec_direction);\n";
- code += " VELOCITY = vec_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
+ code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
+ code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n";
+ code += " angle1_rad += direction.z != 0.0 ? atan(direction.x, direction.z) : sign(direction.x) * (pi / 2.0);\n";
+ code += " angle2_rad += direction.z != 0.0 ? atan(direction.y, abs(direction.z)) : (direction.x != 0.0 ? atan(direction.y, abs(direction.x)) : sign(direction.y) * (pi / 2.0));\n";
+ code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n";
+ code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n";
+ code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n";
+ code += " vec3 vec_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
+ code += " vec_direction = normalize(vec_direction);\n";
+ code += " VELOCITY = vec_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
}
+ code += " }\n";
code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
@@ -345,35 +356,38 @@ void ParticlesMaterial::_update_shader() {
code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n";
code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1)
+ code += " if (RESTART_POSITION) {\n";
+
switch (emission_shape) {
case EMISSION_SHAPE_POINT: {
- //do none
+ //do none, identity (will later be multiplied by emission transform)
+ code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n";
} break;
case EMISSION_SHAPE_SPHERE: {
- code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
- code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
- code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
- code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n";
+ code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";
+ code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n";
+ code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n";
+ code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n";
} break;
case EMISSION_SHAPE_BOX: {
- code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n";
+ code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n";
} break;
case EMISSION_SHAPE_POINTS:
case EMISSION_SHAPE_DIRECTED_POINTS: {
- code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
+ 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]) {
- 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";
- code += " VELOCITY.xy = rotm * VELOCITY.xy;\n";
+ 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";
+ code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n";
} else {
- code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n";
- code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n";
- code += " vec3 tangent = normalize(cross(v0, normal));\n";
- code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
- code += " VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n";
+ code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n";
+ code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n";
+ code += " vec3 tangent = normalize(cross(v0, normal));\n";
+ code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
+ code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n";
}
}
} break;
@@ -381,12 +395,14 @@ void ParticlesMaterial::_update_shader() {
break;
}
}
- code += " VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
- code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
- if (flags[FLAG_DISABLE_Z]) {
- code += " VELOCITY.z = 0.0;\n";
- code += " TRANSFORM[3].z = 0.0;\n";
+
+ code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
+ code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ code += " VELOCITY.z = 0.0;\n";
+ code += " TRANSFORM[3].z = 0.0;\n";
}
+ code += " }\n";
code += " } else {\n";
@@ -397,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 {
@@ -455,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";
@@ -465,17 +481,21 @@ 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 {
code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n";
code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n";
}
+ if (attractor_interaction_enabled) {
+ code += " force += ATTRACTOR_FORCE;\n\n";
+ }
+
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";
@@ -540,15 +560,10 @@ void ParticlesMaterial::_update_shader() {
if (emission_color_texture.is_valid() && (emission_shape == EMISSION_SHAPE_POINTS || emission_shape == EMISSION_SHAPE_DIRECTED_POINTS)) {
code += " COLOR *= texelFetch(emission_texture_color, emission_tex_ofs, 0);\n";
}
- if (trail_color_modifier.is_valid()) {
- code += " if (trail_divisor > 1) {\n";
- code += " COLOR *= textureLod(trail_color_modifier, vec2(float(int(NUMBER) % trail_divisor) / float(trail_divisor - 1), 0.0), 0.0);\n";
- code += " }\n";
- }
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";
@@ -564,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";
@@ -583,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";
}
}
@@ -592,19 +607,49 @@ void ParticlesMaterial::_update_shader() {
code += " if (base_scale < 0.000001) {\n";
code += " base_scale = 0.000001;\n";
code += " }\n";
- if (trail_size_modifier.is_valid()) {
- code += " if (trail_divisor > 1) {\n";
- code += " base_scale *= textureLod(trail_size_modifier, vec2(float(int(NUMBER) % trail_divisor) / float(trail_divisor - 1), 0.0), 0.0).r;\n";
- code += " }\n";
- }
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";
}
+ if (collision_enabled) {
+ code += " if (COLLIDED) {\n";
+ code += " TRANSFORM[3].xyz+=COLLISION_NORMAL * COLLISION_DEPTH;\n";
+ code += " VELOCITY -= COLLISION_NORMAL * dot(COLLISION_NORMAL, VELOCITY) * (1.0 + collision_bounce);\n";
+ code += " VELOCITY = mix(VELOCITY,vec3(0.0),collision_friction * DELTA * 100.0);\n";
+ code += " }\n";
+ }
+ if (sub_emitter_mode != SUB_EMITTER_DISABLED) {
+ code += " int emit_count = 0;\n";
+ switch (sub_emitter_mode) {
+ case SUB_EMITTER_CONSTANT: {
+ code += " float interval_from = CUSTOM.y * LIFETIME - DELTA;\n";
+ code += " float interval_rem = sub_emitter_frequency - mod(interval_from,sub_emitter_frequency);\n";
+ code += " if (DELTA >= interval_rem) emit_count = 1;\n";
+ } break;
+ case SUB_EMITTER_AT_COLLISION: {
+ //not implemented yet
+ code += " if (COLLIDED) emit_count = 1;\n";
+ } break;
+ case SUB_EMITTER_AT_END: {
+ //not implemented yet
+ code += " float unit_delta = DELTA/LIFETIME;\n";
+ code += " float end_time = CUSTOM.w * 0.95;\n"; // if we do at the end we might miss it, as it can just get deactivated by emitter
+ code += " if (CUSTOM.y < end_time && (CUSTOM.y + unit_delta) >= end_time) emit_count = sub_emitter_amount_at_end;\n";
+ } break;
+ default: {
+ }
+ }
+ 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 += " }";
+ }
+
code += " if (CUSTOM.y > CUSTOM.w) {";
code += " ACTIVE = false;\n";
code += " }\n";
@@ -871,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) {
@@ -951,41 +996,6 @@ int ParticlesMaterial::get_emission_point_count() const {
return emission_point_count;
}
-void ParticlesMaterial::set_trail_divisor(int p_divisor) {
- trail_divisor = p_divisor;
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_divisor, p_divisor);
-}
-
-int ParticlesMaterial::get_trail_divisor() const {
- return trail_divisor;
-}
-
-void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail_size_modifier) {
- trail_size_modifier = p_trail_size_modifier;
-
- Ref<CurveTexture> curve = trail_size_modifier;
- if (curve.is_valid()) {
- curve->ensure_default_setup();
- }
-
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_size_modifier, curve);
- _queue_shader_change();
-}
-
-Ref<CurveTexture> ParticlesMaterial::get_trail_size_modifier() const {
- return trail_size_modifier;
-}
-
-void ParticlesMaterial::set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier) {
- trail_color_modifier = p_trail_color_modifier;
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_color_modifier, p_trail_color_modifier);
- _queue_shader_change();
-}
-
-Ref<GradientTexture> ParticlesMaterial::get_trail_color_modifier() const {
- return trail_color_modifier;
-}
-
void ParticlesMaterial::set_gravity(const Vector3 &p_gravity) {
gravity = p_gravity;
Vector3 gset = gravity;
@@ -1038,9 +1048,97 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}
- if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
+ if (property.name == "sub_emitter_frequency" && sub_emitter_mode != SUB_EMITTER_CONSTANT) {
property.usage = 0;
}
+
+ if (property.name == "sub_emitter_amount_at_end" && sub_emitter_mode != SUB_EMITTER_AT_END) {
+ property.usage = 0;
+ }
+
+ if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ property.usage = 0;
+ }
+}
+
+void ParticlesMaterial::set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode) {
+ sub_emitter_mode = p_sub_emitter_mode;
+ _queue_shader_change();
+ _change_notify();
+}
+
+ParticlesMaterial::SubEmitterMode ParticlesMaterial::get_sub_emitter_mode() const {
+ return sub_emitter_mode;
+}
+
+void ParticlesMaterial::set_sub_emitter_frequency(float p_frequency) {
+ sub_emitter_frequency = p_frequency;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_frequency, 1.0 / p_frequency); //pas delta instead of frequency, since its easier to compute
+}
+float ParticlesMaterial::get_sub_emitter_frequency() const {
+ return sub_emitter_frequency;
+}
+
+void ParticlesMaterial::set_sub_emitter_amount_at_end(int p_amount) {
+ sub_emitter_amount_at_end = p_amount;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_amount_at_end, p_amount);
+}
+
+int ParticlesMaterial::get_sub_emitter_amount_at_end() const {
+ return sub_emitter_amount_at_end;
+}
+
+void ParticlesMaterial::set_sub_emitter_keep_velocity(bool p_enable) {
+ sub_emitter_keep_velocity = p_enable;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_keep_velocity, p_enable);
+}
+bool ParticlesMaterial::get_sub_emitter_keep_velocity() const {
+ return sub_emitter_keep_velocity;
+}
+
+void ParticlesMaterial::set_attractor_interaction_enabled(bool p_enable) {
+ attractor_interaction_enabled = p_enable;
+ _queue_shader_change();
+}
+
+bool ParticlesMaterial::is_attractor_interaction_enabled() const {
+ return attractor_interaction_enabled;
+}
+
+void ParticlesMaterial::set_collision_enabled(bool p_enabled) {
+ collision_enabled = p_enabled;
+ _queue_shader_change();
+}
+
+bool ParticlesMaterial::is_collision_enabled() const {
+ return collision_enabled;
+}
+
+void ParticlesMaterial::set_collision_use_scale(bool p_scale) {
+ collision_scale = p_scale;
+ _queue_shader_change();
+}
+
+bool ParticlesMaterial::is_collision_using_scale() const {
+ return collision_scale;
+}
+
+void ParticlesMaterial::set_collision_friction(float p_friction) {
+ collision_friction = p_friction;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->collision_friction, p_friction);
+}
+
+float ParticlesMaterial::get_collision_friction() const {
+ return collision_friction;
+}
+
+void ParticlesMaterial::set_collision_bounce(float p_bounce) {
+ collision_bounce = p_bounce;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->collision_bounce, p_bounce);
+}
+
+float ParticlesMaterial::get_collision_bounce() const {
+ return collision_bounce;
}
Shader::Mode ParticlesMaterial::get_shader_mode() const {
@@ -1072,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);
@@ -1096,27 +1194,42 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticlesMaterial::set_emission_point_count);
ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count);
- ClassDB::bind_method(D_METHOD("set_trail_divisor", "divisor"), &ParticlesMaterial::set_trail_divisor);
- ClassDB::bind_method(D_METHOD("get_trail_divisor"), &ParticlesMaterial::get_trail_divisor);
-
- ClassDB::bind_method(D_METHOD("set_trail_size_modifier", "texture"), &ParticlesMaterial::set_trail_size_modifier);
- ClassDB::bind_method(D_METHOD("get_trail_size_modifier"), &ParticlesMaterial::get_trail_size_modifier);
-
- ClassDB::bind_method(D_METHOD("set_trail_color_modifier", "texture"), &ParticlesMaterial::set_trail_color_modifier);
- ClassDB::bind_method(D_METHOD("get_trail_color_modifier"), &ParticlesMaterial::get_trail_color_modifier);
-
ClassDB::bind_method(D_METHOD("get_gravity"), &ParticlesMaterial::get_gravity);
ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &ParticlesMaterial::set_gravity);
ClassDB::bind_method(D_METHOD("set_lifetime_randomness", "randomness"), &ParticlesMaterial::set_lifetime_randomness);
ClassDB::bind_method(D_METHOD("get_lifetime_randomness"), &ParticlesMaterial::get_lifetime_randomness);
+ ClassDB::bind_method(D_METHOD("get_sub_emitter_mode"), &ParticlesMaterial::get_sub_emitter_mode);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter_mode", "mode"), &ParticlesMaterial::set_sub_emitter_mode);
+
+ ClassDB::bind_method(D_METHOD("get_sub_emitter_frequency"), &ParticlesMaterial::get_sub_emitter_frequency);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter_frequency", "hz"), &ParticlesMaterial::set_sub_emitter_frequency);
+
+ ClassDB::bind_method(D_METHOD("get_sub_emitter_amount_at_end"), &ParticlesMaterial::get_sub_emitter_amount_at_end);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter_amount_at_end", "amount"), &ParticlesMaterial::set_sub_emitter_amount_at_end);
+
+ ClassDB::bind_method(D_METHOD("get_sub_emitter_keep_velocity"), &ParticlesMaterial::get_sub_emitter_keep_velocity);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter_keep_velocity", "enable"), &ParticlesMaterial::set_sub_emitter_keep_velocity);
+
+ ClassDB::bind_method(D_METHOD("set_attractor_interaction_enabled", "enabled"), &ParticlesMaterial::set_attractor_interaction_enabled);
+ ClassDB::bind_method(D_METHOD("is_attractor_interaction_enabled"), &ParticlesMaterial::is_attractor_interaction_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collision_enabled", "enabled"), &ParticlesMaterial::set_collision_enabled);
+ ClassDB::bind_method(D_METHOD("is_collision_enabled"), &ParticlesMaterial::is_collision_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collision_use_scale", "radius"), &ParticlesMaterial::set_collision_use_scale);
+ ClassDB::bind_method(D_METHOD("is_collision_using_scale"), &ParticlesMaterial::is_collision_using_scale);
+
+ ClassDB::bind_method(D_METHOD("set_collision_friction", "friction"), &ParticlesMaterial::set_collision_friction);
+ ClassDB::bind_method(D_METHOD("get_collision_friction"), &ParticlesMaterial::get_collision_friction);
+
+ ClassDB::bind_method(D_METHOD("set_collision_bounce", "bounce"), &ParticlesMaterial::set_collision_bounce);
+ ClassDB::bind_method(D_METHOD("get_collision_bounce"), &ParticlesMaterial::get_collision_bounce);
+
ADD_GROUP("Time", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness");
- ADD_GROUP("Trail", "trail_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_divisor", PROPERTY_HINT_RANGE, "1,1000000,1"), "set_trail_divisor", "get_trail_divisor");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_size_modifier", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_trail_size_modifier", "get_trail_size_modifier");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_color_modifier", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_trail_color_modifier", "get_trail_color_modifier");
+
ADD_GROUP("Emission Shape", "emission_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius");
@@ -1125,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");
@@ -1186,6 +1299,20 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET);
+ ADD_GROUP("Sub Emitter", "sub_emitter_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled,Constant,AtEnd,AtCollision"), "set_sub_emitter_mode", "get_sub_emitter_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sub_emitter_frequency", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_sub_emitter_frequency", "get_sub_emitter_frequency");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_end", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_end", "get_sub_emitter_amount_at_end");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sub_emitter_keep_velocity"), "set_sub_emitter_keep_velocity", "get_sub_emitter_keep_velocity");
+
+ ADD_GROUP("Attractor Interaction", "attractor_interaction_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "attractor_interaction_enabled"), "set_attractor_interaction_enabled", "is_attractor_interaction_enabled");
+ ADD_GROUP("Collision", "collision_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_enabled"), "set_collision_enabled", "is_collision_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_friction", "get_collision_friction");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_bounce", "get_collision_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_use_scale"), "set_collision_use_scale", "is_collision_using_scale");
+
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
@@ -1200,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);
@@ -1211,6 +1338,12 @@ void ParticlesMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_MAX);
+
+ BIND_ENUM_CONSTANT(SUB_EMITTER_DISABLED);
+ BIND_ENUM_CONSTANT(SUB_EMITTER_CONSTANT);
+ BIND_ENUM_CONSTANT(SUB_EMITTER_AT_END);
+ BIND_ENUM_CONSTANT(SUB_EMITTER_AT_COLLISION);
+ BIND_ENUM_CONSTANT(SUB_EMITTER_MAX);
}
ParticlesMaterial::ParticlesMaterial() :
@@ -1233,17 +1366,27 @@ ParticlesMaterial::ParticlesMaterial() :
set_emission_shape(EMISSION_SHAPE_POINT);
set_emission_sphere_radius(1);
set_emission_box_extents(Vector3(1, 1, 1));
- set_trail_divisor(1);
set_gravity(Vector3(0, -9.8, 0));
set_lifetime_randomness(0);
emission_point_count = 1;
+ set_sub_emitter_mode(SUB_EMITTER_DISABLED);
+ set_sub_emitter_frequency(4);
+ set_sub_emitter_amount_at_end(1);
+ set_sub_emitter_keep_velocity(false);
+
+ set_attractor_interaction_enabled(true);
+ set_collision_enabled(true);
+ set_collision_bounce(0.0);
+ set_collision_friction(0.0);
+ set_collision_use_scale(false);
+
for (int i = 0; i < PARAM_MAX; i++) {
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 1c1b6c92f9..7e8f05b706 100644
--- a/scene/resources/particles_material.h
+++ b/scene/resources/particles_material.h
@@ -28,18 +28,24 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/rid.h"
+#include "core/templates/rid.h"
#include "scene/resources/material.h"
#ifndef PARTICLES_MATERIAL_H
#define PARTICLES_MATERIAL_H
+/*
+ TODO:
+-Path following
+-Emitter positions deformable by bones
+-Proper trails
+*/
+
class ParticlesMaterial : public Material {
GDCLASS(ParticlesMaterial, Material);
public:
enum Parameter {
-
PARAM_INITIAL_LINEAR_VELOCITY,
PARAM_ANGULAR_VELOCITY,
PARAM_ORBIT_VELOCITY,
@@ -55,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 {
@@ -71,17 +77,27 @@ public:
EMISSION_SHAPE_MAX
};
+ enum SubEmitterMode {
+ SUB_EMITTER_DISABLED,
+ SUB_EMITTER_CONSTANT,
+ SUB_EMITTER_AT_END,
+ SUB_EMITTER_AT_COLLISION,
+ SUB_EMITTER_MAX
+ };
+
private:
union MaterialKey {
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 trail_size_texture : 1;
- uint32_t trail_color_texture : 1;
uint32_t invalid_key : 1;
uint32_t has_emission_color : 1;
+ uint32_t sub_emitter : 2;
+ uint32_t attractor_enabled : 1;
+ uint32_t collision_enabled : 1;
+ uint32_t collision_scale : 1;
};
uint32_t key;
@@ -108,17 +124,19 @@ 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);
}
}
mk.texture_color = color_ramp.is_valid() ? 1 : 0;
mk.emission_shape = emission_shape;
- mk.trail_color_texture = trail_color_modifier.is_valid() ? 1 : 0;
- mk.trail_size_texture = trail_size_modifier.is_valid() ? 1 : 0;
mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid();
+ mk.sub_emitter = sub_emitter_mode;
+ mk.collision_enabled = collision_enabled;
+ mk.attractor_enabled = attractor_interaction_enabled;
+ mk.collision_scale = collision_scale;
return mk;
}
@@ -178,13 +196,16 @@ private:
StringName emission_texture_normal;
StringName emission_texture_color;
- StringName trail_divisor;
- StringName trail_size_modifier;
- StringName trail_color_modifier;
-
StringName gravity;
StringName lifetime_randomness;
+
+ StringName sub_emitter_frequency;
+ StringName sub_emitter_amount_at_end;
+ StringName sub_emitter_keep_velocity;
+
+ StringName collision_friction;
+ StringName collision_bounce;
};
static ShaderNames *shader_names;
@@ -206,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;
@@ -218,17 +239,22 @@ private:
bool anim_loop;
- int trail_divisor;
-
- Ref<CurveTexture> trail_size_modifier;
- Ref<GradientTexture> trail_color_modifier;
-
Vector3 gravity;
float lifetime_randomness;
+ SubEmitterMode sub_emitter_mode;
+ float sub_emitter_frequency;
+ int sub_emitter_amount_at_end;
+ bool sub_emitter_keep_velocity;
//do not save emission points here
+ bool attractor_interaction_enabled;
+ bool collision_enabled;
+ bool collision_scale;
+ float collision_friction;
+ float collision_bounce;
+
protected:
static void _bind_methods();
virtual void _validate_property(PropertyInfo &property) const override;
@@ -258,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);
@@ -277,25 +303,43 @@ public:
Ref<Texture2D> get_emission_color_texture() const;
int get_emission_point_count() const;
- void set_trail_divisor(int p_divisor);
- int get_trail_divisor() const;
-
- void set_trail_size_modifier(const Ref<CurveTexture> &p_trail_size_modifier);
- Ref<CurveTexture> get_trail_size_modifier() const;
-
- void set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier);
- Ref<GradientTexture> get_trail_color_modifier() const;
-
void set_gravity(const Vector3 &p_gravity);
Vector3 get_gravity() const;
void set_lifetime_randomness(float p_lifetime);
float get_lifetime_randomness() const;
+ void set_attractor_interaction_enabled(bool p_enable);
+ bool is_attractor_interaction_enabled() const;
+
+ void set_collision_enabled(bool p_enabled);
+ bool is_collision_enabled() const;
+
+ void set_collision_use_scale(bool p_scale);
+ bool is_collision_using_scale() const;
+
+ void set_collision_friction(float p_friction);
+ float get_collision_friction() const;
+
+ void set_collision_bounce(float p_bounce);
+ float get_collision_bounce() const;
+
static void init_shaders();
static void finish_shaders();
static void flush_changes();
+ void set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode);
+ SubEmitterMode get_sub_emitter_mode() const;
+
+ void set_sub_emitter_frequency(float p_frequency);
+ float get_sub_emitter_frequency() const;
+
+ void set_sub_emitter_amount_at_end(int p_amount);
+ int get_sub_emitter_amount_at_end() const;
+
+ void set_sub_emitter_keep_velocity(bool p_enable);
+ bool get_sub_emitter_keep_velocity() const;
+
RID get_shader_rid() const;
virtual Shader::Mode get_shader_mode() const override;
@@ -305,7 +349,8 @@ 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)
#endif // PARTICLES_MATERIAL_H
diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp
index 2a5cd1101a..59bf8c0e13 100644
--- a/scene/resources/physics_material.cpp
+++ b/scene/resources/physics_material.cpp
@@ -43,9 +43,9 @@ void PhysicsMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_absorbent", "absorbent"), &PhysicsMaterial::set_absorbent);
ClassDB::bind_method(D_METHOD("is_absorbent"), &PhysicsMaterial::is_absorbent);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction"), "set_friction", "get_friction");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rough"), "set_rough", "is_rough");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce"), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "absorbent"), "set_absorbent", "is_absorbent");
}
diff --git a/scene/resources/physics_material.h b/scene/resources/physics_material.h
index 34aa7066df..e9222ffa1b 100644
--- a/scene/resources/physics_material.h
+++ b/scene/resources/physics_material.h
@@ -31,7 +31,7 @@
#ifndef PHYSICS_MATERIAL_H
#define PHYSICS_MATERIAL_H
-#include "core/resource.h"
+#include "core/io/resource.h"
#include "servers/physics_server_3d.h"
class PhysicsMaterial : public Resource {
diff --git a/scene/resources/polygon_path_finder.h b/scene/resources/polygon_path_finder.h
index baee4dc20d..44a97b4294 100644
--- a/scene/resources/polygon_path_finder.h
+++ b/scene/resources/polygon_path_finder.h
@@ -31,7 +31,7 @@
#ifndef POLYGON_PATH_FINDER_H
#define POLYGON_PATH_FINDER_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class PolygonPathFinder : public Resource {
GDCLASS(PolygonPathFinder, Resource);
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 8d9c5f07b2..06e181cb99 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -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..02aea9c5c8 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -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_3d.cpp b/scene/resources/ray_shape_3d.cpp
index 17205a500a..1705fb0f55 100644
--- a/scene/resources/ray_shape_3d.cpp
+++ b/scene/resources/ray_shape_3d.cpp
@@ -32,7 +32,7 @@
#include "servers/physics_server_3d.h"
-Vector<Vector3> RayShape3D::get_debug_mesh_lines() {
+Vector<Vector3> RayShape3D::get_debug_mesh_lines() const {
Vector<Vector3> points;
points.push_back(Vector3());
points.push_back(Vector3(0, 0, get_length()));
@@ -81,7 +81,7 @@ void RayShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape3D::set_slips_on_slope);
ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape3D::get_slips_on_slope);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_length", "get_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_length", "get_length");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope");
}
diff --git a/scene/resources/ray_shape_3d.h b/scene/resources/ray_shape_3d.h
index 6d974a0a4c..a1a6702564 100644
--- a/scene/resources/ray_shape_3d.h
+++ b/scene/resources/ray_shape_3d.h
@@ -48,7 +48,7 @@ public:
void set_slips_on_slope(bool p_active);
bool get_slips_on_slope() const;
- virtual Vector<Vector3> get_debug_mesh_lines() override;
+ virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
RayShape3D();
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 93db8b725f..58645dbe65 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -30,9 +30,9 @@
#include "resource_format_text.h"
+#include "core/config/project_settings.h"
#include "core/io/resource_format_binary.h"
#include "core/os/dir_access.h"
-#include "core/project_settings.h"
#include "core/version.h"
//version 2: changed names for basis, aabb, Vectors, etc.
@@ -572,7 +572,7 @@ Error ResourceLoaderText::load() {
}
}
- if (progress) {
+ if (progress && resources_total > 0) {
*progress = resource_current / float(resources_total);
}
}
@@ -640,7 +640,7 @@ Error ResourceLoaderText::load() {
return error;
} else {
error = OK;
- if (progress) {
+ if (progress && resources_total > 0) {
*progress = resource_current / float(resources_total);
}
@@ -674,7 +674,7 @@ Error ResourceLoaderText::load() {
resource_current++;
- if (progress) {
+ if (progress && resources_total > 0) {
*progress = resource_current / float(resources_total);
}
@@ -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();
@@ -1763,6 +1767,10 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
for (int i = 0; i < state->get_connection_count(); i++) {
+ if (i == 0) {
+ f->store_line("");
+ }
+
String connstr = "[connection";
connstr += " signal=\"" + String(state->get_connection_signal(i)) + "\"";
connstr += " from=\"" + String(state->get_connection_source(i).simplified()) + "\"";
@@ -1786,7 +1794,10 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
Vector<NodePath> editable_instances = state->get_editable_instances();
for (int i = 0; i < editable_instances.size(); i++) {
- f->store_line("\n[editable path=\"" + editable_instances[i].operator String() + "\"]");
+ if (i == 0) {
+ f->store_line("");
+ }
+ f->store_line("[editable path=\"" + editable_instances[i].operator String() + "\"]");
}
}
diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h
index cf522c9364..ca7b0b021f 100644
--- a/scene/resources/resource_format_text.h
+++ b/scene/resources/resource_format_text.h
@@ -34,7 +34,7 @@
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/os/file_access.h"
-#include "core/variant_parser.h"
+#include "core/variant/variant_parser.h"
#include "scene/resources/packed_scene.h"
class ResourceLoaderText {
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 4249542567..76d37eaa71 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "shader.h"
+
#include "core/os/file_access.h"
#include "scene/scene_string_names.h"
#include "servers/rendering/shader_language.h"
@@ -80,7 +81,7 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const {
params_cache[pi.name] = E->get().name;
if (p_params) {
//small little hack
- if (pi.type == Variant::_RID) {
+ if (pi.type == Variant::RID) {
pi.type = Variant::OBJECT;
}
p_params->push_back(pi);
@@ -142,8 +143,6 @@ void Shader::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_param", "name"), &Shader::has_param);
- //ClassDB::bind_method(D_METHOD("get_param_list"),&Shader::get_fragment_code);
-
ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_code", "get_code");
BIND_ENUM_CONSTANT(MODE_SPATIAL);
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 7dcec7811a..0feaa179b2 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -31,9 +31,9 @@
#ifndef SHADER_H
#define SHADER_H
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/resource.h"
#include "scene/resources/texture.h"
class Shader : public Resource {
@@ -42,7 +42,6 @@ class Shader : public Resource {
public:
enum Mode {
-
MODE_SPATIAL,
MODE_CANVAS_ITEM,
MODE_PARTICLES,
diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h
index bb4688a02d..495ffdd38b 100644
--- a/scene/resources/shape_2d.h
+++ b/scene/resources/shape_2d.h
@@ -31,7 +31,7 @@
#ifndef SHAPE_2D_H
#define SHAPE_2D_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class Shape2D : public Resource {
GDCLASS(Shape2D, Resource);
diff --git a/scene/resources/shape_3d.h b/scene/resources/shape_3d.h
index 8a78b41461..5a9c2e3b9c 100644
--- a/scene/resources/shape_3d.h
+++ b/scene/resources/shape_3d.h
@@ -31,7 +31,7 @@
#ifndef SHAPE_3D_H
#define SHAPE_3D_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class ArrayMesh;
@@ -56,7 +56,7 @@ public:
virtual RID get_rid() const override { return shape; }
Ref<ArrayMesh> get_debug_mesh();
- virtual Vector<Vector3> get_debug_mesh_lines() = 0; // { return Vector<Vector3>(); }
+ virtual Vector<Vector3> get_debug_mesh_lines() const = 0; // { return Vector<Vector3>(); }
/// Returns the radius of a sphere that fully enclose this shape
virtual real_t get_enclosing_radius() const = 0;
diff --git a/scene/resources/skin.h b/scene/resources/skin.h
index 57aaf1afd4..e6ed4f1768 100644
--- a/scene/resources/skin.h
+++ b/scene/resources/skin.h
@@ -31,7 +31,7 @@
#ifndef SKIN_H
#define SKIN_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class Skin : public Resource {
GDCLASS(Skin, Resource)
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index 69e8e0b5bd..05bb13a1e0 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -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 5e23020d66..5411994b7d 100644
--- a/scene/resources/sky_material.h
+++ b/scene/resources/sky_material.h
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/rid.h"
+#include "core/templates/rid.h"
#include "scene/resources/material.h"
#ifndef SKY_MATERIAL_H
diff --git a/scene/resources/sphere_shape_3d.cpp b/scene/resources/sphere_shape_3d.cpp
index d24998ff18..64e0a701b7 100644
--- a/scene/resources/sphere_shape_3d.cpp
+++ b/scene/resources/sphere_shape_3d.cpp
@@ -31,7 +31,7 @@
#include "sphere_shape_3d.h"
#include "servers/physics_server_3d.h"
-Vector<Vector3> SphereShape3D::get_debug_mesh_lines() {
+Vector<Vector3> SphereShape3D::get_debug_mesh_lines() const {
float r = get_radius();
Vector<Vector3> points;
@@ -77,7 +77,7 @@ void SphereShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereShape3D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &SphereShape3D::get_radius);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_radius", "get_radius");
}
SphereShape3D::SphereShape3D() :
diff --git a/scene/resources/sphere_shape_3d.h b/scene/resources/sphere_shape_3d.h
index 78d45b5058..5cad67aea5 100644
--- a/scene/resources/sphere_shape_3d.h
+++ b/scene/resources/sphere_shape_3d.h
@@ -46,7 +46,7 @@ public:
void set_radius(float p_radius);
float get_radius() const;
- virtual Vector<Vector3> get_debug_mesh_lines() override;
+ virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
SphereShape3D();
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index eb65f10ec9..14197c6c68 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -130,18 +130,6 @@ Ref<Texture2D> StyleBoxTexture::get_texture() const {
return texture;
}
-void StyleBoxTexture::set_normal_map(Ref<Texture2D> p_normal_map) {
- if (normal_map == p_normal_map) {
- return;
- }
- normal_map = p_normal_map;
- emit_changed();
-}
-
-Ref<Texture2D> StyleBoxTexture::get_normal_map() const {
- return normal_map;
-}
-
void StyleBoxTexture::set_margin_size(Margin p_margin, float p_size) {
ERR_FAIL_INDEX((int)p_margin, 4);
@@ -187,12 +175,7 @@ void StyleBoxTexture::draw(RID p_canvas_item, const Rect2 &p_rect) const {
rect.size.x += expand_margin[MARGIN_LEFT] + expand_margin[MARGIN_RIGHT];
rect.size.y += expand_margin[MARGIN_TOP] + expand_margin[MARGIN_BOTTOM];
- RID normal_rid;
- if (normal_map.is_valid()) {
- normal_rid = normal_map->get_rid();
- }
-
- 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, normal_rid);
+ 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);
}
void StyleBoxTexture::set_draw_center(bool p_enabled) {
@@ -245,6 +228,7 @@ void StyleBoxTexture::set_region_rect(const Rect2 &p_region_rect) {
region_rect = p_region_rect;
emit_changed();
+ _change_notify("region");
}
Rect2 StyleBoxTexture::get_region_rect() const {
@@ -287,9 +271,6 @@ void StyleBoxTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &StyleBoxTexture::set_texture);
ClassDB::bind_method(D_METHOD("get_texture"), &StyleBoxTexture::get_texture);
- ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &StyleBoxTexture::set_normal_map);
- ClassDB::bind_method(D_METHOD("get_normal_map"), &StyleBoxTexture::get_normal_map);
-
ClassDB::bind_method(D_METHOD("set_margin_size", "margin", "size"), &StyleBoxTexture::set_margin_size);
ClassDB::bind_method(D_METHOD("get_margin_size", "margin"), &StyleBoxTexture::get_margin_size);
@@ -316,7 +297,6 @@ void StyleBoxTexture::_bind_methods() {
ADD_SIGNAL(MethodInfo("texture_changed"));
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_map", "get_normal_map");
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);
@@ -424,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;
@@ -910,7 +880,7 @@ void StyleBoxFlat::_bind_methods() {
ADD_GROUP("Shadow", "shadow_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size"), "set_shadow_size", "get_shadow_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_shadow_size", "get_shadow_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset"), "set_shadow_offset", "get_shadow_offset");
ADD_GROUP("Anti Aliasing", "anti_aliasing_");
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index 3d29e3bd0f..7dd806e840 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -31,7 +31,7 @@
#ifndef STYLE_BOX_H
#define STYLE_BOX_H
-#include "core/resource.h"
+#include "core/io/resource.h"
#include "scene/resources/texture.h"
#include "servers/rendering_server.h"
@@ -90,7 +90,6 @@ private:
float margin[4];
Rect2 region_rect;
Ref<Texture2D> texture;
- Ref<Texture2D> normal_map;
bool draw_center;
Color modulate;
AxisStretchMode axis_h;
@@ -115,9 +114,6 @@ public:
void set_texture(Ref<Texture2D> p_texture);
Ref<Texture2D> get_texture() const;
- void set_normal_map(Ref<Texture2D> p_normal_map);
- Ref<Texture2D> get_normal_map() const;
-
void set_draw_center(bool p_enabled);
bool is_draw_center_enabled() const;
virtual Size2 get_center_size() const override;
@@ -188,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;
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 1a2dcc84bb..7899627048 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -30,8 +30,6 @@
#include "surface_tool.h"
-#include "core/method_bind_ext.gen.inc"
-
#define _VERTEX_SNAP 0.0001
#define EQ_VERTEX_DIST 0.00001
@@ -76,6 +74,12 @@ 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;
+ }
+ }
+
return true;
}
@@ -89,6 +93,7 @@ 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);
return h;
}
@@ -113,8 +118,11 @@ 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;
+ 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
@@ -165,7 +173,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));
@@ -174,7 +182,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));
@@ -183,7 +191,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));
@@ -191,7 +199,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));
@@ -199,7 +207,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));
@@ -207,19 +215,40 @@ 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;
}
@@ -240,15 +269,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]); \
}
@@ -360,18 +389,157 @@ Array SurfaceTool::commit_to_arrays() {
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();
+
+ int idx = 0;
+ for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
+ const Vertex &v = E->get();
+ 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();
+
+ int idx = 0;
+ for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
+ const Vertex &v = E->get();
+ 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();
+
+ int idx = 0;
+ for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
+ const Vertex &v = E->get();
+ 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();
+
+ int idx = 0;
+ for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
+ const Vertex &v = E->get();
+ 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();
+
+ int idx = 0;
+ for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
+ const Vertex &v = E->get();
+ 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();
+
+ int idx = 0;
+ for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
+ const Vertex &v = E->get();
+ 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();
+
+ int idx = 0;
+ for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
+ const Vertex &v = E->get();
+ 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();
+
+ int idx = 0;
+ for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
+ const Vertex &v = E->get();
+ 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) {
+ for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx += count) {
const Vertex &v = E->get();
- ERR_CONTINUE(v.bones.size() != 4);
+ ERR_CONTINUE(v.bones.size() != count);
- for (int j = 0; j < 4; j++) {
+ for (int j = 0; j < count; j++) {
w[idx + j] = v.bones[j];
}
}
@@ -381,15 +549,17 @@ 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) {
+ for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx += count) {
const Vertex &v = E->get();
- ERR_CONTINUE(v.weights.size() != 4);
+ ERR_CONTINUE(v.weights.size() != count);
- for (int j = 0; j < 4; j++) {
+ for (int j = 0; j < count; j++) {
w[idx + j] = v.weights[j];
}
}
@@ -494,13 +664,13 @@ void SurfaceTool::deindex() {
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, List<Vertex> *r_vertex, List<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> SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays, uint32_t *r_format) {
Vector<SurfaceTool::Vertex> ret;
Vector<Vector3> varr = p_arrays[RS::ARRAY_VERTEX];
@@ -511,9 +681,13 @@ 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) {
+ if (r_format) {
+ *r_format = 0;
+ }
return ret;
}
@@ -536,12 +710,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;
+ lformat |= RS::ARRAY_FORMAT_WEIGHTS;
+
+ wcount = barr.size() / varr.size();
+ if (wcount == 8) {
+ lformat |= RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS;
+ }
}
+
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;
@@ -567,112 +769,46 @@ Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_array
}
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;
}
+ 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);
}
- 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;
+ if (r_format) {
+ *r_format = lformat;
}
- 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;
- }
+ return ret;
+}
- 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];
- }
- 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;
- }
+void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, uint32_t &lformat) {
+ Vector<Vertex> arrays = create_vertex_array_from_triangle_arrays(arr, &lformat);
+ ERR_FAIL_COND(arrays.size() == 0);
- r_vertex->push_back(v);
+ for (int i = 0; i < arrays.size(); i++) {
+ r_vertex->push_back(arrays[i]);
}
//indices
@@ -727,7 +863,7 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const
format = 0;
}
- int nformat;
+ uint32_t nformat;
List<Vertex> nvertices;
List<int> nindices;
_create_list(p_existing, p_surface, &nvertices, &nindices, nformat);
@@ -977,19 +1113,48 @@ void SurfaceTool::clear() {
vertex_array.clear();
smooth_groups.clear();
material.unref();
+ 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::_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("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("add_smooth_group", "smooth"), &SurfaceTool::add_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>()));
@@ -1008,13 +1173,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..4a5c7d990c 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -49,12 +49,30 @@ public:
Vector2 uv2;
Vector<int> bones;
Vector<float> weights;
+ Color custom[RS::ARRAY_CUSTOM_COUNT];
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
+ };
+
private:
struct VertexHasher {
static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx);
@@ -71,7 +89,7 @@ private:
bool begun;
bool first;
Mesh::PrimitiveType primitive;
- int format;
+ uint32_t format;
Ref<Material> material;
//arrays
List<Vertex> vertex_array;
@@ -87,8 +105,14 @@ private:
Vector<float> last_weights;
Plane last_tangent;
- 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);
+ SkinWeightCount skin_weights;
+
+ Color last_custom[RS::ARRAY_CUSTOM_COUNT];
+
+ CustomFormat last_custom_format[RS::ARRAY_CUSTOM_COUNT];
+
+ void _create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, uint32_t &lformat);
+ void _create_list(const Ref<Mesh> &p_existing, int p_surface, List<Vertex> *r_vertex, List<int> *r_index, uint32_t &lformat);
//mikktspace callbacks
static int mikktGetNumFaces(const SMikkTSpaceContext *pContext);
@@ -103,18 +127,26 @@ 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 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_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>());
void add_index(int p_index);
@@ -131,14 +163,17 @@ public:
List<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 Vector<Vertex> create_vertex_array_from_triangle_arrays(const Array &p_arrays, 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 4479b822c0..e3e4373fa9 100644
--- a/scene/resources/syntax_highlighter.cpp
+++ b/scene/resources/syntax_highlighter.cpp
@@ -30,7 +30,7 @@
#include "syntax_highlighter.h"
-#include "core/script_language.h"
+#include "core/object/script_language.h"
#include "scene/gui/text_edit.h"
Dictionary SyntaxHighlighter::get_line_syntax_highlighting(int p_line) {
@@ -53,13 +53,13 @@ Dictionary SyntaxHighlighter::get_line_syntax_highlighting(int p_line) {
return color_map;
}
-void SyntaxHighlighter::_line_edited_from(int p_line) {
+void SyntaxHighlighter::_lines_edited_from(int p_from_line, int p_to_line) {
if (highlighting_cache.size() < 1) {
return;
}
int cache_size = highlighting_cache.back()->key();
- for (int i = p_line - 1; i <= cache_size; i++) {
+ for (int i = MIN(p_from_line, p_to_line) - 1; i <= cache_size; i++) {
if (highlighting_cache.has(i)) {
highlighting_cache.erase(i);
}
@@ -93,7 +93,7 @@ void SyntaxHighlighter::update_cache() {
void SyntaxHighlighter::set_text_edit(TextEdit *p_text_edit) {
if (text_edit && ObjectDB::get_instance(text_edit_instance_id)) {
- text_edit->disconnect("line_edited_from", callable_mp(this, &SyntaxHighlighter::_line_edited_from));
+ text_edit->disconnect("lines_edited_from", callable_mp(this, &SyntaxHighlighter::_lines_edited_from));
}
text_edit = p_text_edit;
@@ -101,7 +101,7 @@ void SyntaxHighlighter::set_text_edit(TextEdit *p_text_edit) {
return;
}
text_edit_instance_id = text_edit->get_instance_id();
- text_edit->connect("line_edited_from", callable_mp(this, &SyntaxHighlighter::_line_edited_from));
+ text_edit->connect("lines_edited_from", callable_mp(this, &SyntaxHighlighter::_lines_edited_from));
update_cache();
}
@@ -125,11 +125,11 @@ void SyntaxHighlighter::_bind_methods() {
////////////////////////////////////////////////////////////////////////////////
-static bool _is_char(CharType c) {
+static bool _is_char(char32_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
-static bool _is_hex_symbol(CharType c) {
+static bool _is_hex_symbol(char32_t c) {
return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
}
@@ -149,6 +149,13 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) {
color_region_cache[p_line] = -1;
int in_region = -1;
if (p_line != 0) {
+ int prev_region_line = p_line - 1;
+ while (prev_region_line > 0 && !color_region_cache.has(prev_region_line)) {
+ prev_region_line--;
+ }
+ for (int i = prev_region_line; i < p_line - 1; i++) {
+ get_line_syntax_highlighting(i);
+ }
if (!color_region_cache.has(p_line - 1)) {
get_line_syntax_highlighting(p_line - 1);
}
@@ -195,7 +202,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) {
/* search the line */
bool match = true;
- const CharType *start_key = color_regions[c].start_key.c_str();
+ const char32_t *start_key = color_regions[c].start_key.get_data();
for (int k = 0; k < start_key_length; k++) {
if (start_key[k] != str[from + k]) {
match = false;
@@ -229,18 +236,16 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) {
/* if we are in one find the end key */
if (in_region != -1) {
- /* check there is enough room */
- int chars_left = line_length - from;
- int end_key_length = color_regions[in_region].end_key.length();
- if (chars_left < end_key_length) {
- continue;
- }
-
/* search the line */
int region_end_index = -1;
- const CharType *end_key = color_regions[in_region].start_key.c_str();
+ int end_key_length = color_regions[in_region].end_key.length();
+ const char32_t *end_key = color_regions[in_region].end_key.get_data();
for (; from < line_length; from++) {
- if (!is_a_symbol) {
+ if (line_length - from < end_key_length) {
+ break;
+ }
+
+ if (!is_symbol(str[from])) {
continue;
}
@@ -249,9 +254,10 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) {
continue;
}
+ region_end_index = from;
for (int k = 0; k < end_key_length; k++) {
- if (end_key[k] == str[from + k]) {
- region_end_index = from;
+ if (end_key[k] != str[from + k]) {
+ region_end_index = -1;
break;
}
}
@@ -265,7 +271,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) {
highlighter_info["color"] = color_regions[in_region].color;
color_map[j] = highlighter_info;
- j = from;
+ j = from + (end_key_length - 1);
if (region_end_index == -1) {
color_region_cache[p_line] = in_region;
}
@@ -484,8 +490,12 @@ void CodeHighlighter::add_color_region(const String &p_start_key, const String &
}
}
+ int at = 0;
for (int i = 0; i < color_regions.size(); i++) {
ERR_FAIL_COND_MSG(color_regions[i].start_key == p_start_key, "color region with start key '" + p_start_key + "' already exists.");
+ if (p_start_key.length() < color_regions[i].start_key.length()) {
+ at++;
+ }
}
ColorRegion color_region;
@@ -493,7 +503,7 @@ void CodeHighlighter::add_color_region(const String &p_start_key, const String &
color_region.start_key = p_start_key;
color_region.end_key = p_end_key;
color_region.line_only = p_line_only || p_end_key == "";
- color_regions.push_back(color_region);
+ color_regions.insert(at, color_region);
clear_highlighting_cache();
}
diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h
index 40a8870b45..62865920d3 100644
--- a/scene/resources/syntax_highlighter.h
+++ b/scene/resources/syntax_highlighter.h
@@ -31,7 +31,7 @@
#ifndef SYNTAX_HIGHLIGHTER_H
#define SYNTAX_HIGHLIGHTER_H
-#include "core/resource.h"
+#include "core/io/resource.h"
class TextEdit;
@@ -40,7 +40,7 @@ class SyntaxHighlighter : public Resource {
private:
Map<int, Dictionary> highlighting_cache;
- void _line_edited_from(int p_line);
+ void _lines_edited_from(int p_from_line, int p_to_line);
protected:
ObjectID text_edit_instance_id; // For validity check
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
new file mode 100644
index 0000000000..cc9b6758b6
--- /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-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 "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.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..6ed3558bd9
--- /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-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 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..fd6dd071eb
--- /dev/null
+++ b/scene/resources/text_paragraph.cpp
@@ -0,0 +1,532 @@
+/*************************************************************************/
+/* text_paragraph.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 "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("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_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("draw", "canvas", "pos", "color"), &TextParagraph::draw, DEFVAL(Color(1, 1, 1)));
+ ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "outline_size", "color"), &TextParagraph::draw_outline, DEFVAL(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("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.empty()) {
+ TS->shaped_text_tab_align(rid, tab_stops);
+ }
+
+ Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width, 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);
+ if (!tab_stops.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];
+}
+
+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);
+}
+
+void TextParagraph::set_preserve_invalid(bool p_enabled) {
+ TS->shaped_text_set_preserve_invalid(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);
+ 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);
+ 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);
+ dirty_lines = true;
+}
+
+TextServer::Orientation TextParagraph::get_orientation() const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ return TS->shaped_text_get_orientation(rid);
+}
+
+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]);
+}
+
+void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ Vector2 ofs = p_pos;
+ for (int i = 0; i < lines.size(); i++) {
+ 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;
+ } else {
+ ofs.y = p_pos.y;
+ ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
+ }
+ 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((width - length) / 2.0);
+ } else {
+ ofs.y += Math::floor((width - length) / 2.0);
+ }
+ } break;
+ case HALIGN_RIGHT: {
+ if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += width - length;
+ } else {
+ ofs.y += 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 + 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 {
+ const_cast<TextParagraph *>(this)->_shape_lines();
+ Vector2 ofs = p_pos;
+ for (int i = 0; i < lines.size(); i++) {
+ 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;
+ } else {
+ ofs.y = p_pos.y;
+ ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
+ }
+ 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((width - length) / 2.0);
+ } else {
+ ofs.y += Math::floor((width - length) / 2.0);
+ }
+ } break;
+ case HALIGN_RIGHT: {
+ if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += width - length;
+ } else {
+ ofs.y += 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 + 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_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();
+}
+
+TextParagraph::~TextParagraph() {
+ for (int i = 0; i < lines.size(); i++) {
+ TS->free(lines[i]);
+ }
+ lines.clear();
+ TS->free(rid);
+}
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
new file mode 100644
index 0000000000..f7f49e9058
--- /dev/null
+++ b/scene/resources/text_paragraph.h
@@ -0,0 +1,126 @@
+/*************************************************************************/
+/* text_paragraph.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 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 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;
+
+ 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 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;
+
+ 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;
+
+ 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;
+
+ 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 5681613c04..1507537cd0 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -32,7 +32,6 @@
#include "core/core_string_names.h"
#include "core/io/image_loader.h"
-#include "core/method_bind_ext.gen.inc"
#include "core/os/os.h"
#include "mesh.h"
#include "scene/resources/bit_map.h"
@@ -46,28 +45,21 @@ bool Texture2D::is_pixel_opaque(int p_x, int p_y) const {
return true;
}
-void Texture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, get_size()), get_rid(), false, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+void Texture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, get_size()), get_rid(), false, p_modulate, p_transpose);
}
-void Texture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, get_rid(), p_tile, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+void Texture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, get_rid(), p_tile, p_modulate, p_transpose);
}
-void Texture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, get_rid(), p_src_rect, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_clip_uv, p_texture_filter, p_texture_repeat);
+void Texture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, get_rid(), p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
bool Texture2D::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
r_rect = p_rect;
r_src_rect = p_src_rect;
-
return true;
}
@@ -76,9 +68,9 @@ void Texture2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_height"), &Texture2D::get_height);
ClassDB::bind_method(D_METHOD("get_size"), &Texture2D::get_size);
ClassDB::bind_method(D_METHOD("has_alpha"), &Texture2D::has_alpha);
- ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "modulate", "transpose", "normal_map", "specular_map", "specular_color_shininess", "texture_filter", "texture_repeat"), &Texture2D::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT), DEFVAL(RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT));
- ClassDB::bind_method(D_METHOD("draw_rect", "canvas_item", "rect", "tile", "modulate", "transpose", "normal_map", "specular_map", "specular_color_shininess", "texture_filter", "texture_repeat"), &Texture2D::draw_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT), DEFVAL(RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT));
- ClassDB::bind_method(D_METHOD("draw_rect_region", "canvas_item", "rect", "src_rect", "modulate", "transpose", "normal_map", "specular_map", "specular_color_shininess", "texture_filter", "texture_repeat", "clip_uv"), &Texture2D::draw_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(Color(1, 1, 1, 1)), DEFVAL(RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT), DEFVAL(RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "modulate", "transpose"), &Texture2D::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("draw_rect", "canvas_item", "rect", "tile", "modulate", "transpose"), &Texture2D::draw_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("draw_rect_region", "canvas_item", "rect", "src_rect", "modulate", "transpose", "clip_uv"), &Texture2D::draw_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_data"), &Texture2D::get_data);
ADD_GROUP("", "");
@@ -159,7 +151,7 @@ void ImageTexture::_reload_hook(const RID &p_hook) {
}
void ImageTexture::create_from_image(const Ref<Image> &p_image) {
- ERR_FAIL_COND(p_image.is_null());
+ ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image");
w = p_image->get_width();
h = p_image->get_height();
format = p_image->get_format();
@@ -182,11 +174,14 @@ Image::Format ImageTexture::get_format() const {
}
void ImageTexture::update(const Ref<Image> &p_image, bool p_immediate) {
- ERR_FAIL_COND(p_image.is_null());
- ERR_FAIL_COND(texture.is_null());
- ERR_FAIL_COND(p_image->get_width() != w || p_image->get_height() != h);
- ERR_FAIL_COND(p_image->get_format() != format);
- ERR_FAIL_COND(mipmaps != p_image->has_mipmaps());
+ ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image");
+ ERR_FAIL_COND_MSG(texture.is_null(), "Texture is not initialized.");
+ ERR_FAIL_COND_MSG(p_image->get_width() != w || p_image->get_height() != h,
+ "The new image dimensions must match the texture size.");
+ ERR_FAIL_COND_MSG(p_image->get_format() != format,
+ "The new image format must match the texture's image format.");
+ ERR_FAIL_COND_MSG(mipmaps != p_image->has_mipmaps(),
+ "The new image mipmaps configuration must match the texture's image mipmaps configuration");
if (p_immediate) {
RenderingServer::get_singleton()->texture_2d_update_immediate(texture, p_image);
@@ -233,31 +228,25 @@ bool ImageTexture::has_alpha() const {
return (format == Image::FORMAT_LA8 || format == Image::FORMAT_RGBA8);
}
-void ImageTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void ImageTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);
}
-void ImageTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void ImageTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
}
-void ImageTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
+void ImageTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_clip_uv, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const {
@@ -512,7 +501,7 @@ Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &t
ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER);
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
uint8_t header[4];
f->get_buffer(header, 4);
@@ -652,31 +641,25 @@ RID StreamTexture2D::get_rid() const {
return texture;
}
-void StreamTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void StreamTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);
}
-void StreamTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void StreamTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
}
-void StreamTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
+void StreamTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
if ((w | h) == 0) {
return;
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, p_clip_uv, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
}
bool StreamTexture2D::has_alpha() const {
@@ -791,8 +774,312 @@ String ResourceFormatLoaderStreamTexture2D::get_resource_type(const String &p_pa
return "";
}
+////////////////////////////////////
+
+TypedArray<Image> Texture3D::_get_data() const {
+ Vector<Ref<Image>> data = get_data();
+
+ TypedArray<Image> ret;
+ ret.resize(data.size());
+ for (int i = 0; i < data.size(); i++) {
+ ret[i] = data[i];
+ }
+ return ret;
+}
+
+void Texture3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_format"), &Texture3D::get_format);
+ ClassDB::bind_method(D_METHOD("get_width"), &Texture3D::get_width);
+ ClassDB::bind_method(D_METHOD("get_height"), &Texture3D::get_height);
+ ClassDB::bind_method(D_METHOD("get_depth"), &Texture3D::get_depth);
+ ClassDB::bind_method(D_METHOD("has_mipmaps"), &Texture3D::has_mipmaps);
+ ClassDB::bind_method(D_METHOD("get_data"), &Texture3D::_get_data);
+}
//////////////////////////////////////////
+Image::Format ImageTexture3D::get_format() const {
+ return format;
+}
+int ImageTexture3D::get_width() const {
+ return width;
+}
+int ImageTexture3D::get_height() const {
+ return height;
+}
+int ImageTexture3D::get_depth() const {
+ return depth;
+}
+bool ImageTexture3D::has_mipmaps() const {
+ return mipmaps;
+}
+
+Error ImageTexture3D::_create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const TypedArray<Image> &p_data) {
+ Vector<Ref<Image>> images;
+ images.resize(p_data.size());
+ for (int i = 0; i < images.size(); i++) {
+ images.write[i] = p_data[i];
+ }
+ return create(p_format, p_width, p_height, p_depth, p_mipmaps, images);
+}
+
+void ImageTexture3D::_update(const TypedArray<Image> &p_data) {
+ Vector<Ref<Image>> images;
+ images.resize(p_data.size());
+ for (int i = 0; i < images.size(); i++) {
+ images.write[i] = p_data[i];
+ }
+ return update(images);
+}
+
+Error ImageTexture3D::create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) {
+ RID tex = RenderingServer::get_singleton()->texture_3d_create(p_format, p_width, p_height, p_depth, p_mipmaps, p_data);
+ ERR_FAIL_COND_V(tex.is_null(), ERR_CANT_CREATE);
+
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_replace(texture, tex);
+ }
+
+ return OK;
+}
+
+void ImageTexture3D::update(const Vector<Ref<Image>> &p_data) {
+ ERR_FAIL_COND(!texture.is_valid());
+ RenderingServer::get_singleton()->texture_3d_update(texture, p_data);
+}
+
+Vector<Ref<Image>> ImageTexture3D::get_data() const {
+ ERR_FAIL_COND_V(!texture.is_valid(), Vector<Ref<Image>>());
+ return RS::get_singleton()->texture_3d_get(texture);
+}
+
+RID ImageTexture3D::get_rid() const {
+ if (!texture.is_valid()) {
+ texture = RS::get_singleton()->texture_3d_placeholder_create();
+ }
+ return texture;
+}
+void ImageTexture3D::set_path(const String &p_path, bool p_take_over) {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ Resource::set_path(p_path, p_take_over);
+}
+
+void ImageTexture3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create", "format", "width", "height", "depth", "use_mipmaps", "data"), &ImageTexture3D::_create);
+ ClassDB::bind_method(D_METHOD("update", "data"), &ImageTexture3D::_update);
+}
+
+ImageTexture3D::ImageTexture3D() {
+}
+
+ImageTexture3D::~ImageTexture3D() {
+ if (texture.is_valid()) {
+ RS::get_singleton()->free(texture);
+ }
+}
+
+////////////////////////////////////////////
+
+void StreamTexture3D::set_path(const String &p_path, bool p_take_over) {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ Resource::set_path(p_path, p_take_over);
+}
+
+Image::Format StreamTexture3D::get_format() const {
+ return format;
+}
+
+Error StreamTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps) {
+ FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
+
+ uint8_t header[4];
+ f->get_buffer(header, 4);
+ ERR_FAIL_COND_V(header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L', ERR_FILE_UNRECOGNIZED);
+
+ //stored as stream textures (used for lossless and lossy compression)
+ uint32_t version = f->get_32();
+
+ if (version > FORMAT_VERSION) {
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new.");
+ }
+
+ r_depth = f->get_32(); //depth
+ f->get_32(); //ignored (mode)
+ f->get_32(); // ignored (data format)
+
+ f->get_32(); //ignored
+ int mipmaps = f->get_32();
+ f->get_32(); //ignored
+ f->get_32(); //ignored
+
+ r_mipmaps = mipmaps != 0;
+
+ r_data.clear();
+
+ 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);
+ if (i == 0) {
+ r_format = image->get_format();
+ r_width = image->get_width();
+ r_height = image->get_height();
+ }
+ r_data.push_back(image);
+ }
+
+ return OK;
+}
+
+Error StreamTexture3D::load(const String &p_path) {
+ Vector<Ref<Image>> data;
+
+ int tw, th, td;
+ Image::Format tfmt;
+ bool tmm;
+
+ Error err = _load_data(p_path, data, tfmt, tw, th, td, tmm);
+ if (err) {
+ return err;
+ }
+
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data);
+ }
+
+ w = tw;
+ h = th;
+ d = td;
+ mipmaps = tmm;
+ format = tfmt;
+
+ path_to_file = p_path;
+
+ if (get_path() == String()) {
+ //temporarily set path if no path set for resource, helps find errors
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ _change_notify();
+ emit_changed();
+ return OK;
+}
+
+String StreamTexture3D::get_load_path() const {
+ return path_to_file;
+}
+
+int StreamTexture3D::get_width() const {
+ return w;
+}
+
+int StreamTexture3D::get_height() const {
+ return h;
+}
+
+int StreamTexture3D::get_depth() const {
+ return d;
+}
+
+bool StreamTexture3D::has_mipmaps() const {
+ return mipmaps;
+}
+
+RID StreamTexture3D::get_rid() const {
+ if (!texture.is_valid()) {
+ texture = RS::get_singleton()->texture_3d_placeholder_create();
+ }
+ return texture;
+}
+
+Vector<Ref<Image>> StreamTexture3D::get_data() const {
+ if (texture.is_valid()) {
+ return RS::get_singleton()->texture_3d_get(texture);
+ } else {
+ return Vector<Ref<Image>>();
+ }
+}
+
+void StreamTexture3D::reload_from_file() {
+ String path = get_path();
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ path = ResourceLoader::path_remap(path); //remap for translation
+ path = ResourceLoader::import_remap(path); //remap for import
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ load(path);
+}
+
+void StreamTexture3D::_validate_property(PropertyInfo &property) const {
+}
+
+void StreamTexture3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("load", "path"), &StreamTexture3D::load);
+ ClassDB::bind_method(D_METHOD("get_load_path"), &StreamTexture3D::get_load_path);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path");
+}
+
+StreamTexture3D::StreamTexture3D() {
+ format = Image::FORMAT_MAX;
+ w = 0;
+ h = 0;
+ d = 0;
+ mipmaps = false;
+}
+
+StreamTexture3D::~StreamTexture3D() {
+ if (texture.is_valid()) {
+ RS::get_singleton()->free(texture);
+ }
+}
+
+/////////////////////////////
+
+RES ResourceFormatLoaderStreamTexture3D::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) {
+ Ref<StreamTexture3D> st;
+ st.instance();
+ Error err = st->load(p_path);
+ if (r_error) {
+ *r_error = err;
+ }
+ if (err != OK) {
+ return RES();
+ }
+
+ return st;
+}
+
+void ResourceFormatLoaderStreamTexture3D::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("stex3d");
+}
+
+bool ResourceFormatLoaderStreamTexture3D::handles_type(const String &p_type) const {
+ return p_type == "StreamTexture3D";
+}
+
+String ResourceFormatLoaderStreamTexture3D::get_resource_type(const String &p_path) const {
+ if (p_path.get_extension().to_lower() == "stex3d") {
+ return "StreamTexture3D";
+ }
+ return "";
+}
+
+////////////////////////////////////////////
+
int AtlasTexture::get_width() const {
if (region.size.width == 0) {
if (atlas.is_valid()) {
@@ -900,7 +1187,7 @@ void AtlasTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_clip"), "set_filter_clip", "has_filter_clip");
}
-void AtlasTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void AtlasTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if (!atlas.is_valid()) {
return;
}
@@ -915,12 +1202,10 @@ void AtlasTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_m
rc.size.height = atlas->get_height();
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(p_pos + margin.position, rc.size), atlas->get_rid(), rc, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, filter_clip, p_texture_filter, p_texture_repeat);
+ RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(p_pos + margin.position, rc.size), atlas->get_rid(), rc, p_modulate, p_transpose, filter_clip);
}
-void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if (!atlas.is_valid()) {
return;
}
@@ -938,12 +1223,10 @@ void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile
Vector2 scale = p_rect.size / (region.size + margin.size);
Rect2 dr(p_rect.position + margin.position * scale, rc.size * scale);
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), rc, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, filter_clip, p_texture_filter, p_texture_repeat);
+ RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), rc, p_modulate, p_transpose, filter_clip);
}
-void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
+void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
//this might not necessarily work well if using a rect, needs to be fixed properly
if (!atlas.is_valid()) {
return;
@@ -953,9 +1236,7 @@ void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons
Rect2 src_c;
get_rect_region(p_rect, p_src_rect, dr, src_c);
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), src_c, p_modulate, p_transpose, normal_rid, specular_rid, p_specular_color_shininess, filter_clip, p_texture_filter, p_texture_repeat);
+ RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), src_c, p_modulate, p_transpose, filter_clip);
}
bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
@@ -1060,7 +1341,7 @@ Ref<Texture2D> MeshTexture::get_base_texture() const {
return base_texture;
}
-void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if (mesh.is_null() || base_texture.is_null()) {
return;
}
@@ -1070,12 +1351,10 @@ void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_mo
SWAP(xform.elements[0][1], xform.elements[1][0]);
SWAP(xform.elements[0][0], xform.elements[1][1]);
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
}
-void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if (mesh.is_null() || base_texture.is_null()) {
return;
}
@@ -1094,12 +1373,10 @@ void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile,
SWAP(xform.elements[0][1], xform.elements[1][0]);
SWAP(xform.elements[0][0], xform.elements[1][1]);
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
}
-void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
+void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
if (mesh.is_null() || base_texture.is_null()) {
return;
}
@@ -1118,9 +1395,7 @@ void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const
SWAP(xform.elements[0][1], xform.elements[1][0]);
SWAP(xform.elements[0][0], xform.elements[1][1]);
}
- RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- RID specular_rid = p_specular_map.is_valid() ? p_specular_map->get_rid() : RID();
- RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid(), normal_rid, specular_rid, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
}
bool MeshTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
@@ -1265,14 +1540,14 @@ void LargeTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}
-void LargeTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void LargeTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
for (int i = 0; i < pieces.size(); i++) {
// TODO
- pieces[i].texture->draw(p_canvas_item, pieces[i].offset + p_pos, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ pieces[i].texture->draw(p_canvas_item, pieces[i].offset + p_pos, p_modulate, p_transpose);
}
}
-void LargeTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat) const {
+void LargeTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
//tiling not supported for this
if (size.x == 0 || size.y == 0) {
return;
@@ -1282,11 +1557,11 @@ void LargeTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile
for (int i = 0; i < pieces.size(); i++) {
// TODO
- pieces[i].texture->draw_rect(p_canvas_item, Rect2(pieces[i].offset * scale + p_rect.position, pieces[i].texture->get_size() * scale), false, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, p_texture_filter, p_texture_repeat);
+ pieces[i].texture->draw_rect(p_canvas_item, Rect2(pieces[i].offset * scale + p_rect.position, pieces[i].texture->get_size() * scale), false, p_modulate, p_transpose);
}
}
-void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture2D> &p_normal_map, const Ref<Texture2D> &p_specular_map, const Color &p_specular_color_shininess, RS::CanvasItemTextureFilter p_texture_filter, RS::CanvasItemTextureRepeat p_texture_repeat, bool p_clip_uv) const {
+void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
//tiling not supported for this
if (p_src_rect.size.x == 0 || p_src_rect.size.y == 0) {
return;
@@ -1305,7 +1580,7 @@ void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons
target.size *= scale;
target.position = p_rect.position + (p_src_rect.position + rect.position) * scale;
local.position -= rect.position;
- pieces[i].texture->draw_rect_region(p_canvas_item, target, local, p_modulate, p_transpose, p_normal_map, p_specular_map, p_specular_color_shininess, p_texture_filter, p_texture_repeat, false);
+ pieces[i].texture->draw_rect_region(p_canvas_item, target, local, p_modulate, p_transpose, false);
}
}
@@ -1435,12 +1710,6 @@ CurveTexture::~CurveTexture() {
//////////////////
-//setter and getter names for property serialization
-#define COLOR_RAMP_GET_OFFSETS "get_offsets"
-#define COLOR_RAMP_GET_COLORS "get_colors"
-#define COLOR_RAMP_SET_OFFSETS "set_offsets"
-#define COLOR_RAMP_SET_COLORS "set_colors"
-
GradientTexture::GradientTexture() {
update_pending = false;
width = 2048;
@@ -2059,7 +2328,7 @@ Error StreamTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>>
ERR_FAIL_COND_V(images.size() != 0, ERR_INVALID_PARAMETER);
FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
uint8_t header[4];
f->get_buffer(header, 4);
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index d439d34c95..a8d8b785fa 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -31,13 +31,13 @@
#ifndef TEXTURE_H
#define TEXTURE_H
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/math/rect2.h"
#include "core/os/file_access.h"
#include "core/os/mutex.h"
#include "core/os/rw_lock.h"
#include "core/os/thread_safe.h"
-#include "core/resource.h"
#include "scene/resources/curve.h"
#include "scene/resources/gradient.h"
#include "servers/camera_server.h"
@@ -66,9 +66,9 @@ public:
virtual bool has_alpha() const = 0;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const;
virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const;
virtual Ref<Image> get_data() const { return Ref<Image>(); }
@@ -115,9 +115,9 @@ public:
virtual RID get_rid() const override;
bool has_alpha() const override;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
bool is_pixel_opaque(int p_x, int p_y) const override;
@@ -194,9 +194,9 @@ public:
virtual void set_path(const String &p_path, bool p_take_over) override;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
virtual bool has_alpha() const override;
bool is_pixel_opaque(int p_x, int p_y) const override;
@@ -246,9 +246,9 @@ public:
void set_filter_clip(const bool p_enable);
bool has_filter_clip() const;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const override;
bool is_pixel_opaque(int p_x, int p_y) const override;
@@ -285,9 +285,9 @@ public:
void set_base_texture(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_base_texture() const;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const override;
bool is_pixel_opaque(int p_x, int p_y) const override;
@@ -331,9 +331,9 @@ public:
Ref<Texture2D> get_piece_texture(int p_idx) const;
Ref<Image> to_image() const;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture2D> &p_normal_map = Ref<Texture2D>(), const Ref<Texture2D> &p_specular_map = Ref<Texture2D>(), const Color &p_specular_color_shininess = Color(1, 1, 1, 1), RS::CanvasItemTextureFilter p_texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, RS::CanvasItemTextureRepeat p_texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, bool p_clip_uv = true) const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
bool is_pixel_opaque(int p_x, int p_y) const override;
@@ -515,6 +515,122 @@ public:
virtual String get_resource_type(const String &p_path) const;
};
+class Texture3D : public Texture {
+ GDCLASS(Texture3D, Texture);
+
+protected:
+ static void _bind_methods();
+
+ TypedArray<Image> _get_data() const;
+
+public:
+ virtual Image::Format get_format() const = 0;
+ virtual int get_width() const = 0;
+ virtual int get_height() const = 0;
+ virtual int get_depth() const = 0;
+ virtual bool has_mipmaps() const = 0;
+ virtual Vector<Ref<Image>> get_data() const = 0;
+};
+
+class ImageTexture3D : public Texture3D {
+ GDCLASS(ImageTexture3D, Texture3D);
+
+ mutable RID texture;
+
+ Image::Format format = Image::FORMAT_MAX;
+ int width = 1;
+ int height = 1;
+ int depth = 1;
+ bool mipmaps = false;
+
+protected:
+ static void _bind_methods();
+
+ Error _create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const TypedArray<Image> &p_data);
+ void _update(const TypedArray<Image> &p_data);
+
+public:
+ virtual Image::Format get_format() const override;
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual int get_depth() const override;
+ virtual bool has_mipmaps() const override;
+
+ Error create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data);
+ void update(const Vector<Ref<Image>> &p_data);
+ virtual Vector<Ref<Image>> get_data() const override;
+
+ virtual RID get_rid() const override;
+ virtual void set_path(const String &p_path, bool p_take_over = false) override;
+
+ ImageTexture3D();
+ ~ImageTexture3D();
+};
+
+class StreamTexture3D : public Texture3D {
+ GDCLASS(StreamTexture3D, Texture3D);
+
+public:
+ enum DataFormat {
+ DATA_FORMAT_IMAGE,
+ DATA_FORMAT_LOSSLESS,
+ DATA_FORMAT_LOSSY,
+ DATA_FORMAT_BASIS_UNIVERSAL,
+ };
+
+ enum {
+ FORMAT_VERSION = 1
+ };
+
+ enum FormatBits {
+ FORMAT_MASK_IMAGE_FORMAT = (1 << 20) - 1,
+ FORMAT_BIT_LOSSLESS = 1 << 20,
+ FORMAT_BIT_LOSSY = 1 << 21,
+ FORMAT_BIT_STREAM = 1 << 22,
+ FORMAT_BIT_HAS_MIPMAPS = 1 << 23,
+ };
+
+private:
+ Error _load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps);
+ String path_to_file;
+ mutable RID texture;
+ Image::Format format;
+ int w, h, d;
+ bool mipmaps;
+
+ virtual void reload_from_file() override;
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const override;
+
+public:
+ Image::Format get_format() const override;
+ Error load(const String &p_path);
+ String get_load_path() const;
+
+ int get_width() const override;
+ int get_height() const override;
+ int get_depth() const override;
+ virtual bool has_mipmaps() const override;
+ virtual RID get_rid() const override;
+
+ virtual void set_path(const String &p_path, bool p_take_over) override;
+
+ virtual Vector<Ref<Image>> get_data() const override;
+
+ StreamTexture3D();
+ ~StreamTexture3D();
+};
+
+class ResourceFormatLoaderStreamTexture3D : 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;
+};
+
class CurveTexture : public Texture2D {
GDCLASS(CurveTexture, Texture2D);
RES_BASE_EXTENSION("curvetex")
@@ -548,7 +664,6 @@ public:
};
/*
enum CubeMapSide {
-
CUBEMAP_LEFT,
CUBEMAP_RIGHT,
CUBEMAP_BOTTOM,
@@ -632,11 +747,12 @@ class AnimatedTexture : public Texture2D {
//use readers writers lock for this, since its far more times read than written to
RWLock *rw_lock;
-private:
+public:
enum {
MAX_FRAMES = 256
};
+private:
RID proxy_ph;
RID proxy;
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index 6a85d357ff..6a752d32e7 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -30,17 +30,17 @@
#include "theme.h"
#include "core/os/file_access.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
void Theme::_emit_theme_changed() {
emit_changed();
}
-Vector<String> Theme::_get_icon_list(const String &p_type) const {
+Vector<String> Theme::_get_icon_list(const String &p_node_type) const {
Vector<String> ilret;
List<StringName> il;
- get_icon_list(p_type, &il);
+ get_icon_list(p_node_type, &il);
ilret.resize(il.size());
int i = 0;
@@ -51,11 +51,11 @@ Vector<String> Theme::_get_icon_list(const String &p_type) const {
return ilret;
}
-Vector<String> Theme::_get_stylebox_list(const String &p_type) const {
+Vector<String> Theme::_get_stylebox_list(const String &p_node_type) const {
Vector<String> ilret;
List<StringName> il;
- get_stylebox_list(p_type, &il);
+ get_stylebox_list(p_node_type, &il);
ilret.resize(il.size());
int i = 0;
@@ -81,11 +81,11 @@ Vector<String> Theme::_get_stylebox_types() const {
return ilret;
}
-Vector<String> Theme::_get_font_list(const String &p_type) const {
+Vector<String> Theme::_get_font_list(const String &p_node_type) const {
Vector<String> ilret;
List<StringName> il;
- get_font_list(p_type, &il);
+ get_font_list(p_node_type, &il);
ilret.resize(il.size());
int i = 0;
@@ -96,11 +96,11 @@ Vector<String> Theme::_get_font_list(const String &p_type) const {
return ilret;
}
-Vector<String> Theme::_get_color_list(const String &p_type) const {
+Vector<String> Theme::_get_font_size_list(const String &p_node_type) const {
Vector<String> ilret;
List<StringName> il;
- get_color_list(p_type, &il);
+ get_font_size_list(p_node_type, &il);
ilret.resize(il.size());
int i = 0;
@@ -111,11 +111,11 @@ Vector<String> Theme::_get_color_list(const String &p_type) const {
return ilret;
}
-Vector<String> Theme::_get_constant_list(const String &p_type) const {
+Vector<String> Theme::_get_color_list(const String &p_node_type) const {
Vector<String> ilret;
List<StringName> il;
- get_constant_list(p_type, &il);
+ get_color_list(p_node_type, &il);
ilret.resize(il.size());
int i = 0;
@@ -126,7 +126,22 @@ Vector<String> Theme::_get_constant_list(const String &p_type) const {
return ilret;
}
-Vector<String> Theme::_get_type_list(const String &p_type) const {
+Vector<String> Theme::_get_constant_list(const String &p_node_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_constant_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_type_list(const String &p_node_type) const {
Vector<String> ilret;
List<StringName> il;
@@ -291,11 +306,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,19 +356,23 @@ void Theme::set_default_font(const Ref<Font> &p_font) {
default_font = p_font;
}
-void Theme::set_icon(const StringName &p_name, const StringName &p_type, const Ref<Texture2D> &p_icon) {
+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());
- bool new_value = !icon_map.has(p_type) || !icon_map[p_type].has(p_name);
+ bool new_value = !icon_map.has(p_node_type) || !icon_map[p_node_type].has(p_name);
- if (icon_map[p_type].has(p_name) && icon_map[p_type][p_name].is_valid()) {
- icon_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ if (icon_map[p_node_type].has(p_name) && icon_map[p_node_type][p_name].is_valid()) {
+ icon_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
- icon_map[p_type][p_name] = p_icon;
+ icon_map[p_node_type][p_name] = p_icon;
if (p_icon.is_valid()) {
- icon_map[p_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ icon_map[p_node_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
if (new_value) {
@@ -346,176 +381,182 @@ void Theme::set_icon(const StringName &p_name, const StringName &p_type, const R
}
}
-Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_type) const {
- if (icon_map.has(p_type) && icon_map[p_type].has(p_name) && icon_map[p_type][p_name].is_valid()) {
- return icon_map[p_type][p_name];
+Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_node_type) const {
+ if (icon_map.has(p_node_type) && icon_map[p_node_type].has(p_name) && icon_map[p_node_type][p_name].is_valid()) {
+ return icon_map[p_node_type][p_name];
} else {
return default_icon;
}
}
-bool Theme::has_icon(const StringName &p_name, const StringName &p_type) const {
- return (icon_map.has(p_type) && icon_map[p_type].has(p_name) && icon_map[p_type][p_name].is_valid());
+bool Theme::has_icon(const StringName &p_name, const StringName &p_node_type) const {
+ return (icon_map.has(p_node_type) && icon_map[p_node_type].has(p_name) && icon_map[p_node_type][p_name].is_valid());
}
-void Theme::clear_icon(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!icon_map.has(p_type));
- ERR_FAIL_COND(!icon_map[p_type].has(p_name));
+void Theme::clear_icon(const StringName &p_name, const StringName &p_node_type) {
+ ERR_FAIL_COND(!icon_map.has(p_node_type));
+ ERR_FAIL_COND(!icon_map[p_node_type].has(p_name));
- if (icon_map[p_type][p_name].is_valid()) {
- icon_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ if (icon_map[p_node_type][p_name].is_valid()) {
+ icon_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
- icon_map[p_type].erase(p_name);
+ icon_map[p_node_type].erase(p_name);
_change_notify();
emit_changed();
}
-void Theme::get_icon_list(StringName p_type, List<StringName> *p_list) const {
+void Theme::get_icon_list(StringName p_node_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!icon_map.has(p_type)) {
+ if (!icon_map.has(p_node_type)) {
return;
}
const StringName *key = nullptr;
- while ((key = icon_map[p_type].next(key))) {
+ while ((key = icon_map[p_node_type].next(key))) {
p_list->push_back(*key);
}
}
-void Theme::set_shader(const StringName &p_name, const StringName &p_type, const Ref<Shader> &p_shader) {
- bool new_value = !shader_map.has(p_type) || !shader_map[p_type].has(p_name);
+void Theme::set_stylebox(const StringName &p_name, const StringName &p_node_type, const Ref<StyleBox> &p_style) {
+ //ERR_FAIL_COND(p_style.is_null());
+
+ bool new_value = !style_map.has(p_node_type) || !style_map[p_node_type].has(p_name);
+
+ if (style_map[p_node_type].has(p_name) && style_map[p_node_type][p_name].is_valid()) {
+ style_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
- shader_map[p_type][p_name] = p_shader;
+ style_map[p_node_type][p_name] = p_style;
+
+ if (p_style.is_valid()) {
+ style_map[p_node_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
if (new_value) {
_change_notify();
- emit_changed();
}
+ emit_changed();
}
-Ref<Shader> Theme::get_shader(const StringName &p_name, const StringName &p_type) const {
- if (shader_map.has(p_type) && shader_map[p_type].has(p_name) && shader_map[p_type][p_name].is_valid()) {
- return shader_map[p_type][p_name];
+Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_node_type) const {
+ if (style_map.has(p_node_type) && style_map[p_node_type].has(p_name) && style_map[p_node_type][p_name].is_valid()) {
+ return style_map[p_node_type][p_name];
} else {
- return nullptr;
+ return default_style;
}
}
-bool Theme::has_shader(const StringName &p_name, const StringName &p_type) const {
- return (shader_map.has(p_type) && shader_map[p_type].has(p_name) && shader_map[p_type][p_name].is_valid());
+bool Theme::has_stylebox(const StringName &p_name, const StringName &p_node_type) const {
+ return (style_map.has(p_node_type) && style_map[p_node_type].has(p_name) && style_map[p_node_type][p_name].is_valid());
}
-void Theme::clear_shader(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!shader_map.has(p_type));
- ERR_FAIL_COND(!shader_map[p_type].has(p_name));
+void Theme::clear_stylebox(const StringName &p_name, const StringName &p_node_type) {
+ ERR_FAIL_COND(!style_map.has(p_node_type));
+ ERR_FAIL_COND(!style_map[p_node_type].has(p_name));
+
+ if (style_map[p_node_type][p_name].is_valid()) {
+ style_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+
+ style_map[p_node_type].erase(p_name);
- shader_map[p_type].erase(p_name);
_change_notify();
emit_changed();
}
-void Theme::get_shader_list(const StringName &p_type, List<StringName> *p_list) const {
+void Theme::get_stylebox_list(StringName p_node_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!shader_map.has(p_type)) {
+ if (!style_map.has(p_node_type)) {
return;
}
const StringName *key = nullptr;
- while ((key = shader_map[p_type].next(key))) {
+ while ((key = style_map[p_node_type].next(key))) {
p_list->push_back(*key);
}
}
-void Theme::set_stylebox(const StringName &p_name, const StringName &p_type, const Ref<StyleBox> &p_style) {
- //ERR_FAIL_COND(p_style.is_null());
+void Theme::get_stylebox_types(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
- bool new_value = !style_map.has(p_type) || !style_map[p_type].has(p_name);
+ const StringName *key = nullptr;
+ while ((key = style_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
- if (style_map[p_type].has(p_name) && style_map[p_type][p_name].is_valid()) {
- style_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+void Theme::set_font(const StringName &p_name, const StringName &p_node_type, const Ref<Font> &p_font) {
+ //ERR_FAIL_COND(p_font.is_null());
+
+ bool new_value = !font_map.has(p_node_type) || !font_map[p_node_type].has(p_name);
+
+ if (font_map[p_node_type][p_name].is_valid()) {
+ font_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
- style_map[p_type][p_name] = p_style;
+ font_map[p_node_type][p_name] = p_font;
- if (p_style.is_valid()) {
- style_map[p_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ if (p_font.is_valid()) {
+ font_map[p_node_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
if (new_value) {
_change_notify();
+ emit_changed();
}
- emit_changed();
}
-Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_type) const {
- if (style_map.has(p_type) && style_map[p_type].has(p_name) && style_map[p_type][p_name].is_valid()) {
- return style_map[p_type][p_name];
+Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_node_type) const {
+ if (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[p_node_type][p_name];
+ } else if (default_theme_font.is_valid()) {
+ return default_theme_font;
} else {
- return default_style;
+ return default_font;
}
}
-bool Theme::has_stylebox(const StringName &p_name, const StringName &p_type) const {
- return (style_map.has(p_type) && style_map[p_type].has(p_name) && style_map[p_type][p_name].is_valid());
+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()) || default_theme_font.is_valid());
}
-void Theme::clear_stylebox(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!style_map.has(p_type));
- ERR_FAIL_COND(!style_map[p_type].has(p_name));
+void Theme::clear_font(const StringName &p_name, const StringName &p_node_type) {
+ ERR_FAIL_COND(!font_map.has(p_node_type));
+ ERR_FAIL_COND(!font_map[p_node_type].has(p_name));
- if (style_map[p_type][p_name].is_valid()) {
- style_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ if (font_map[p_node_type][p_name].is_valid()) {
+ font_map[p_node_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
- style_map[p_type].erase(p_name);
-
+ font_map[p_node_type].erase(p_name);
_change_notify();
emit_changed();
}
-void Theme::get_stylebox_list(StringName p_type, List<StringName> *p_list) const {
+void Theme::get_font_list(StringName p_node_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!style_map.has(p_type)) {
+ if (!font_map.has(p_node_type)) {
return;
}
const StringName *key = nullptr;
- while ((key = style_map[p_type].next(key))) {
+ while ((key = font_map[p_node_type].next(key))) {
p_list->push_back(*key);
}
}
-void Theme::get_stylebox_types(List<StringName> *p_list) const {
- ERR_FAIL_NULL(p_list);
+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);
- const StringName *key = nullptr;
- while ((key = style_map.next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::set_font(const StringName &p_name, const StringName &p_type, const Ref<Font> &p_font) {
- //ERR_FAIL_COND(p_font.is_null());
-
- bool new_value = !font_map.has(p_type) || !font_map[p_type].has(p_name);
-
- if (font_map[p_type][p_name].is_valid()) {
- font_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
-
- font_map[p_type][p_name] = p_font;
-
- if (p_font.is_valid()) {
- font_map[p_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
- }
+ font_size_map[p_node_type][p_name] = p_font_size;
if (new_value) {
_change_notify();
@@ -523,51 +564,47 @@ void Theme::set_font(const StringName &p_name, const StringName &p_type, const R
}
}
-Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_type) const {
- if (font_map.has(p_type) && font_map[p_type].has(p_name) && font_map[p_type][p_name].is_valid()) {
- return font_map[p_type][p_name];
- } else if (default_theme_font.is_valid()) {
- return default_theme_font;
+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;
+ return default_font_size;
}
}
-bool Theme::has_font(const StringName &p_name, const StringName &p_type) const {
- return (font_map.has(p_type) && font_map[p_type].has(p_name) && font_map[p_type][p_name].is_valid());
+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(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!font_map.has(p_type));
- ERR_FAIL_COND(!font_map[p_type].has(p_name));
+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));
- if (font_map[p_type][p_name].is_valid()) {
- font_map[p_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
-
- font_map[p_type].erase(p_name);
+ font_size_map[p_node_type].erase(p_name);
_change_notify();
emit_changed();
}
-void Theme::get_font_list(StringName p_type, List<StringName> *p_list) const {
+void Theme::get_font_size_list(StringName p_node_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!font_map.has(p_type)) {
+ if (!font_size_map.has(p_node_type)) {
return;
}
const StringName *key = nullptr;
- while ((key = font_map[p_type].next(key))) {
+ 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_type, const Color &p_color) {
- bool new_value = !color_map.has(p_type) || !color_map[p_type].has(p_name);
+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);
- color_map[p_type][p_name] = p_color;
+ color_map[p_node_type][p_name] = p_color;
if (new_value) {
_change_notify();
@@ -575,44 +612,44 @@ void Theme::set_color(const StringName &p_name, const StringName &p_type, const
}
}
-Color Theme::get_color(const StringName &p_name, const StringName &p_type) const {
- if (color_map.has(p_type) && color_map[p_type].has(p_name)) {
- return color_map[p_type][p_name];
+Color Theme::get_color(const StringName &p_name, const StringName &p_node_type) const {
+ if (color_map.has(p_node_type) && color_map[p_node_type].has(p_name)) {
+ return color_map[p_node_type][p_name];
} else {
return Color();
}
}
-bool Theme::has_color(const StringName &p_name, const StringName &p_type) const {
- return (color_map.has(p_type) && color_map[p_type].has(p_name));
+bool Theme::has_color(const StringName &p_name, const StringName &p_node_type) const {
+ return (color_map.has(p_node_type) && color_map[p_node_type].has(p_name));
}
-void Theme::clear_color(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!color_map.has(p_type));
- ERR_FAIL_COND(!color_map[p_type].has(p_name));
+void Theme::clear_color(const StringName &p_name, const StringName &p_node_type) {
+ ERR_FAIL_COND(!color_map.has(p_node_type));
+ ERR_FAIL_COND(!color_map[p_node_type].has(p_name));
- color_map[p_type].erase(p_name);
+ color_map[p_node_type].erase(p_name);
_change_notify();
emit_changed();
}
-void Theme::get_color_list(StringName p_type, List<StringName> *p_list) const {
+void Theme::get_color_list(StringName p_node_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!color_map.has(p_type)) {
+ if (!color_map.has(p_node_type)) {
return;
}
const StringName *key = nullptr;
- while ((key = color_map[p_type].next(key))) {
+ while ((key = color_map[p_node_type].next(key))) {
p_list->push_back(*key);
}
}
-void Theme::set_constant(const StringName &p_name, const StringName &p_type, int p_constant) {
- bool new_value = !constant_map.has(p_type) || !constant_map[p_type].has(p_name);
- constant_map[p_type][p_name] = p_constant;
+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;
if (new_value) {
_change_notify();
@@ -620,37 +657,37 @@ void Theme::set_constant(const StringName &p_name, const StringName &p_type, int
}
}
-int Theme::get_constant(const StringName &p_name, const StringName &p_type) const {
- if (constant_map.has(p_type) && constant_map[p_type].has(p_name)) {
- return constant_map[p_type][p_name];
+int Theme::get_constant(const StringName &p_name, const StringName &p_node_type) const {
+ if (constant_map.has(p_node_type) && constant_map[p_node_type].has(p_name)) {
+ return constant_map[p_node_type][p_name];
} else {
return 0;
}
}
-bool Theme::has_constant(const StringName &p_name, const StringName &p_type) const {
- return (constant_map.has(p_type) && constant_map[p_type].has(p_name));
+bool Theme::has_constant(const StringName &p_name, const StringName &p_node_type) const {
+ return (constant_map.has(p_node_type) && constant_map[p_node_type].has(p_name));
}
-void Theme::clear_constant(const StringName &p_name, const StringName &p_type) {
- ERR_FAIL_COND(!constant_map.has(p_type));
- ERR_FAIL_COND(!constant_map[p_type].has(p_name));
+void Theme::clear_constant(const StringName &p_name, const StringName &p_node_type) {
+ ERR_FAIL_COND(!constant_map.has(p_node_type));
+ ERR_FAIL_COND(!constant_map[p_node_type].has(p_name));
- constant_map[p_type].erase(p_name);
+ constant_map[p_node_type].erase(p_name);
_change_notify();
emit_changed();
}
-void Theme::get_constant_list(StringName p_type, List<StringName> *p_list) const {
+void Theme::get_constant_list(StringName p_node_type, List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
- if (!constant_map.has(p_type)) {
+ if (!constant_map.has(p_node_type)) {
return;
}
const StringName *key = nullptr;
- while ((key = constant_map[p_type].next(key))) {
+ while ((key = constant_map[p_node_type].next(key))) {
p_list->push_back(*key);
}
}
@@ -699,7 +736,6 @@ void Theme::clear() {
icon_map.clear();
style_map.clear();
font_map.clear();
- shader_map.clear();
color_map.clear();
constant_map.clear();
@@ -754,7 +790,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();
@@ -800,48 +835,58 @@ void Theme::get_type_list(List<StringName> *p_list) const {
}
void Theme::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_icon", "name", "type", "texture"), &Theme::set_icon);
- ClassDB::bind_method(D_METHOD("get_icon", "name", "type"), &Theme::get_icon);
- ClassDB::bind_method(D_METHOD("has_icon", "name", "type"), &Theme::has_icon);
- ClassDB::bind_method(D_METHOD("clear_icon", "name", "type"), &Theme::clear_icon);
- ClassDB::bind_method(D_METHOD("get_icon_list", "type"), &Theme::_get_icon_list);
-
- ClassDB::bind_method(D_METHOD("set_stylebox", "name", "type", "texture"), &Theme::set_stylebox);
- ClassDB::bind_method(D_METHOD("get_stylebox", "name", "type"), &Theme::get_stylebox);
- ClassDB::bind_method(D_METHOD("has_stylebox", "name", "type"), &Theme::has_stylebox);
- ClassDB::bind_method(D_METHOD("clear_stylebox", "name", "type"), &Theme::clear_stylebox);
- ClassDB::bind_method(D_METHOD("get_stylebox_list", "type"), &Theme::_get_stylebox_list);
+ ClassDB::bind_method(D_METHOD("set_icon", "name", "node_type", "texture"), &Theme::set_icon);
+ ClassDB::bind_method(D_METHOD("get_icon", "name", "node_type"), &Theme::get_icon);
+ 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("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("set_font", "name", "type", "font"), &Theme::set_font);
- ClassDB::bind_method(D_METHOD("get_font", "name", "type"), &Theme::get_font);
- ClassDB::bind_method(D_METHOD("has_font", "name", "type"), &Theme::has_font);
- ClassDB::bind_method(D_METHOD("clear_font", "name", "type"), &Theme::clear_font);
- ClassDB::bind_method(D_METHOD("get_font_list", "type"), &Theme::_get_font_list);
-
- ClassDB::bind_method(D_METHOD("set_color", "name", "type", "color"), &Theme::set_color);
- ClassDB::bind_method(D_METHOD("get_color", "name", "type"), &Theme::get_color);
- ClassDB::bind_method(D_METHOD("has_color", "name", "type"), &Theme::has_color);
- ClassDB::bind_method(D_METHOD("clear_color", "name", "type"), &Theme::clear_color);
- ClassDB::bind_method(D_METHOD("get_color_list", "type"), &Theme::_get_color_list);
-
- ClassDB::bind_method(D_METHOD("set_constant", "name", "type", "constant"), &Theme::set_constant);
- ClassDB::bind_method(D_METHOD("get_constant", "name", "type"), &Theme::get_constant);
- ClassDB::bind_method(D_METHOD("has_constant", "name", "type"), &Theme::has_constant);
- ClassDB::bind_method(D_METHOD("clear_constant", "name", "type"), &Theme::clear_constant);
- ClassDB::bind_method(D_METHOD("get_constant_list", "type"), &Theme::_get_constant_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("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("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("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", "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", "node_type"), &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 3c72ddd8a2..6ac47e8931 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -31,10 +31,9 @@
#ifndef THEME_H
#define THEME_H
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
-#include "core/resource.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,18 @@ 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_type) const;
- Vector<String> _get_stylebox_list(const String &p_type) const;
+ Vector<String> _get_icon_list(const String &p_node_type) const;
+ Vector<String> _get_stylebox_list(const String &p_node_type) const;
Vector<String> _get_stylebox_types() const;
- Vector<String> _get_font_list(const String &p_type) const;
- Vector<String> _get_color_list(const String &p_type) const;
- Vector<String> _get_constant_list(const String &p_type) const;
- Vector<String> _get_type_list(const String &p_type) const;
+ Vector<String> _get_font_list(const String &p_node_type) 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_constant_list(const String &p_node_type) const;
+ Vector<String> _get_type_list(const String &p_node_type) const;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -69,8 +69,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 +86,50 @@ 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_icon(const StringName &p_name, const StringName &p_type, const Ref<Texture2D> &p_icon);
- Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_type) const;
- bool has_icon(const StringName &p_name, const StringName &p_type) const;
- void clear_icon(const StringName &p_name, const StringName &p_type);
- void get_icon_list(StringName p_type, List<StringName> *p_list) const;
-
- void set_shader(const StringName &p_name, const StringName &p_type, const Ref<Shader> &p_shader);
- Ref<Shader> get_shader(const StringName &p_name, const StringName &p_type) const;
- bool has_shader(const StringName &p_name, const StringName &p_type) const;
- void clear_shader(const StringName &p_name, const StringName &p_type);
- void get_shader_list(const StringName &p_type, List<StringName> *p_list) const;
-
- void set_stylebox(const StringName &p_name, const StringName &p_type, const Ref<StyleBox> &p_style);
- Ref<StyleBox> get_stylebox(const StringName &p_name, const StringName &p_type) const;
- bool has_stylebox(const StringName &p_name, const StringName &p_type) const;
- void clear_stylebox(const StringName &p_name, const StringName &p_type);
- void get_stylebox_list(StringName p_type, List<StringName> *p_list) 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_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 set_font(const StringName &p_name, const StringName &p_type, const Ref<Font> &p_font);
- Ref<Font> get_font(const StringName &p_name, const StringName &p_type) const;
- bool has_font(const StringName &p_name, const StringName &p_type) const;
- void clear_font(const StringName &p_name, const StringName &p_type);
- void get_font_list(StringName p_type, List<StringName> *p_list) const;
-
- void set_color(const StringName &p_name, const StringName &p_type, const Color &p_color);
- Color get_color(const StringName &p_name, const StringName &p_type) const;
- bool has_color(const StringName &p_name, const StringName &p_type) const;
- void clear_color(const StringName &p_name, const StringName &p_type);
- void get_color_list(StringName p_type, List<StringName> *p_list) const;
-
- void set_constant(const StringName &p_name, const StringName &p_type, int p_constant);
- int get_constant(const StringName &p_name, const StringName &p_type) const;
- bool has_constant(const StringName &p_name, const StringName &p_type) const;
- void clear_constant(const StringName &p_name, const StringName &p_type);
- void get_constant_list(StringName p_type, 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 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 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_type_list(List<StringName> *p_list) const;
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 6992360df7..4581763e9c 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -30,9 +30,9 @@
#include "tile_set.h"
-#include "core/array.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "core/math/geometry_2d.h"
+#include "core/variant/array.h"
bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
String n = p_name;
@@ -40,7 +40,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
if (slash == -1) {
return false;
}
- int id = String::to_int(n.c_str(), slash);
+ int id = String::to_int(n.get_data(), slash);
if (!tile_map.has(id)) {
create_tile(id);
@@ -51,8 +51,6 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
tile_set_name(id, p_value);
} else if (what == "texture") {
tile_set_texture(id, p_value);
- } else if (what == "normal_map") {
- tile_set_normal_map(id, p_value);
} else if (what == "tex_offset") {
tile_set_texture_offset(id, p_value);
} else if (what == "material") {
@@ -216,7 +214,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
if (slash == -1) {
return false;
}
- int id = String::to_int(n.c_str(), slash);
+ int id = String::to_int(n.get_data(), slash);
ERR_FAIL_COND_V(!tile_map.has(id), false);
@@ -226,8 +224,6 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = tile_get_name(id);
} else if (what == "texture") {
r_ret = tile_get_texture(id);
- } else if (what == "normal_map") {
- r_ret = tile_get_normal_map(id);
} else if (what == "tex_offset") {
r_ret = tile_get_texture_offset(id);
} else if (what == "material") {
@@ -331,7 +327,6 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
String pre = itos(id) + "/";
p_list->push_back(PropertyInfo(Variant::STRING, pre + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "tex_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::COLOR, pre + "modulate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
@@ -402,17 +397,6 @@ Ref<Texture2D> TileSet::tile_get_texture(int p_id) const {
return tile_map[p_id].texture;
}
-void TileSet::tile_set_normal_map(int p_id, const Ref<Texture2D> &p_normal_map) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].normal_map = p_normal_map;
- emit_changed();
-}
-
-Ref<Texture2D> TileSet::tile_get_normal_map(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<Texture2D>());
- return tile_map[p_id].normal_map;
-}
-
void TileSet::tile_set_material(int p_id, const Ref<ShaderMaterial> &p_material) {
ERR_FAIL_COND(!tile_map.has(p_id));
tile_map[p_id].material = p_material;
@@ -1128,8 +1112,6 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("tile_get_name", "id"), &TileSet::tile_get_name);
ClassDB::bind_method(D_METHOD("tile_set_texture", "id", "texture"), &TileSet::tile_set_texture);
ClassDB::bind_method(D_METHOD("tile_get_texture", "id"), &TileSet::tile_get_texture);
- ClassDB::bind_method(D_METHOD("tile_set_normal_map", "id", "normal_map"), &TileSet::tile_set_normal_map);
- ClassDB::bind_method(D_METHOD("tile_get_normal_map", "id"), &TileSet::tile_get_normal_map);
ClassDB::bind_method(D_METHOD("tile_set_material", "id", "material"), &TileSet::tile_set_material);
ClassDB::bind_method(D_METHOD("tile_get_material", "id"), &TileSet::tile_get_material);
ClassDB::bind_method(D_METHOD("tile_set_modulate", "id", "color"), &TileSet::tile_set_modulate);
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 78f34e46ce..79f1b4aa95 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -31,8 +31,8 @@
#ifndef TILE_SET_H
#define TILE_SET_H
-#include "core/array.h"
-#include "core/resource.h"
+#include "core/io/resource.h"
+#include "core/variant/array.h"
#include "scene/2d/light_occluder_2d.h"
#include "scene/2d/navigation_region_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
@@ -106,7 +106,6 @@ private:
struct TileData {
String name;
Ref<Texture2D> texture;
- Ref<Texture2D> normal_map;
Vector2 offset;
Rect2i region;
Vector<ShapeData> shapes_data;
@@ -149,9 +148,6 @@ public:
void tile_set_texture(int p_id, const Ref<Texture2D> &p_texture);
Ref<Texture2D> tile_get_texture(int p_id) const;
- void tile_set_normal_map(int p_id, const Ref<Texture2D> &p_normal_map);
- Ref<Texture2D> tile_get_normal_map(int p_id) const;
-
void tile_set_texture_offset(int p_id, const Vector2 &p_offset);
Vector2 tile_get_texture_offset(int p_id) const;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index e4851ad9f7..34129e35da 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -30,7 +30,7 @@
#include "visual_shader.h"
-#include "core/vmap.h"
+#include "core/templates/vmap.h"
#include "servers/rendering/shader_types.h"
#include "visual_shader_nodes.h"
@@ -65,20 +65,16 @@ bool VisualShaderNode::is_port_separator(int p_index) const {
bool VisualShaderNode::is_output_port_connected(int p_port) const {
if (connected_output_ports.has(p_port)) {
- return connected_output_ports[p_port];
+ return connected_output_ports[p_port] > 0;
}
return false;
}
void VisualShaderNode::set_output_port_connected(int p_port, bool p_connected) {
if (p_connected) {
- connected_output_ports[p_port] = true;
- ++connected_output_count;
+ connected_output_ports[p_port]++;
} else {
- --connected_output_count;
- if (connected_output_count == 0) {
- connected_output_ports[p_port] = false;
- }
+ connected_output_ports[p_port]--;
}
}
@@ -101,6 +97,14 @@ bool VisualShaderNode::is_code_generated() const {
return true;
}
+bool VisualShaderNode::is_show_prop_names() const {
+ return false;
+}
+
+bool VisualShaderNode::is_use_prop_slots() const {
+ return false;
+}
+
Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
return Vector<VisualShader::DefaultTextureParam>();
}
@@ -172,8 +176,6 @@ void VisualShaderNode::_bind_methods() {
}
VisualShaderNode::VisualShaderNode() {
- port_preview = -1;
- simple_decl = true;
}
/////////////////////////////////////////////////////////
@@ -319,6 +321,14 @@ VisualShaderNodeCustom::VisualShaderNodeCustom() {
/////////////////////////////////////////////////////////
+void VisualShader::set_shader_type(Type p_type) {
+ current_type = p_type;
+}
+
+VisualShader::Type VisualShader::get_shader_type() const {
+ return current_type;
+}
+
void VisualShader::set_version(const String &p_version) {
version = p_version;
}
@@ -801,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);
@@ -820,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;
@@ -920,8 +933,12 @@ VisualShader::RenderModeEnums VisualShader::render_mode_enums[] = {
static const char *type_string[VisualShader::TYPE_MAX] = {
"vertex",
"fragment",
- "light"
+ "light",
+ "emit",
+ "process",
+ "end"
};
+
bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
if (name == "mode") {
@@ -978,7 +995,7 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
set_node_position(type, id, p_value);
return true;
} else if (what == "size") {
- ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_size(p_value);
+ ((VisualShaderNodeResizableBase *)get_node(type, id).ptr())->set_size(p_value);
return true;
} else if (what == "input_ports") {
((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->set_inputs(p_value);
@@ -1045,7 +1062,7 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_node_position(type, id);
return true;
} else if (what == "size") {
- r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_size();
+ r_ret = ((VisualShaderNodeResizableBase *)get_node(type, id).ptr())->get_size();
return true;
} else if (what == "input_ports") {
r_ret = ((VisualShaderNodeGroupBase *)get_node(type, id).ptr())->get_inputs();
@@ -1229,9 +1246,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
} else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
inputs[i] = src_var + " > 0 ? true : false";
} else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- inputs[i] = src_var + " ? 1.0 : 0.0";
+ inputs[i] = "(" + src_var + " ? 1.0 : 0.0)";
} else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR_INT && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- inputs[i] = src_var + " ? 1 : 0";
+ inputs[i] = "(" + src_var + " ? 1 : 0)";
} else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
inputs[i] = "vec3(" + src_var + " ? 1.0 : 0.0)";
} else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
@@ -1344,6 +1361,19 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
return OK;
}
+bool VisualShader::has_func_name(RenderingServer::ShaderMode p_mode, const String &p_func_name) const {
+ if (!ShaderTypes::get_singleton()->get_functions(p_mode).has(p_func_name)) {
+ if (p_mode == RenderingServer::ShaderMode::SHADER_PARTICLES) {
+ if (p_func_name == "emit" || p_func_name == "process" || p_func_name == "end") {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ return true;
+}
+
void VisualShader::_update_shader() const {
if (!dirty) {
return;
@@ -1367,10 +1397,19 @@ void VisualShader::_update_shader() const {
{
//fill render mode enums
int idx = 0;
+ bool specular = false;
while (render_mode_enums[idx].string) {
if (shader_mode == render_mode_enums[idx].mode) {
- if (modes.has(render_mode_enums[idx].string)) {
- int which = modes[render_mode_enums[idx].string];
+ if (shader_mode == Shader::MODE_SPATIAL) {
+ if (String(render_mode_enums[idx].string) == "specular") {
+ specular = true;
+ }
+ }
+ if (modes.has(render_mode_enums[idx].string) || specular) {
+ int which = 0;
+ if (modes.has(render_mode_enums[idx].string)) {
+ which = modes[render_mode_enums[idx].string];
+ }
int count = 0;
for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode)).size(); i++) {
String mode = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode))[i];
@@ -1406,14 +1445,14 @@ void VisualShader::_update_shader() const {
global_code += "render_mode " + render_mode + ";\n\n";
}
- static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light" };
+ static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "emit", "process", "end" };
String global_expressions;
Set<String> used_uniform_names;
List<VisualShaderNodeUniform *> uniforms;
for (int i = 0, index = 0; i < TYPE_MAX; i++) {
- if (!ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader_mode)).has(func_name[i])) {
+ if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) {
continue;
}
@@ -1448,8 +1487,10 @@ void VisualShader::_update_shader() const {
}
}
+ Map<int, String> code_map;
+
for (int i = 0; i < TYPE_MAX; i++) {
- if (!ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader_mode)).has(func_name[i])) {
+ if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) {
continue;
}
@@ -1457,6 +1498,8 @@ void VisualShader::_update_shader() const {
VMap<ConnectionKey, const List<Connection>::Element *> input_connections;
VMap<ConnectionKey, const List<Connection>::Element *> output_connections;
+ StringBuilder func_code;
+
for (const List<Connection>::Element *E = graph[i].connections.front(); E; E = E->next()) {
ConnectionKey from_key;
from_key.node = E->get().from_node;
@@ -1470,14 +1513,30 @@ void VisualShader::_update_shader() const {
input_connections.insert(to_key, E);
}
-
- code += "\nvoid " + String(func_name[i]) + "() {\n";
+ if (shader_mode != Shader::MODE_PARTICLES) {
+ func_code += "\nvoid " + String(func_name[i]) + "() {\n";
+ }
+ insertion_pos.insert(i, code.get_string_length() + func_code.get_string_length());
Set<int> processed;
- Error err = _write_node(Type(i), global_code, global_code_per_node, global_code_per_func, code, default_tex_params, input_connections, output_connections, NODE_ID_OUTPUT, processed, false, classes);
+ Error err = _write_node(Type(i), global_code, global_code_per_node, global_code_per_func, func_code, default_tex_params, input_connections, output_connections, NODE_ID_OUTPUT, processed, false, classes);
ERR_FAIL_COND(err != OK);
- insertion_pos.insert(i, code.get_string_length());
+ if (shader_mode == Shader::MODE_PARTICLES) {
+ code_map.insert(i, func_code);
+ } else {
+ func_code += "}\n";
+ code += func_code;
+ }
+ }
+
+ if (shader_mode == Shader::MODE_PARTICLES) {
+ code += "\nvoid compute() {\n";
+ code += "\tif (RESTART) {\n";
+ code += code_map[TYPE_EMIT];
+ code += "\t} else {\n";
+ code += code_map[TYPE_PROCESS];
+ code += "\t}\n";
code += "}\n";
}
@@ -1488,7 +1547,7 @@ void VisualShader::_update_shader() const {
final_code += global_expressions;
String tcode = code;
for (int i = 0; i < TYPE_MAX; i++) {
- if (!ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader_mode)).has(func_name[i])) {
+ if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) {
continue;
}
tcode = tcode.insert(insertion_pos[i], global_code_per_func[Type(i)]);
@@ -1567,9 +1626,14 @@ void VisualShader::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_version", "get_version");
+ ADD_PROPERTY_DEFAULT("code", ""); // Inherited from Shader, prevents showing default code as override in docs.
+
BIND_ENUM_CONSTANT(TYPE_VERTEX);
BIND_ENUM_CONSTANT(TYPE_FRAGMENT);
BIND_ENUM_CONSTANT(TYPE_LIGHT);
+ BIND_ENUM_CONSTANT(TYPE_EMIT);
+ BIND_ENUM_CONSTANT(TYPE_PROCESS);
+ BIND_ENUM_CONSTANT(TYPE_END);
BIND_ENUM_CONSTANT(TYPE_MAX);
BIND_CONSTANT(NODE_ID_INVALID);
@@ -1577,8 +1641,6 @@ void VisualShader::_bind_methods() {
}
VisualShader::VisualShader() {
- shader_mode = Shader::MODE_SPATIAL;
-
for (int i = 0; i < TYPE_MAX; i++) {
Ref<VisualShaderNodeOutput> output;
output.instance();
@@ -1587,8 +1649,6 @@ VisualShader::VisualShader() {
graph[i].nodes[NODE_ID_OUTPUT].node = output;
graph[i].nodes[NODE_ID_OUTPUT].position = Vector2(400, 150);
}
-
- dirty = true;
}
///////////////////////////////////////////////////////////
@@ -1649,12 +1709,13 @@ 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" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
@@ -1666,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)" },
@@ -1687,46 +1748,79 @@ 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" },
-
- // Particles, Vertex
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
-
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
-
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ { 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" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ // Particles, Process
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ // Particles, End
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Sky, Fragment
{ Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" },
@@ -1763,7 +1857,6 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
};
const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
-
// Spatial, Fragment
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
@@ -1777,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)" },
@@ -1869,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";
@@ -2028,10 +2121,6 @@ void VisualShaderNodeInput::_bind_methods() {
}
VisualShaderNodeInput::VisualShaderNodeInput() {
- input_name = "[None]";
- // changed when set
- shader_type = VisualShader::TYPE_MAX;
- shader_mode = Shader::MODE_MAX;
}
////////////// UniformRef
@@ -2046,6 +2135,15 @@ void VisualShaderNodeUniformRef::clear_uniforms() {
uniforms.clear();
}
+bool VisualShaderNodeUniformRef::has_uniform(const String &p_name) {
+ for (List<VisualShaderNodeUniformRef::Uniform>::Element *E = uniforms.front(); E; E = E->next()) {
+ if (E->get().name == p_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
String VisualShaderNodeUniformRef::get_caption() const {
return "UniformRef";
}
@@ -2063,10 +2161,6 @@ String VisualShaderNodeUniformRef::get_input_port_name(int p_port) const {
}
int VisualShaderNodeUniformRef::get_output_port_count() const {
- if (uniform_name == "[None]") {
- return 0;
- }
-
switch (uniform_type) {
case UniformType::UNIFORM_TYPE_FLOAT:
return 1;
@@ -2085,7 +2179,7 @@ int VisualShaderNodeUniformRef::get_output_port_count() const {
default:
break;
}
- return 0;
+ return 1;
}
VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_output_port_type(int p_port) const {
@@ -2145,8 +2239,8 @@ String VisualShaderNodeUniformRef::get_output_port_name(int p_port) const {
void VisualShaderNodeUniformRef::set_uniform_name(const String &p_name) {
uniform_name = p_name;
- if (p_name != "[None]") {
- uniform_type = get_uniform_type_by_name(p_name);
+ if (uniform_name != "[None]") {
+ uniform_type = get_uniform_type_by_name(uniform_name);
} else {
uniform_type = UniformType::UNIFORM_TYPE_FLOAT;
}
@@ -2187,6 +2281,9 @@ VisualShaderNodeUniformRef::UniformType VisualShaderNodeUniformRef::get_uniform_
String VisualShaderNodeUniformRef::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
switch (uniform_type) {
case UniformType::UNIFORM_TYPE_FLOAT:
+ if (uniform_name == "[None]") {
+ return "\t" + p_output_vars[0] + " = 0.0;\n";
+ }
return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
case UniformType::UNIFORM_TYPE_INT:
return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
@@ -2209,22 +2306,33 @@ String VisualShaderNodeUniformRef::generate_code(Shader::Mode p_mode, VisualShad
return "";
}
+void VisualShaderNodeUniformRef::_set_uniform_type(int p_uniform_type) {
+ uniform_type = (UniformType)p_uniform_type;
+}
+
+int VisualShaderNodeUniformRef::_get_uniform_type() const {
+ return (int)uniform_type;
+}
+
void VisualShaderNodeUniformRef::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_uniform_name", "name"), &VisualShaderNodeUniformRef::set_uniform_name);
ClassDB::bind_method(D_METHOD("get_uniform_name"), &VisualShaderNodeUniformRef::get_uniform_name);
+ ClassDB::bind_method(D_METHOD("_set_uniform_type", "type"), &VisualShaderNodeUniformRef::_set_uniform_type);
+ ClassDB::bind_method(D_METHOD("_get_uniform_type"), &VisualShaderNodeUniformRef::_get_uniform_type);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "uniform_name", PROPERTY_HINT_ENUM, ""), "set_uniform_name", "get_uniform_name");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "uniform_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_uniform_type", "_get_uniform_type");
}
Vector<StringName> VisualShaderNodeUniformRef::get_editable_properties() const {
Vector<StringName> props;
props.push_back("uniform_name");
+ props.push_back("uniform_type");
return props;
}
VisualShaderNodeUniformRef::VisualShaderNodeUniformRef() {
- uniform_name = "[None]";
- uniform_type = UniformType::UNIFORM_TYPE_FLOAT;
}
////////////////////////////////////////////
@@ -2261,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
@@ -2283,13 +2391,30 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
// 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" },
- // Particles, Vertex
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ // Particles, Emit
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
+ // Particles, Process
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
+ // Particles, End
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
// Sky, Fragment
{ Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" },
{ Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
@@ -2364,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;
}
@@ -2477,23 +2602,39 @@ Vector<StringName> VisualShaderNodeUniform::get_editable_properties() const {
}
VisualShaderNodeUniform::VisualShaderNodeUniform() {
- qualifier = QUAL_NONE;
}
-////////////// GroupBase
-
-String VisualShaderNodeGroupBase::get_caption() const {
- return "Group";
-}
+////////////// ResizeableBase
-void VisualShaderNodeGroupBase::set_size(const Vector2 &p_size) {
+void VisualShaderNodeResizableBase::set_size(const Vector2 &p_size) {
size = p_size;
}
-Vector2 VisualShaderNodeGroupBase::get_size() const {
+Vector2 VisualShaderNodeResizableBase::get_size() const {
return size;
}
+void VisualShaderNodeResizableBase::set_allow_v_resize(bool p_enabled) {
+ allow_v_resize = p_enabled;
+}
+
+bool VisualShaderNodeResizableBase::is_allow_v_resize() const {
+ return allow_v_resize;
+}
+
+void VisualShaderNodeResizableBase::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &VisualShaderNodeResizableBase::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &VisualShaderNodeResizableBase::get_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
+}
+
+VisualShaderNodeResizableBase::VisualShaderNodeResizableBase() {
+ set_allow_v_resize(true);
+}
+
+////////////// GroupBase
+
void VisualShaderNodeGroupBase::set_inputs(const String &p_inputs) {
if (inputs == p_inputs) {
return;
@@ -2609,6 +2750,7 @@ void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const Strin
}
_apply_port_changes();
+ emit_changed();
}
void VisualShaderNodeGroupBase::remove_input_port(int p_id) {
@@ -2628,11 +2770,14 @@ 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();
+ emit_changed();
}
int VisualShaderNodeGroupBase::get_input_port_count() const {
@@ -2677,6 +2822,7 @@ void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const Stri
}
_apply_port_changes();
+ emit_changed();
}
void VisualShaderNodeGroupBase::remove_output_port(int p_id) {
@@ -2696,11 +2842,14 @@ 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();
+ emit_changed();
}
int VisualShaderNodeGroupBase::get_output_port_count() const {
@@ -2747,6 +2896,7 @@ void VisualShaderNodeGroupBase::set_input_port_type(int p_id, int p_type) {
inputs = inputs.insert(index, itos(p_type));
_apply_port_changes();
+ emit_changed();
}
VisualShaderNodeGroupBase::PortType VisualShaderNodeGroupBase::get_input_port_type(int p_id) const {
@@ -2782,6 +2932,7 @@ void VisualShaderNodeGroupBase::set_input_port_name(int p_id, const String &p_na
inputs = inputs.insert(index, p_name);
_apply_port_changes();
+ emit_changed();
}
String VisualShaderNodeGroupBase::get_input_port_name(int p_id) const {
@@ -2817,6 +2968,7 @@ void VisualShaderNodeGroupBase::set_output_port_type(int p_id, int p_type) {
outputs = outputs.insert(index, itos(p_type));
_apply_port_changes();
+ emit_changed();
}
VisualShaderNodeGroupBase::PortType VisualShaderNodeGroupBase::get_output_port_type(int p_id) const {
@@ -2852,6 +3004,7 @@ void VisualShaderNodeGroupBase::set_output_port_name(int p_id, const String &p_n
outputs = outputs.insert(index, p_name);
_apply_port_changes();
+ emit_changed();
}
String VisualShaderNodeGroupBase::get_output_port_name(int p_id) const {
@@ -2912,9 +3065,6 @@ bool VisualShaderNodeGroupBase::is_editable() const {
}
void VisualShaderNodeGroupBase::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_size", "size"), &VisualShaderNodeGroupBase::set_size);
- ClassDB::bind_method(D_METHOD("get_size"), &VisualShaderNodeGroupBase::get_size);
-
ClassDB::bind_method(D_METHOD("set_inputs", "inputs"), &VisualShaderNodeGroupBase::set_inputs);
ClassDB::bind_method(D_METHOD("get_inputs"), &VisualShaderNodeGroupBase::get_inputs);
@@ -2942,8 +3092,6 @@ void VisualShaderNodeGroupBase::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_free_input_port_id"), &VisualShaderNodeGroupBase::get_free_input_port_id);
ClassDB::bind_method(D_METHOD("get_free_output_port_id"), &VisualShaderNodeGroupBase::get_free_output_port_id);
-
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
}
String VisualShaderNodeGroupBase::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
@@ -2951,10 +3099,6 @@ String VisualShaderNodeGroupBase::generate_code(Shader::Mode p_mode, VisualShade
}
VisualShaderNodeGroupBase::VisualShaderNodeGroupBase() {
- size = Size2(0, 0);
- inputs = "";
- outputs = "";
- editable = false;
simple_decl = false;
}
@@ -2966,6 +3110,7 @@ String VisualShaderNodeExpression::get_caption() const {
void VisualShaderNodeExpression::set_expression(const String &p_expression) {
expression = p_expression;
+ emit_changed();
}
String VisualShaderNodeExpression::get_expression() const {
@@ -3079,7 +3224,6 @@ void VisualShaderNodeExpression::_bind_methods() {
}
VisualShaderNodeExpression::VisualShaderNodeExpression() {
- expression = "";
set_editable(true);
}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 05d8950be9..a38c2886e2 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -31,7 +31,7 @@
#ifndef VISUAL_SHADER_H
#define VISUAL_SHADER_H
-#include "core/string_builder.h"
+#include "core/string/string_builder.h"
#include "scene/gui/control.h"
#include "scene/resources/shader.h"
@@ -50,6 +50,9 @@ public:
TYPE_VERTEX,
TYPE_FRAGMENT,
TYPE_LIGHT,
+ TYPE_EMIT,
+ TYPE_PROCESS,
+ TYPE_END,
TYPE_MAX
};
@@ -66,6 +69,8 @@ public:
};
private:
+ Type current_type;
+
struct Node {
Ref<VisualShaderNode> node;
Vector2 position;
@@ -77,7 +82,7 @@ private:
List<Connection> connections;
} graph[TYPE_MAX];
- Shader::Mode shader_mode;
+ Shader::Mode shader_mode = Shader::MODE_SPATIAL;
mutable String previous_code;
Array _get_node_connections(Type p_type) const;
@@ -94,7 +99,7 @@ private:
static RenderModeEnums render_mode_enums[];
- volatile mutable bool dirty;
+ volatile mutable bool dirty = true;
void _queue_update();
union ConnectionKey {
@@ -111,6 +116,7 @@ private:
Error _write_node(Type p_type, StringBuilder &global_code, StringBuilder &global_code_per_node, Map<Type, StringBuilder> &global_code_per_func, StringBuilder &code, Vector<DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview, Set<StringName> &r_classes) const;
void _input_type_changed(Type p_type, int p_id);
+ bool has_func_name(RenderingServer::ShaderMode p_mode, const String &p_func_name) const;
protected:
virtual void _update_shader() const override;
@@ -120,6 +126,10 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+public: // internal methods
+ void set_shader_type(Type p_type);
+ Type get_shader_type() const;
+
public:
void set_version(const String &p_version);
String get_version() const;
@@ -165,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();
@@ -179,15 +189,14 @@ VARIANT_ENUM_CAST(VisualShader::Type)
class VisualShaderNode : public Resource {
GDCLASS(VisualShaderNode, Resource);
- int port_preview;
+ int port_preview = -1;
Map<int, Variant> default_input_values;
Map<int, bool> connected_input_ports;
- Map<int, bool> connected_output_ports;
- int connected_output_count = 0;
+ Map<int, int> connected_output_ports;
protected:
- bool simple_decl;
+ bool simple_decl = true;
static void _bind_methods();
public:
@@ -232,6 +241,8 @@ public:
virtual bool is_generate_input_var(int p_port) const;
virtual bool is_code_generated() const;
+ virtual bool is_show_prop_names() const;
+ virtual bool is_use_prop_slots() const;
virtual Vector<StringName> get_editable_properties() const;
@@ -289,8 +300,8 @@ class VisualShaderNodeInput : public VisualShaderNode {
GDCLASS(VisualShaderNodeInput, VisualShaderNode);
friend class VisualShader;
- VisualShader::Type shader_type;
- Shader::Mode shader_mode;
+ VisualShader::Type shader_type = VisualShader::TYPE_MAX;
+ Shader::Mode shader_mode = Shader::MODE_MAX;
struct Port {
Shader::Mode mode;
@@ -303,7 +314,7 @@ class VisualShaderNodeInput : public VisualShaderNode {
static const Port ports[];
static const Port preview_ports[];
- String input_name;
+ String input_name = "[None]";
protected:
static void _bind_methods();
@@ -387,8 +398,8 @@ public:
};
private:
- String uniform_name;
- Qualifier qualifier;
+ String uniform_name = "";
+ Qualifier qualifier = QUAL_NONE;
bool global_code_generated = false;
protected:
@@ -435,8 +446,8 @@ public:
};
private:
- String uniform_name;
- UniformType uniform_type;
+ String uniform_name = "[None]";
+ UniformType uniform_type = UniformType::UNIFORM_TYPE_FLOAT;
protected:
static void _bind_methods();
@@ -444,6 +455,7 @@ protected:
public:
static void add_uniform(const String &p_name, UniformType p_type);
static void clear_uniforms();
+ static bool has_uniform(const String &p_name);
public:
virtual String get_caption() const override;
@@ -459,6 +471,9 @@ public:
void set_uniform_name(const String &p_name);
String get_uniform_name() const;
+ void _set_uniform_type(int p_uniform_type);
+ int _get_uniform_type() const;
+
int get_uniforms_count() const;
String get_uniform_name_by_index(int p_idx) const;
UniformType get_uniform_type_by_name(const String &p_name) const;
@@ -471,17 +486,36 @@ public:
VisualShaderNodeUniformRef();
};
-class VisualShaderNodeGroupBase : public VisualShaderNode {
- GDCLASS(VisualShaderNodeGroupBase, VisualShaderNode);
+class VisualShaderNodeResizableBase : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeResizableBase, VisualShaderNode);
+
+protected:
+ Vector2 size = Size2(0, 0);
+ bool allow_v_resize = true;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_size(const Vector2 &p_size);
+ Vector2 get_size() const;
+
+ bool is_allow_v_resize() const;
+ void set_allow_v_resize(bool p_enabled);
+
+ VisualShaderNodeResizableBase();
+};
+
+class VisualShaderNodeGroupBase : public VisualShaderNodeResizableBase {
+ GDCLASS(VisualShaderNodeGroupBase, VisualShaderNodeResizableBase);
private:
void _apply_port_changes();
protected:
- Vector2 size;
- String inputs;
- String outputs;
- bool editable;
+ String inputs = "";
+ String outputs = "";
+ bool editable = false;
struct Port {
PortType type;
@@ -496,11 +530,6 @@ protected:
static void _bind_methods();
public:
- virtual String get_caption() const override;
-
- void set_size(const Vector2 &p_size);
- Vector2 get_size() const;
-
void set_inputs(const String &p_inputs);
String get_inputs() const;
@@ -549,7 +578,7 @@ class VisualShaderNodeExpression : public VisualShaderNodeGroupBase {
GDCLASS(VisualShaderNodeExpression, VisualShaderNodeGroupBase);
protected:
- String expression;
+ String expression = "";
static void _bind_methods();
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index b0c871bc71..a3358ea8c7 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -87,7 +87,6 @@ void VisualShaderNodeFloatConstant::_bind_methods() {
}
VisualShaderNodeFloatConstant::VisualShaderNodeFloatConstant() {
- constant = 0.0;
}
////////////// Scalar(Int)
@@ -147,7 +146,6 @@ void VisualShaderNodeIntConstant::_bind_methods() {
}
VisualShaderNodeIntConstant::VisualShaderNodeIntConstant() {
- constant = 0;
}
////////////// Boolean
@@ -207,7 +205,6 @@ void VisualShaderNodeBooleanConstant::_bind_methods() {
}
VisualShaderNodeBooleanConstant::VisualShaderNodeBooleanConstant() {
- constant = false;
}
////////////// Color
@@ -271,7 +268,6 @@ void VisualShaderNodeColorConstant::_bind_methods() {
}
VisualShaderNodeColorConstant::VisualShaderNodeColorConstant() {
- constant = Color(1, 1, 1, 1);
}
////////////// Vector
@@ -456,7 +452,7 @@ String VisualShaderNodeTexture::get_output_port_name(int p_port) const {
String VisualShaderNodeTexture::get_input_port_default_hint(int p_port) const {
if (p_port == 0) {
- return "UV.xy";
+ return "default";
}
return "";
}
@@ -495,15 +491,22 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade
}
String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ String default_uv;
+ if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ default_uv = "UV.xy";
+ } else {
+ default_uv = "vec2(0.0)";
+ }
+
if (source == SOURCE_TEXTURE) {
String id = make_unique_id(p_type, p_id, "tex");
String code;
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\tvec4 " + id + "_read = texture(" + id + ", UV.xy);\n";
+ code += "\tvec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += "\tvec4 " + id + "_read = textureLod(" + id + ", UV.xy, " + p_input_vars[1] + ");\n";
+ code += "\tvec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
@@ -529,9 +532,9 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", UV.xy);\n";
+ code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", UV.xy, " + p_input_vars[1] + ");\n";
+ code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
@@ -553,9 +556,9 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
if (p_input_vars[0] == String() || p_for_preview) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, UV.xy, 0.0 );\n";
+ code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", 0.0 );\n";
} else {
- code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, UV.xy, " + p_input_vars[1] + ");\n";
+ code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
@@ -576,9 +579,9 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 _tex_read = texture(TEXTURE , UV.xy);\n";
+ code += "\t\tvec4 _tex_read = texture(TEXTURE, " + default_uv + ");\n";
} else {
- code += "\t\tvec4 _tex_read = textureLod(TEXTURE, UV.xy, " + p_input_vars[1] + ");\n";
+ code += "\t\tvec4 _tex_read = textureLod(TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
@@ -599,9 +602,9 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 _tex_read = texture(NORMAL_TEXTURE, UV.xy);\n";
+ code += "\t\tvec4 _tex_read = texture(NORMAL_TEXTURE, " + default_uv + ");\n";
} else {
- code += "\t\tvec4 _tex_read = textureLod(NORMAL_TEXTURE, UV.xy, " + p_input_vars[1] + ");\n";
+ code += "\t\tvec4 _tex_read = textureLod(NORMAL_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
@@ -632,9 +635,9 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tfloat _depth = texture(DEPTH_TEXTURE, UV.xy).r;\n";
+ code += "\t\tfloat _depth = texture(DEPTH_TEXTURE, " + default_uv + ").r;\n";
} else {
- code += "\t\tfloat _depth = textureLod(DEPTH_TEXTURE, UV.xy, " + p_input_vars[1] + ").r;\n";
+ code += "\t\tfloat _depth = textureLod(DEPTH_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ").r;\n";
}
} else if (p_input_vars[1] == String()) {
@@ -721,6 +724,10 @@ Vector<StringName> VisualShaderNodeTexture::get_editable_properties() const {
}
String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ if (is_input_port_connected(2) && source != SOURCE_PORT) {
+ return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ }
+
if (source == SOURCE_TEXTURE) {
return String(); // all good
}
@@ -777,8 +784,90 @@ void VisualShaderNodeTexture::_bind_methods() {
}
VisualShaderNodeTexture::VisualShaderNodeTexture() {
- texture_type = TYPE_DATA;
- source = SOURCE_TEXTURE;
+}
+
+////////////// Curve
+
+String VisualShaderNodeCurveTexture::get_caption() const {
+ return "CurveTexture";
+}
+
+int VisualShaderNodeCurveTexture::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeCurveTexture::PortType VisualShaderNodeCurveTexture::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeCurveTexture::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeCurveTexture::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeCurveTexture::PortType VisualShaderNodeCurveTexture::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeCurveTexture::get_output_port_name(int p_port) const {
+ return String();
+}
+
+void VisualShaderNodeCurveTexture::set_texture(Ref<CurveTexture> p_texture) {
+ texture = p_texture;
+ emit_changed();
+}
+
+Ref<CurveTexture> VisualShaderNodeCurveTexture::get_texture() const {
+ return texture;
+}
+
+Vector<StringName> VisualShaderNodeCurveTexture::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("texture");
+ return props;
+}
+
+String VisualShaderNodeCurveTexture::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform sampler2D " + make_unique_id(p_type, p_id, "curve") + ";\n";
+}
+
+String VisualShaderNodeCurveTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ if (p_input_vars[0] == String()) {
+ return "\t" + p_output_vars[0] + " = 0.0;\n";
+ }
+ String id = make_unique_id(p_type, p_id, "curve");
+ String code;
+ code += "\t" + p_output_vars[0] + " = texture(" + id + ", vec2(" + p_input_vars[0] + ", 0.0)).r;\n";
+ return code;
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCurveTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ VisualShader::DefaultTextureParam dtp;
+ dtp.name = make_unique_id(p_type, p_id, "curve");
+ dtp.param = texture;
+ Vector<VisualShader::DefaultTextureParam> ret;
+ ret.push_back(dtp);
+ return ret;
+}
+
+void VisualShaderNodeCurveTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &VisualShaderNodeCurveTexture::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &VisualShaderNodeCurveTexture::get_texture);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_texture", "get_texture");
+}
+
+bool VisualShaderNodeCurveTexture::is_use_prop_slots() const {
+ return true;
+}
+
+VisualShaderNodeCurveTexture::VisualShaderNodeCurveTexture() {
+ simple_decl = true;
+ allow_v_resize = false;
}
////////////// Sample3D
@@ -825,12 +914,19 @@ String VisualShaderNodeSample3D::get_output_port_name(int p_port) const {
String VisualShaderNodeSample3D::get_input_port_default_hint(int p_port) const {
if (p_port == 0) {
- return "vec3(UV.xy, 0.0)";
+ return "default";
}
return "";
}
String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ String default_uv;
+ if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ default_uv = "vec3(UV, 0.0)";
+ } else {
+ default_uv = "vec3(0.0)";
+ }
+
String code;
if (source == SOURCE_TEXTURE || source == SOURCE_PORT) {
String id;
@@ -843,9 +939,9 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader
if (id != String()) {
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", vec3(UV.xy, 0.0));\n";
+ code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", vec3(UV.xy, 0.0), " + p_input_vars[1] + ");\n";
+ code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
@@ -888,6 +984,10 @@ void VisualShaderNodeSample3D::_bind_methods() {
}
String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ if (is_input_port_connected(2) && source != SOURCE_PORT) {
+ return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ }
+
if (source == SOURCE_TEXTURE) {
return String(); // all good
}
@@ -898,7 +998,6 @@ String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader::
}
VisualShaderNodeSample3D::VisualShaderNodeSample3D() {
- source = SOURCE_TEXTURE;
simple_decl = false;
}
@@ -958,6 +1057,64 @@ void VisualShaderNodeTexture2DArray::_bind_methods() {
VisualShaderNodeTexture2DArray::VisualShaderNodeTexture2DArray() {
}
+
+////////////// Texture3D
+
+String VisualShaderNodeTexture3D::get_caption() const {
+ return "Texture3D";
+}
+
+String VisualShaderNodeTexture3D::get_input_port_name(int p_port) const {
+ if (p_port == 2) {
+ return "sampler3D";
+ }
+ return VisualShaderNodeSample3D::get_input_port_name(p_port);
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture3D::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ VisualShader::DefaultTextureParam dtp;
+ dtp.name = make_unique_id(p_type, p_id, "tex3d");
+ dtp.param = texture;
+ Vector<VisualShader::DefaultTextureParam> ret;
+ ret.push_back(dtp);
+ return ret;
+}
+
+String VisualShaderNodeTexture3D::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ if (source == SOURCE_TEXTURE) {
+ return "uniform sampler3D " + make_unique_id(p_type, p_id, "tex3d") + ";\n";
+ }
+ return String();
+}
+
+void VisualShaderNodeTexture3D::set_texture(Ref<Texture3D> p_value) {
+ texture = p_value;
+ emit_changed();
+}
+
+Ref<Texture3D> VisualShaderNodeTexture3D::get_texture() const {
+ return texture;
+}
+
+Vector<StringName> VisualShaderNodeTexture3D::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("source");
+ if (source == SOURCE_TEXTURE) {
+ props.push_back("texture");
+ }
+ return props;
+}
+
+void VisualShaderNodeTexture3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture", "value"), &VisualShaderNodeTexture3D::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &VisualShaderNodeTexture3D::get_texture);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture");
+}
+
+VisualShaderNodeTexture3D::VisualShaderNodeTexture3D() {
+}
+
////////////// Cubemap
String VisualShaderNodeCubemap::get_caption() const {
@@ -1034,6 +1191,13 @@ String VisualShaderNodeCubemap::generate_global(Shader::Mode p_mode, VisualShade
}
String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ String default_uv;
+ if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ default_uv = "vec3(UV, 0.0)";
+ } else {
+ default_uv = "vec3(0.0)";
+ }
+
String code;
String id;
if (source == SOURCE_TEXTURE) {
@@ -1057,9 +1221,9 @@ String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader:
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 " + id + "_read = texture(" + id + " , vec3(UV, 0.0));\n";
+ code += "\t\tvec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += "\t\tvec4 " + id + "_read = textureLod(" + id + " , vec3(UV, 0.0)" + " , " + p_input_vars[1] + " );\n";
+ code += "\t\tvec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + " );\n";
}
} else if (p_input_vars[1] == String()) {
@@ -1077,7 +1241,7 @@ String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader:
String VisualShaderNodeCubemap::get_input_port_default_hint(int p_port) const {
if (p_port == 0) {
- return "vec3(UV, 0.0)";
+ return "default";
}
return "";
}
@@ -1120,6 +1284,13 @@ Vector<StringName> VisualShaderNodeCubemap::get_editable_properties() const {
return props;
}
+String VisualShaderNodeCubemap::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ if (is_input_port_connected(2) && source != SOURCE_PORT) {
+ return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ }
+ return String();
+}
+
void VisualShaderNodeCubemap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_source", "value"), &VisualShaderNodeCubemap::set_source);
ClassDB::bind_method(D_METHOD("get_source"), &VisualShaderNodeCubemap::get_source);
@@ -1143,8 +1314,6 @@ void VisualShaderNodeCubemap::_bind_methods() {
}
VisualShaderNodeCubemap::VisualShaderNodeCubemap() {
- texture_type = TYPE_DATA;
- source = SOURCE_TEXTURE;
simple_decl = false;
}
@@ -1250,7 +1419,6 @@ void VisualShaderNodeFloatOp::_bind_methods() {
}
VisualShaderNodeFloatOp::VisualShaderNodeFloatOp() {
- op = OP_ADD;
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
}
@@ -1345,7 +1513,6 @@ void VisualShaderNodeIntOp::_bind_methods() {
}
VisualShaderNodeIntOp::VisualShaderNodeIntOp() {
- op = OP_ADD;
set_input_port_default_value(0, 0);
set_input_port_default_value(1, 0);
}
@@ -1460,7 +1627,6 @@ void VisualShaderNodeVectorOp::_bind_methods() {
}
VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() {
- op = OP_ADD;
set_input_port_default_value(0, Vector3());
set_input_port_default_value(1, Vector3());
}
@@ -1628,7 +1794,6 @@ void VisualShaderNodeColorOp::_bind_methods() {
}
VisualShaderNodeColorOp::VisualShaderNodeColorOp() {
- op = OP_SCREEN;
set_input_port_default_value(0, Vector3());
set_input_port_default_value(1, Vector3());
}
@@ -1703,7 +1868,6 @@ void VisualShaderNodeTransformMult::_bind_methods() {
}
VisualShaderNodeTransformMult::VisualShaderNodeTransformMult() {
- op = OP_AxB;
set_input_port_default_value(0, Transform());
set_input_port_default_value(1, Transform());
}
@@ -1778,7 +1942,6 @@ void VisualShaderNodeTransformVecMult::_bind_methods() {
}
VisualShaderNodeTransformVecMult::VisualShaderNodeTransformVecMult() {
- op = OP_AxB;
set_input_port_default_value(0, Transform());
set_input_port_default_value(1, Vector3());
}
@@ -1908,7 +2071,6 @@ void VisualShaderNodeFloatFunc::_bind_methods() {
}
VisualShaderNodeFloatFunc::VisualShaderNodeFloatFunc() {
- func = FUNC_SIGN;
set_input_port_default_value(0, 0.0);
}
@@ -2003,7 +2165,6 @@ void VisualShaderNodeIntFunc::_bind_methods() {
}
VisualShaderNodeIntFunc::VisualShaderNodeIntFunc() {
- func = FUNC_SIGN;
set_input_port_default_value(0, 0);
}
@@ -2169,7 +2330,6 @@ void VisualShaderNodeVectorFunc::_bind_methods() {
}
VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() {
- func = FUNC_NORMALIZE;
set_input_port_default_value(0, Vector3());
}
@@ -2256,9 +2416,8 @@ void VisualShaderNodeColorFunc::_bind_methods() {
}
VisualShaderNodeColorFunc::VisualShaderNodeColorFunc() {
- func = FUNC_GRAYSCALE;
- set_input_port_default_value(0, Vector3());
simple_decl = false;
+ set_input_port_default_value(0, Vector3());
}
////////////// Transform Func
@@ -2328,7 +2487,6 @@ void VisualShaderNodeTransformFunc::_bind_methods() {
}
VisualShaderNodeTransformFunc::VisualShaderNodeTransformFunc() {
- func = FUNC_INVERSE;
set_input_port_default_value(0, Transform());
}
@@ -2516,7 +2674,6 @@ void VisualShaderNodeScalarDerivativeFunc::_bind_methods() {
}
VisualShaderNodeScalarDerivativeFunc::VisualShaderNodeScalarDerivativeFunc() {
- func = FUNC_SUM;
set_input_port_default_value(0, 0.0);
}
@@ -2589,7 +2746,6 @@ void VisualShaderNodeVectorDerivativeFunc::_bind_methods() {
}
VisualShaderNodeVectorDerivativeFunc::VisualShaderNodeVectorDerivativeFunc() {
- func = FUNC_SUM;
set_input_port_default_value(0, Vector3());
}
@@ -3454,6 +3610,14 @@ String VisualShaderNodeFloatUniform::generate_code(Shader::Mode p_mode, VisualSh
return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
+bool VisualShaderNodeFloatUniform::is_show_prop_names() const {
+ return true;
+}
+
+bool VisualShaderNodeFloatUniform::is_use_prop_slots() const {
+ return true;
+}
+
void VisualShaderNodeFloatUniform::set_hint(Hint p_hint) {
hint = p_hint;
emit_changed();
@@ -3561,12 +3725,6 @@ Vector<StringName> VisualShaderNodeFloatUniform::get_editable_properties() const
}
VisualShaderNodeFloatUniform::VisualShaderNodeFloatUniform() {
- hint = HINT_NONE;
- hint_range_min = 0.0;
- hint_range_max = 1.0;
- hint_range_step = 0.1;
- default_value_enabled = false;
- default_value = 0.0;
}
////////////// Integer Uniform
@@ -3619,6 +3777,14 @@ String VisualShaderNodeIntUniform::generate_code(Shader::Mode p_mode, VisualShad
return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
+bool VisualShaderNodeIntUniform::is_show_prop_names() const {
+ return true;
+}
+
+bool VisualShaderNodeIntUniform::is_use_prop_slots() const {
+ return true;
+}
+
void VisualShaderNodeIntUniform::set_hint(Hint p_hint) {
hint = p_hint;
emit_changed();
@@ -3726,12 +3892,6 @@ Vector<StringName> VisualShaderNodeIntUniform::get_editable_properties() const {
}
VisualShaderNodeIntUniform::VisualShaderNodeIntUniform() {
- hint = HINT_NONE;
- hint_range_min = 0;
- hint_range_max = 100;
- hint_range_step = 1;
- default_value_enabled = false;
- default_value = 0;
}
////////////// Boolean Uniform
@@ -3799,6 +3959,14 @@ String VisualShaderNodeBooleanUniform::generate_code(Shader::Mode p_mode, Visual
return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
+bool VisualShaderNodeBooleanUniform::is_show_prop_names() const {
+ return true;
+}
+
+bool VisualShaderNodeBooleanUniform::is_use_prop_slots() const {
+ return true;
+}
+
void VisualShaderNodeBooleanUniform::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeBooleanUniform::set_default_value_enabled);
ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeBooleanUniform::is_default_value_enabled);
@@ -3824,8 +3992,6 @@ Vector<StringName> VisualShaderNodeBooleanUniform::get_editable_properties() con
}
VisualShaderNodeBooleanUniform::VisualShaderNodeBooleanUniform() {
- default_value_enabled = false;
- default_value = false;
}
////////////// Color Uniform
@@ -3891,6 +4057,10 @@ String VisualShaderNodeColorUniform::generate_code(Shader::Mode p_mode, VisualSh
return code;
}
+bool VisualShaderNodeColorUniform::is_show_prop_names() const {
+ return true;
+}
+
void VisualShaderNodeColorUniform::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeColorUniform::set_default_value_enabled);
ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeColorUniform::is_default_value_enabled);
@@ -3916,8 +4086,6 @@ Vector<StringName> VisualShaderNodeColorUniform::get_editable_properties() const
}
VisualShaderNodeColorUniform::VisualShaderNodeColorUniform() {
- default_value_enabled = false;
- default_value = Color(1.0, 1.0, 1.0, 1.0);
}
////////////// Vector Uniform
@@ -3992,6 +4160,14 @@ void VisualShaderNodeVec3Uniform::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "default_value"), "set_default_value", "get_default_value");
}
+bool VisualShaderNodeVec3Uniform::is_show_prop_names() const {
+ return true;
+}
+
+bool VisualShaderNodeVec3Uniform::is_use_prop_slots() const {
+ return true;
+}
+
bool VisualShaderNodeVec3Uniform::is_qualifier_supported(Qualifier p_qual) const {
return true; // all qualifiers are supported
}
@@ -4006,8 +4182,6 @@ Vector<StringName> VisualShaderNodeVec3Uniform::get_editable_properties() const
}
VisualShaderNodeVec3Uniform::VisualShaderNodeVec3Uniform() {
- default_value_enabled = false;
- default_value = Vector3(0.0, 0.0, 0.0);
}
////////////// Transform Uniform
@@ -4086,6 +4260,14 @@ void VisualShaderNodeTransformUniform::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "default_value"), "set_default_value", "get_default_value");
}
+bool VisualShaderNodeTransformUniform::is_show_prop_names() const {
+ return true;
+}
+
+bool VisualShaderNodeTransformUniform::is_use_prop_slots() const {
+ return true;
+}
+
bool VisualShaderNodeTransformUniform::is_qualifier_supported(Qualifier p_qual) const {
return true; // all qualifiers are supported
}
@@ -4100,8 +4282,6 @@ Vector<StringName> VisualShaderNodeTransformUniform::get_editable_properties() c
}
VisualShaderNodeTransformUniform::VisualShaderNodeTransformUniform() {
- default_value_enabled = false;
- default_value = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0);
}
////////////// Texture Uniform
@@ -4186,13 +4366,20 @@ bool VisualShaderNodeTextureUniform::is_code_generated() const {
}
String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ String default_uv;
+ if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ default_uv = "UV.xy";
+ } else {
+ default_uv = "vec2(0.0)";
+ }
+
String id = get_uniform_name();
String code = "\t{\n";
if (p_input_vars[0] == String()) { // Use UV by default.
if (p_input_vars[1] == String()) {
- code += "\t\tvec4 n_tex_read = texture(" + id + ", UV.xy);\n";
+ code += "\t\tvec4 n_tex_read = texture(" + id + ", " + default_uv + ");\n";
} else {
- code += "\t\tvec4 n_tex_read = textureLod(" + id + ", UV.xy, " + p_input_vars[1] + ");\n";
+ code += "\t\tvec4 n_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n";
}
} else if (p_input_vars[1] == String()) {
//no lod
@@ -4253,7 +4440,7 @@ void VisualShaderNodeTextureUniform::_bind_methods() {
String VisualShaderNodeTextureUniform::get_input_port_default_hint(int p_port) const {
if (p_port == 0) {
- return "UV.xy";
+ return "default";
}
return "";
}
@@ -4271,8 +4458,6 @@ bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) co
}
VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() {
- texture_type = TYPE_DATA;
- color_default = COLOR_DEFAULT_WHITE;
simple_decl = false;
}
@@ -4345,13 +4530,13 @@ String VisualShaderNodeTextureUniformTriplanar::generate_code(Shader::Mode p_mod
String code = "\t{\n";
if (p_input_vars[0] == String() && p_input_vars[1] == String()) {
- code += "\t\tvec4 n_tex_read = triplanar_texture( " + id + ", triplanar_power_normal, triplanar_pos );\n";
+ code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, triplanar_pos);\n";
} else if (p_input_vars[0] != String() && p_input_vars[1] == String()) {
- code += "\t\tvec4 n_tex_read = triplanar_texture( " + id + ", " + p_input_vars[0] + ", triplanar_pos );\n";
+ code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", triplanar_pos);\n";
} else if (p_input_vars[0] == String() && p_input_vars[1] != String()) {
- code += "\t\tvec4 n_tex_read = triplanar_texture( " + id + ", triplanar_power_normal," + p_input_vars[1] + " );\n";
+ code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, " + p_input_vars[1] + ");\n";
} else {
- code += "\t\tvec4 n_tex_read = triplanar_texture( " + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + " );\n";
+ code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
code += "\t\t" + p_output_vars[0] + " = n_tex_read.rgb;\n";
@@ -4441,6 +4626,74 @@ String VisualShaderNodeTexture2DArrayUniform::generate_code(Shader::Mode p_mode,
VisualShaderNodeTexture2DArrayUniform::VisualShaderNodeTexture2DArrayUniform() {
}
+////////////// Texture3D Uniform
+
+String VisualShaderNodeTexture3DUniform::get_caption() const {
+ return "Texture3DUniform";
+}
+
+int VisualShaderNodeTexture3DUniform::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeTexture3DUniform::PortType VisualShaderNodeTexture3DUniform::get_output_port_type(int p_port) const {
+ return PORT_TYPE_SAMPLER;
+}
+
+String VisualShaderNodeTexture3DUniform::get_output_port_name(int p_port) const {
+ return "sampler3D";
+}
+
+int VisualShaderNodeTexture3DUniform::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeTexture3DUniform::PortType VisualShaderNodeTexture3DUniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeTexture3DUniform::get_input_port_name(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeTexture3DUniform::get_input_port_default_hint(int p_port) const {
+ return "";
+}
+
+String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code = _get_qual_str() + "uniform sampler3D " + get_uniform_name();
+
+ switch (texture_type) {
+ case TYPE_DATA:
+ if (color_default == COLOR_DEFAULT_BLACK)
+ code += " : hint_black;\n";
+ else
+ code += ";\n";
+ break;
+ case TYPE_COLOR:
+ if (color_default == COLOR_DEFAULT_BLACK)
+ code += " : hint_black_albedo;\n";
+ else
+ code += " : hint_albedo;\n";
+ break;
+ case TYPE_NORMALMAP:
+ code += " : hint_normal;\n";
+ break;
+ case TYPE_ANISO:
+ code += " : hint_aniso;\n";
+ break;
+ }
+
+ return code;
+}
+
+String VisualShaderNodeTexture3DUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return String();
+}
+
+VisualShaderNodeTexture3DUniform::VisualShaderNodeTexture3DUniform() {
+}
+
////////////// Cubemap Uniform
String VisualShaderNodeCubemapUniform::get_caption() const {
@@ -4577,13 +4830,13 @@ String VisualShaderNodeIf::generate_code(Shader::Mode p_mode, VisualShader::Type
}
VisualShaderNodeIf::VisualShaderNodeIf() {
+ simple_decl = false;
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, CMP_EPSILON);
set_input_port_default_value(3, Vector3(0.0, 0.0, 0.0));
set_input_port_default_value(4, Vector3(0.0, 0.0, 0.0));
set_input_port_default_value(5, Vector3(0.0, 0.0, 0.0));
- simple_decl = false;
}
////////////// Switch
@@ -4642,10 +4895,10 @@ String VisualShaderNodeSwitch::generate_code(Shader::Mode p_mode, VisualShader::
}
VisualShaderNodeSwitch::VisualShaderNodeSwitch() {
+ simple_decl = false;
set_input_port_default_value(0, false);
set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
- simple_decl = false;
}
////////////// Switch(scalar)
@@ -4836,7 +5089,6 @@ void VisualShaderNodeIs::_bind_methods() {
}
VisualShaderNodeIs::VisualShaderNodeIs() {
- func = FUNC_IS_INF;
set_input_port_default_value(0, 0.0);
}
@@ -5072,9 +5324,6 @@ void VisualShaderNodeCompare::_bind_methods() {
}
VisualShaderNodeCompare::VisualShaderNodeCompare() {
- ctype = CTYPE_SCALAR;
- func = FUNC_EQUAL;
- condition = COND_ALL;
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, CMP_EPSILON);
@@ -5091,7 +5340,7 @@ int VisualShaderNodeMultiplyAdd::get_input_port_count() const {
}
VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_input_port_type(int p_port) const {
- if (type == TYPE_SCALAR) {
+ if (op_type == OP_TYPE_SCALAR) {
return PORT_TYPE_SCALAR;
}
return PORT_TYPE_VECTOR;
@@ -5113,7 +5362,7 @@ int VisualShaderNodeMultiplyAdd::get_output_port_count() const {
}
VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_output_port_type(int p_port) const {
- if (type == TYPE_SCALAR) {
+ if (op_type == OP_TYPE_SCALAR) {
return PORT_TYPE_SCALAR;
} else {
return PORT_TYPE_VECTOR;
@@ -5128,10 +5377,10 @@ String VisualShaderNodeMultiplyAdd::generate_code(Shader::Mode p_mode, VisualSha
return "\t" + p_output_vars[0] + " = fma(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
-void VisualShaderNodeMultiplyAdd::set_type(Type p_type) {
- ERR_FAIL_INDEX((int)p_type, TYPE_MAX);
- if (p_type != type) {
- if (p_type == TYPE_SCALAR) {
+void VisualShaderNodeMultiplyAdd::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ if (p_op_type != op_type) {
+ if (p_op_type == OP_TYPE_SCALAR) {
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, 0.0);
@@ -5141,33 +5390,32 @@ void VisualShaderNodeMultiplyAdd::set_type(Type p_type) {
set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
}
}
- type = p_type;
+ op_type = p_op_type;
emit_changed();
}
-VisualShaderNodeMultiplyAdd::Type VisualShaderNodeMultiplyAdd::get_type() const {
- return type;
+VisualShaderNodeMultiplyAdd::OpType VisualShaderNodeMultiplyAdd::get_op_type() const {
+ return op_type;
}
Vector<StringName> VisualShaderNodeMultiplyAdd::get_editable_properties() const {
Vector<StringName> props;
- props.push_back("type");
+ props.push_back("op_type");
return props;
}
void VisualShaderNodeMultiplyAdd::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_type", "type"), &VisualShaderNodeMultiplyAdd::set_type);
- ClassDB::bind_method(D_METHOD("get_type"), &VisualShaderNodeMultiplyAdd::get_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeMultiplyAdd::set_op_type);
+ ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMultiplyAdd::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Scalar,Vector"), "set_type", "get_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector"), "set_op_type", "get_op_type");
- BIND_ENUM_CONSTANT(TYPE_SCALAR);
- BIND_ENUM_CONSTANT(TYPE_VECTOR);
- BIND_ENUM_CONSTANT(TYPE_MAX);
+ BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
VisualShaderNodeMultiplyAdd::VisualShaderNodeMultiplyAdd() {
- type = TYPE_SCALAR;
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, 0.0);
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index b9c40d0521..4b39c76388 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -39,7 +39,7 @@
class VisualShaderNodeFloatConstant : public VisualShaderNode {
GDCLASS(VisualShaderNodeFloatConstant, VisualShaderNode);
- float constant;
+ float constant = 0.0f;
protected:
static void _bind_methods();
@@ -69,7 +69,7 @@ public:
class VisualShaderNodeIntConstant : public VisualShaderNode {
GDCLASS(VisualShaderNodeIntConstant, VisualShaderNode);
- int constant;
+ int constant = 0;
protected:
static void _bind_methods();
@@ -99,7 +99,7 @@ public:
class VisualShaderNodeBooleanConstant : public VisualShaderNode {
GDCLASS(VisualShaderNodeBooleanConstant, VisualShaderNode);
- bool constant;
+ bool constant = false;
protected:
static void _bind_methods();
@@ -129,7 +129,7 @@ public:
class VisualShaderNodeColorConstant : public VisualShaderNode {
GDCLASS(VisualShaderNodeColorConstant, VisualShaderNode);
- Color constant;
+ Color constant = Color(1, 1, 1, 1);
protected:
static void _bind_methods();
@@ -240,8 +240,8 @@ public:
};
private:
- Source source;
- TextureType texture_type;
+ Source source = SOURCE_TEXTURE;
+ TextureType texture_type = TYPE_DATA;
protected:
static void _bind_methods();
@@ -284,6 +284,39 @@ VARIANT_ENUM_CAST(VisualShaderNodeTexture::Source)
///////////////////////////////////////
+class VisualShaderNodeCurveTexture : public VisualShaderNodeResizableBase {
+ GDCLASS(VisualShaderNodeCurveTexture, VisualShaderNodeResizableBase);
+ Ref<CurveTexture> texture;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ void set_texture(Ref<CurveTexture> p_value);
+ Ref<CurveTexture> get_texture() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+ virtual bool is_use_prop_slots() const override;
+
+ VisualShaderNodeCurveTexture();
+};
+
+///////////////////////////////////////
+
class VisualShaderNodeSample3D : public VisualShaderNode {
GDCLASS(VisualShaderNodeSample3D, VisualShaderNode);
@@ -294,7 +327,7 @@ public:
};
protected:
- Source source;
+ Source source = SOURCE_TEXTURE;
static void _bind_methods();
@@ -343,6 +376,29 @@ public:
VisualShaderNodeTexture2DArray();
};
+class VisualShaderNodeTexture3D : public VisualShaderNodeSample3D {
+ GDCLASS(VisualShaderNodeTexture3D, VisualShaderNodeSample3D);
+ Ref<Texture3D> texture;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+
+ void set_texture(Ref<Texture3D> p_value);
+ Ref<Texture3D> get_texture() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeTexture3D();
+};
+
class VisualShaderNodeCubemap : public VisualShaderNode {
GDCLASS(VisualShaderNodeCubemap, VisualShaderNode);
Ref<Cubemap> cube_map;
@@ -360,8 +416,8 @@ public:
};
private:
- Source source;
- TextureType texture_type;
+ Source source = SOURCE_TEXTURE;
+ TextureType texture_type = TYPE_DATA;
protected:
static void _bind_methods();
@@ -392,6 +448,7 @@ public:
TextureType get_texture_type() const;
virtual Vector<StringName> get_editable_properties() const override;
+ virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override;
VisualShaderNodeCubemap();
};
@@ -421,7 +478,7 @@ public:
};
protected:
- Operator op;
+ Operator op = OP_ADD;
static void _bind_methods();
@@ -463,7 +520,7 @@ public:
};
protected:
- Operator op;
+ Operator op = OP_ADD;
static void _bind_methods();
@@ -510,7 +567,7 @@ public:
};
protected:
- Operator op;
+ Operator op = OP_ADD;
static void _bind_methods();
@@ -556,7 +613,7 @@ public:
};
protected:
- Operator op;
+ Operator op = OP_SCREEN;
static void _bind_methods();
@@ -599,7 +656,7 @@ public:
};
protected:
- Operator op;
+ Operator op = OP_AxB;
static void _bind_methods();
@@ -642,7 +699,7 @@ public:
};
protected:
- Operator op;
+ Operator op = OP_AxB;
static void _bind_methods();
@@ -713,7 +770,7 @@ public:
};
protected:
- Function func;
+ Function func = FUNC_SIGN;
static void _bind_methods();
@@ -756,7 +813,7 @@ public:
};
protected:
- Function func;
+ Function func = FUNC_SIGN;
static void _bind_methods();
@@ -830,7 +887,7 @@ public:
};
protected:
- Function func;
+ Function func = FUNC_NORMALIZE;
static void _bind_methods();
@@ -871,7 +928,7 @@ public:
};
protected:
- Function func;
+ Function func = FUNC_GRAYSCALE;
static void _bind_methods();
@@ -912,7 +969,7 @@ public:
};
protected:
- Function func;
+ Function func = FUNC_INVERSE;
static void _bind_methods();
@@ -1067,7 +1124,7 @@ public:
};
protected:
- Function func;
+ Function func = FUNC_SUM;
static void _bind_methods();
@@ -1107,7 +1164,7 @@ public:
};
protected:
- Function func;
+ Function func = FUNC_SUM;
static void _bind_methods();
@@ -1482,12 +1539,12 @@ public:
};
private:
- Hint hint;
- float hint_range_min;
- float hint_range_max;
- float hint_range_step;
- bool default_value_enabled;
- float default_value;
+ Hint hint = HINT_NONE;
+ float hint_range_min = 0.0f;
+ float hint_range_max = 1.0f;
+ float hint_range_step = 0.1f;
+ bool default_value_enabled = false;
+ float default_value = 0.0f;
protected:
static void _bind_methods();
@@ -1506,6 +1563,9 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ virtual bool is_show_prop_names() const override;
+ virtual bool is_use_prop_slots() const override;
+
void set_hint(Hint p_hint);
Hint get_hint() const;
@@ -1544,12 +1604,12 @@ public:
};
private:
- Hint hint;
- int hint_range_min;
- int hint_range_max;
- int hint_range_step;
- bool default_value_enabled;
- int default_value;
+ Hint hint = HINT_NONE;
+ int hint_range_min = 0;
+ int hint_range_max = 100;
+ int hint_range_step = 1;
+ bool default_value_enabled = false;
+ int default_value = 0;
protected:
static void _bind_methods();
@@ -1568,6 +1628,9 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ virtual bool is_show_prop_names() const override;
+ virtual bool is_use_prop_slots() const override;
+
void set_hint(Hint p_hint);
Hint get_hint() const;
@@ -1601,8 +1664,8 @@ class VisualShaderNodeBooleanUniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeBooleanUniform, VisualShaderNodeUniform);
private:
- bool default_value_enabled;
- bool default_value;
+ bool default_value_enabled = false;
+ bool default_value = false;
protected:
static void _bind_methods();
@@ -1621,6 +1684,9 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ virtual bool is_show_prop_names() const override;
+ virtual bool is_use_prop_slots() const override;
+
void set_default_value_enabled(bool p_enabled);
bool is_default_value_enabled() const;
@@ -1640,8 +1706,8 @@ class VisualShaderNodeColorUniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeColorUniform, VisualShaderNodeUniform);
private:
- bool default_value_enabled;
- Color default_value;
+ bool default_value_enabled = false;
+ Color default_value = Color(1.0, 1.0, 1.0, 1.0);
protected:
static void _bind_methods();
@@ -1660,6 +1726,8 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ virtual bool is_show_prop_names() const override;
+
void set_default_value_enabled(bool p_enabled);
bool is_default_value_enabled() const;
@@ -1679,7 +1747,7 @@ class VisualShaderNodeVec3Uniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeVec3Uniform, VisualShaderNodeUniform);
private:
- bool default_value_enabled;
+ bool default_value_enabled = false;
Vector3 default_value;
protected:
@@ -1699,6 +1767,9 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ virtual bool is_show_prop_names() const override;
+ virtual bool is_use_prop_slots() const override;
+
void set_default_value_enabled(bool p_enabled);
bool is_default_value_enabled() const;
@@ -1718,8 +1789,8 @@ class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeTransformUniform, VisualShaderNodeUniform);
private:
- bool default_value_enabled;
- Transform default_value;
+ bool default_value_enabled = false;
+ Transform default_value = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0);
protected:
static void _bind_methods();
@@ -1738,6 +1809,9 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+ virtual bool is_show_prop_names() const override;
+ virtual bool is_use_prop_slots() const override;
+
void set_default_value_enabled(bool p_enabled);
bool is_default_value_enabled() const;
@@ -1770,8 +1844,8 @@ public:
};
protected:
- TextureType texture_type;
- ColorDefault color_default;
+ TextureType texture_type = TYPE_DATA;
+ ColorDefault color_default = COLOR_DEFAULT_WHITE;
protected:
static void _bind_methods();
@@ -1855,6 +1929,29 @@ public:
///////////////////////////////////////
+class VisualShaderNodeTexture3DUniform : public VisualShaderNodeTextureUniform {
+ GDCLASS(VisualShaderNodeTexture3DUniform, VisualShaderNodeTextureUniform);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual String get_input_port_default_hint(int p_port) const override;
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
+
+ VisualShaderNodeTexture3DUniform();
+};
+
+///////////////////////////////////////
+
class VisualShaderNodeCubemapUniform : public VisualShaderNodeTextureUniform {
GDCLASS(VisualShaderNodeCubemapUniform, VisualShaderNodeTextureUniform);
@@ -1973,7 +2070,7 @@ public:
};
protected:
- Function func;
+ Function func = FUNC_IS_INF;
protected:
static void _bind_methods();
@@ -2032,9 +2129,9 @@ public:
};
protected:
- ComparisonType ctype;
- Function func;
- Condition condition;
+ ComparisonType ctype = CTYPE_SCALAR;
+ Function func = FUNC_EQUAL;
+ Condition condition = COND_ALL;
protected:
static void _bind_methods();
@@ -2075,14 +2172,14 @@ class VisualShaderNodeMultiplyAdd : public VisualShaderNode {
GDCLASS(VisualShaderNodeMultiplyAdd, VisualShaderNode);
public:
- enum Type {
- TYPE_SCALAR,
- TYPE_VECTOR,
- TYPE_MAX,
+ enum OpType {
+ OP_TYPE_SCALAR,
+ OP_TYPE_VECTOR,
+ OP_TYPE_MAX,
};
protected:
- Type type;
+ OpType op_type = OP_TYPE_SCALAR;
protected:
static void _bind_methods();
@@ -2100,14 +2197,14 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty
- void set_type(Type p_type);
- Type get_type() const;
+ void set_op_type(OpType p_type);
+ OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
VisualShaderNodeMultiplyAdd();
};
-VARIANT_ENUM_CAST(VisualShaderNodeMultiplyAdd::Type)
+VARIANT_ENUM_CAST(VisualShaderNodeMultiplyAdd::OpType)
#endif // VISUAL_SHADER_NODES_H
diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp
index d2bc2bea31..41d3fe20be 100644
--- a/scene/resources/world_2d.cpp
+++ b/scene/resources/world_2d.cpp
@@ -30,7 +30,7 @@
#include "world_2d.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "scene/2d/camera_2d.h"
#include "scene/2d/visibility_notifier_2d.h"
#include "scene/main/window.h"
@@ -341,8 +341,8 @@ void World2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World2D::get_direct_space_state);
- ADD_PROPERTY(PropertyInfo(Variant::_RID, "canvas", PROPERTY_HINT_NONE, "", 0), "", "get_canvas");
- ADD_PROPERTY(PropertyInfo(Variant::_RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "canvas", PROPERTY_HINT_NONE, "", 0), "", "get_canvas");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState2D", 0), "", "get_direct_space_state");
}
diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h
index c330719104..11614f9aa4 100644
--- a/scene/resources/world_2d.h
+++ b/scene/resources/world_2d.h
@@ -31,8 +31,8 @@
#ifndef WORLD_2D_H
#define WORLD_2D_H
-#include "core/project_settings.h"
-#include "core/resource.h"
+#include "core/config/project_settings.h"
+#include "core/io/resource.h"
#include "servers/physics_server_2d.h"
class VisibilityNotifier2D;
diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp
index 8100f150ef..b8fb3825ce 100644
--- a/scene/resources/world_3d.cpp
+++ b/scene/resources/world_3d.cpp
@@ -321,8 +321,8 @@ void World3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_fallback_environment", "get_fallback_environment");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects");
- ADD_PROPERTY(PropertyInfo(Variant::_RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space");
- ADD_PROPERTY(PropertyInfo(Variant::_RID, "scenario", PROPERTY_HINT_NONE, "", 0), "", "get_scenario");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "scenario", PROPERTY_HINT_NONE, "", 0), "", "get_scenario");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState3D", 0), "", "get_direct_space_state");
}
diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h
index 02a821637f..93e9c72e59 100644
--- a/scene/resources/world_3d.h
+++ b/scene/resources/world_3d.h
@@ -31,7 +31,7 @@
#ifndef WORLD_3D_H
#define WORLD_3D_H
-#include "core/resource.h"
+#include "core/io/resource.h"
#include "scene/resources/camera_effects.h"
#include "scene/resources/environment.h"
#include "servers/physics_server_3d.h"
diff --git a/scene/resources/world_margin_shape_3d.cpp b/scene/resources/world_margin_shape_3d.cpp
index d613413b33..0936fcc657 100644
--- a/scene/resources/world_margin_shape_3d.cpp
+++ b/scene/resources/world_margin_shape_3d.cpp
@@ -32,7 +32,7 @@
#include "servers/physics_server_3d.h"
-Vector<Vector3> WorldMarginShape3D::get_debug_mesh_lines() {
+Vector<Vector3> WorldMarginShape3D::get_debug_mesh_lines() const {
Plane p = get_plane();
Vector<Vector3> points;
diff --git a/scene/resources/world_margin_shape_3d.h b/scene/resources/world_margin_shape_3d.h
index c920dc513c..8099592d80 100644
--- a/scene/resources/world_margin_shape_3d.h
+++ b/scene/resources/world_margin_shape_3d.h
@@ -45,7 +45,7 @@ public:
void set_plane(Plane p_plane);
Plane get_plane() const;
- virtual Vector<Vector3> get_debug_mesh_lines() override;
+ virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override {
// Should be infinite?
return 0;
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index 7cf9a4fedd..a72066e7a8 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -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 1ae244492e..b4d2429d7f 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -31,8 +31,8 @@
#ifndef SCENE_STRING_NAMES_H
#define SCENE_STRING_NAMES_H
-#include "core/node_path.h"
-#include "core/string_name.h"
+#include "core/string/node_path.h"
+#include "core/string/string_name.h"
class SceneStringNames {
friend void register_scene_types();
@@ -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;