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.cpp7
-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.cpp60
-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.cpp2
-rw-r--r--scene/2d/remote_transform_2d.cpp9
-rw-r--r--scene/2d/skeleton_2d.cpp4
-rw-r--r--scene/2d/sprite_2d.cpp64
-rw-r--r--scene/2d/sprite_2d.h14
-rw-r--r--scene/2d/tile_map.cpp6
-rw-r--r--scene/2d/tile_map.h4
-rw-r--r--scene/2d/visibility_notifier_2d.cpp11
-rw-r--r--scene/3d/area_3d.cpp38
-rw-r--r--scene/3d/area_3d.h6
-rw-r--r--scene/3d/audio_stream_player_3d.cpp7
-rw-r--r--scene/3d/baked_lightmap.cpp2
-rw-r--r--scene/3d/baked_lightmap.h2
-rw-r--r--scene/3d/camera_3d.cpp8
-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.cpp26
-rw-r--r--scene/3d/cpu_particles_3d.cpp76
-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.cpp16
-rw-r--r--scene/3d/gpu_particles_3d.h5
-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.cpp25
-rw-r--r--scene/3d/light_3d.h4
-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.cpp22
-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.cpp68
-rw-r--r--scene/3d/physics_joint_3d.h12
-rw-r--r--scene/3d/proximity_group_3d.cpp94
-rw-r--r--scene/3d/proximity_group_3d.h27
-rw-r--r--scene/3d/ray_cast_3d.cpp2
-rw-r--r--scene/3d/reflection_probe.cpp14
-rw-r--r--scene/3d/reflection_probe.h4
-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.cpp13
-rw-r--r--scene/3d/spring_arm_3d.cpp2
-rw-r--r--scene/3d/sprite_3d.cpp16
-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.cpp16
-rw-r--r--scene/3d/visual_instance_3d.h7
-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/SCsub18
-rw-r--r--scene/animation/SCsub21
-rw-r--r--scene/animation/animation_node_state_machine.cpp4
-rw-r--r--scene/animation/animation_player.cpp98
-rw-r--r--scene/animation/animation_player.h20
-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.cpp58
-rw-r--r--scene/gui/base_button.h12
-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.cpp30
-rw-r--r--scene/gui/color_picker.cpp4
-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.cpp643
-rw-r--r--scene/gui/control.h116
-rw-r--r--scene/gui/dialogs.cpp22
-rw-r--r--scene/gui/dialogs.h6
-rw-r--r--scene/gui/file_dialog.cpp27
-rw-r--r--scene/gui/gradient_edit.cpp2
-rw-r--r--scene/gui/graph_edit.cpp407
-rw-r--r--scene/gui/graph_edit.h68
-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.cpp725
-rw-r--r--scene/gui/label.h56
-rw-r--r--scene/gui/line_edit.cpp1144
-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.cpp20
-rw-r--r--scene/gui/popup.h7
-rw-r--r--scene/gui/popup_menu.cpp319
-rw-r--r--scene/gui/popup_menu.h34
-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.cpp33
-rw-r--r--scene/gui/rich_text_effect.h31
-rw-r--r--scene/gui/rich_text_label.cpp2736
-rw-r--r--scene/gui/rich_text_label.h274
-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.h2
-rw-r--r--scene/gui/spin_box.cpp21
-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.cpp258
-rw-r--r--scene/gui/tabs.h21
-rw-r--r--scene/gui/text_edit.cpp1923
-rw-r--r--scene/gui/text_edit.h146
-rw-r--r--scene/gui/texture_progress_bar.cpp (renamed from scene/gui/texture_progress.cpp)116
-rw-r--r--scene/gui/texture_progress_bar.h (renamed from scene/gui/texture_progress.h)16
-rw-r--r--scene/gui/texture_rect.h7
-rw-r--r--scene/gui/tree.cpp518
-rw-r--r--scene/gui/tree.h52
-rw-r--r--scene/main/canvas_item.cpp370
-rw-r--r--scene/main/canvas_item.h96
-rw-r--r--scene/main/http_request.cpp2
-rw-r--r--scene/main/node.cpp6
-rw-r--r--scene/main/node.h25
-rw-r--r--scene/main/scene_tree.cpp36
-rw-r--r--scene/main/scene_tree.h2
-rw-r--r--scene/main/shader_globals_override.cpp11
-rw-r--r--scene/main/timer.cpp6
-rw-r--r--scene/main/viewport.cpp357
-rw-r--r--scene/main/viewport.h56
-rw-r--r--scene/main/window.cpp83
-rw-r--r--scene/main/window.h21
-rw-r--r--scene/register_scene_types.cpp90
-rw-r--r--scene/resources/SCsub21
-rw-r--r--scene/resources/animation.cpp12
-rw-r--r--scene/resources/animation.h6
-rw-r--r--scene/resources/audio_stream_sample.cpp3
-rw-r--r--scene/resources/bit_map.cpp6
-rw-r--r--scene/resources/bit_map.h4
-rw-r--r--scene/resources/camera_effects.h4
-rw-r--r--scene/resources/capsule_shape_3d.cpp4
-rw-r--r--scene/resources/curve.h2
-rw-r--r--scene/resources/cylinder_shape_3d.cpp4
-rw-r--r--scene/resources/default_theme/arrow_left.pngbin0 -> 159 bytes
-rw-r--r--scene/resources/default_theme/default_theme.cpp163
-rw-r--r--scene/resources/default_theme/font_lodpi.inc2
-rw-r--r--scene/resources/default_theme/icon_grid_minimap.pngbin0 -> 640 bytes
-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.cpp123
-rw-r--r--scene/resources/environment.h21
-rw-r--r--scene/resources/font.cpp1267
-rw-r--r--scene/resources/font.h261
-rw-r--r--scene/resources/gradient.cpp22
-rw-r--r--scene/resources/gradient.h2
-rw-r--r--scene/resources/material.cpp176
-rw-r--r--scene/resources/material.h142
-rw-r--r--scene/resources/mesh.cpp288
-rw-r--r--scene/resources/mesh.h90
-rw-r--r--scene/resources/mesh_library.h4
-rw-r--r--scene/resources/packed_scene.cpp18
-rw-r--r--scene/resources/packed_scene.h3
-rw-r--r--scene/resources/particles_material.cpp162
-rw-r--r--scene/resources/particles_material.h62
-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.cpp2
-rw-r--r--scene/resources/resource_format_text.cpp17
-rw-r--r--scene/resources/resource_format_text.h2
-rw-r--r--scene/resources/shader.cpp3
-rw-r--r--scene/resources/shader.h3
-rw-r--r--scene/resources/shape_2d.h2
-rw-r--r--scene/resources/shape_3d.h2
-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.cpp2
-rw-r--r--scene/resources/style_box.cpp35
-rw-r--r--scene/resources/style_box.h7
-rw-r--r--scene/resources/surface_tool.cpp727
-rw-r--r--scene/resources/surface_tool.h81
-rw-r--r--scene/resources/syntax_highlighter.cpp9
-rw-r--r--scene/resources/syntax_highlighter.h2
-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.cpp141
-rw-r--r--scene/resources/texture.h39
-rw-r--r--scene/resources/theme.cpp491
-rw-r--r--scene/resources/theme.h106
-rw-r--r--scene/resources/tile_set.cpp22
-rw-r--r--scene/resources/tile_set.h8
-rw-r--r--scene/resources/visual_shader.cpp202
-rw-r--r--scene/resources/visual_shader.h43
-rw-r--r--scene/resources/visual_shader_nodes.cpp247
-rw-r--r--scene/resources/visual_shader_nodes.h67
-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/scene_string_names.cpp3
-rw-r--r--scene/scene_string_names.h7
279 files changed, 16359 insertions, 8321 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 fd4d5981ff..0d09d21a71 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"
@@ -77,6 +77,9 @@ void Camera2D::_update_process_mode() {
}
void Camera2D::set_zoom(const Vector2 &p_zoom) {
+ // Setting zoom to zero causes 'affine_invert' issues
+ ERR_FAIL_COND_MSG(Math::is_zero_approx(p_zoom.x) || Math::is_zero_approx(p_zoom.y), "Zoom level must be different from 0 (can be negative).");
+
zoom = p_zoom;
Point2 old_smoothed_camera_pos = smoothed_camera_pos;
_update_scroll();
@@ -198,10 +201,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()*/);
/*
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 e3a632c98a..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) {
@@ -914,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();
@@ -1060,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) {
@@ -1144,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());
@@ -1214,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");
@@ -1236,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);
@@ -1264,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);
@@ -1291,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");
@@ -1369,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);
@@ -1433,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 a00db36077..e53f89c46d 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -31,7 +31,7 @@
#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"
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 d1be93e55d..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,11 +444,6 @@ 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");
@@ -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 c2951559a4..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"
@@ -529,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);
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index b9dd8f5405..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"
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 dc35f069c0..b1adb0e88e 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;
@@ -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;
@@ -517,11 +505,11 @@ bool Area3D::is_overriding_audio_bus() const {
return audio_bus_override;
}
-void Area3D::set_audio_bus(const StringName &p_audio_bus) {
+void Area3D::set_audio_bus_name(const StringName &p_audio_bus) {
audio_bus = p_audio_bus;
}
-StringName Area3D::get_audio_bus() const {
+StringName Area3D::get_audio_bus_name() const {
for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
if (AudioServer::get_singleton()->get_bus_name(i) == audio_bus) {
return audio_bus;
@@ -637,8 +625,8 @@ void Area3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_audio_bus_override", "enable"), &Area3D::set_audio_bus_override);
ClassDB::bind_method(D_METHOD("is_overriding_audio_bus"), &Area3D::is_overriding_audio_bus);
- ClassDB::bind_method(D_METHOD("set_audio_bus", "name"), &Area3D::set_audio_bus);
- ClassDB::bind_method(D_METHOD("get_audio_bus"), &Area3D::get_audio_bus);
+ ClassDB::bind_method(D_METHOD("set_audio_bus_name", "name"), &Area3D::set_audio_bus_name);
+ ClassDB::bind_method(D_METHOD("get_audio_bus_name"), &Area3D::get_audio_bus_name);
ClassDB::bind_method(D_METHOD("set_use_reverb_bus", "enable"), &Area3D::set_use_reverb_bus);
ClassDB::bind_method(D_METHOD("is_using_reverb_bus"), &Area3D::is_using_reverb_bus);
@@ -677,7 +665,7 @@ void Area3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
ADD_GROUP("Audio Bus", "audio_bus_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_bus_override"), "set_audio_bus_override", "is_overriding_audio_bus");
- ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "audio_bus_name", PROPERTY_HINT_ENUM, ""), "set_audio_bus", "get_audio_bus");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "audio_bus_name", PROPERTY_HINT_ENUM, ""), "set_audio_bus_name", "get_audio_bus_name");
ADD_GROUP("Reverb Bus", "reverb_bus_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reverb_bus_enable"), "set_use_reverb_bus", "is_using_reverb_bus");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "reverb_bus_name", PROPERTY_HINT_ENUM, ""), "set_reverb_bus", "get_reverb_bus");
diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h
index 07d24f39a7..51f6317517 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 {
@@ -190,8 +190,8 @@ public:
void set_audio_bus_override(bool p_override);
bool is_overriding_audio_bus() const;
- void set_audio_bus(const StringName &p_audio_bus);
- StringName get_audio_bus() const;
+ void set_audio_bus_name(const StringName &p_audio_bus);
+ StringName get_audio_bus_name() const;
void set_use_reverb_bus(bool p_enable);
bool is_using_reverb_bus() const;
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 6e4db8f382..2907eb3c7e 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);
@@ -487,7 +484,7 @@ void AudioStreamPlayer3D::_notification(int p_what) {
if (area) {
if (area->is_overriding_audio_bus()) {
//override audio bus
- StringName bus_name = area->get_audio_bus();
+ StringName bus_name = area->get_audio_bus_name();
output.bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name);
}
diff --git a/scene/3d/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..178c5c8ff8 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,10,0.001,or_greater"), "set_znear", "get_znear");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_EXP_RANGE, "0.01,4000,0.01,or_greater"), "set_zfar", "get_zfar");
BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE);
BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL);
@@ -662,7 +662,7 @@ Camera3D::Camera3D() {
viewport = nullptr;
force_change = false;
mode = PROJECTION_PERSPECTIVE;
- set_perspective(75.0, 0.05, 100.0);
+ set_perspective(75.0, 0.05, 4000.0);
keep_aspect = KEEP_HEIGHT;
layers = 0xfffff;
v_offset = 0;
diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h
index 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 e7f3f53ca9..e1c691b89a 100644
--- a/scene/3d/collision_shape_3d.cpp
+++ b/scene/3d/collision_shape_3d.cpp
@@ -124,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() {
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index ad8760251f..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,7 +725,7 @@ void CPUParticles3D::_particles_process(float p_delta) {
p.transform.origin = emission_points.get(random_idx);
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS && emission_normals.size() == pc) {
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
Vector3 normal = emission_normals.get(random_idx);
Vector2 normal_2d(normal.x, normal.y);
Transform2D m2;
@@ -762,7 +762,7 @@ void CPUParticles3D::_particles_process(float p_delta) {
p.transform = emission_xform * p.transform;
}
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
p.velocity.z = 0.0;
p.transform.origin.z = 0.0;
}
@@ -783,7 +783,7 @@ void CPUParticles3D::_particles_process(float p_delta) {
}
float tex_orbit_velocity = 0.0;
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(p.custom[1]);
}
@@ -830,7 +830,7 @@ void CPUParticles3D::_particles_process(float p_delta) {
Vector3 force = gravity;
Vector3 position = p.transform.origin;
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
position.z = 0.0;
}
//apply linear acceleration
@@ -840,7 +840,7 @@ void CPUParticles3D::_particles_process(float p_delta) {
Vector3 diff = position - org;
force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector3();
//apply tangential acceleration;
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
Vector2 yx = Vector2(diff.y, diff.x);
Vector2 yx2 = (yx * Vector2(-1.0, 1.0)).normalized();
force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3();
@@ -852,7 +852,7 @@ void CPUParticles3D::_particles_process(float p_delta) {
//apply attractor forces
p.velocity += force * local_delta;
//orbit velocity
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]);
if (orbit_amount != 0.0) {
float ang = orbit_amount * local_delta * Math_PI * 2.0;
@@ -923,8 +923,8 @@ void CPUParticles3D::_particles_process(float p_delta) {
p.color *= p.base_color;
- if (flags[FLAG_DISABLE_Z]) {
- if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) {
if (p.velocity.length() > 0.0) {
p.transform.basis.set_axis(1, p.velocity.normalized());
} else {
@@ -941,7 +941,7 @@ void CPUParticles3D::_particles_process(float p_delta) {
} else {
//orient particle Y towards velocity
- if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
+ if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) {
if (p.velocity.length() > 0.0) {
p.transform.basis.set_axis(1, p.velocity.normalized());
} else {
@@ -959,7 +959,7 @@ void CPUParticles3D::_particles_process(float p_delta) {
}
//turn particle by rotation in Y
- if (flags[FLAG_ROTATE_Y]) {
+ if (particle_flags[PARTICLE_FLAG_ROTATE_Y]) {
Basis rot_y(Vector3(0, 1, 0), p.custom[0]);
p.transform.basis = p.transform.basis * rot_y;
}
@@ -973,7 +973,7 @@ void CPUParticles3D::_particles_process(float p_delta) {
p.transform.basis.scale(Vector3(1, 1, 1) * base_scale);
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
p.velocity.z = 0.0;
p.transform.origin.z = 0.0;
}
@@ -1199,9 +1199,9 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) {
set_color_ramp(gt->get_gradient());
}
- set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY));
- set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y));
- set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z));
+ set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY));
+ set_particle_flag(PARTICLE_FLAG_ROTATE_Y, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ROTATE_Y));
+ set_particle_flag(PARTICLE_FLAG_DISABLE_Z, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_DISABLE_Z));
set_emission_shape(EmissionShape(material->get_emission_shape()));
set_emission_sphere_radius(material->get_emission_sphere_radius());
@@ -1318,8 +1318,8 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles3D::set_color_ramp);
ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles3D::get_color_ramp);
- ClassDB::bind_method(D_METHOD("set_particle_flag", "flag", "enable"), &CPUParticles3D::set_particle_flag);
- ClassDB::bind_method(D_METHOD("get_particle_flag", "flag"), &CPUParticles3D::get_particle_flag);
+ ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &CPUParticles3D::set_particle_flag);
+ ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &CPUParticles3D::get_particle_flag);
ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &CPUParticles3D::set_emission_shape);
ClassDB::bind_method(D_METHOD("get_emission_shape"), &CPUParticles3D::get_emission_shape);
@@ -1345,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");
@@ -1426,10 +1426,10 @@ void CPUParticles3D::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET);
BIND_ENUM_CONSTANT(PARAM_MAX);
- BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY);
- BIND_ENUM_CONSTANT(FLAG_ROTATE_Y);
- BIND_ENUM_CONSTANT(FLAG_DISABLE_Z);
- BIND_ENUM_CONSTANT(FLAG_MAX);
+ BIND_ENUM_CONSTANT(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY);
+ BIND_ENUM_CONSTANT(PARTICLE_FLAG_ROTATE_Y);
+ BIND_ENUM_CONSTANT(PARTICLE_FLAG_DISABLE_Z);
+ BIND_ENUM_CONSTANT(PARTICLE_FLAG_MAX);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE);
@@ -1493,8 +1493,8 @@ CPUParticles3D::CPUParticles3D() {
set_param_randomness(Parameter(i), 0);
}
- for (int i = 0; i < FLAG_MAX; i++) {
- flags[i] = false;
+ for (int i = 0; i < PARTICLE_FLAG_MAX; i++) {
+ particle_flags[i] = false;
}
can_update = false;
diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h
index 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 6fa0fc6ecb..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;
@@ -381,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);
@@ -395,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);
@@ -426,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");
@@ -469,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 0d8dadd31d..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"
@@ -65,6 +65,7 @@ private:
int fixed_fps;
bool fractional_delta;
NodePath sub_emitter;
+ float collision_base_size;
Ref<Material> process_material;
@@ -94,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;
@@ -106,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;
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 2eb12d10fa..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 {
@@ -394,6 +394,15 @@ bool DirectionalLight3D::is_blend_splits_enabled() const {
return blend_splits;
}
+void DirectionalLight3D::set_sky_only(bool p_sky_only) {
+ sky_only = p_sky_only;
+ RS::get_singleton()->light_directional_set_sky_only(light, p_sky_only);
+}
+
+bool DirectionalLight3D::is_sky_only() const {
+ return sky_only;
+}
+
void DirectionalLight3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shadow_mode", "mode"), &DirectionalLight3D::set_shadow_mode);
ClassDB::bind_method(D_METHOD("get_shadow_mode"), &DirectionalLight3D::get_shadow_mode);
@@ -404,6 +413,9 @@ void DirectionalLight3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_blend_splits", "enabled"), &DirectionalLight3D::set_blend_splits);
ClassDB::bind_method(D_METHOD("is_blend_splits_enabled"), &DirectionalLight3D::is_blend_splits_enabled);
+ ClassDB::bind_method(D_METHOD("set_sky_only", "priority"), &DirectionalLight3D::set_sky_only);
+ ClassDB::bind_method(D_METHOD("is_sky_only"), &DirectionalLight3D::is_sky_only);
+
ADD_GROUP("Directional Shadow", "directional_shadow_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_mode", PROPERTY_HINT_ENUM, "Orthogonal (Fast),PSSM 2 Splits (Average),PSSM 4 Splits (Slow)"), "set_shadow_mode", "get_shadow_mode");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_1", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_1_OFFSET);
@@ -415,6 +427,8 @@ void DirectionalLight3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_max_distance", PROPERTY_HINT_EXP_RANGE, "0,8192,0.1,or_greater"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_pancake_size", PROPERTY_HINT_EXP_RANGE, "0,1024,0.1,or_greater"), "set_param", "get_param", PARAM_SHADOW_PANCAKE_SIZE);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_in_sky_only"), "set_sky_only", "is_sky_only");
+
BIND_ENUM_CONSTANT(SHADOW_ORTHOGONAL);
BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS);
BIND_ENUM_CONSTANT(SHADOW_PARALLEL_4_SPLITS);
@@ -448,7 +462,7 @@ String OmniLight3D::get_configuration_warning() const {
String warning = Light3D::get_configuration_warning();
if (!has_shadow() && get_projector().is_valid()) {
- if (warning != String()) {
+ if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("Projector texture only works with shadows active.");
@@ -482,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 69c0478b86..08287a3313 100644
--- a/scene/3d/light_3d.h
+++ b/scene/3d/light_3d.h
@@ -158,6 +158,7 @@ private:
bool blend_splits;
ShadowMode shadow_mode;
ShadowDepthRange shadow_depth_range;
+ bool sky_only = false;
protected:
static void _bind_methods();
@@ -172,6 +173,9 @@ public:
void set_blend_splits(bool p_enable);
bool is_blend_splits_enabled() const;
+ void set_sky_only(bool p_sky_only);
+ bool is_sky_only() const;
+
DirectionalLight3D();
};
diff --git a/scene/3d/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..ae6bbad8bf 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) {
@@ -94,10 +94,6 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) {
return;
}
- if (delta_offset == 0) {
- return;
- }
-
float bl = c->get_baked_length();
if (bl == 0.0) {
return;
@@ -156,7 +152,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) {
t.origin = pos;
- if (p_update_xyz_rot) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree
+ if (p_update_xyz_rot && delta_offset != 0) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree.
Vector3 t_prev = (pos - c->interpolate_baked(offset - delta_offset, cubic)).normalized();
Vector3 t_cur = (c->interpolate_baked(offset + delta_offset, cubic) - pos).normalized();
@@ -250,16 +246,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..06de5ad0ae 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 (!body_a && !body_b) {
+ warning = TTR("Joint is not connected to any PhysicsBody3Ds");
+ update_configuration_warning();
+ return;
+ }
- if (!joint.is_valid()) {
+ 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");
@@ -669,9 +710,6 @@ void Generic6DOFJoint3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_flag_z", "flag", "value"), &Generic6DOFJoint3D::set_flag_z);
ClassDB::bind_method(D_METHOD("get_flag_z", "flag"), &Generic6DOFJoint3D::get_flag_z);
- ClassDB::bind_method(D_METHOD("set_precision", "precision"), &Generic6DOFJoint3D::set_precision);
- ClassDB::bind_method(D_METHOD("get_precision"), &Generic6DOFJoint3D::get_precision);
-
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/upper_distance"), "set_param_x", "get_param_x", PARAM_LINEAR_UPPER_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/lower_distance"), "set_param_x", "get_param_x", PARAM_LINEAR_LOWER_LIMIT);
@@ -760,8 +798,6 @@ void Generic6DOFJoint3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/damping"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "precision", PROPERTY_HINT_RANGE, "1,99999,1"), "set_precision", "get_precision");
-
BIND_ENUM_CONSTANT(PARAM_LINEAR_LOWER_LIMIT);
BIND_ENUM_CONSTANT(PARAM_LINEAR_UPPER_LIMIT);
BIND_ENUM_CONSTANT(PARAM_LINEAR_LIMIT_SOFTNESS);
@@ -880,14 +916,6 @@ bool Generic6DOFJoint3D::get_flag_z(Flag p_flag) const {
return flags_z[p_flag];
}
-void Generic6DOFJoint3D::set_precision(int p_precision) {
- precision = p_precision;
-
- PhysicsServer3D::get_singleton()->generic_6dof_joint_set_precision(
- get_joint(),
- precision);
-}
-
RID Generic6DOFJoint3D::_configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) {
Transform gt = get_global_transform();
//Vector3 cone_twistpos = gt.origin;
diff --git a/scene/3d/physics_joint_3d.h b/scene/3d/physics_joint_3d.h
index 8e2de82527..250ae8bf52 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,
@@ -299,8 +300,6 @@ protected:
float params_z[PARAM_MAX];
bool flags_z[FLAG_MAX];
- int precision = 1;
-
virtual RID _configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) override;
static void _bind_methods();
@@ -323,11 +322,6 @@ public:
void set_flag_z(Flag p_flag, bool p_enabled);
bool get_flag_z(Flag p_flag) const;
- void set_precision(int p_precision);
- int get_precision() const {
- return precision;
- }
-
Generic6DOFJoint3D();
};
diff --git a/scene/3d/proximity_group_3d.cpp b/scene/3d/proximity_group_3d.cpp
index 1a0677c603..7e25255885 100644
--- a/scene/3d/proximity_group_3d.cpp
+++ b/scene/3d/proximity_group_3d.cpp
@@ -32,7 +32,7 @@
#include "core/math/math_funcs.h"
-void ProximityGroup3D::clear_groups() {
+void ProximityGroup3D::_clear_groups() {
Map<StringName, uint32_t>::Element *E;
{
@@ -43,21 +43,21 @@ void ProximityGroup3D::clear_groups() {
while (E && num < size) {
if (E->get() != group_version) {
remove_list[num++] = E->key();
- };
+ }
E = E->next();
- };
+ }
for (int i = 0; i < num; i++) {
groups.erase(remove_list[i]);
- };
- };
+ }
+ }
if (E) {
- clear_groups(); // call until we go through the whole list
- };
-};
+ _clear_groups(); // call until we go through the whole list
+ }
+}
-void ProximityGroup3D::update_groups() {
+void ProximityGroup3D::_update_groups() {
if (grid_radius == Vector3(0, 0, 0)) {
return;
}
@@ -68,20 +68,20 @@ void ProximityGroup3D::update_groups() {
Vector3 vcell = pos / cell_size;
int cell[3] = { Math::fast_ftoi(vcell.x), Math::fast_ftoi(vcell.y), Math::fast_ftoi(vcell.z) };
- add_groups(cell, group_name, 0);
+ _add_groups(cell, group_name, 0);
- clear_groups();
-};
+ _clear_groups();
+}
-void ProximityGroup3D::add_groups(int *p_cell, String p_base, int p_depth) {
+void ProximityGroup3D::_add_groups(int *p_cell, String p_base, int p_depth) {
p_base = p_base + "|";
if (grid_radius[p_depth] == 0) {
if (p_depth == 2) {
_new_group(p_base);
} else {
- add_groups(p_cell, p_base, p_depth + 1);
- };
- };
+ _add_groups(p_cell, p_base, p_depth + 1);
+ }
+ }
int start = p_cell[p_depth] - grid_radius[p_depth];
int end = p_cell[p_depth] + grid_radius[p_depth];
@@ -91,72 +91,72 @@ void ProximityGroup3D::add_groups(int *p_cell, String p_base, int p_depth) {
if (p_depth == 2) {
_new_group(gname);
} else {
- add_groups(p_cell, gname, p_depth + 1);
- };
- };
-};
+ _add_groups(p_cell, gname, p_depth + 1);
+ }
+ }
+}
void ProximityGroup3D::_new_group(StringName p_name) {
const Map<StringName, uint32_t>::Element *E = groups.find(p_name);
if (!E) {
add_to_group(p_name);
- };
+ }
groups[p_name] = group_version;
-};
+}
void ProximityGroup3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_EXIT_TREE:
++group_version;
- clear_groups();
+ _clear_groups();
break;
case NOTIFICATION_TRANSFORM_CHANGED:
- update_groups();
+ _update_groups();
break;
- };
-};
+ }
+}
-void ProximityGroup3D::broadcast(String p_name, Variant p_params) {
+void ProximityGroup3D::broadcast(String p_method, Variant p_parameters) {
Map<StringName, uint32_t>::Element *E;
E = groups.front();
while (E) {
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFAULT, E->key(), "_proximity_group_broadcast", p_name, p_params);
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFAULT, E->key(), "_proximity_group_broadcast", p_method, p_parameters);
E = E->next();
- };
-};
+ }
+}
-void ProximityGroup3D::_proximity_group_broadcast(String p_name, Variant p_params) {
+void ProximityGroup3D::_proximity_group_broadcast(String p_method, Variant p_parameters) {
if (dispatch_mode == MODE_PROXY) {
- get_parent()->call(p_name, p_params);
+ get_parent()->call(p_method, p_parameters);
} else {
- emit_signal("broadcast", p_name, p_params);
- };
-};
+ emit_signal("broadcast", p_method, p_parameters);
+ }
+}
void ProximityGroup3D::set_group_name(const String &p_group_name) {
group_name = p_group_name;
-};
+}
String ProximityGroup3D::get_group_name() const {
return group_name;
-};
+}
void ProximityGroup3D::set_dispatch_mode(DispatchMode p_mode) {
dispatch_mode = p_mode;
-};
+}
ProximityGroup3D::DispatchMode ProximityGroup3D::get_dispatch_mode() const {
return dispatch_mode;
-};
+}
void ProximityGroup3D::set_grid_radius(const Vector3 &p_radius) {
grid_radius = p_radius;
-};
+}
Vector3 ProximityGroup3D::get_grid_radius() const {
return grid_radius;
-};
+}
void ProximityGroup3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_group_name", "name"), &ProximityGroup3D::set_group_name);
@@ -165,19 +165,21 @@ void ProximityGroup3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_dispatch_mode"), &ProximityGroup3D::get_dispatch_mode);
ClassDB::bind_method(D_METHOD("set_grid_radius", "radius"), &ProximityGroup3D::set_grid_radius);
ClassDB::bind_method(D_METHOD("get_grid_radius"), &ProximityGroup3D::get_grid_radius);
- ClassDB::bind_method(D_METHOD("broadcast", "name", "parameters"), &ProximityGroup3D::broadcast);
- ClassDB::bind_method(D_METHOD("_proximity_group_broadcast", "name", "params"), &ProximityGroup3D::_proximity_group_broadcast);
+
+ ClassDB::bind_method(D_METHOD("broadcast", "method", "parameters"), &ProximityGroup3D::broadcast);
+
+ ClassDB::bind_method(D_METHOD("_proximity_group_broadcast", "method", "parameters"), &ProximityGroup3D::_proximity_group_broadcast);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "group_name"), "set_group_name", "get_group_name");
ADD_PROPERTY(PropertyInfo(Variant::INT, "dispatch_mode", PROPERTY_HINT_ENUM, "Proxy,Signal"), "set_dispatch_mode", "get_dispatch_mode");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "grid_radius"), "set_grid_radius", "get_grid_radius");
- ADD_SIGNAL(MethodInfo("broadcast", PropertyInfo(Variant::STRING, "group_name"), PropertyInfo(Variant::ARRAY, "parameters")));
+ ADD_SIGNAL(MethodInfo("broadcast", PropertyInfo(Variant::STRING, "method"), PropertyInfo(Variant::ARRAY, "parameters")));
BIND_ENUM_CONSTANT(MODE_PROXY);
BIND_ENUM_CONSTANT(MODE_SIGNAL);
-};
+}
ProximityGroup3D::ProximityGroup3D() {
set_notify_transform(true);
-};
+}
diff --git a/scene/3d/proximity_group_3d.h b/scene/3d/proximity_group_3d.h
index dd3a2f0a87..d52843e9a2 100644
--- a/scene/3d/proximity_group_3d.h
+++ b/scene/3d/proximity_group_3d.h
@@ -35,7 +35,6 @@
class ProximityGroup3D : public Node3D {
GDCLASS(ProximityGroup3D, Node3D);
- OBJ_CATEGORY("3D");
public:
enum DispatchMode {
@@ -43,25 +42,25 @@ public:
MODE_SIGNAL,
};
-public:
- void clear_groups();
- void update_groups();
-
- void _notification(int p_what);
-
- DispatchMode dispatch_mode = MODE_PROXY;
-
+private:
Map<StringName, uint32_t> groups;
+
String group_name;
+ DispatchMode dispatch_mode = MODE_PROXY;
+ Vector3 grid_radius = Vector3(1, 1, 1);
float cell_size = 1.0;
- Vector3 grid_radius = Vector3(1, 1, 1);
uint32_t group_version = 0;
- void add_groups(int *p_cell, String p_base, int p_depth);
+ void _clear_groups();
+ void _update_groups();
+ void _add_groups(int *p_cell, String p_base, int p_depth);
void _new_group(StringName p_name);
- void _proximity_group_broadcast(String p_name, Variant p_params);
+ void _proximity_group_broadcast(String p_method, Variant p_parameters);
+
+protected:
+ void _notification(int p_what);
static void _bind_methods();
@@ -75,7 +74,7 @@ public:
void set_grid_radius(const Vector3 &p_radius);
Vector3 get_grid_radius() const;
- void broadcast(String p_name, Variant p_params);
+ void broadcast(String p_method, Variant p_parameters);
ProximityGroup3D();
~ProximityGroup3D() {}
@@ -83,4 +82,4 @@ public:
VARIANT_ENUM_CAST(ProximityGroup3D::DispatchMode);
-#endif
+#endif // PROXIMITY_GROUP_H
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index 69f09ef569..811e8a331b 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -31,7 +31,7 @@
#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"
diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp
index c7948395d3..c82ed423a7 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -76,6 +76,15 @@ float ReflectionProbe::get_max_distance() const {
return max_distance;
}
+void ReflectionProbe::set_lod_threshold(float p_pixels) {
+ lod_threshold = p_pixels;
+ RS::get_singleton()->reflection_probe_set_lod_threshold(probe, p_pixels);
+}
+
+float ReflectionProbe::get_lod_threshold() const {
+ return lod_threshold;
+}
+
void ReflectionProbe::set_extents(const Vector3 &p_extents) {
extents = p_extents;
@@ -199,6 +208,9 @@ void ReflectionProbe::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_distance", "max_distance"), &ReflectionProbe::set_max_distance);
ClassDB::bind_method(D_METHOD("get_max_distance"), &ReflectionProbe::get_max_distance);
+ ClassDB::bind_method(D_METHOD("set_lod_threshold", "ratio"), &ReflectionProbe::set_lod_threshold);
+ ClassDB::bind_method(D_METHOD("get_lod_threshold"), &ReflectionProbe::get_lod_threshold);
+
ClassDB::bind_method(D_METHOD("set_extents", "extents"), &ReflectionProbe::set_extents);
ClassDB::bind_method(D_METHOD("get_extents"), &ReflectionProbe::get_extents);
@@ -229,6 +241,7 @@ void ReflectionProbe::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_as_interior", "is_set_as_interior");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_shadows"), "set_enable_shadows", "are_shadows_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold");
ADD_GROUP("Ambient", "ambient_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_mode", PROPERTY_HINT_ENUM, "Disabled,Environment,ConstantColor"), "set_ambient_mode", "get_ambient_mode");
@@ -256,6 +269,7 @@ ReflectionProbe::ReflectionProbe() {
enable_shadows = false;
cull_mask = (1 << 20) - 1;
update_mode = UPDATE_ONCE;
+ lod_threshold = 1.0;
probe = RenderingServer::get_singleton()->reflection_probe_create();
RS::get_singleton()->instance_set_base(get_instance(), probe);
diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h
index 56177d0f95..4bff2f8bf9 100644
--- a/scene/3d/reflection_probe.h
+++ b/scene/3d/reflection_probe.h
@@ -63,6 +63,7 @@ private:
AmbientMode ambient_mode;
Color ambient_color;
float ambient_color_energy;
+ float lod_threshold;
uint32_t cull_mask;
UpdateMode update_mode;
@@ -90,6 +91,9 @@ public:
void set_max_distance(float p_distance);
float get_max_distance() const;
+ void set_lod_threshold(float p_pixels);
+ float get_lod_threshold() const;
+
void set_extents(const Vector3 &p_extents);
Vector3 get_extents() const;
diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp
index 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 d3d7cdc1ce..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"
@@ -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/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 6e38196ba6..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];
@@ -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.cpp b/scene/3d/visual_instance_3d.cpp
index a1c498e8ab..0b70b0f920 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -278,6 +278,16 @@ float GeometryInstance3D::get_extra_cull_margin() const {
return extra_cull_margin;
}
+void GeometryInstance3D::set_lod_bias(float p_bias) {
+ ERR_FAIL_COND(p_bias < 0.0);
+ lod_bias = p_bias;
+ RS::get_singleton()->instance_geometry_set_lod_bias(get_instance(), lod_bias);
+}
+
+float GeometryInstance3D::get_lod_bias() const {
+ return lod_bias;
+}
+
void GeometryInstance3D::set_shader_instance_uniform(const StringName &p_uniform, const Variant &p_value) {
if (p_value.get_type() == Variant::NIL) {
Variant def_value = RS::get_singleton()->instance_geometry_get_shader_parameter_default_value(get_instance(), p_uniform);
@@ -361,6 +371,9 @@ void GeometryInstance3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_gi_mode", "mode"), &GeometryInstance3D::set_gi_mode);
ClassDB::bind_method(D_METHOD("get_gi_mode"), &GeometryInstance3D::get_gi_mode);
+ ClassDB::bind_method(D_METHOD("set_lod_bias", "p_bias"), &GeometryInstance3D::set_lod_bias);
+ ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias);
+
ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &GeometryInstance3D::set_custom_aabb);
ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance3D::get_aabb);
@@ -369,6 +382,7 @@ void GeometryInstance3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_override", "get_material_override");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias");
ADD_GROUP("Global Illumination", "gi_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Baked,Dynamic"), "set_gi_mode", "get_gi_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, "1x,2x,4x,8x"), "set_lightmap_scale", "get_lightmap_scale");
@@ -403,6 +417,8 @@ GeometryInstance3D::GeometryInstance3D() {
lod_min_hysteresis = 0;
lod_max_hysteresis = 0;
+ lod_bias = 1.0;
+
gi_mode = GI_MODE_DISABLED;
lightmap_scale = LIGHTMAP_SCALE_1X;
diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h
index 195674f62d..0810b7b4ce 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"
@@ -112,6 +112,8 @@ private:
float lod_min_hysteresis;
float lod_max_hysteresis;
+ float lod_bias;
+
mutable HashMap<StringName, Variant> instance_uniforms;
mutable HashMap<StringName, StringName> instance_uniform_property_remap;
@@ -151,6 +153,9 @@ public:
void set_extra_cull_margin(float p_margin);
float get_extra_cull_margin() const;
+ void set_lod_bias(float p_bias);
+ float get_lod_bias() const;
+
void set_gi_mode(GIMode p_mode);
GIMode get_gi_mode() const;
diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp
index 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/SCsub b/scene/SCsub
index f9fc00f3f2..ccd2bab8ff 100644
--- a/scene/SCsub
+++ b/scene/SCsub
@@ -4,24 +4,9 @@ Import("env")
env.scene_sources = []
-# Thirdparty code
-thirdparty_dir = "#thirdparty/misc/"
-thirdparty_sources = [
- # C++ sources
- "easing_equations.cpp",
- # C sources
- "mikktspace.c",
-]
-thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
-env_thirdparty = env.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.scene_sources, thirdparty_sources)
-
-# Godot's own sources
+# Godot source files
env.add_source_files(env.scene_sources, "*.cpp")
-
# Chain load SCsubs
SConscript("main/SCsub")
SConscript("gui/SCsub")
@@ -32,7 +17,6 @@ SConscript("audio/SCsub")
SConscript("resources/SCsub")
SConscript("debugger/SCsub")
-
# Build it all as a library
lib = env.add_library("scene", env.scene_sources)
env.Prepend(LIBS=[lib])
diff --git a/scene/animation/SCsub b/scene/animation/SCsub
index fc61250247..cc33a5af84 100644
--- a/scene/animation/SCsub
+++ b/scene/animation/SCsub
@@ -2,4 +2,23 @@
Import("env")
-env.add_source_files(env.scene_sources, "*.cpp")
+# Thirdparty code
+
+thirdparty_obj = []
+
+thirdparty_sources = "#thirdparty/misc/easing_equations.cpp"
+
+env_thirdparty = env.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.scene_sources += thirdparty_obj
+
+# Godot source files
+
+scene_obj = []
+
+env.add_source_files(scene_obj, "*.cpp")
+env.scene_sources += scene_obj
+
+# Needed to force rebuilding the scene files when the thirdparty code is updated.
+env.Depends(scene_obj, thirdparty_obj)
diff --git a/scene/animation/animation_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..159ccae130 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -30,12 +30,13 @@
#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"
#ifdef TOOLS_ENABLED
+#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "scene/2d/skeleton_2d.h"
@@ -53,6 +54,21 @@ void AnimatedValuesBackup::update_skeletons() {
}
}
}
+
+void AnimatedValuesBackup::restore() const {
+ for (int i = 0; i < entries.size(); i++) {
+ const AnimatedValuesBackup::Entry *entry = &entries[i];
+ if (entry->bone_idx == -1) {
+ entry->object->set_indexed(entry->subpath, entry->value);
+ } else {
+ Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value);
+ }
+ }
+}
+
+void AnimatedValuesBackup::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("restore"), &AnimatedValuesBackup::restore);
+}
#endif
bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) {
@@ -762,12 +778,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 +1308,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();
}
@@ -1381,6 +1395,14 @@ String AnimationPlayer::get_autoplay() const {
return autoplay;
}
+void AnimationPlayer::set_reset_on_save_enabled(bool p_enabled) {
+ reset_on_save = p_enabled;
+}
+
+bool AnimationPlayer::is_reset_on_save_enabled() const {
+ return reset_on_save;
+}
+
void AnimationPlayer::set_animation_process_mode(AnimationProcessMode p_mode) {
if (animation_process_mode == p_mode) {
return;
@@ -1475,15 +1497,15 @@ void AnimationPlayer::get_argument_options(const StringName &p_function, int p_i
}
#ifdef TOOLS_ENABLED
-AnimatedValuesBackup AnimationPlayer::backup_animated_values() {
+Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values() {
+ Ref<AnimatedValuesBackup> backup;
if (!playback.current.from) {
- return AnimatedValuesBackup();
+ return backup;
}
_ensure_node_caches(playback.current.from);
- AnimatedValuesBackup backup;
-
+ backup.instance();
for (int i = 0; i < playback.current.from->node_cache.size(); i++) {
TrackNodeCache *nc = playback.current.from->node_cache[i];
if (!nc) {
@@ -1499,7 +1521,7 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() {
entry.object = nc->skeleton;
entry.bone_idx = nc->bone_idx;
entry.value = nc->skeleton->get_bone_pose(nc->bone_idx);
- backup.entries.push_back(entry);
+ backup->entries.push_back(entry);
} else {
if (nc->spatial) {
AnimatedValuesBackup::Entry entry;
@@ -1507,7 +1529,7 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() {
entry.subpath.push_back("transform");
entry.value = nc->spatial->get_transform();
entry.bone_idx = -1;
- backup.entries.push_back(entry);
+ backup->entries.push_back(entry);
} else {
for (Map<StringName, TrackNodeCache::PropertyAnim>::Element *E = nc->property_anim.front(); E; E = E->next()) {
AnimatedValuesBackup::Entry entry;
@@ -1517,7 +1539,7 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() {
entry.value = E->value().object->get_indexed(E->value().subpath, &valid);
entry.bone_idx = -1;
if (valid) {
- backup.entries.push_back(entry);
+ backup->entries.push_back(entry);
}
}
}
@@ -1527,15 +1549,40 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() {
return backup;
}
-void AnimationPlayer::restore_animated_values(const AnimatedValuesBackup &p_backup) {
- for (int i = 0; i < p_backup.entries.size(); i++) {
- const AnimatedValuesBackup::Entry *entry = &p_backup.entries[i];
- if (entry->bone_idx == -1) {
- entry->object->set_indexed(entry->subpath, entry->value);
- } else {
- Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value);
- }
+Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
+ ERR_FAIL_COND_V(!can_apply_reset(), Ref<AnimatedValuesBackup>());
+
+ Ref<Animation> reset_anim = animation_set["RESET"].animation;
+ ERR_FAIL_COND_V(reset_anim.is_null(), Ref<AnimatedValuesBackup>());
+
+ Node *root_node = get_node_or_null(root);
+ ERR_FAIL_COND_V(!root_node, Ref<AnimatedValuesBackup>());
+
+ AnimationPlayer *aux_player = memnew(AnimationPlayer);
+ EditorNode::get_singleton()->add_child(aux_player);
+ aux_player->set_root(aux_player->get_path_to(root_node));
+ aux_player->add_animation("RESET", reset_anim);
+ aux_player->set_assigned_animation("RESET");
+ Ref<AnimatedValuesBackup> old_values = aux_player->backup_animated_values();
+ aux_player->seek(0.0f, true);
+ aux_player->queue_delete();
+
+ if (p_user_initiated) {
+ Ref<AnimatedValuesBackup> new_values = aux_player->backup_animated_values();
+ old_values->restore();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Anim Apply Reset"));
+ ur->add_do_method(new_values.ptr(), "restore");
+ ur->add_undo_method(old_values.ptr(), "restore");
+ ur->commit_action();
}
+
+ return old_values;
+}
+
+bool AnimationPlayer::can_apply_reset() const {
+ return has_animation("RESET") && playback.assigned != StringName("RESET");
}
#endif
@@ -1579,6 +1626,9 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_autoplay", "name"), &AnimationPlayer::set_autoplay);
ClassDB::bind_method(D_METHOD("get_autoplay"), &AnimationPlayer::get_autoplay);
+ ClassDB::bind_method(D_METHOD("set_reset_on_save_enabled", "enabled"), &AnimationPlayer::set_reset_on_save_enabled);
+ ClassDB::bind_method(D_METHOD("is_reset_on_save_enabled"), &AnimationPlayer::is_reset_on_save_enabled);
+
ClassDB::bind_method(D_METHOD("set_root", "path"), &AnimationPlayer::set_root);
ClassDB::bind_method(D_METHOD("get_root"), &AnimationPlayer::get_root);
@@ -1602,6 +1652,7 @@ void AnimationPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ANIMATE_AS_TRIGGER), "set_current_animation", "get_current_animation");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "assigned_animation", PROPERTY_HINT_NONE, "", 0), "set_assigned_animation", "get_assigned_animation");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_autoplay", "get_autoplay");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_length", PROPERTY_HINT_NONE, "", 0), "", "get_current_animation_length");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_position", PROPERTY_HINT_NONE, "", 0), "", "get_current_animation_position");
@@ -1633,6 +1684,7 @@ AnimationPlayer::AnimationPlayer() {
speed_scale = 1;
end_reached = false;
end_notify = false;
+ reset_on_save = true;
animation_process_mode = ANIMATION_PROCESS_IDLE;
method_call_mode = ANIMATION_METHOD_CALL_DEFERRED;
processing = false;
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index e1b9dffb1f..7f0d5630e1 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -37,8 +37,9 @@
#include "scene/resources/animation.h"
#ifdef TOOLS_ENABLED
-// To save/restore animated values
-class AnimatedValuesBackup {
+class AnimatedValuesBackup : public Reference {
+ GDCLASS(AnimatedValuesBackup, Reference);
+
struct Entry {
Object *object;
Vector<StringName> subpath; // Unused if bone
@@ -49,8 +50,12 @@ class AnimatedValuesBackup {
friend class AnimationPlayer;
+protected:
+ static void _bind_methods();
+
public:
void update_skeletons();
+ void restore() const;
};
#endif
@@ -72,7 +77,6 @@ public:
private:
enum {
-
NODE_CACHE_UPDATE_MAX = 1024,
BLEND_FROM_MAX = 3
};
@@ -216,6 +220,7 @@ private:
bool end_notify;
String autoplay;
+ bool reset_on_save;
AnimationProcessMode animation_process_mode;
AnimationMethodCallMode method_call_mode;
bool processing;
@@ -305,6 +310,9 @@ public:
void set_autoplay(const String &p_name);
String get_autoplay() const;
+ void set_reset_on_save_enabled(bool p_enabled);
+ bool is_reset_on_save_enabled() const;
+
void set_animation_process_mode(AnimationProcessMode p_mode);
AnimationProcessMode get_animation_process_mode() const;
@@ -326,9 +334,9 @@ public:
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
#ifdef TOOLS_ENABLED
- // These may be interesting for games, but are too dangerous for general use
- AnimatedValuesBackup backup_animated_values();
- void restore_animated_values(const AnimatedValuesBackup &p_backup);
+ Ref<AnimatedValuesBackup> backup_animated_values();
+ Ref<AnimatedValuesBackup> apply_reset(bool p_user_initiated = false);
+ bool can_apply_reset() const;
#endif
AnimationPlayer();
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 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 2e77d20d4e..dadb1bea31 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -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;
}
@@ -328,16 +317,21 @@ bool BaseButton::is_keep_pressed_outside() const {
void BaseButton::set_shortcut(const Ref<Shortcut> &p_shortcut) {
shortcut = p_shortcut;
- set_process_unhandled_input(shortcut.is_valid());
+ set_process_unhandled_key_input(shortcut.is_valid());
}
Ref<Shortcut> BaseButton::get_shortcut() const {
return shortcut;
}
-void BaseButton::_unhandled_input(Ref<InputEvent> p_event) {
+void BaseButton::_unhandled_key_input(Ref<InputEvent> p_event) {
+ if (!_is_focus_owner_in_shorcut_context()) {
+ return;
+ }
+
if (!is_disabled() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->is_shortcut(p_event)) {
on_action_event(p_event);
+ accept_event();
}
}
@@ -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, "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 8e71931f8b..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;
+ 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,9 +117,6 @@ 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;
@@ -126,6 +125,9 @@ public:
void set_button_group(const Ref<ButtonGroup> &p_group);
Ref<ButtonGroup> get_button_group() const;
+ void set_shortcut_context(Node *p_node);
+ Node *get_shortcut_context() const;
+
BaseButton();
~BaseButton();
};
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index 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
index 56cf432b38..59cfbccf99 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -34,9 +34,9 @@ void CodeEdit::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_ENTER_TREE: {
- set_gutter_width(main_gutter, cache.row_height);
- set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0').width);
- set_gutter_width(fold_gutter, cache.row_height / 1.2);
+ set_gutter_width(main_gutter, get_row_height());
+ set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0', 0, cache.font_size).width);
+ set_gutter_width(fold_gutter, get_row_height() / 1.2);
breakpoint_color = get_theme_color("breakpoint_color");
breakpoint_icon = get_theme_icon("breakpoint");
@@ -132,6 +132,7 @@ void CodeEdit::set_line_as_breakpoint(int p_line, bool p_breakpointed) {
breakpointed_lines.erase(p_line);
}
emit_signal("breakpoint_toggled", p_line);
+ update();
}
bool CodeEdit::is_line_breakpointed(int p_line) const {
@@ -160,6 +161,7 @@ Array CodeEdit::get_breakpointed_lines() const {
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 {
@@ -188,6 +190,7 @@ Array CodeEdit::get_bookmarked_lines() const {
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 {
@@ -231,14 +234,16 @@ bool CodeEdit::is_line_numbers_zero_padded() const {
}
void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
- String fc = String::num(p_line + 1).lpad(line_number_digits, line_number_padding);
-
- int yofs = p_region.position.y + (cache.row_height - cache.font->get_height()) / 2;
+ String fc = TS->format_number(String::num(p_line + 1).lpad(line_number_digits, line_number_padding));
+ Ref<TextLine> tl;
+ tl.instance();
+ tl->add_string(fc, cache.font, cache.font_size);
+ int yofs = p_region.position.y + (get_row_height() - tl->get_size().y) / 2;
Color number_color = get_line_gutter_item_color(p_line, line_number_gutter);
if (number_color == Color(1, 1, 1)) {
number_color = line_number_color;
}
- cache.font->draw(get_canvas_item(), Point2(p_region.position.x, yofs + cache.font->get_ascent()), fc, number_color);
+ tl->draw(get_canvas_item(), Point2(p_region.position.x, yofs), number_color);
}
/* Fold Gutter */
@@ -338,7 +343,10 @@ void CodeEdit::_gutter_clicked(int p_line, int p_gutter) {
}
if (p_gutter == line_number_gutter) {
- cursor_set_line(p_line);
+ 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;
}
@@ -362,7 +370,7 @@ void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) {
while (lc /= 10) {
line_number_digits++;
}
- set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0').width);
+ set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0', 0, cache.font_size).width);
int from_line = MIN(p_from_line, p_to_line);
int line_count = (p_to_line - p_from_line);
@@ -404,6 +412,10 @@ void CodeEdit::_update_gutter_indexes() {
}
CodeEdit::CodeEdit() {
+ /* Text Direction */
+ set_layout_direction(LAYOUT_DIRECTION_LTR);
+ set_text_direction(TEXT_DIRECTION_LTR);
+
/* Gutters */
int gutter_idx = 0;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index cbe0367c7b..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
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..bdbb0d4684 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) {
@@ -2159,14 +2206,14 @@ String Control::_get_tooltip() const {
return data.tooltip;
}
-void Control::set_focus_neighbour(Margin p_margin, const NodePath &p_neighbour) {
+void Control::set_focus_neighbor(Margin p_margin, const NodePath &p_neighbor) {
ERR_FAIL_INDEX((int)p_margin, 4);
- data.focus_neighbour[p_margin] = p_neighbour;
+ data.focus_neighbor[p_margin] = p_neighbor;
}
-NodePath Control::get_focus_neighbour(Margin p_margin) const {
+NodePath Control::get_focus_neighbor(Margin p_margin) const {
ERR_FAIL_INDEX_V((int)p_margin, 4, NodePath());
- return data.focus_neighbour[p_margin];
+ return data.focus_neighbor[p_margin];
}
void Control::set_focus_next(const NodePath &p_next) {
@@ -2185,17 +2232,17 @@ NodePath Control::get_focus_previous() const {
return data.focus_prev;
}
-#define MAX_NEIGHBOUR_SEARCH_COUNT 512
+#define MAX_NEIGHBOR_SEARCH_COUNT 512
-Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) {
+Control *Control::_get_focus_neighbor(Margin p_margin, int p_count) {
ERR_FAIL_INDEX_V((int)p_margin, 4, nullptr);
- if (p_count >= MAX_NEIGHBOUR_SEARCH_COUNT) {
+ if (p_count >= MAX_NEIGHBOR_SEARCH_COUNT) {
return nullptr;
}
- if (!data.focus_neighbour[p_margin].is_empty()) {
+ if (!data.focus_neighbor[p_margin].is_empty()) {
Control *c = nullptr;
- Node *n = get_node(data.focus_neighbour[p_margin]);
+ Node *n = get_node(data.focus_neighbor[p_margin]);
if (n) {
c = Object::cast_to<Control>(n);
ERR_FAIL_COND_V_MSG(!c, nullptr, "Neighbor focus node is not a control: " + n->get_name() + ".");
@@ -2213,7 +2260,7 @@ Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) {
return c;
}
- c = c->_get_focus_neighbour(p_margin, p_count + 1);
+ c = c->_get_focus_neighbor(p_margin, p_count + 1);
return c;
}
@@ -2263,12 +2310,12 @@ Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) {
return nullptr;
}
- _window_find_focus_neighbour(vdir, base, points, maxd, dist, &result);
+ _window_find_focus_neighbor(vdir, base, points, maxd, dist, &result);
return result;
}
-void Control::_window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest) {
+void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest) {
if (Object::cast_to<Viewport>(p_at)) {
return; //bye
}
@@ -2321,7 +2368,7 @@ void Control::_window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, con
if (childc && childc->data.RI) {
continue; //subwindow, ignore
}
- _window_find_focus_neighbour(p_dir, p_at->get_child(i), p_points, p_min, r_closest_dist, r_closest);
+ _window_find_focus_neighbor(p_dir, p_at->get_child(i), p_points, p_min, r_closest_dist, r_closest);
}
}
@@ -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);
@@ -2703,8 +2843,8 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_default_cursor_shape"), &Control::get_default_cursor_shape);
ClassDB::bind_method(D_METHOD("get_cursor_shape", "position"), &Control::get_cursor_shape, DEFVAL(Point2()));
- ClassDB::bind_method(D_METHOD("set_focus_neighbour", "margin", "neighbour"), &Control::set_focus_neighbour);
- ClassDB::bind_method(D_METHOD("get_focus_neighbour", "margin"), &Control::get_focus_neighbour);
+ ClassDB::bind_method(D_METHOD("set_focus_neighbor", "margin", "neighbor"), &Control::set_focus_neighbor);
+ ClassDB::bind_method(D_METHOD("get_focus_neighbor", "margin"), &Control::get_focus_neighbor);
ClassDB::bind_method(D_METHOD("set_focus_next", "next"), &Control::set_focus_next);
ClassDB::bind_method(D_METHOD("get_focus_next"), &Control::get_focus_next);
@@ -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");
@@ -2771,10 +2922,10 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip");
ADD_GROUP("Focus", "focus_");
- ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_top", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_right", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_bottom", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", MARGIN_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_top", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", MARGIN_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_right", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", MARGIN_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_bottom", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", MARGIN_BOTTOM);
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "focus_next", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_next", "get_focus_next");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "focus_previous", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_previous", "get_focus_previous");
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
@@ -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..d314c7357b 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;
@@ -185,14 +207,14 @@ private:
CanvasItem *parent_canvas_item;
- NodePath focus_neighbour[4];
+ NodePath focus_neighbor[4];
NodePath focus_next;
NodePath focus_prev;
HashMap<StringName, Ref<Texture2D>> icon_override;
- HashMap<StringName, Ref<Shader>> shader_override;
HashMap<StringName, Ref<StyleBox>> style_override;
HashMap<StringName, Ref<Font>> font_override;
+ HashMap<StringName, int> font_size_override;
HashMap<StringName, Color> color_override;
HashMap<StringName, int> constant_override;
@@ -201,8 +223,8 @@ private:
// used internally
Control *_find_control_at_pos(CanvasItem *p_node, const Point2 &p_pos, const Transform2D &p_xform, Transform2D &r_inv_xform);
- void _window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest);
- Control *_get_focus_neighbour(Margin p_margin, int p_count = 0);
+ void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest);
+ Control *_get_focus_neighbor(Margin p_margin, int p_count = 0);
void _set_anchor(Margin p_margin, float p_anchor);
void _set_position(const Point2 &p_point);
@@ -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;
@@ -407,8 +436,8 @@ public:
Control *find_next_valid_focus() const;
Control *find_prev_valid_focus() const;
- void set_focus_neighbour(Margin p_margin, const NodePath &p_neighbour);
- NodePath get_focus_neighbour(Margin p_margin) const;
+ void set_focus_neighbor(Margin p_margin, const NodePath &p_neighbor);
+ NodePath get_focus_neighbor(Margin p_margin) const;
void set_focus_next(const NodePath &p_next);
NodePath get_focus_next() 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..4e80498108 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
@@ -60,7 +60,7 @@ void AcceptDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
- get_ok()->grab_focus();
+ get_ok_button()->grab_focus();
_update_child_rects();
parent_visible = get_parent_visible_window();
if (parent_visible) {
@@ -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;
}
@@ -253,7 +253,7 @@ Button *AcceptDialog::add_button(const String &p_text, bool p_right, const Strin
return button;
}
-Button *AcceptDialog::add_cancel(const String &p_cancel) {
+Button *AcceptDialog::add_cancel_button(const String &p_cancel) {
String c = p_cancel;
if (p_cancel == "") {
c = RTR("Cancel");
@@ -264,12 +264,12 @@ Button *AcceptDialog::add_cancel(const String &p_cancel) {
}
void AcceptDialog::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_ok"), &AcceptDialog::get_ok);
+ ClassDB::bind_method(D_METHOD("get_ok_button"), &AcceptDialog::get_ok_button);
ClassDB::bind_method(D_METHOD("get_label"), &AcceptDialog::get_label);
ClassDB::bind_method(D_METHOD("set_hide_on_ok", "enabled"), &AcceptDialog::set_hide_on_ok);
ClassDB::bind_method(D_METHOD("get_hide_on_ok"), &AcceptDialog::get_hide_on_ok);
ClassDB::bind_method(D_METHOD("add_button", "text", "right", "action"), &AcceptDialog::add_button, DEFVAL(false), DEFVAL(""));
- ClassDB::bind_method(D_METHOD("add_cancel", "name"), &AcceptDialog::add_cancel);
+ ClassDB::bind_method(D_METHOD("add_cancel_button", "name"), &AcceptDialog::add_cancel_button);
ClassDB::bind_method(D_METHOD("register_text_enter", "line_edit"), &AcceptDialog::register_text_enter);
ClassDB::bind_method(D_METHOD("set_text", "text"), &AcceptDialog::set_text);
ClassDB::bind_method(D_METHOD("get_text"), &AcceptDialog::get_text);
@@ -337,10 +337,10 @@ AcceptDialog::~AcceptDialog() {
// ConfirmationDialog
void ConfirmationDialog::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_cancel"), &ConfirmationDialog::get_cancel);
+ ClassDB::bind_method(D_METHOD("get_cancel_button"), &ConfirmationDialog::get_cancel_button);
}
-Button *ConfirmationDialog::get_cancel() {
+Button *ConfirmationDialog::get_cancel_button() {
return cancel;
}
@@ -349,5 +349,5 @@ ConfirmationDialog::ConfirmationDialog() {
#ifdef TOOLS_ENABLED
set_min_size(Size2(200, 70) * EDSCALE);
#endif
- cancel = add_cancel();
+ cancel = add_cancel_button();
}
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index de08685ce2..8f6e0e86f9 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -79,9 +79,9 @@ public:
void register_text_enter(Node *p_line_edit);
- Button *get_ok() { return ok; }
+ Button *get_ok_button() { return ok; }
Button *add_button(const String &p_text, bool p_right = false, const String &p_action = "");
- Button *add_cancel(const String &p_cancel = "");
+ Button *add_cancel_button(const String &p_cancel = "");
void set_hide_on_ok(bool p_hide);
bool get_hide_on_ok() const;
@@ -104,7 +104,7 @@ protected:
static void _bind_methods();
public:
- Button *get_cancel();
+ Button *get_cancel_button();
ConfirmationDialog();
};
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 4aea2928f4..041b8ef174 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;
@@ -324,15 +324,15 @@ void FileDialog::deselect_items() {
// And change get_ok title.
if (!tree->is_anything_selected()) {
- get_ok()->set_disabled(_is_open_should_be_disabled());
+ get_ok_button()->set_disabled(_is_open_should_be_disabled());
switch (mode) {
case FILE_MODE_OPEN_FILE:
case FILE_MODE_OPEN_FILES:
- get_ok()->set_text(RTR("Open"));
+ get_ok_button()->set_text(RTR("Open"));
break;
case FILE_MODE_OPEN_DIR:
- get_ok()->set_text(RTR("Select Current Folder"));
+ get_ok_button()->set_text(RTR("Select Current Folder"));
break;
case FILE_MODE_OPEN_ANY:
case FILE_MODE_SAVE_FILE:
@@ -356,10 +356,10 @@ void FileDialog::_tree_selected() {
if (!d["dir"]) {
file->set_text(d["name"]);
} else if (mode == FILE_MODE_OPEN_DIR) {
- get_ok()->set_text(RTR("Select This Folder"));
+ get_ok_button()->set_text(RTR("Select This Folder"));
}
- get_ok()->set_disabled(_is_open_should_be_disabled());
+ get_ok_button()->set_disabled(_is_open_should_be_disabled());
}
void FileDialog::_tree_item_activated() {
@@ -646,35 +646,35 @@ void FileDialog::set_file_mode(FileMode p_mode) {
mode = p_mode;
switch (mode) {
case FILE_MODE_OPEN_FILE:
- get_ok()->set_text(RTR("Open"));
+ get_ok_button()->set_text(RTR("Open"));
if (mode_overrides_title) {
set_title(RTR("Open a File"));
}
makedir->hide();
break;
case FILE_MODE_OPEN_FILES:
- get_ok()->set_text(RTR("Open"));
+ get_ok_button()->set_text(RTR("Open"));
if (mode_overrides_title) {
set_title(RTR("Open File(s)"));
}
makedir->hide();
break;
case FILE_MODE_OPEN_DIR:
- get_ok()->set_text(RTR("Select Current Folder"));
+ get_ok_button()->set_text(RTR("Select Current Folder"));
if (mode_overrides_title) {
set_title(RTR("Open a Directory"));
}
makedir->show();
break;
case FILE_MODE_OPEN_ANY:
- get_ok()->set_text(RTR("Open"));
+ get_ok_button()->set_text(RTR("Open"));
if (mode_overrides_title) {
set_title(RTR("Open a File or Directory"));
}
makedir->show();
break;
case FILE_MODE_SAVE_FILE:
- get_ok()->set_text(RTR("Save"));
+ get_ok_button()->set_text(RTR("Save"));
if (mode_overrides_title) {
set_title(RTR("Save a File"));
}
@@ -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 467d252c81..2bcc1890fe 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -31,6 +31,7 @@
#include "graph_edit.h"
#include "core/input/input.h"
+#include "core/math/math_funcs.h"
#include "core/os/keyboard.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
@@ -44,6 +45,9 @@
#define MIN_ZOOM (((1 / ZOOM_SCALE) / ZOOM_SCALE) / ZOOM_SCALE)
#define MAX_ZOOM (1 * ZOOM_SCALE * ZOOM_SCALE * ZOOM_SCALE)
+#define MINIMAP_OFFSET 12
+#define MINIMAP_PADDING 5
+
bool GraphEditFilter::has_point(const Point2 &p_point) const {
return ge->_filter_input(p_point);
}
@@ -52,6 +56,141 @@ GraphEditFilter::GraphEditFilter(GraphEdit *p_edit) {
ge = p_edit;
}
+void GraphEditMinimap::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_gui_input"), &GraphEditMinimap::_gui_input);
+}
+
+GraphEditMinimap::GraphEditMinimap(GraphEdit *p_edit) {
+ ge = p_edit;
+
+ graph_proportions = Vector2(1, 1);
+ graph_padding = Vector2(0, 0);
+ camera_position = Vector2(100, 50);
+ camera_size = Vector2(200, 200);
+ minimap_padding = Vector2(MINIMAP_PADDING, MINIMAP_PADDING);
+ minimap_offset = minimap_padding + _convert_from_graph_position(graph_padding);
+
+ is_pressing = false;
+ is_resizing = false;
+}
+
+void GraphEditMinimap::update_minimap() {
+ Vector2 graph_offset = _get_graph_offset();
+ Vector2 graph_size = _get_graph_size();
+
+ camera_position = ge->get_scroll_ofs() - graph_offset;
+ camera_size = ge->get_size();
+
+ Vector2 render_size = _get_render_size();
+ float target_ratio = render_size.x / render_size.y;
+ float graph_ratio = graph_size.x / graph_size.y;
+
+ graph_proportions = graph_size;
+ graph_padding = Vector2(0, 0);
+ if (graph_ratio > target_ratio) {
+ graph_proportions.x = graph_size.x;
+ graph_proportions.y = graph_size.x / target_ratio;
+ graph_padding.y = Math::abs(graph_size.y - graph_proportions.y) / 2;
+ } else {
+ graph_proportions.x = graph_size.y * target_ratio;
+ graph_proportions.y = graph_size.y;
+ graph_padding.x = Math::abs(graph_size.x - graph_proportions.x) / 2;
+ }
+
+ // This centers minimap inside the minimap rectangle.
+ minimap_offset = minimap_padding + _convert_from_graph_position(graph_padding);
+}
+
+Rect2 GraphEditMinimap::get_camera_rect() {
+ Vector2 camera_center = _convert_from_graph_position(camera_position + camera_size / 2) + minimap_offset;
+ Vector2 camera_viewport = _convert_from_graph_position(camera_size);
+ Vector2 camera_position = (camera_center - camera_viewport / 2);
+ return Rect2(camera_position, camera_viewport);
+}
+
+Vector2 GraphEditMinimap::_get_render_size() {
+ if (!is_inside_tree()) {
+ return Vector2(0, 0);
+ }
+
+ return get_size() - 2 * minimap_padding;
+}
+
+Vector2 GraphEditMinimap::_get_graph_offset() {
+ return Vector2(ge->h_scroll->get_min(), ge->v_scroll->get_min());
+}
+
+Vector2 GraphEditMinimap::_get_graph_size() {
+ Vector2 graph_size = Vector2(ge->h_scroll->get_max(), ge->v_scroll->get_max()) - Vector2(ge->h_scroll->get_min(), ge->v_scroll->get_min());
+
+ if (graph_size.x == 0) {
+ graph_size.x = 1;
+ }
+ if (graph_size.y == 0) {
+ graph_size.y = 1;
+ }
+
+ return graph_size;
+}
+
+Vector2 GraphEditMinimap::_convert_from_graph_position(const Vector2 &p_position) {
+ Vector2 map_position = Vector2(0, 0);
+ Vector2 render_size = _get_render_size();
+
+ map_position.x = p_position.x * render_size.x / graph_proportions.x;
+ map_position.y = p_position.y * render_size.y / graph_proportions.y;
+
+ return map_position;
+}
+
+Vector2 GraphEditMinimap::_convert_to_graph_position(const Vector2 &p_position) {
+ Vector2 graph_position = Vector2(0, 0);
+ Vector2 render_size = _get_render_size();
+
+ graph_position.x = p_position.x * graph_proportions.x / render_size.x;
+ graph_position.y = p_position.y * graph_proportions.y / render_size.y;
+
+ return graph_position;
+}
+
+void GraphEditMinimap::_gui_input(const Ref<InputEvent> &p_ev) {
+ Ref<InputEventMouseButton> mb = p_ev;
+ Ref<InputEventMouseMotion> mm = p_ev;
+
+ if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) {
+ if (mb->is_pressed()) {
+ is_pressing = true;
+
+ Ref<Texture2D> resizer = get_theme_icon("resizer");
+ Rect2 resizer_hitbox = Rect2(Point2(), resizer->get_size());
+ if (resizer_hitbox.has_point(mb->get_position())) {
+ is_resizing = true;
+ } else {
+ Vector2 click_position = _convert_to_graph_position(mb->get_position() - minimap_padding) - graph_padding;
+ _adjust_graph_scroll(click_position);
+ }
+ } else {
+ is_pressing = false;
+ is_resizing = false;
+ }
+ accept_event();
+ } else if (mm.is_valid() && is_pressing) {
+ if (is_resizing) {
+ ge->set_minimap_size(ge->get_minimap_size() - mm->get_relative());
+ update();
+ } else {
+ Vector2 click_position = _convert_to_graph_position(mm->get_position() - minimap_padding) - graph_padding;
+ _adjust_graph_scroll(click_position);
+ }
+ accept_event();
+ }
+}
+
+void GraphEditMinimap::_adjust_graph_scroll(const Vector2 &p_offset) {
+ Vector2 graph_offset = _get_graph_offset();
+ ge->set_scroll_ofs(p_offset + graph_offset - camera_size / 2);
+}
+
Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
if (is_node_connected(p_from, p_from_port, p_to, p_to_port)) {
return OK;
@@ -64,6 +203,7 @@ Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const S
c.activity = 0;
connections.push_back(c);
top_layer->update();
+ minimap->update();
update();
connections_layer->update();
@@ -85,6 +225,7 @@ void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const
if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) {
connections.erase(E);
top_layer->update();
+ minimap->update();
update();
connections_layer->update();
return;
@@ -118,6 +259,7 @@ void GraphEdit::_scroll_moved(double) {
awaiting_scroll_offset_update = true;
}
top_layer->update();
+ minimap->update();
update();
if (!setting_scroll_ofs) { //in godot, signals on change value are avoided as a convention
@@ -234,6 +376,7 @@ void GraphEdit::_graph_node_moved(Node *p_gn) {
GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
ERR_FAIL_COND(!gn);
top_layer->update();
+ minimap->update();
update();
connections_layer->update();
}
@@ -241,13 +384,15 @@ void GraphEdit::_graph_node_moved(Node *p_gn) {
void GraphEdit::add_child_notify(Node *p_child) {
Control::add_child_notify(p_child);
- top_layer->call_deferred("raise"); //top layer always on top!
+ top_layer->call_deferred("raise"); // Top layer always on top!
+
GraphNode *gn = Object::cast_to<GraphNode>(p_child);
if (gn) {
gn->set_scale(Vector2(zoom, zoom));
gn->connect("offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved), varray(gn));
gn->connect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised), varray(gn));
gn->connect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::update));
+ gn->connect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::update));
_graph_node_moved(gn);
gn->set_mouse_filter(MOUSE_FILTER_PASS);
}
@@ -255,14 +400,17 @@ void GraphEdit::add_child_notify(Node *p_child) {
void GraphEdit::remove_child_notify(Node *p_child) {
Control::remove_child_notify(p_child);
+
if (is_inside_tree()) {
- top_layer->call_deferred("raise"); //top layer always on top!
+ top_layer->call_deferred("raise"); // Top layer always on top!
}
+
GraphNode *gn = Object::cast_to<GraphNode>(p_child);
if (gn) {
gn->disconnect("offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved));
gn->disconnect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised));
gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::update));
+ gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::update));
}
}
@@ -275,6 +423,7 @@ void GraphEdit::_notification(int p_what) {
zoom_reset->set_icon(get_theme_icon("reset"));
zoom_plus->set_icon(get_theme_icon("more"));
snap_button->set_icon(get_theme_icon("snap"));
+ minimap_button->set_icon(get_theme_icon("minimap"));
}
if (p_what == NOTIFICATION_READY) {
Size2 hmin = h_scroll->get_combined_minimum_size();
@@ -338,6 +487,7 @@ void GraphEdit::_notification(int p_what) {
if (p_what == NOTIFICATION_RESIZED) {
_update_scroll();
top_layer->update();
+ minimap->update();
}
}
@@ -472,6 +622,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_to = mm->get_position();
connecting_target = false;
top_layer->update();
+ minimap->update();
connecting_valid = just_disconnected || click_pos.distance_to(connecting_to) > 20.0 * zoom;
if (connecting_valid) {
@@ -541,13 +692,14 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting = false;
top_layer->update();
+ minimap->update();
update();
connections_layer->update();
}
}
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;
}
@@ -632,12 +784,12 @@ void GraphEdit::_bake_segment2d(Vector<Vector2> &points, Vector<Color> &colors,
}
}
-void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color) {
+void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_bezier_ratio = 1.0) {
//cubic bezier code
float diff = p_to.x - p_from.x;
float cp_offset;
- int cp_len = get_theme_constant("bezier_len_pos");
- int cp_neg_len = get_theme_constant("bezier_len_neg");
+ int cp_len = get_theme_constant("bezier_len_pos") * p_bezier_ratio;
+ int cp_neg_len = get_theme_constant("bezier_len_neg") * p_bezier_ratio;
if (diff > 0) {
cp_offset = MIN(cp_len, diff * 0.5);
@@ -659,9 +811,9 @@ void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const
colors.push_back(p_to_color);
#ifdef TOOLS_ENABLED
- p_where->draw_polyline_colors(points, colors, Math::floor(2 * EDSCALE));
+ p_where->draw_polyline_colors(points, colors, Math::floor(p_width * EDSCALE), lines_antialiased);
#else
- p_where->draw_polyline_colors(points, colors, 2);
+ p_where->draw_polyline_colors(points, colors, p_width, lines_antialiased);
#endif
}
@@ -708,7 +860,7 @@ void GraphEdit::_connections_layer_draw() {
color = color.lerp(activity_color, E->get().activity);
tocolor = tocolor.lerp(activity_color, E->get().activity);
}
- _draw_cos_line(connections_layer, frompos, topos, color, tocolor);
+ _draw_cos_line(connections_layer, frompos, topos, color, tocolor, lines_thickness);
}
while (to_erase.size()) {
@@ -747,7 +899,7 @@ void GraphEdit::_top_layer_draw() {
if (!connecting_out) {
SWAP(pos, topos);
}
- _draw_cos_line(top_layer, pos, topos, col, col);
+ _draw_cos_line(top_layer, pos, topos, col, col, lines_thickness);
}
if (box_selecting) {
@@ -756,6 +908,114 @@ void GraphEdit::_top_layer_draw() {
}
}
+void GraphEdit::_minimap_draw() {
+ if (!is_minimap_enabled()) {
+ return;
+ }
+
+ minimap->update_minimap();
+
+ // Draw the minimap background.
+ Rect2 minimap_rect = Rect2(Point2(), minimap->get_size());
+ minimap->draw_style_box(minimap->get_theme_stylebox("bg"), minimap_rect);
+
+ Vector2 graph_offset = minimap->_get_graph_offset();
+ Vector2 minimap_offset = minimap->minimap_offset;
+
+ // Draw comment graph nodes.
+ for (int i = get_child_count() - 1; i >= 0; i--) {
+ GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
+ if (!gn || !gn->is_comment()) {
+ continue;
+ }
+
+ Vector2 node_position = minimap->_convert_from_graph_position(gn->get_offset() * zoom - graph_offset) + minimap_offset;
+ Vector2 node_size = minimap->_convert_from_graph_position(gn->get_size() * zoom);
+ Rect2 node_rect = Rect2(node_position, node_size);
+
+ Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox("node")->duplicate();
+
+ // Override default values with colors provided by the GraphNode's stylebox, if possible.
+ Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "commentfocus" : "comment");
+ if (sbf.is_valid()) {
+ Color node_color = sbf->get_bg_color();
+ sb_minimap->set_bg_color(node_color);
+ }
+
+ minimap->draw_style_box(sb_minimap, node_rect);
+ }
+
+ // Draw regular graph nodes.
+ for (int i = get_child_count() - 1; i >= 0; i--) {
+ GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
+ if (!gn || gn->is_comment()) {
+ continue;
+ }
+
+ Vector2 node_position = minimap->_convert_from_graph_position(gn->get_offset() * zoom - graph_offset) + minimap_offset;
+ Vector2 node_size = minimap->_convert_from_graph_position(gn->get_size() * zoom);
+ Rect2 node_rect = Rect2(node_position, node_size);
+
+ Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox("node")->duplicate();
+
+ // Override default values with colors provided by the GraphNode's stylebox, if possible.
+ Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "selectedframe" : "frame");
+ if (sbf.is_valid()) {
+ Color node_color = sbf->get_border_color();
+ sb_minimap->set_bg_color(node_color);
+ }
+
+ minimap->draw_style_box(sb_minimap, node_rect);
+ }
+
+ // Draw node connections.
+ Color activity_color = get_theme_color("activity");
+ for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
+ NodePath fromnp(E->get().from);
+
+ Node *from = get_node(fromnp);
+ if (!from) {
+ continue;
+ }
+ GraphNode *gfrom = Object::cast_to<GraphNode>(from);
+ if (!gfrom) {
+ continue;
+ }
+
+ NodePath tonp(E->get().to);
+ Node *to = get_node(tonp);
+ if (!to) {
+ continue;
+ }
+ GraphNode *gto = Object::cast_to<GraphNode>(to);
+ if (!gto) {
+ continue;
+ }
+
+ Vector2 from_slot_position = gfrom->get_offset() * zoom + gfrom->get_connection_output_position(E->get().from_port);
+ Vector2 from_position = minimap->_convert_from_graph_position(from_slot_position - graph_offset) + minimap_offset;
+ Color from_color = gfrom->get_connection_output_color(E->get().from_port);
+ Vector2 to_slot_position = gto->get_offset() * zoom + gto->get_connection_input_position(E->get().to_port);
+ Vector2 to_position = minimap->_convert_from_graph_position(to_slot_position - graph_offset) + minimap_offset;
+ Color to_color = gto->get_connection_input_color(E->get().to_port);
+
+ if (E->get().activity > 0) {
+ from_color = from_color.lerp(activity_color, E->get().activity);
+ to_color = to_color.lerp(activity_color, E->get().activity);
+ }
+ _draw_cos_line(minimap, from_position, to_position, from_color, to_color, 1.0, 0.5);
+ }
+
+ // Draw the "camera" viewport.
+ Rect2 camera_rect = minimap->get_camera_rect();
+ minimap->draw_style_box(minimap->get_theme_stylebox("camera"), camera_rect);
+
+ // Draw the resizer control.
+ Ref<Texture2D> resizer = minimap->get_theme_icon("resizer");
+ Color resizer_color = minimap->get_theme_color("resizer_color");
+ minimap->draw_texture(resizer, Point2(), resizer_color);
+}
+
void GraphEdit::set_selected(Node *p_child) {
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
@@ -775,6 +1035,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--) {
@@ -831,6 +1096,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
}
top_layer->update();
+ minimap->update();
}
Ref<InputEventMouseButton> b = p_ev;
@@ -853,10 +1119,12 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
gn->set_selected(select);
}
top_layer->update();
+ minimap->update();
} else {
if (connecting) {
connecting = false;
top_layer->update();
+ minimap->update();
} else {
emit_signal("popup_request", b->get_global_position());
}
@@ -881,21 +1149,23 @@ 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;
top_layer->update();
+ minimap->update();
update();
connections_layer->update();
}
@@ -1006,6 +1276,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
box_selecting = false;
previus_selected.clear();
top_layer->update();
+ minimap->update();
}
if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) {
@@ -1073,6 +1344,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por
if (Math::is_equal_approx(E->get().activity, p_activity)) {
//update only if changed
top_layer->update();
+ minimap->update();
connections_layer->update();
}
E->get().activity = p_activity;
@@ -1083,6 +1355,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por
void GraphEdit::clear_connections() {
connections.clear();
+ minimap->update();
update();
connections_layer->update();
}
@@ -1106,6 +1379,7 @@ void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) {
top_layer->update();
_update_scroll();
+ minimap->update();
connections_layer->update();
if (is_visible_in_tree()) {
@@ -1223,6 +1497,63 @@ void GraphEdit::_snap_value_changed(double) {
update();
}
+void GraphEdit::set_minimap_size(Vector2 p_size) {
+ minimap->set_size(p_size);
+ Vector2 minimap_size = minimap->get_size(); // The size might've been adjusted by the minimum size.
+
+ minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT);
+ minimap->set_margin(Margin::MARGIN_LEFT, -minimap_size.x - MINIMAP_OFFSET);
+ minimap->set_margin(Margin::MARGIN_TOP, -minimap_size.y - MINIMAP_OFFSET);
+ minimap->set_margin(Margin::MARGIN_RIGHT, -MINIMAP_OFFSET);
+ minimap->set_margin(Margin::MARGIN_BOTTOM, -MINIMAP_OFFSET);
+ minimap->update();
+}
+
+Vector2 GraphEdit::get_minimap_size() const {
+ return minimap->get_size();
+}
+
+void GraphEdit::set_minimap_opacity(float p_opacity) {
+ minimap->set_modulate(Color(1, 1, 1, p_opacity));
+ minimap->update();
+}
+
+float GraphEdit::get_minimap_opacity() const {
+ Color minimap_modulate = minimap->get_modulate();
+ return minimap_modulate.a;
+}
+
+void GraphEdit::set_minimap_enabled(bool p_enable) {
+ minimap_button->set_pressed(p_enable);
+ minimap->update();
+}
+
+bool GraphEdit::is_minimap_enabled() const {
+ return minimap_button->is_pressed();
+}
+
+void GraphEdit::_minimap_toggled() {
+ minimap->update();
+}
+
+void GraphEdit::set_connection_lines_thickness(float p_thickness) {
+ lines_thickness = p_thickness;
+ update();
+}
+
+float GraphEdit::get_connection_lines_thickness() const {
+ return lines_thickness;
+}
+
+void GraphEdit::set_connection_lines_antialiased(bool p_antialiased) {
+ lines_antialiased = p_antialiased;
+ update();
+}
+
+bool GraphEdit::is_connection_lines_antialiased() const {
+ return lines_antialiased;
+}
+
HBoxContainer *GraphEdit::get_zoom_hbox() {
return zoom_hb;
}
@@ -1254,6 +1585,20 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_snap", "enable"), &GraphEdit::set_use_snap);
ClassDB::bind_method(D_METHOD("is_using_snap"), &GraphEdit::is_using_snap);
+ ClassDB::bind_method(D_METHOD("set_connection_lines_thickness", "pixels"), &GraphEdit::set_connection_lines_thickness);
+ ClassDB::bind_method(D_METHOD("get_connection_lines_thickness"), &GraphEdit::get_connection_lines_thickness);
+
+ ClassDB::bind_method(D_METHOD("set_connection_lines_antialiased", "pixels"), &GraphEdit::set_connection_lines_antialiased);
+ ClassDB::bind_method(D_METHOD("is_connection_lines_antialiased"), &GraphEdit::is_connection_lines_antialiased);
+
+ ClassDB::bind_method(D_METHOD("set_minimap_size", "p_size"), &GraphEdit::set_minimap_size);
+ ClassDB::bind_method(D_METHOD("get_minimap_size"), &GraphEdit::get_minimap_size);
+ ClassDB::bind_method(D_METHOD("set_minimap_opacity", "p_opacity"), &GraphEdit::set_minimap_opacity);
+ ClassDB::bind_method(D_METHOD("get_minimap_opacity"), &GraphEdit::get_minimap_opacity);
+
+ ClassDB::bind_method(D_METHOD("set_minimap_enabled", "enable"), &GraphEdit::set_minimap_enabled);
+ ClassDB::bind_method(D_METHOD("is_minimap_enabled"), &GraphEdit::is_minimap_enabled);
+
ClassDB::bind_method(D_METHOD("set_right_disconnects", "enable"), &GraphEdit::set_right_disconnects);
ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled);
@@ -1269,6 +1614,12 @@ void GraphEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_distance"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_snap"), "set_use_snap", "is_using_snap");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_thickness"), "set_connection_lines_thickness", "get_connection_lines_thickness");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "connection_lines_antialiased"), "set_connection_lines_antialiased", "is_connection_lines_antialiased");
+ ADD_GROUP("Minimap", "minimap");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_enabled"), "set_minimap_enabled", "is_minimap_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimap_size"), "set_minimap_size", "get_minimap_size");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "minimap_opacity"), "set_minimap_opacity", "get_minimap_opacity");
ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot")));
ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot")));
@@ -1281,8 +1632,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")));
}
@@ -1374,6 +1725,32 @@ GraphEdit::GraphEdit() {
snap_amount->connect("value_changed", callable_mp(this, &GraphEdit::_snap_value_changed));
zoom_hb->add_child(snap_amount);
+ minimap_button = memnew(Button);
+ minimap_button->set_flat(true);
+ minimap_button->set_toggle_mode(true);
+ minimap_button->set_tooltip(RTR("Enable grid minimap."));
+ minimap_button->connect("pressed", callable_mp(this, &GraphEdit::_minimap_toggled));
+ minimap_button->set_pressed(true);
+ minimap_button->set_focus_mode(FOCUS_NONE);
+ zoom_hb->add_child(minimap_button);
+
+ Vector2 minimap_size = Vector2(240, 160);
+ float minimap_opacity = 0.65;
+
+ minimap = memnew(GraphEditMinimap(this));
+ top_layer->add_child(minimap);
+ minimap->set_name("_minimap");
+ minimap->set_modulate(Color(1, 1, 1, minimap_opacity));
+ minimap->set_mouse_filter(MOUSE_FILTER_STOP);
+ minimap->set_custom_minimum_size(Vector2(50, 50));
+ minimap->set_size(minimap_size);
+ minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT);
+ minimap->set_margin(Margin::MARGIN_LEFT, -minimap_size.x - MINIMAP_OFFSET);
+ minimap->set_margin(Margin::MARGIN_TOP, -minimap_size.y - MINIMAP_OFFSET);
+ minimap->set_margin(Margin::MARGIN_RIGHT, -MINIMAP_OFFSET);
+ minimap->set_margin(Margin::MARGIN_BOTTOM, -MINIMAP_OFFSET);
+ minimap->connect("draw", callable_mp(this, &GraphEdit::_minimap_draw));
+
setting_scroll_ofs = false;
just_disconnected = false;
set_clip_contents(true);
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 37cb5989e9..d081789784 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -45,6 +45,7 @@ class GraphEditFilter : public Control {
GDCLASS(GraphEditFilter, Control);
friend class GraphEdit;
+ friend class GraphEditMinimap;
GraphEdit *ge;
virtual bool has_point(const Point2 &p_point) const override;
@@ -52,6 +53,45 @@ public:
GraphEditFilter(GraphEdit *p_edit);
};
+class GraphEditMinimap : public Control {
+ GDCLASS(GraphEditMinimap, Control);
+
+ friend class GraphEdit;
+ friend class GraphEditFilter;
+ GraphEdit *ge;
+
+protected:
+ static void _bind_methods();
+
+public:
+ GraphEditMinimap(GraphEdit *p_edit);
+
+ void update_minimap();
+ Rect2 get_camera_rect();
+
+private:
+ Vector2 minimap_padding;
+ Vector2 minimap_offset;
+ Vector2 graph_proportions;
+ Vector2 graph_padding;
+ Vector2 camera_position;
+ Vector2 camera_size;
+
+ bool is_pressing;
+ bool is_resizing;
+
+ Vector2 _get_render_size();
+ Vector2 _get_graph_offset();
+ Vector2 _get_graph_size();
+
+ Vector2 _convert_from_graph_position(const Vector2 &p_position);
+ Vector2 _convert_to_graph_position(const Vector2 &p_position);
+
+ void _gui_input(const Ref<InputEvent> &p_ev);
+
+ void _adjust_graph_scroll(const Vector2 &p_offset);
+};
+
class GraphEdit : public Control {
GDCLASS(GraphEdit, Control);
@@ -72,6 +112,8 @@ private:
Button *snap_button;
SpinBox *snap_amount;
+ Button *minimap_button;
+
void _zoom_minus();
void _zoom_reset();
void _zoom_plus();
@@ -98,6 +140,7 @@ private:
bool dragging;
bool just_selected;
+ bool moving_selection;
Vector2 drag_accum;
float zoom;
@@ -115,9 +158,12 @@ private:
bool awaiting_scroll_offset_update;
List<Connection> connections;
+ float lines_thickness = 2.0f;
+ bool lines_antialiased = true;
+
void _bake_segment2d(Vector<Vector2> &points, Vector<Color> &colors, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_min_depth, int p_max_depth, float p_tol, const Color &p_color, const Color &p_to_color, int &lines) const;
- void _draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color);
+ void _draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_bezier_ratio);
void _graph_node_raised(Node *p_gn);
void _graph_node_moved(Node *p_gn);
@@ -128,12 +174,14 @@ private:
Control *connections_layer;
GraphEditFilter *top_layer;
+ GraphEditMinimap *minimap;
void _top_layer_input(const Ref<InputEvent> &p_ev);
bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos);
void _top_layer_draw();
void _connections_layer_draw();
+ void _minimap_draw();
void _update_scroll_offset();
Array _get_connection_list() const;
@@ -170,6 +218,9 @@ private:
void _snap_toggled();
void _snap_value_changed(double);
+ friend class GraphEditMinimap;
+ void _minimap_toggled();
+
bool _check_clickable_control(Control *p_control, const Vector2 &pos);
protected:
@@ -195,7 +246,16 @@ public:
void set_zoom_custom(float p_zoom, const Vector2 &p_center);
float get_zoom() const;
+ void set_minimap_size(Vector2 p_size);
+ Vector2 get_minimap_size() const;
+ void set_minimap_opacity(float p_opacity);
+ float get_minimap_opacity() const;
+
+ void set_minimap_enabled(bool p_enable);
+ bool is_minimap_enabled() const;
+
GraphEditFilter *get_top_layer() const { return top_layer; }
+ GraphEditMinimap *get_minimap() const { return minimap; }
void get_connection_list(List<Connection> *r_connections) const;
void set_right_disconnects(bool p_enable);
@@ -218,6 +278,12 @@ public:
int get_snap() const;
void set_snap(int p_snap);
+ void set_connection_lines_thickness(float p_thickness);
+ float get_connection_lines_thickness() const;
+
+ void set_connection_lines_antialiased(bool p_antialiased);
+ bool is_connection_lines_antialiased() const;
+
HBoxContainer *get_zoom_hbox();
GraphEdit();
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 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 9e3418a5c9..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->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) {
- char32_t c = xl_text[i + pos];
- char32_t n = xl_text[i + pos + 1];
- if (uppercase) {
- c = String::char_uppercase(c);
- n = String::char_uppercase(n);
- }
-
- float move = drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + shadow_ofs, c, n, font_color_shadow);
- if (use_outline) {
- drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, shadow_ofs.y), c, n, font_color_shadow);
- drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow);
- drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow);
- }
- x_ofs_shadow += move;
- chars_total_shadow++;
+ if (visible_glyphs != -1) {
+ if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ outlines_drawn++;
+ if (outlines_drawn >= visible_glyphs) {
+ break;
}
}
}
- for (int i = 0; i < from->word_len; i++) {
- if (visible_chars < 0 || chars_total < visible_chars) {
- char32_t c = xl_text[i + pos];
- char32_t n = xl_text[i + pos + 1];
- if (uppercase) {
- c = String::char_uppercase(c);
- n = String::char_uppercase(n);
+ }
+ ofs.x = x;
+
+ for (int j = 0; j < gl_size; j++) {
+ for (int k = 0; k < glyphs[j].repeat; k++) {
+ if (glyphs[j].font_rid != RID()) {
+ TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off), glyphs[j].index, font_color);
+ } else if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ TS->draw_hex_code_box(ci, glyphs[j].font_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off), glyphs[j].index, font_color);
+ }
+ ofs.x += glyphs[j].advance;
+ }
+ if (visible_glyphs != -1) {
+ if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ glyhps_drawn++;
+ if (glyhps_drawn >= visible_glyphs) {
+ return;
}
-
- x_ofs += drawer.draw_char(ci, Point2(x_ofs, y_ofs), c, n, font_color);
- chars_total++;
}
}
- from = from->next;
}
- wc = to ? to->next : nullptr;
- line++;
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(Font::SPACING_BOTTOM);
}
}
if (p_what == NOTIFICATION_THEME_CHANGED) {
- word_cache_dirty = true;
+ dirty = true;
update();
}
if (p_what == NOTIFICATION_RESIZED) {
- word_cache_dirty = true;
+ lines_dirty = true;
}
}
@@ -287,8 +360,8 @@ Size2 Label::get_minimum_size() const {
Size2 min_style = get_theme_stylebox("normal")->get_minimum_size();
// don't want to mutable everything
- if (word_cache_dirty) {
- const_cast<Label *>(this)->regenerate_word_cache();
+ if (dirty || lines_dirty) {
+ const_cast<Label *>(this)->_shape();
}
if (autowrap) {
@@ -302,230 +375,50 @@ Size2 Label::get_minimum_size() const {
}
}
-int Label::get_longest_line_width() const {
- Ref<Font> font = get_theme_font("font");
- real_t max_line_width = 0;
- real_t line_width = 0;
-
- for (int i = 0; i < xl_text.size(); i++) {
- char32_t current = xl_text[i];
- if (uppercase) {
- current = String::char_uppercase(current);
- }
-
- if (current < 32) {
- if (current == '\n') {
- if (line_width > max_line_width) {
- max_line_width = line_width;
- }
- line_width = 0;
- }
- } else {
- real_t char_width = font->get_char_size(current, xl_text[i + 1]).width;
- line_width += char_width;
- }
- }
-
- if (line_width > max_line_width) {
- max_line_width = line_width;
- }
-
- // ceiling to ensure autowrapping does not cut text
- return Math::ceil(max_line_width);
-}
-
int Label::get_line_count() const {
if (!is_inside_tree()) {
return 1;
}
- if (word_cache_dirty) {
- const_cast<Label *>(this)->regenerate_word_cache();
+ if (dirty || lines_dirty) {
+ const_cast<Label *>(this)->_shape();
}
- return line_count;
+ return lines_rid.size();
}
int Label::get_visible_line_count() const {
- int line_spacing = get_theme_constant("line_spacing");
- int font_h = get_theme_font("font")->get_height() + line_spacing;
- int lines_visible = (get_size().height - get_theme_stylebox("normal")->get_minimum_size().height + line_spacing) / font_h;
-
- if (lines_visible > line_count) {
- lines_visible = line_count;
- }
-
- if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
- lines_visible = max_lines_visible;
- }
-
- return lines_visible;
-}
-
-void Label::regenerate_word_cache() {
- while (word_cache) {
- WordCache *current = word_cache;
- word_cache = current->next;
- memdelete(current);
- }
-
- int width;
- if (autowrap) {
- Ref<StyleBox> style = get_theme_stylebox("normal");
- width = MAX(get_size().width, get_custom_minimum_size().width) - style->get_minimum_size().width;
- } else {
- width = get_longest_line_width();
- }
-
Ref<Font> font = get_theme_font("font");
-
- real_t current_word_size = 0;
- int word_pos = 0;
- real_t line_width = 0;
- int space_count = 0;
- real_t space_width = font->get_char_size(' ').width;
+ Ref<StyleBox> style = get_theme_stylebox("normal");
int line_spacing = get_theme_constant("line_spacing");
- line_count = 1;
- total_char_cache = 0;
-
- WordCache *last = nullptr;
-
- for (int i = 0; i <= xl_text.length(); i++) {
- char32_t current = i < xl_text.length() ? xl_text[i] : L' '; //always a space at the end, so the algo works
-
- if (uppercase) {
- current = String::char_uppercase(current);
- }
-
- // ranges taken from http://www.unicodemap.org/
- // if your language is not well supported, consider helping improve
- // the unicode support in Godot.
- bool separatable = (current >= 0x2E08 && current <= 0xFAFF) || (current >= 0xFE30 && current <= 0xFE4F);
- //current>=33 && (current < 65||current >90) && (current<97||current>122) && (current<48||current>57);
- bool insert_newline = false;
- real_t char_width = 0;
-
- if (current < 33) {
- if (current_word_size > 0) {
- WordCache *wc = memnew(WordCache);
- if (word_cache) {
- last->next = wc;
- } else {
- word_cache = wc;
- }
- last = wc;
-
- wc->pixel_width = current_word_size;
- wc->char_pos = word_pos;
- wc->word_len = i - word_pos;
- wc->space_count = space_count;
- current_word_size = 0;
- space_count = 0;
- } else if ((i == xl_text.length() || current == '\n') && last != nullptr && space_count != 0) {
- //in case there are trailing white spaces we add a placeholder word cache with just the spaces
- WordCache *wc = memnew(WordCache);
- if (word_cache) {
- last->next = wc;
- } else {
- word_cache = wc;
- }
- last = wc;
-
- wc->pixel_width = 0;
- wc->char_pos = 0;
- wc->word_len = 0;
- wc->space_count = space_count;
- current_word_size = 0;
- space_count = 0;
- }
-
- if (current == '\n') {
- insert_newline = true;
- } else if (current != ' ') {
- total_char_cache++;
- }
-
- if (i < xl_text.length() && xl_text[i] == ' ') {
- if (line_width > 0 || last == nullptr || last->char_pos != WordCache::CHAR_WRAPLINE) {
- space_count++;
- line_width += space_width;
- } else {
- space_count = 0;
- }
- }
-
- } else {
- // latin characters
- if (current_word_size == 0) {
- word_pos = i;
- }
- char_width = font->get_char_size(current, xl_text[i + 1]).width;
- current_word_size += char_width;
- line_width += char_width;
- total_char_cache++;
-
- // allow autowrap to cut words when they exceed line width
- if (autowrap && (current_word_size > width)) {
- separatable = true;
- }
- }
-
- if ((autowrap && (line_width >= width) && ((last && last->char_pos >= 0) || separatable)) || insert_newline) {
- if (separatable) {
- if (current_word_size > 0) {
- WordCache *wc = memnew(WordCache);
- if (word_cache) {
- last->next = wc;
- } else {
- word_cache = wc;
- }
- last = wc;
-
- wc->pixel_width = current_word_size - char_width;
- wc->char_pos = word_pos;
- wc->word_len = i - word_pos;
- wc->space_count = space_count;
- current_word_size = char_width;
- word_pos = i;
- }
- }
-
- WordCache *wc = memnew(WordCache);
- if (word_cache) {
- last->next = wc;
- } else {
- word_cache = wc;
- }
- last = wc;
-
- wc->pixel_width = 0;
- wc->char_pos = insert_newline ? WordCache::CHAR_NEWLINE : WordCache::CHAR_WRAPLINE;
-
- line_width = current_word_size;
- line_count++;
- space_count = 0;
+ int lines_visible = 0;
+ float total_h = 0;
+ for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
+ if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
+ break;
}
+ lines_visible++;
}
- if (!autowrap) {
- minsize.width = width;
+ if (lines_visible > lines_rid.size()) {
+ lines_visible = lines_rid.size();
}
- if (max_lines_visible > 0 && line_count > max_lines_visible) {
- minsize.height = (font->get_height() * max_lines_visible) + (line_spacing * (max_lines_visible - 1));
- } else {
- minsize.height = (font->get_height() * line_count) + (line_spacing * (line_count - 1));
+ if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
+ lines_visible = max_lines_visible;
}
- if (!autowrap || !clip) {
- //helps speed up some labels that may change a lot, as no resizing is requested. Do not change.
- minimum_size_changed();
- }
- word_cache_dirty = false;
+ return lines_visible;
}
void Label::set_align(Align p_align) {
ERR_FAIL_INDEX((int)p_align, 4);
- align = p_align;
+ if (align != p_align) {
+ if (align == ALIGN_FILL || p_align == ALIGN_FILL) {
+ lines_dirty = true; // Reshape lines.
+ }
+ align = p_align;
+ }
update();
}
@@ -549,13 +442,83 @@ void Label::set_text(const String &p_string) {
}
text = p_string;
xl_text = tr(p_string);
- word_cache_dirty = true;
+ dirty = true;
if (percent_visible < 1) {
visible_chars = get_total_character_count() * percent_visible;
}
update();
}
+void Label::set_text_direction(Control::TextDirection p_text_direction) {
+ ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
+ if (text_direction != p_text_direction) {
+ text_direction = p_text_direction;
+ dirty = true;
+ update();
+ }
+}
+
+void Label::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
+ if (st_parser != p_parser) {
+ st_parser = p_parser;
+ dirty = true;
+ update();
+ }
+}
+
+Control::StructuredTextParser Label::get_structured_text_bidi_override() const {
+ return st_parser;
+}
+
+void Label::set_structured_text_bidi_override_options(Array p_args) {
+ st_args = p_args;
+ dirty = true;
+ update();
+}
+
+Array Label::get_structured_text_bidi_override_options() const {
+ return st_args;
+}
+
+Control::TextDirection Label::get_text_direction() const {
+ return text_direction;
+}
+
+void Label::clear_opentype_features() {
+ opentype_features.clear();
+ dirty = true;
+ update();
+}
+
+void Label::set_opentype_feature(const String &p_name, int p_value) {
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
+ opentype_features[tag] = p_value;
+ dirty = true;
+ update();
+ }
+}
+
+int Label::get_opentype_feature(const String &p_name) const {
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!opentype_features.has(tag)) {
+ return -1;
+ }
+ return opentype_features[tag];
+}
+
+void Label::set_language(const String &p_language) {
+ if (language != p_language) {
+ language = p_language;
+ dirty = true;
+ update();
+ }
+}
+
+String Label::get_language() const {
+ return language;
+}
+
void Label::set_clip_text(bool p_clip) {
clip = p_clip;
update();
@@ -573,7 +536,7 @@ String Label::get_text() const {
void Label::set_visible_characters(int p_amount) {
visible_chars = p_amount;
if (get_total_character_count() > 0) {
- percent_visible = (float)p_amount / (float)total_char_cache;
+ percent_visible = (float)p_amount / (float)get_total_character_count();
}
_change_notify("percent_visible");
update();
@@ -602,6 +565,7 @@ float Label::get_percent_visible() const {
void Label::set_lines_skipped(int p_lines) {
lines_skipped = p_lines;
+ _update_visible();
update();
}
@@ -611,6 +575,7 @@ int Label::get_lines_skipped() const {
void Label::set_max_lines_visible(int p_lines) {
max_lines_visible = p_lines;
+ _update_visible();
update();
}
@@ -619,11 +584,61 @@ int Label::get_max_lines_visible() const {
}
int Label::get_total_character_count() const {
- if (word_cache_dirty) {
- const_cast<Label *>(this)->regenerate_word_cache();
+ if (dirty || lines_dirty) {
+ const_cast<Label *>(this)->_shape();
+ }
+
+ return xl_text.length();
+}
+
+bool Label::_set(const StringName &p_name, const Variant &p_value) {
+ String str = p_name;
+ if (str.begins_with("opentype_features/")) {
+ String name = str.get_slicec('/', 1);
+ int32_t tag = TS->name_to_tag(name);
+ double value = p_value;
+ if (value == -1) {
+ if (opentype_features.has(tag)) {
+ opentype_features.erase(tag);
+ dirty = true;
+ update();
+ }
+ } else {
+ if ((double)opentype_features[tag] != value) {
+ opentype_features[tag] = value;
+ dirty = true;
+ update();
+ }
+ }
+ _change_notify();
+ return true;
}
- return total_char_cache;
+ return false;
+}
+
+bool Label::_get(const StringName &p_name, Variant &r_ret) const {
+ String str = p_name;
+ if (str.begins_with("opentype_features/")) {
+ String name = str.get_slicec('/', 1);
+ int32_t tag = TS->name_to_tag(name);
+ if (opentype_features.has(tag)) {
+ r_ret = opentype_features[tag];
+ return true;
+ } else {
+ r_ret = -1;
+ return true;
+ }
+ }
+ return false;
+}
+
+void Label::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
+ String name = TS->tag_to_name(*ftr);
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ }
+ p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
void Label::_bind_methods() {
@@ -633,13 +648,20 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_valign"), &Label::get_valign);
ClassDB::bind_method(D_METHOD("set_text", "text"), &Label::set_text);
ClassDB::bind_method(D_METHOD("get_text"), &Label::get_text);
+ ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label::set_text_direction);
+ ClassDB::bind_method(D_METHOD("get_text_direction"), &Label::get_text_direction);
+ ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &Label::set_opentype_feature);
+ ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &Label::get_opentype_feature);
+ ClassDB::bind_method(D_METHOD("clear_opentype_features"), &Label::clear_opentype_features);
+ ClassDB::bind_method(D_METHOD("set_language", "language"), &Label::set_language);
+ ClassDB::bind_method(D_METHOD("get_language"), &Label::get_language);
ClassDB::bind_method(D_METHOD("set_autowrap", "enable"), &Label::set_autowrap);
ClassDB::bind_method(D_METHOD("has_autowrap"), &Label::has_autowrap);
ClassDB::bind_method(D_METHOD("set_clip_text", "enable"), &Label::set_clip_text);
ClassDB::bind_method(D_METHOD("is_clipping_text"), &Label::is_clipping_text);
ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &Label::set_uppercase);
ClassDB::bind_method(D_METHOD("is_uppercase"), &Label::is_uppercase);
- ClassDB::bind_method(D_METHOD("get_line_height"), &Label::get_line_height);
+ ClassDB::bind_method(D_METHOD("get_line_height", "line"), &Label::get_line_height, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_line_count"), &Label::get_line_count);
ClassDB::bind_method(D_METHOD("get_visible_line_count"), &Label::get_visible_line_count);
ClassDB::bind_method(D_METHOD("get_total_character_count"), &Label::get_total_character_count);
@@ -651,6 +673,10 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_lines_skipped"), &Label::get_lines_skipped);
ClassDB::bind_method(D_METHOD("set_max_lines_visible", "lines_visible"), &Label::set_max_lines_visible);
ClassDB::bind_method(D_METHOD("get_max_lines_visible"), &Label::get_max_lines_visible);
+ ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &Label::set_structured_text_bidi_override);
+ ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &Label::get_structured_text_bidi_override);
+ ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label::set_structured_text_bidi_override_options);
+ ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label::get_structured_text_bidi_override_options);
BIND_ENUM_CONSTANT(ALIGN_LEFT);
BIND_ENUM_CONSTANT(ALIGN_CENTER);
@@ -663,6 +689,8 @@ void Label::_bind_methods() {
BIND_ENUM_CONSTANT(VALIGN_FILL);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
ADD_PROPERTY(PropertyInfo(Variant::INT, "valign", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_valign", "get_valign");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autowrap"), "set_autowrap", "has_autowrap");
@@ -672,18 +700,23 @@ void Label::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible");
+ ADD_GROUP("Structured Text", "structured_text_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
}
Label::Label(const String &p_text) {
+ text_rid = TS->create_shaped_text();
+
set_mouse_filter(MOUSE_FILTER_IGNORE);
set_text(p_text);
set_v_size_flags(SIZE_SHRINK_CENTER);
}
Label::~Label() {
- while (word_cache) {
- WordCache *current = word_cache;
- word_cache = current->next;
- memdelete(current);
+ for (int i = 0; i < lines_rid.size(); i++) {
+ TS->free(lines_rid[i]);
}
+ lines_rid.clear();
+ TS->free(text_rid);
}
diff --git a/scene/gui/label.h b/scene/gui/label.h
index 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 1b8f04297d..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(char32_t c) {
- return !is_symbol(c);
-}
void LineEdit::_gui_input(Ref<InputEvent> p_event) {
Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) {
+ if (ime_text.length() != 0) {
+ // Ignore mouse clicks in IME input mode.
+ return;
+ }
if (b->is_pressed() && b->get_button_index() == BUTTON_RIGHT && context_menu_enabled) {
menu->set_position(get_screen_transform().xform(get_local_mouse_position()));
menu->set_size(Vector2(1, 1));
@@ -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) {
@@ -334,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);
@@ -389,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());
@@ -445,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());
@@ -515,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;
@@ -561,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();
}
@@ -604,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();
}
@@ -633,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;
@@ -679,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: {
@@ -704,6 +713,7 @@ void LineEdit::_notification(int p_what) {
}
int width, height;
+ bool rtl = is_layout_rtl();
Size2 size = get_size();
width = size.width;
@@ -716,7 +726,6 @@ void LineEdit::_notification(int p_what) {
style = get_theme_stylebox("read_only");
draw_caret = false;
}
-
Ref<Font> font = get_theme_font("font");
style->draw(ci, Rect2(Point2(), size));
@@ -727,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;
@@ -780,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));
@@ -790,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;
- }
-
- char32_t cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs];
- char32_t next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1];
- int im_char_width = font->get_char_size(cchar, next).width;
-
- if ((x_ofs + im_char_width) > ofs_max) {
- break;
- }
-
- bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y;
- if (selected) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 3)), font_color);
- } else {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color);
- }
-
- drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
+#ifdef TOOLS_ENABLED
+ int caret_width = Math::round(EDSCALE);
+#else
+ int caret_width = 1;
+#endif
- x_ofs += im_char_width;
- ofs++;
+ // Draw selections rects.
+ Vector2 ofs = Point2(x_ofs + scroll_offset, y_ofs);
+ if (selection.enabled) {
+ Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, selection.begin, selection.end);
+ for (int i = 0; i < sel.size(); i++) {
+ Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height);
+ if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) {
+ continue;
+ }
+ if (rect.position.x < x_ofs) {
+ rect.size.x -= (x_ofs - rect.position.x);
+ rect.position.x = x_ofs;
+ } else if (rect.position.x + rect.size.x > ofs_max) {
+ rect.size.x = ofs_max - rect.position.x;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_color);
+ }
+ }
+ const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(text_rid);
+ const TextServer::Glyph *glyphs = visual.ptr();
+ int gl_size = visual.size();
+
+ // Draw text.
+ ofs.y += TS->shaped_text_get_ascent(text_rid);
+ for (int i = 0; i < gl_size; i++) {
+ bool selected = selection.enabled && glyphs[i].start >= selection.begin && glyphs[i].end <= selection.end;
+ for (int j = 0; j < glyphs[i].repeat; j++) {
+ if (ceil(ofs.x) >= x_ofs && (ofs.x + glyphs[i].advance) <= ofs_max) {
+ if (glyphs[i].font_rid != RID()) {
+ TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_color_selected : font_color);
+ } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ TS->draw_hex_code_box(ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_color_selected : font_color);
}
}
+ ofs.x += glyphs[i].advance;
}
-
- char32_t cchar = (pass && !text.empty()) ? secret_character[0] : t[char_ofs];
- char32_t next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1];
- int char_width = font->get_char_size(cchar, next).width;
-
- // End of widget, break.
- if ((x_ofs + char_width) > ofs_max) {
+ if (ofs.x >= ofs_max) {
break;
}
-
- bool selected = selection.enabled && char_ofs >= selection.begin && char_ofs < selection.end;
-
- if (selected) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(char_width, caret_height)), selection_color);
- }
-
- int yofs = y_ofs + (caret_height - font->get_height()) / 2;
- drawer.draw_char(ci, Point2(x_ofs, yofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
-
- if (char_ofs == cursor_pos && draw_caret && !using_placeholder) {
- if (ime_text.length() == 0) {
-#ifdef TOOLS_ENABLED
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color);
-#else
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(1, caret_height)), cursor_color);
-#endif
- }
- }
-
- x_ofs += char_width;
- char_ofs++;
}
- if (char_ofs == cursor_pos) {
- if (ime_text.length() > 0) {
- int ofs = 0;
- while (true) {
- if (ofs >= ime_text.length()) {
- break;
+ // Draw carets.
+ ofs.x = x_ofs + scroll_offset;
+ if (draw_caret) {
+ if (ime_text.length() == 0) {
+ // Normal caret.
+ Rect2 l_caret, t_caret;
+ TextServer::Direction l_dir, t_dir;
+ TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir);
+
+ if (l_caret == Rect2() && t_caret == Rect2()) {
+ // No carets, add one at the start.
+ int h = get_theme_font("font")->get_height(get_theme_font_size("font_size"));
+ int y = style->get_offset().y + (y_area - h) / 2;
+ if (rtl) {
+ l_dir = TextServer::DIRECTION_RTL;
+ l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h));
+ } else {
+ l_dir = TextServer::DIRECTION_LTR;
+ l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h));
}
-
- char32_t cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs];
- char32_t next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1];
- int im_char_width = font->get_char_size(cchar, next).width;
-
- if ((x_ofs + im_char_width) > ofs_max) {
- break;
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color);
+ } else {
+ if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) {
+ // Draw extra marker on top of mid caret.
+ Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width);
+ trect.position += ofs;
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, cursor_color);
}
- bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y;
- if (selected) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 3)), font_color);
- } else {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color);
- }
+ l_caret.position += ofs;
+ l_caret.size.x = caret_width;
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color);
- drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
+ t_caret.position += ofs;
+ t_caret.size.x = caret_width;
- x_ofs += im_char_width;
- ofs++;
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, cursor_color);
}
- }
- }
-
- if ((char_ofs == cursor_pos || using_placeholder) && draw_caret) { // May be at the end, or placeholder.
- if (ime_text.length() == 0) {
- int caret_x_ofs = x_ofs;
- if (using_placeholder) {
- switch (align) {
- case ALIGN_LEFT:
- case ALIGN_FILL: {
- caret_x_ofs = style->get_offset().x;
- } break;
- case ALIGN_CENTER: {
- caret_x_ofs = ofs_max / 2;
- } break;
- case ALIGN_RIGHT: {
- caret_x_ofs = ofs_max;
- } break;
+ } else {
+ {
+ // IME intermidiet text range.
+ Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos, cursor_pos + ime_text.length());
+ for (int i = 0; i < sel.size(); i++) {
+ Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height);
+ if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) {
+ continue;
+ }
+ if (rect.position.x < x_ofs) {
+ rect.size.x -= (x_ofs - rect.position.x);
+ rect.position.x = x_ofs;
+ } else if (rect.position.x + rect.size.x > ofs_max) {
+ rect.size.x = ofs_max - rect.position.x;
+ }
+ rect.size.y = caret_width;
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color);
+ }
+ }
+ {
+ // IME caret.
+ Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos + ime_selection.x, cursor_pos + ime_selection.x + ime_selection.y);
+ for (int i = 0; i < sel.size(); i++) {
+ Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height);
+ if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) {
+ continue;
+ }
+ if (rect.position.x < x_ofs) {
+ rect.size.x -= (x_ofs - rect.position.x);
+ rect.position.x = x_ofs;
+ } else if (rect.position.x + rect.size.x > ofs_max) {
+ rect.size.x = ofs_max - rect.position.x;
+ }
+ rect.size.y = caret_width * 3;
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color);
}
}
-#ifdef TOOLS_ENABLED
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(caret_x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color);
-#else
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(caret_x_ofs, y_ofs), Size2(1, caret_height)), cursor_color);
-#endif
}
}
if (has_focus()) {
if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
- DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + Point2(using_placeholder ? 0 : x_ofs, y_ofs + caret_height), get_viewport()->get_window_id());
+ DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + Point2(using_placeholder ? 0 : x_ofs, y_ofs + TS->shaped_text_get_size(text_rid).y), get_viewport()->get_window_id());
}
}
} break;
@@ -961,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();
@@ -971,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;
@@ -1022,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();
}
@@ -1043,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();
}
@@ -1070,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));
}
- ofs++;
}
- return pixel_ofs;
+ Vector2i ret;
+ Rect2 l_caret, t_caret;
+ TextServer::Direction l_dir, t_dir;
+ // Get position of the start of caret.
+ if (ime_text.length() != 0 && ime_selection.x != 0) {
+ TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x, l_caret, l_dir, t_caret, t_dir);
+ } else {
+ TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir);
+ }
+
+ if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
+ ret.x = x_ofs + l_caret.position.x + scroll_offset;
+ } else {
+ ret.x = x_ofs + t_caret.position.x + scroll_offset;
+ }
+
+ // Get position of the end of caret.
+ if (ime_text.length() != 0) {
+ if (ime_selection.y != 0) {
+ TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir);
+ } else {
+ TS->shaped_text_get_carets(text_rid, cursor_pos + ime_text.size(), l_caret, l_dir, t_caret, t_dir);
+ }
+ if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
+ ret.y = x_ofs + l_caret.position.x + scroll_offset;
+ } else {
+ ret.y = x_ofs + t_caret.position.x + scroll_offset;
+ }
+ } else {
+ ret.y = ret.x;
+ }
+
+ return ret;
+}
+
+void LineEdit::set_mid_grapheme_caret_enabled(const bool p_enabled) {
+ mid_grapheme_caret_enabled = p_enabled;
+}
+
+bool LineEdit::get_mid_grapheme_caret_enabled() const {
+ return mid_grapheme_caret_enabled;
}
bool LineEdit::cursor_get_blink_enabled() const {
@@ -1226,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()) {
@@ -1282,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();
- cursor_pos = 0;
- window_pos = 0;
+}
+
+void LineEdit::set_opentype_feature(const String &p_name, int p_value) {
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
+ opentype_features[tag] = p_value;
+ _shape();
+ update();
+ }
+}
+
+int LineEdit::get_opentype_feature(const String &p_name) const {
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!opentype_features.has(tag)) {
+ return -1;
+ }
+ return opentype_features[tag];
+}
+
+void LineEdit::set_language(const String &p_language) {
+ if (language != p_language) {
+ language = p_language;
+ _shape();
+ update();
+ }
+}
+
+String LineEdit::get_language() const {
+ return language;
+}
+
+void LineEdit::set_draw_control_chars(bool p_draw_control_chars) {
+ if (draw_control_chars != p_draw_control_chars) {
+ draw_control_chars = p_draw_control_chars;
+ menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
+ _shape();
+ update();
+ }
+}
+
+bool LineEdit::get_draw_control_chars() const {
+ return draw_control_chars;
+}
+
+void LineEdit::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
+ if (st_parser != p_parser) {
+ st_parser = p_parser;
+ _shape();
+ update();
+ }
+}
+
+Control::StructuredTextParser LineEdit::get_structured_text_bidi_override() const {
+ return st_parser;
+}
+
+void LineEdit::set_structured_text_bidi_override_options(Array p_args) {
+ st_args = p_args;
+ _shape();
+ update();
+}
+
+Array LineEdit::get_structured_text_bidi_override_options() const {
+ return st_args;
}
void LineEdit::clear() {
@@ -1303,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();
}
@@ -1331,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");
-
- 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();
- }
+ bool rtl = is_layout_rtl();
- 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();
}
@@ -1389,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");
@@ -1411,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;
@@ -1526,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();
}
@@ -1540,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();
}
@@ -1618,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));
+ }
}
}
}
@@ -1644,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 {
@@ -1656,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();
}
@@ -1701,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();
}
@@ -1710,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();
}
@@ -1724,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));
}
}
@@ -1769,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);
}
@@ -1795,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() {
@@ -1810,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);
@@ -1866,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");
@@ -1881,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");
@@ -1889,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 8a65aa5032..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"
@@ -93,7 +93,7 @@ void Popup::_notification(int p_what) {
}
void Popup::_parent_focused() {
- if (popped_up) {
+ if (popped_up && close_on_parent_focus) {
_close_pressed();
}
}
@@ -112,7 +112,19 @@ 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"));
}
@@ -191,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;
}
@@ -215,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 3e5b89ccf3..48e7ea9452 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -33,13 +33,14 @@
#include "scene/main/window.h"
-#include "core/local_vector.h"
+#include "core/templates/local_vector.h"
class Popup : public Window {
GDCLASS(Popup, Window);
LocalVector<Window *> visible_parents;
bool popped_up = false;
+ bool close_on_parent_focus = true;
void _input_from_window(const Ref<InputEvent> &p_event);
@@ -57,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 bc70809ad5..07f03ad40e 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -33,16 +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"
-
-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();
}
@@ -53,11 +51,9 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
Size2 minsize = get_theme_stylebox("panel")->get_minimum_size(); // Accounts for margin in the margin container
minsize.x += scroll_container->get_v_scrollbar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
- Ref<Font> font = get_theme_font("font");
float max_w = 0;
float icon_w = 0;
- int font_h = font->get_height();
int check_w = MAX(get_theme_icon("checked")->get_width(), get_theme_icon("radio_checked")->get_width()) + hseparation;
int accel_max_w = 0;
bool has_check = false;
@@ -66,7 +62,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
Size2 size;
Size2 icon_size = items[i].get_icon_size();
- size.height = MAX(icon_size.height, font_h);
+ size.height = MAX(icon_size.height, items[i].text_buf->get_size().y);
icon_w = MAX(icon_size.width, icon_w);
size.width += items[i].h_ofs;
@@ -75,15 +71,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);
}
@@ -112,13 +107,12 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
}
int PopupMenu::_get_items_total_height() const {
- int font_height = get_theme_font("font")->get_height();
int vsep = get_theme_constant("vseparation");
// Get total height of all items by taking max of icon height and font height
int items_total_height = 0;
for (int i = 0; i < items.size(); i++) {
- items_total_height += MAX(items[i].get_icon_size().height, font_height) + vsep;
+ items_total_height += MAX(items[i].get_icon_size().height, items[i].text_buf->get_size().y) + vsep;
}
// Subtract a separator which is not needed for the last item.
@@ -150,7 +144,6 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
Ref<StyleBox> style = get_theme_stylebox("panel"); // Accounts for margin in the margin container
int vseparation = get_theme_constant("vseparation");
- float font_h = get_theme_font("font")->get_height();
Point2 ofs = style->get_offset() + Point2(0, vseparation / 2);
@@ -163,7 +156,7 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
ofs.y += vseparation;
}
- ofs.y += MAX(items[i].get_icon_size().height, font_h);
+ ofs.y += MAX(items[i].get_icon_size().height, items[i].text_buf->get_size().y);
if (p_over.y - control->get_position().y < ofs.y) {
return i;
@@ -173,11 +166,11 @@ 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 + ".");
+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[over].submenu + ".");
+ ERR_FAIL_COND_MSG(!submenu_popup, "Item subnode is not a Popup: " + items[p_over].submenu + ".");
if (submenu_popup->is_visible()) {
return; //already visible!
}
@@ -190,14 +183,24 @@ void PopupMenu::_activate_submenu(int over) {
float scroll_offset = control->get_position().y;
- Point2 submenu_pos = this_pos + Point2(this_rect.size.width, items[over]._ofs_cache + scroll_offset);
+ Point2 submenu_pos;
Size2 submenu_size = submenu_popup->get_size();
+ if (control->is_layout_rtl()) {
+ submenu_pos = this_pos + Point2(-submenu_size.width, items[p_over]._ofs_cache + scroll_offset);
+ } else {
+ submenu_pos = this_pos + Point2(this_rect.size.width, items[p_over]._ofs_cache + scroll_offset);
+ }
// Fix pos if going outside parent rect
+ if (submenu_pos.x < get_parent_rect().position.x) {
+ submenu_pos.x = this_pos.x + submenu_size.width;
+ }
+
if (submenu_pos.x + submenu_size.width > get_parent_rect().size.width) {
submenu_pos.x = this_pos.x - submenu_size.width;
}
+ 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();
@@ -210,11 +213,11 @@ void PopupMenu::_activate_submenu(int over) {
// 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[over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2));
+ 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[over]._ofs_cache + items[over]._height_cache + scroll_offset <= control->get_size().height) {
- int from = items[over]._ofs_cache + items[over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height;
+ 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));
}
}
@@ -229,12 +232,13 @@ void PopupMenu::_submenu_timeout() {
}
void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
- if (p_event->is_action("ui_down") && p_event->is_pressed() && mouse_over != items.size() - 1) {
+ if (p_event->is_action("ui_down") && p_event->is_pressed()) {
int search_from = mouse_over + 1;
if (search_from >= items.size()) {
search_from = 0;
}
+ bool match_found = false;
for (int i = search_from; i < items.size(); i++) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
@@ -242,15 +246,31 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
_scroll_to_item(i);
control->update();
set_input_as_handled();
+ match_found = true;
break;
}
}
- } else if (p_event->is_action("ui_up") && p_event->is_pressed() && mouse_over != 0) {
+
+ if (!match_found) {
+ // If the last item is not selectable, try re-searching from the start.
+ for (int i = 0; i < search_from; i++) {
+ if (!items[i].separator && !items[i].disabled) {
+ mouse_over = i;
+ emit_signal("id_focused", i);
+ _scroll_to_item(i);
+ control->update();
+ set_input_as_handled();
+ break;
+ }
+ }
+ }
+ } else if (p_event->is_action("ui_up") && p_event->is_pressed()) {
int search_from = mouse_over - 1;
if (search_from < 0) {
search_from = items.size() - 1;
}
+ bool match_found = false;
for (int i = search_from; i >= 0; i--) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
@@ -258,9 +278,24 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
_scroll_to_item(i);
control->update();
set_input_as_handled();
+ match_found = true;
break;
}
}
+
+ if (!match_found) {
+ // If the first item is not selectable, try re-searching from the end.
+ for (int i = items.size() - 1; i >= search_from; i--) {
+ if (!items[i].separator && !items[i].disabled) {
+ mouse_over = i;
+ emit_signal("id_focused", i);
+ _scroll_to_item(i);
+ control->update();
+ set_input_as_handled();
+ break;
+ }
+ }
+ }
} else if (p_event->is_action("ui_left") && p_event->is_pressed()) {
Node *n = get_parent();
if (n && Object::cast_to<PopupMenu>(n)) {
@@ -286,7 +321,11 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
// Make an area which does not include v scrollbar, so that items are not activated when dragging scrollbar.
Rect2 item_clickable_area = scroll_container->get_rect();
if (scroll_container->get_v_scrollbar()->is_visible_in_tree()) {
- item_clickable_area.size.width -= scroll_container->get_v_scrollbar()->get_size().width;
+ if (is_layout_rtl()) {
+ item_clickable_area.position.x += scroll_container->get_v_scrollbar()->get_size().width;
+ } else {
+ item_clickable_area.size.width -= scroll_container->get_v_scrollbar()->get_size().width;
+ }
}
Ref<InputEventMouseButton> b = p_event;
@@ -305,12 +344,14 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
during_grabbed_click = false;
initial_button_mask = 0;
- int over = _get_mouse_over(b->get_position());
-
- if (invalidated_click) {
- invalidated_click = false;
+ // 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;
}
+
+ int over = _get_mouse_over(b->get_position());
if (over < 0) {
if (!was_during_grabbed_click) {
hide();
@@ -338,13 +379,6 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (invalidated_click) {
- moved += m->get_relative();
- if (moved.length() > 4) {
- invalidated_click = false;
- }
- }
-
for (List<Rect2>::Element *E = autohide_areas.front(); E; E = E->next()) {
if (!Rect2(Point2(), get_size()).has_point(m->get_position()) && E->get().has_point(m->get_position())) {
_close_pressed();
@@ -421,13 +455,19 @@ void PopupMenu::_draw_items() {
margin_size.width = margin_container->get_theme_constant("margin_right") + margin_container->get_theme_constant("margin_left");
margin_size.height = margin_container->get_theme_constant("margin_top") + margin_container->get_theme_constant("margin_bottom");
+ bool rtl = control->is_layout_rtl();
Ref<StyleBox> style = get_theme_stylebox("panel");
Ref<StyleBox> hover = get_theme_stylebox("hover");
- Ref<Font> font = get_theme_font("font");
// In Item::checkable_type enum order (less the non-checkable member)
Ref<Texture2D> check[] = { get_theme_icon("checked"), get_theme_icon("radio_checked") };
Ref<Texture2D> uncheck[] = { get_theme_icon("unchecked"), get_theme_icon("radio_unchecked") };
- Ref<Texture2D> submenu = get_theme_icon("submenu");
+ Ref<Texture2D> submenu;
+ if (rtl) {
+ submenu = get_theme_icon("submenu_mirrored");
+ } else {
+ submenu = get_theme_icon("submenu");
+ }
+
Ref<StyleBox> separator = get_theme_stylebox("separator");
Ref<StyleBox> labeled_separator_left = get_theme_stylebox("labeled_separator_left");
Ref<StyleBox> labeled_separator_right = get_theme_stylebox("labeled_separator_right");
@@ -438,7 +478,7 @@ void PopupMenu::_draw_items() {
Color font_color_disabled = get_theme_color("font_color_disabled");
Color font_color_accel = get_theme_color("font_color_accel");
Color font_color_hover = get_theme_color("font_color_hover");
- float font_h = font->get_height();
+ Color font_color_separator = get_theme_color("font_color_separator");
float scroll_width = scroll_container->get_v_scrollbar()->is_visible_in_tree() ? scroll_container->get_v_scrollbar()->get_size().width : 0;
float display_width = control->get_size().width - scroll_width;
@@ -471,12 +511,18 @@ void PopupMenu::_draw_items() {
ofs.y += vseparation;
}
+ _shape_item(i);
+
Point2 item_ofs = ofs;
Size2 icon_size = items[i].get_icon_size();
- float h = MAX(icon_size.height, font_h);
+ float h = MAX(icon_size.height, items[i].text_buf->get_size().y);
if (i == mouse_over) {
- hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(display_width + hseparation * 2, h + vseparation)));
+ if (rtl) {
+ hover->draw(ci, Rect2(item_ofs + Point2(-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;
@@ -486,7 +532,7 @@ void PopupMenu::_draw_items() {
if (items[i].separator) {
int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
if (text != String()) {
- int text_size = font->get_string_size(text).width;
+ int text_size = items[i].text_buf->get_size().width;
int text_center = display_width / 2;
int text_left = text_center - text_size / 2;
int text_right = text_center + text_size / 2;
@@ -506,36 +552,54 @@ void PopupMenu::_draw_items() {
// Checkboxes
if (items[i].checkable_type) {
Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
- icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color);
+ if (rtl) {
+ icon->draw(ci, Size2(control->get_size().width - item_ofs.x - icon->get_width(), item_ofs.y) + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color);
+ } else {
+ icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color);
+ }
}
// Icon
if (!items[i].icon.is_null()) {
- items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ if (rtl) {
+ items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ } else {
+ items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ }
}
// Submenu arrow on right hand side
if (items[i].submenu != "") {
- submenu->draw(ci, Point2(display_width - submenu->get_width(), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
+ if (rtl) {
+ submenu->draw(ci, Point2(scroll_width + style->get_margin(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);
+ }
}
// Text
- item_ofs.y += font->get_ascent();
if (items[i].separator) {
if (text != String()) {
- int center = (display_width - font->get_string_size(text).width) / 2;
- font->draw(ci, Point2(center, item_ofs.y + Math::floor((h - font_h) / 2.0)), text, font_color_disabled);
+ int center = (display_width - items[i].text_buf->get_size().width) / 2;
+ items[i].text_buf->draw(ci, Point2(center, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0)), font_color_separator);
}
} else {
item_ofs.x += icon_ofs + check_ofs;
- font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color));
+ if (rtl) {
+ items[i].text_buf->draw(ci, Size2(control->get_size().width - items[i].text_buf->get_size().width - item_ofs.x, item_ofs.y) + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)), items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color));
+ } else {
+ items[i].text_buf->draw(ci, item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)), items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color));
+ }
}
// Accelerator / Shortcut
if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->is_valid())) {
- String sc_text = _get_accel_text(i);
- item_ofs.x = display_width - font->get_string_size(sc_text).width;
- font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), sc_text, i == mouse_over ? font_color_hover : font_color_accel);
+ if (rtl) {
+ item_ofs.x = scroll_width + style->get_margin(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
@@ -552,6 +616,52 @@ void PopupMenu::_draw_background() {
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: {
@@ -562,16 +672,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)) {
@@ -655,6 +768,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();
}
@@ -664,6 +778,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();
}
@@ -673,6 +788,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();
}
@@ -683,6 +799,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();
}
@@ -692,6 +809,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();
}
@@ -702,6 +820,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();
}
@@ -712,6 +831,7 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
item.max_states = p_max_states;
item.state = p_default_state;
items.push_back(item);
+ _shape_item(items.size() - 1);
control->update();
child_controls_changed();
}
@@ -729,6 +849,7 @@ void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_g
Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
items.push_back(item);
+ _shape_item(items.size() - 1);
control->update();
child_controls_changed();
}
@@ -738,6 +859,7 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortc
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.icon = p_icon;
items.push_back(item);
+ _shape_item(items.size() - 1);
control->update();
child_controls_changed();
}
@@ -747,6 +869,7 @@ void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bo
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
+ _shape_item(items.size() - 1);
control->update();
child_controls_changed();
}
@@ -757,6 +880,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
+ _shape_item(items.size() - 1);
control->update();
child_controls_changed();
}
@@ -766,6 +890,7 @@ void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
+ _shape_item(items.size() - 1);
control->update();
child_controls_changed();
}
@@ -776,6 +901,7 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, cons
item.icon = p_icon;
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
items.push_back(item);
+ _shape_item(items.size() - 1);
control->update();
child_controls_changed();
}
@@ -787,6 +913,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();
}
@@ -800,11 +927,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;
@@ -833,6 +997,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();
@@ -871,6 +1036,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) {
@@ -977,6 +1161,7 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bo
}
items.write[p_idx].shortcut = p_shortcut;
items.write[p_idx].shortcut_is_global = p_global;
+ items.write[p_idx].dirty = true;
if (items[p_idx].shortcut.is_valid()) {
_ref_shortcut(items[p_idx].shortcut);
@@ -1369,6 +1554,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);
@@ -1388,6 +1576,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);
@@ -1443,7 +1635,7 @@ 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);
}
@@ -1475,7 +1667,6 @@ PopupMenu::PopupMenu() {
submenu_over = -1;
initial_button_mask = 0;
during_grabbed_click = false;
- invalidated_click = false;
allow_search = true;
search_time_msec = 0;
@@ -1490,6 +1681,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 9535fd07d7..a082fcf0e7 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -35,6 +35,7 @@
#include "scene/gui/popup.h"
#include "scene/gui/scroll_container.h"
#include "scene/gui/shortcut.h"
+#include "scene/resources/text_line.h"
class PopupMenu : public Popup {
GDCLASS(PopupMenu, Popup);
@@ -43,6 +44,13 @@ class PopupMenu : public Popup {
Ref<Texture2D> icon;
String text;
String xl_text;
+ Ref<TextLine> text_buf;
+ Ref<TextLine> accel_text_buf;
+
+ Dictionary opentype_features;
+ String language;
+ Control::TextDirection text_direction = Control::TEXT_DIRECTION_AUTO;
+
bool checked;
enum {
CHECKABLE_TYPE_NONE,
@@ -53,6 +61,7 @@ class PopupMenu : public Popup {
int state;
bool separator;
bool disabled;
+ bool dirty;
int id;
Variant metadata;
String submenu;
@@ -71,6 +80,9 @@ class PopupMenu : public Popup {
}
Item() {
+ text_buf.instance();
+ accel_text_buf.instance();
+ dirty = true;
checked = false;
checkable_type = CHECKABLE_TYPE_NONE;
separator = false;
@@ -86,6 +98,9 @@ class PopupMenu : public Popup {
}
};
+ bool close_allowed = false;
+
+ Timer *minimum_lifetime_timer = nullptr;
Timer *submenu_timer;
List<Rect2> autohide_areas;
Vector<Item> items;
@@ -94,18 +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;
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;
@@ -130,6 +147,9 @@ class PopupMenu : public Popup {
void _draw_items();
void _draw_background();
+ void _minimum_lifetime_timeout();
+ void _close_pressed();
+
protected:
friend class MenuButton;
void _notification(int p_what);
@@ -155,6 +175,11 @@ public:
void add_submenu_item(const String &p_label, const String &p_submenu, int p_id = -1);
void set_item_text(int p_idx, const String &p_text);
+
+ void set_item_text_direction(int p_idx, Control::TextDirection p_text_direction);
+ void set_item_opentype_feature(int p_idx, const String &p_name, int p_value);
+ void clear_item_opentype_features(int p_idx);
+ void set_item_language(int p_idx, const String &p_language);
void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon);
void set_item_checked(int p_idx, bool p_checked);
void set_item_id(int p_idx, int p_id);
@@ -175,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;
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..5b201f45d3 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")));
@@ -64,11 +64,8 @@ RichTextEffect::RichTextEffect() {
}
void CharFXTransform::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_relative_index"), &CharFXTransform::get_relative_index);
- ClassDB::bind_method(D_METHOD("set_relative_index", "index"), &CharFXTransform::set_relative_index);
-
- ClassDB::bind_method(D_METHOD("get_absolute_index"), &CharFXTransform::get_absolute_index);
- ClassDB::bind_method(D_METHOD("set_absolute_index", "index"), &CharFXTransform::set_absolute_index);
+ ClassDB::bind_method(D_METHOD("get_range"), &CharFXTransform::get_range);
+ ClassDB::bind_method(D_METHOD("set_range", "range"), &CharFXTransform::set_range);
ClassDB::bind_method(D_METHOD("get_elapsed_time"), &CharFXTransform::get_elapsed_time);
ClassDB::bind_method(D_METHOD("set_elapsed_time", "time"), &CharFXTransform::set_elapsed_time);
@@ -76,6 +73,9 @@ void CharFXTransform::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_visible"), &CharFXTransform::is_visible);
ClassDB::bind_method(D_METHOD("set_visibility", "visibility"), &CharFXTransform::set_visibility);
+ ClassDB::bind_method(D_METHOD("is_outline"), &CharFXTransform::is_outline);
+ ClassDB::bind_method(D_METHOD("set_outline", "outline"), &CharFXTransform::set_outline);
+
ClassDB::bind_method(D_METHOD("get_offset"), &CharFXTransform::get_offset);
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &CharFXTransform::set_offset);
@@ -85,27 +85,24 @@ void CharFXTransform::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_environment"), &CharFXTransform::get_environment);
ClassDB::bind_method(D_METHOD("set_environment", "environment"), &CharFXTransform::set_environment);
- ClassDB::bind_method(D_METHOD("get_character"), &CharFXTransform::get_character);
- ClassDB::bind_method(D_METHOD("set_character", "character"), &CharFXTransform::set_character);
+ ClassDB::bind_method(D_METHOD("get_glyph_index"), &CharFXTransform::get_glyph_index);
+ ClassDB::bind_method(D_METHOD("set_glyph_index", "glyph_index"), &CharFXTransform::set_glyph_index);
+
+ ClassDB::bind_method(D_METHOD("get_font"), &CharFXTransform::get_font);
+ ClassDB::bind_method(D_METHOD("set_font", "font"), &CharFXTransform::set_font);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_index"), "set_relative_index", "get_relative_index");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "absolute_index"), "set_absolute_index", "get_absolute_index");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "range"), "set_range", "get_range");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "elapsed_time"), "set_elapsed_time", "get_elapsed_time");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visibility", "is_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "outline"), "set_outline", "is_outline");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "env"), "set_environment", "get_environment");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "character"), "set_character", "get_character");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "glyph_index"), "set_glyph_index", "get_glyph_index");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "font"), "set_font", "get_font");
}
CharFXTransform::CharFXTransform() {
- relative_index = 0;
- absolute_index = 0;
- visibility = true;
- offset = Point2();
- color = Color();
- character = 0;
- elapsed_time = 0.0f;
}
CharFXTransform::~CharFXTransform() {
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
index a5401f7eaa..1aa62c1c83 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);
@@ -54,32 +54,37 @@ protected:
static void _bind_methods();
public:
- uint64_t relative_index;
- uint64_t absolute_index;
- bool visibility;
+ Vector2i range;
+ bool visibility = true;
+ bool outline = false;
Point2 offset;
Color color;
- char32_t character;
- float elapsed_time;
+ float elapsed_time = 0.0f;
Dictionary environment;
+ uint32_t glpyh_index = 0;
+ RID font;
CharFXTransform();
~CharFXTransform();
- uint64_t get_relative_index() { return relative_index; }
- void set_relative_index(uint64_t p_index) { relative_index = p_index; }
- uint64_t get_absolute_index() { return absolute_index; }
- void set_absolute_index(uint64_t p_index) { absolute_index = p_index; }
+ Vector2i get_range() { return range; }
+ void set_range(const Vector2i &p_range) { range = p_range; }
float get_elapsed_time() { return elapsed_time; }
void set_elapsed_time(float p_elapsed_time) { elapsed_time = p_elapsed_time; }
bool is_visible() { return visibility; }
- void set_visibility(bool p_vis) { visibility = p_vis; }
+ void set_visibility(bool p_visibility) { visibility = p_visibility; }
+ bool is_outline() { return outline; }
+ void set_outline(bool p_outline) { outline = p_outline; }
Point2 get_offset() { return offset; }
void set_offset(Point2 p_offset) { offset = p_offset; }
Color get_color() { return color; }
void set_color(Color p_color) { color = p_color; }
- int get_character() { return (int)character; }
- void set_character(int p_char) { character = (char32_t)p_char; }
+
+ uint32_t get_glyph_index() const { return glpyh_index; };
+ void set_glyph_index(uint32_t p_glpyh_index) { glpyh_index = p_glpyh_index; };
+ RID get_font() const { return font; };
+ void set_font(RID p_font) { font = p_font; };
+
Dictionary get_environment() { return environment; }
void set_environment(Dictionary p_environment) { environment = p_environment; }
};
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index fb4931ea91..244d90ca6b 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -140,741 +140,1088 @@ Rect2 RichTextLabel::_get_text_rect() {
return Rect2(style->get_offset(), get_size() - style->get_minimum_size());
}
-int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) {
- ERR_FAIL_INDEX_V((int)p_mode, 3, 0);
-
- RID ci;
- if (r_outside) {
- *r_outside = false;
+RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item_from, RichTextLabel::Item *p_item_to, int p_position) {
+ int offset = 0;
+ for (Item *it = p_item_from; it && it != p_item_to; it = _get_next_item(it)) {
+ switch (it->type) {
+ case ITEM_TEXT: {
+ ItemText *t = (ItemText *)it;
+ offset += t->text.length();
+ if (offset > p_position) {
+ return it;
+ }
+ } break;
+ case ITEM_NEWLINE:
+ case ITEM_IMAGE:
+ case ITEM_TABLE: {
+ offset += 1;
+ } break;
+ default:
+ break;
+ }
}
- if (p_mode == PROCESS_DRAW) {
- ci = get_canvas_item();
+ return p_item_from;
+}
- if (r_click_item) {
- *r_click_item = nullptr;
- }
+String RichTextLabel::_roman(int p_num, bool p_capitalize) const {
+ if (p_num > 3999) {
+ return "ERR";
+ };
+ String s;
+ if (p_capitalize) {
+ String M[] = { "", "M", "MM", "MMM" };
+ String C[] = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" };
+ String X[] = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" };
+ String I[] = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" };
+ s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10];
+ } else {
+ String M[] = { "", "m", "mm", "mmm" };
+ String C[] = { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" };
+ String X[] = { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" };
+ String I[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" };
+ s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10];
}
- Line &l = p_frame->lines.write[p_line];
- Item *it = l.from;
+ return s;
+}
- int line_ofs = 0;
- int margin = _find_margin(it, p_base_font);
- Align align = _find_align(it);
- int line = 0;
- int spaces = 0;
+String RichTextLabel::_letters(int p_num, bool p_capitalize) const {
+ int64_t n = p_num;
- int height = get_size().y;
+ int chars = 0;
+ do {
+ n /= 24;
+ chars++;
+ } while (n);
- if (p_mode != PROCESS_CACHE) {
- ERR_FAIL_INDEX_V(line, l.offset_caches.size(), 0);
- line_ofs = l.offset_caches[line];
- }
+ String s;
+ s.resize(chars + 1);
+ char32_t *c = s.ptrw();
+ c[chars] = 0;
+ n = p_num;
+ do {
+ int mod = ABS(n % 24);
+ char a = (p_capitalize ? 'A' : 'a');
+ c[--chars] = a + mod - 1;
- if (p_mode == PROCESS_CACHE) {
- l.offset_caches.clear();
- l.height_caches.clear();
- l.ascent_caches.clear();
- l.descent_caches.clear();
- l.char_count = 0;
- l.minimum_width = 0;
- l.maximum_width = 0;
- }
+ n /= 24;
+ } while (n);
- int wofs = margin;
- int spaces_size = 0;
- int align_ofs = 0;
+ return s;
+}
- if (p_mode != PROCESS_CACHE && align != ALIGN_FILL) {
- wofs += line_ofs;
- }
+void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width) {
+ ERR_FAIL_COND(p_frame == nullptr);
+ ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
+
+ Line &l = p_frame->lines.write[p_line];
- int begin = margin;
+ l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size);
+ l.text_buf->set_width(p_width - l.offset.x);
- Ref<Font> cfont = _find_font(it);
- if (cfont.is_null()) {
- cfont = p_base_font;
+ if (tab_size > 0) { // Align inline tabs.
+ Vector<float> tabs;
+ tabs.push_back(tab_size * p_base_font->get_char_size('m', 0, p_base_font_size).width);
+ l.text_buf->tab_align(tabs);
}
- //line height should be the font height for the first time, this ensures that an empty line will never have zero height and successive newlines are displayed
- int line_height = cfont->get_height();
- int line_ascent = cfont->get_ascent();
- int line_descent = cfont->get_descent();
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ switch (it->type) {
+ case ITEM_TABLE: {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ int hseparation = get_theme_constant("table_hseparation");
+ int vseparation = get_theme_constant("table_vseparation");
+ int col_count = table->columns.size();
- int backtrack = 0; // for dynamic hidden content.
+ for (int i = 0; i < col_count; i++) {
+ table->columns.write[i].width = 0;
+ }
- int nonblank_line_count = 0; //number of nonblank lines as counted during PROCESS_DRAW
+ int idx = 0;
+ for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
+ ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E->get());
+ for (int i = 0; i < frame->lines.size(); i++) {
+ _resize_line(frame, i, p_base_font, p_base_font_size, 1);
+ }
+ idx++;
+ }
- Variant meta;
+ // Compute minimum width for each cell.
+ const int available_width = p_width - hseparation * (col_count - 1);
-#define RETURN return nonblank_line_count
-
-#define NEW_LINE \
- { \
- if (p_mode != PROCESS_CACHE) { \
- line++; \
- backtrack = 0; \
- if (!line_is_blank) { \
- nonblank_line_count++; \
- } \
- line_is_blank = true; \
- if (line < l.offset_caches.size()) \
- line_ofs = l.offset_caches[line]; \
- wofs = margin; \
- if (align != ALIGN_FILL) \
- wofs += line_ofs; \
- } else { \
- int used = wofs - margin; \
- switch (align) { \
- case ALIGN_LEFT: \
- l.offset_caches.push_back(0); \
- break; \
- case ALIGN_CENTER: \
- l.offset_caches.push_back(((p_width - margin) - used) / 2); \
- break; \
- case ALIGN_RIGHT: \
- l.offset_caches.push_back(((p_width - margin) - used)); \
- break; \
- case ALIGN_FILL: \
- l.offset_caches.push_back(line_wrapped ? ((p_width - margin) - used) : 0); \
- break; \
- } \
- l.height_caches.push_back(line_height); \
- l.ascent_caches.push_back(line_ascent); \
- l.descent_caches.push_back(line_descent); \
- l.space_caches.push_back(spaces); \
- } \
- line_wrapped = false; \
- y += line_height + get_theme_constant(SceneStringNames::get_singleton()->line_separation); \
- line_height = 0; \
- line_ascent = 0; \
- line_descent = 0; \
- spaces = 0; \
- spaces_size = 0; \
- wofs = begin; \
- align_ofs = 0; \
- if (p_mode != PROCESS_CACHE) { \
- lh = line < l.height_caches.size() ? l.height_caches[line] : 1; \
- line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; \
- line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; \
- if (align != ALIGN_FILL) { \
- if (line < l.offset_caches.size()) { \
- wofs = l.offset_caches[line]; \
- } \
- } \
- } \
- if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x < p_ofs.x + wofs) { \
- if (r_outside) \
- *r_outside = true; \
- *r_click_item = it; \
- *r_click_char = rchar; \
- RETURN; \
- } \
- }
-
-#define ENSURE_WIDTH(m_width) \
- if (p_mode == PROCESS_CACHE) { \
- l.maximum_width = MAX(l.maximum_width, MIN(p_width, wofs + m_width)); \
- l.minimum_width = MAX(l.minimum_width, m_width); \
- } \
- if (wofs - backtrack + m_width > p_width) { \
- line_wrapped = true; \
- if (p_mode == PROCESS_CACHE) { \
- if (spaces > 0) \
- spaces -= 1; \
- } \
- const bool x_in_range = (p_click_pos.x > p_ofs.x + wofs) && (!p_frame->cell || p_click_pos.x < p_ofs.x + p_width); \
- if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && x_in_range) { \
- if (r_outside) \
- *r_outside = true; \
- *r_click_item = it; \
- *r_click_char = rchar; \
- RETURN; \
- } \
- NEW_LINE \
- }
-
-#define ADVANCE(m_width) \
- { \
- if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x >= p_ofs.x + wofs && p_click_pos.x < p_ofs.x + wofs + m_width) { \
- if (r_outside) \
- *r_outside = false; \
- *r_click_item = it; \
- *r_click_char = rchar; \
- RETURN; \
- } \
- wofs += m_width; \
- }
-
-#define CHECK_HEIGHT(m_height) \
- if (m_height > line_height) { \
- line_height = m_height; \
- }
-
-#define YRANGE_VISIBLE(m_top, m_height) \
- (m_height > 0 && ((m_top >= 0 && m_top < height) || ((m_top + m_height - 1) >= 0 && (m_top + m_height - 1) < height)))
-
- Color selection_fg;
- Color selection_bg;
-
- if (p_mode == PROCESS_DRAW) {
- selection_fg = get_theme_color("font_color_selected");
- selection_bg = get_theme_color("selection_color");
- }
-
- int rchar = 0;
- int lh = 0;
- bool line_is_blank = true;
- bool line_wrapped = false;
- int fh = 0;
+ // Compute available width and total ratio (for expanders).
+ int total_ratio = 0;
+ int remaining_width = available_width;
+ table->total_width = hseparation;
- while (it) {
- switch (it->type) {
- case ITEM_ALIGN: {
- ItemAlign *align_it = static_cast<ItemAlign *>(it);
+ for (int i = 0; i < col_count; i++) {
+ remaining_width -= table->columns[i].min_width;
+ if (table->columns[i].max_width > table->columns[i].min_width) {
+ table->columns.write[i].expand = true;
+ }
+ if (table->columns[i].expand) {
+ total_ratio += table->columns[i].expand_ratio;
+ }
+ }
- align = align_it->align;
+ // Assign actual widths.
+ for (int i = 0; i < col_count; i++) {
+ table->columns.write[i].width = table->columns[i].min_width;
+ if (table->columns[i].expand && total_ratio > 0) {
+ table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
+ }
+ table->total_width += table->columns[i].width + hseparation;
+ }
- } break;
- case ITEM_INDENT: {
- if (it != l.from) {
- ItemIndent *indent_it = static_cast<ItemIndent *>(it);
-
- int indent = indent_it->level * tab_size * cfont->get_char_size(' ').width;
- margin += indent;
- begin += indent;
- wofs += indent;
+ // Resize to max_width if needed and distribute the remaining space.
+ bool table_need_fit = true;
+ while (table_need_fit) {
+ table_need_fit = false;
+ // Fit slim.
+ for (int i = 0; i < col_count; i++) {
+ if (!table->columns[i].expand) {
+ continue;
+ }
+ int dif = table->columns[i].width - table->columns[i].max_width;
+ if (dif > 0) {
+ table_need_fit = true;
+ table->columns.write[i].width = table->columns[i].max_width;
+ table->total_width -= dif;
+ total_ratio -= table->columns[i].expand_ratio;
+ }
+ }
+ // Grow.
+ remaining_width = available_width - table->total_width;
+ if (remaining_width > 0 && total_ratio > 0) {
+ for (int i = 0; i < col_count; i++) {
+ if (table->columns[i].expand) {
+ int dif = table->columns[i].max_width - table->columns[i].width;
+ if (dif > 0) {
+ int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
+ int incr = MIN(dif, slice);
+ table->columns.write[i].width += incr;
+ table->total_width += incr;
+ }
+ }
+ }
+ }
}
+ // Update line width and get total height.
+ idx = 0;
+ table->total_height = 0;
+ table->rows.clear();
+
+ Vector2 offset;
+ float row_height = 0;
+
+ for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
+ ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E->get());
+
+ int column = idx % col_count;
+
+ offset.x += frame->padding.position.x;
+ float yofs = frame->padding.position.y;
+ for (int i = 0; i < frame->lines.size(); i++) {
+ frame->lines.write[i].text_buf->set_width(table->columns[column].width);
+ table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
+
+ if (i > 0) {
+ frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y;
+ } else {
+ frame->lines.write[i].offset.y = 0;
+ }
+ frame->lines.write[i].offset += Vector2(offset.x, offset.y);
+
+ float h = frame->lines[i].text_buf->get_size().y;
+ if (frame->min_size_over.y > 0) {
+ h = MAX(h, frame->min_size_over.y);
+ }
+ if (frame->max_size_over.y > 0) {
+ h = MIN(h, frame->max_size_over.y);
+ }
+ yofs += h;
+ }
+ yofs += frame->padding.size.y;
+ offset.x += table->columns[column].width + hseparation + frame->padding.size.x;
+
+ row_height = MAX(yofs, row_height);
+ if (column == col_count - 1) {
+ offset.x = 0;
+ row_height += vseparation;
+ table->total_height += row_height;
+ offset.y += row_height;
+ table->rows.push_back(row_height);
+ row_height = 0;
+ }
+ idx++;
+ }
+ l.text_buf->resize_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align);
} break;
- case ITEM_TEXT: {
- ItemText *text = static_cast<ItemText *>(it);
+ default:
+ break;
+ }
+ }
+ if (p_line > 0) {
+ l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y;
+ } else {
+ l.offset.y = 0;
+ }
+}
+
+void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset) {
+ ERR_FAIL_COND(p_frame == nullptr);
+ ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
+
+ Line &l = p_frame->lines.write[p_line];
+
+ // Clear cache.
+ l.text_buf->clear();
+ l.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
+ l.char_offset = *r_char_offset;
+ l.char_count = 0;
+
+ // Add indent.
+ l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size);
+ l.text_buf->set_width(p_width - l.offset.x);
+ l.text_buf->set_align((HAlign)_find_align(l.from));
+ l.text_buf->set_direction(_find_direction(l.from));
+
+ if (tab_size > 0) { // Align inline tabs.
+ Vector<float> tabs;
+ tabs.push_back(tab_size * p_base_font->get_char_size('m', 0, p_base_font_size).width);
+ l.text_buf->tab_align(tabs);
+ }
+
+ // Shape current paragraph.
+ String text;
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ if (visible_characters >= 0 && l.char_offset + l.char_count > visible_characters) {
+ break;
+ }
+ switch (it->type) {
+ case ITEM_NEWLINE: {
Ref<Font> font = _find_font(it);
if (font.is_null()) {
font = p_base_font;
}
+ int font_size = _find_font_size(it);
+ if (font_size == -1) {
+ font_size = p_base_font_size;
+ }
+ l.text_buf->add_string("\n", font, font_size, Dictionary(), "");
+ text += "\n";
+ l.char_count += 1;
+ } break;
+ case ITEM_TEXT: {
+ ItemText *t = (ItemText *)it;
+ Ref<Font> font = _find_font(it);
+ if (font.is_null()) {
+ font = p_base_font;
+ }
+ int font_size = _find_font_size(it);
+ if (font_size == -1) {
+ font_size = p_base_font_size;
+ }
+ Dictionary font_ftr = _find_font_features(it);
+ String lang = _find_language(it);
+ String tx = t->text;
+ if (visible_characters >= 0 && l.char_offset + l.char_count + tx.length() > visible_characters) {
+ tx = tx.substr(0, l.char_offset + l.char_count + tx.length() - visible_characters);
+ }
- const char32_t *c = text->text.get_data();
- const char32_t *cf = c;
- int ascent = font->get_ascent();
- int descent = font->get_descent();
-
- Color color;
- Color font_color_shadow;
- bool underline = false;
- bool strikethrough = false;
- ItemFade *fade = nullptr;
- int it_char_start = p_char_count;
-
- Vector<ItemFX *> fx_stack = Vector<ItemFX *>();
- _fetch_item_fx_stack(text, fx_stack);
- bool custom_fx_ok = true;
-
- if (p_mode == PROCESS_DRAW) {
- color = _find_color(text, p_base_color);
- font_color_shadow = _find_color(text, p_font_color_shadow);
- if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) {
- underline = true;
- } else if (_find_strikethrough(text)) {
- strikethrough = true;
- }
+ l.text_buf->add_string(tx, font, font_size, font_ftr, lang);
+ text += tx;
+ l.char_count += tx.length();
+ } break;
+ case ITEM_IMAGE: {
+ ItemImage *img = (ItemImage *)it;
+ l.text_buf->add_object((uint64_t)it, img->image->get_size(), img->inline_align, 1);
+ text += String::chr(0xfffc);
+ } break;
+ case ITEM_TABLE: {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ int hseparation = get_theme_constant("table_hseparation");
+ int vseparation = get_theme_constant("table_vseparation");
+ int col_count = table->columns.size();
+ int t_char_count = 0;
+ // Set minimums to zero.
+ for (int i = 0; i < col_count; i++) {
+ table->columns.write[i].min_width = 0;
+ table->columns.write[i].max_width = 0;
+ table->columns.write[i].width = 0;
+ }
+ // Compute minimum width for each cell.
+ const int available_width = p_width - hseparation * (col_count - 1);
- Item *fade_item = it;
- while (fade_item) {
- if (fade_item->type == ITEM_FADE) {
- fade = static_cast<ItemFade *>(fade_item);
- break;
- }
- fade_item = fade_item->parent;
- }
+ int idx = 0;
+ for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
+ ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E->get());
- } else if (p_mode == PROCESS_CACHE) {
- l.char_count += text->text.length();
+ int column = idx % col_count;
+ for (int i = 0; i < frame->lines.size(); i++) {
+ int char_offset = l.char_offset + l.char_count;
+ _shape_line(frame, i, p_base_font, p_base_font_size, 1, &char_offset);
+ int cell_ch = (char_offset - (l.char_offset + l.char_count));
+ l.char_count += cell_ch;
+ t_char_count += cell_ch;
+
+ table->columns.write[column].min_width = MAX(table->columns[column].min_width, ceil(frame->lines[i].text_buf->get_size().x));
+ table->columns.write[column].max_width = MAX(table->columns[column].max_width, ceil(frame->lines[i].text_buf->get_non_wraped_size().x));
+ }
+ idx++;
}
- rchar = 0;
- FontDrawer drawer(font, Color(1, 1, 1));
- while (*c) {
- int end = 0;
- int w = 0;
- int fw = 0;
+ // Compute available width and total ratio (for expanders).
+ int total_ratio = 0;
+ int remaining_width = available_width;
+ table->total_width = hseparation;
- lh = 0;
+ for (int i = 0; i < col_count; i++) {
+ remaining_width -= table->columns[i].min_width;
+ if (table->columns[i].max_width > table->columns[i].min_width) {
+ table->columns.write[i].expand = true;
+ }
+ if (table->columns[i].expand) {
+ total_ratio += table->columns[i].expand_ratio;
+ }
+ }
- if (p_mode != PROCESS_CACHE) {
- lh = line < l.height_caches.size() ? l.height_caches[line] : 1;
- line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1;
- line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1;
+ // Assign actual widths.
+ for (int i = 0; i < col_count; i++) {
+ table->columns.write[i].width = table->columns[i].min_width;
+ if (table->columns[i].expand && total_ratio > 0) {
+ table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
}
- while (c[end] != 0 && !(end && c[end - 1] == ' ' && c[end] != ' ')) {
- int cw = font->get_char_size(c[end], c[end + 1]).width;
- if (c[end] == '\t') {
- cw = tab_size * font->get_char_size(' ').width;
- }
+ table->total_width += table->columns[i].width + hseparation;
+ }
- if (end > 0 && fw + cw + begin > p_width) {
- break; //don't allow lines longer than assigned width
+ // Resize to max_width if needed and distribute the remaining space.
+ bool table_need_fit = true;
+ while (table_need_fit) {
+ table_need_fit = false;
+ // Fit slim.
+ for (int i = 0; i < col_count; i++) {
+ if (!table->columns[i].expand) {
+ continue;
+ }
+ int dif = table->columns[i].width - table->columns[i].max_width;
+ if (dif > 0) {
+ table_need_fit = true;
+ table->columns.write[i].width = table->columns[i].max_width;
+ table->total_width -= dif;
+ total_ratio -= table->columns[i].expand_ratio;
}
-
- fw += cw;
-
- end++;
}
- CHECK_HEIGHT(fh);
- ENSURE_WIDTH(fw);
-
- line_ascent = MAX(line_ascent, ascent);
- line_descent = MAX(line_descent, descent);
- fh = line_ascent + line_descent;
-
- if (end && c[end - 1] == ' ') {
- if (p_mode == PROCESS_CACHE) {
- spaces_size += font->get_char_size(' ').width;
- } else if (align == ALIGN_FILL) {
- int ln = MIN(l.offset_caches.size() - 1, line);
- if (l.space_caches[ln]) {
- align_ofs = spaces * l.offset_caches[ln] / l.space_caches[ln];
+ // Grow.
+ remaining_width = available_width - table->total_width;
+ if (remaining_width > 0 && total_ratio > 0) {
+ for (int i = 0; i < col_count; i++) {
+ if (table->columns[i].expand) {
+ int dif = table->columns[i].max_width - table->columns[i].width;
+ if (dif > 0) {
+ int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
+ int incr = MIN(dif, slice);
+ table->columns.write[i].width += incr;
+ table->total_width += incr;
+ }
}
}
- spaces++;
}
+ }
- {
- int ofs = 0 - backtrack;
+ // Update line width and get total height.
+ idx = 0;
+ table->total_height = 0;
+ table->rows.clear();
- for (int i = 0; i < end; i++) {
- int pofs = wofs + ofs;
+ Vector2 offset;
+ float row_height = 0;
- if (p_mode == PROCESS_POINTER && r_click_char && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) {
- int cw = font->get_char_size(c[i], c[i + 1]).x;
+ for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
+ ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E->get());
- if (c[i] == '\t') {
- cw = tab_size * font->get_char_size(' ').width;
- }
+ int column = idx % col_count;
- if (p_click_pos.x - cw / 2 > p_ofs.x + align_ofs + pofs) {
- rchar = int((&c[i]) - cf);
- }
+ offset.x += frame->padding.position.x;
+ float yofs = frame->padding.position.y;
+ for (int i = 0; i < frame->lines.size(); i++) {
+ frame->lines.write[i].text_buf->set_width(table->columns[column].width);
+ table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
- ofs += cw;
- } else if (p_mode == PROCESS_DRAW) {
- bool selected = false;
- Color fx_color = Color(color);
- Point2 fx_offset;
- char32_t fx_char = c[i];
-
- if (selection.active) {
- int cofs = (&c[i]) - cf;
- if ((text->index > selection.from->index || (text->index == selection.from->index && cofs >= selection.from_char)) && (text->index < selection.to->index || (text->index == selection.to->index && cofs <= selection.to_char))) {
- selected = true;
- }
- }
+ if (i > 0) {
+ frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y;
+ } else {
+ frame->lines.write[i].offset.y = 0;
+ }
+ frame->lines.write[i].offset += Vector2(offset.x, offset.y);
- int cw = 0;
- int c_item_offset = p_char_count - it_char_start;
+ float h = frame->lines[i].text_buf->get_size().y;
+ if (frame->min_size_over.y > 0) {
+ h = MAX(h, frame->min_size_over.y);
+ }
+ if (frame->max_size_over.y > 0) {
+ h = MIN(h, frame->max_size_over.y);
+ }
+ yofs += h;
+ }
+ yofs += frame->padding.size.y;
+ offset.x += table->columns[column].width + hseparation + frame->padding.size.x;
- float faded_visibility = 1.0f;
- if (fade) {
- if (c_item_offset >= fade->starting_index) {
- faded_visibility -= (float)(c_item_offset - fade->starting_index) / (float)fade->length;
- faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
- }
- fx_color.a = faded_visibility;
- }
+ row_height = MAX(yofs, row_height);
+ if (column == col_count - 1) {
+ offset.x = 0;
+ row_height += vseparation;
+ table->total_height += row_height;
+ offset.y += row_height;
+ table->rows.push_back(row_height);
+ row_height = 0;
+ }
+ idx++;
+ }
- bool visible = visible_characters < 0 || ((p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)) &&
- faded_visibility > 0.0f);
+ l.text_buf->add_object((uint64_t)it, Size2(table->total_width, table->total_height), table->inline_align, t_char_count);
+ text += String::chr(0xfffc).repeat(t_char_count);
+ } break;
+ default:
+ break;
+ }
+ }
- const bool previously_visible = visible;
+ //Apply BiDi override.
+ l.text_buf->set_bidi_override(structured_text_parser(_find_stt(l.from), st_args, text));
- for (int j = 0; j < fx_stack.size(); j++) {
- ItemFX *item_fx = fx_stack[j];
+ *r_char_offset = l.char_offset + l.char_count;
- if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
- ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
+ if (p_line > 0) {
+ l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y;
+ } else {
+ l.offset.y = 0;
+ }
+}
- Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
- Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
+float RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &p_shadow_ofs) {
+ Vector2 off;
- if (!custom_effect.is_null()) {
- charfx->elapsed_time = item_custom->elapsed_time;
- charfx->relative_index = c_item_offset;
- charfx->absolute_index = p_char_count;
- charfx->visibility = visible;
- charfx->offset = fx_offset;
- charfx->color = fx_color;
- charfx->character = fx_char;
+ ERR_FAIL_COND_V(p_frame == nullptr, off.y);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), off.y);
- bool effect_status = custom_effect->_process_effect_impl(charfx);
- custom_fx_ok = effect_status;
+ Line &l = p_frame->lines.write[p_line];
- fx_offset += charfx->offset;
- fx_color = charfx->color;
- visible &= charfx->visibility;
- fx_char = charfx->character;
- }
- } else if (item_fx->type == ITEM_SHAKE) {
- ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
-
- uint64_t char_current_rand = item_shake->offset_random(c_item_offset);
- uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset);
- uint64_t max_rand = 2147483647;
- double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
- double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
- double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
- n_time = (n_time > 1.0) ? 1.0 : n_time;
- fx_offset += Point2(Math::lerp(Math::sin(previous_offset),
- Math::sin(current_offset),
- n_time),
- Math::lerp(Math::cos(previous_offset),
- Math::cos(current_offset),
- n_time)) *
- (float)item_shake->strength / 10.0f;
- } else if (item_fx->type == ITEM_WAVE) {
- ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
-
- double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f);
- fx_offset += Point2(0, 1) * value;
- } else if (item_fx->type == ITEM_TORNADO) {
- ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
-
- double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius);
- double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius);
- fx_offset += Point2(torn_x, torn_y);
- } else if (item_fx->type == ITEM_RAINBOW) {
- ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
-
- fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)),
- item_rainbow->saturation,
- item_rainbow->value,
- fx_color.a);
- }
- }
+ Item *it_from = l.from;
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ Variant meta;
- if (visible) {
- line_is_blank = false;
- w += font->get_char_size(c[i], c[i + 1]).x;
- }
+ if (it_from == nullptr) {
+ return off.y;
+ }
- if (c[i] == '\t') {
- visible = false;
- }
+ RID ci = get_canvas_item();
+ bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL);
+ bool lrtl = is_layout_rtl();
- if (visible) {
- if (selected) {
- cw = font->get_char_size(fx_char, c[i + 1]).x;
- draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
- }
+ Vector<int> list_index;
+ Vector<ItemList *> list_items;
+ _find_list(l.from, list_index, list_items);
- if (p_font_color_shadow.a > 0) {
- float x_ofs_shadow = align_ofs + pofs;
- float y_ofs_shadow = y + lh - line_descent;
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs + fx_offset, fx_char, c[i + 1], p_font_color_shadow);
+ String prefix;
+ for (int i = 0; i < list_index.size(); i++) {
+ if (rtl) {
+ prefix = prefix + ".";
+ } else {
+ prefix = "." + prefix;
+ }
+ String segment;
+ if (list_items[i]->list_type == LIST_DOTS) {
+ static const char32_t _prefix[2] = { 0x25CF, 0 };
+ prefix = _prefix;
+ break;
+ } else if (list_items[i]->list_type == LIST_NUMBERS) {
+ segment = TS->format_number(itos(list_index[i]), _find_language(l.from));
+ } else if (list_items[i]->list_type == LIST_LETTERS) {
+ segment = _letters(list_index[i], list_items[i]->capitalize);
+ } else if (list_items[i]->list_type == LIST_ROMAN) {
+ segment = _roman(list_index[i], list_items[i]->capitalize);
+ }
+ if (rtl) {
+ prefix = prefix + segment;
+ } else {
+ prefix = segment + prefix;
+ }
+ }
+ if (prefix != "") {
+ Ref<Font> font = _find_font(l.from);
+ if (font.is_null()) {
+ font = get_theme_font("normal_font");
+ }
+ int font_size = _find_font_size(l.from);
+ if (font_size == -1) {
+ font_size = get_theme_font_size("normal_font_size");
+ }
+ if (rtl) {
+ float offx = 0.0f;
+ if (!lrtl && p_frame == main) { // Skip Scrollbar.
+ offx -= scroll_w;
+ }
+ font->draw_string(ci, p_ofs + Vector2(p_width - l.offset.x + offx, l.text_buf->get_line_ascent(0)), " " + prefix, HALIGN_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color));
+ } else {
+ float offx = 0.0f;
+ if (lrtl && p_frame == main) { // Skip Scrollbar.
+ offx += scroll_w;
+ }
+ font->draw_string(ci, p_ofs + Vector2(offx, l.text_buf->get_line_ascent(0)), prefix + " ", HALIGN_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color));
+ }
+ }
- if (p_shadow_as_outline) {
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], p_font_color_shadow);
- }
- }
+ // Draw text.
+ for (int line = 0; line < l.text_buf->get_line_count(); line++) {
+ RID rid = l.text_buf->get_line_rid(line);
- if (selected) {
- drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], override_selected_font_color ? selection_fg : fx_color);
- } else {
- cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], fx_color);
- }
- } else if (previously_visible && c[i] != '\t') {
- backtrack += font->get_char_size(fx_char, c[i + 1]).x;
- }
+ float width = l.text_buf->get_width();
+ float length = TS->shaped_text_get_width(rid);
+
+ // Draw line.
+
+ if (rtl) {
+ off.x = p_width - l.offset.x - width;
+ if (!lrtl && p_frame == main) { // Skip Scrollbar.
+ off.x -= scroll_w;
+ }
+ } else {
+ off.x = l.offset.x;
+ if (lrtl && p_frame == main) { // Skip Scrollbar.
+ off.x += scroll_w;
+ }
+ }
- p_char_count++;
- if (c[i] == '\t') {
- cw = tab_size * font->get_char_size(' ').width;
- backtrack = MAX(0, backtrack - cw);
+ // Draw text.
+ switch (l.text_buf->get_align()) {
+ case HALIGN_FILL:
+ case HALIGN_LEFT: {
+ if (rtl) {
+ off.x += width - length;
+ }
+ } break;
+ case HALIGN_CENTER: {
+ off.x += Math::floor((width - length) / 2.0);
+ } break;
+ case HALIGN_RIGHT: {
+ if (!rtl) {
+ off.x += width - length;
+ }
+ } break;
+ }
+
+ //draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS
+
+ off.y += TS->shaped_text_get_ascent(rid);
+ // Draw inlined objects.
+ Array objects = TS->shaped_text_get_objects(rid);
+ for (int i = 0; i < objects.size(); i++) {
+ Item *it = (Item *)(uint64_t)objects[i];
+ if (it != nullptr) {
+ Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
+ //draw_rect(rect, Color(1,0,0), false, 2); //DEBUG_RECTS
+ switch (it->type) {
+ case ITEM_IMAGE: {
+ ItemImage *img = static_cast<ItemImage *>(it);
+ img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off, rect.size), false, img->color);
+ } break;
+ case ITEM_TABLE: {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ Color odd_row_bg = get_theme_color("table_odd_row_bg");
+ Color even_row_bg = get_theme_color("table_even_row_bg");
+ Color border = get_theme_color("table_border");
+ int col_count = table->columns.size();
+ int row_count = table->rows.size();
+
+ int idx = 0;
+ for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
+ ItemFrame *frame = static_cast<ItemFrame *>(E->get());
+
+ int col = idx % col_count;
+ int row = idx / col_count;
+
+ if (frame->lines.size() != 0 && row < row_count) {
+ Vector2 coff = frame->lines[0].offset;
+ if (rtl) {
+ coff.x = rect.size.width - table->columns[col].width - coff.x;
+ }
+ if (row % 2 == 0) {
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width, table->rows[row])), (frame->odd_row_bg != Color(0, 0, 0, 0) ? frame->odd_row_bg : odd_row_bg), true);
+ } else {
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width, table->rows[row])), (frame->even_row_bg != Color(0, 0, 0, 0) ? frame->even_row_bg : even_row_bg), true);
}
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width, table->rows[row])), (frame->border != Color(0, 0, 0, 0) ? frame->border : border), false);
+ }
- ofs += cw;
+ for (int j = 0; j < frame->lines.size(); j++) {
+ _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_color_shadow, p_shadow_as_outline, p_shadow_ofs);
}
+ idx++;
}
+ } break;
+ default:
+ break;
+ }
+ }
+ }
- if (underline) {
- Color uc = color;
- uc.a *= 0.5;
- int uy = y + lh - line_descent + font->get_underline_position();
- float underline_width = font->get_underline_thickness();
-#ifdef TOOLS_ENABLED
- underline_width *= EDSCALE;
-#endif
- RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, underline_width);
- } else if (strikethrough) {
- Color uc = color;
- uc.a *= 0.5;
- int uy = y + lh - (line_ascent + line_descent) / 2;
- float strikethrough_width = font->get_underline_thickness();
-#ifdef TOOLS_ENABLED
- strikethrough_width *= EDSCALE;
-#endif
- RS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, strikethrough_width);
- }
- }
+ const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid);
+ const TextServer::Glyph *glyphs = visual.ptr();
+ int gl_size = visual.size();
- ADVANCE(fw);
- CHECK_HEIGHT(fh); //must be done somewhere
- c = &c[end];
- }
+ Vector2 gloff = off;
+ // Draw oulines and shadow.
+ for (int i = 0; i < gl_size; i++) {
+ Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
+ int size = _find_outline_size(it);
+ Color font_color = _find_outline_color(it, Color(0, 0, 0, 0));
+ if (size <= 0) {
+ gloff.x += glyphs[i].advance;
+ continue;
+ }
- } break;
- case ITEM_IMAGE: {
- lh = 0;
- if (p_mode != PROCESS_CACHE) {
- lh = line < l.height_caches.size() ? l.height_caches[line] : 1;
- } else {
- l.char_count += 1; //images count as chars too
+ // Get FX.
+ ItemFade *fade = nullptr;
+ Item *fade_item = it;
+ while (fade_item) {
+ if (fade_item->type == ITEM_FADE) {
+ fade = static_cast<ItemFade *>(fade_item);
+ break;
}
+ fade_item = fade_item->parent;
+ }
- ItemImage *img = static_cast<ItemImage *>(it);
+ Vector<ItemFX *> fx_stack;
+ _fetch_item_fx_stack(it, fx_stack);
+ bool custom_fx_ok = true;
- Ref<Font> font = _find_font(it);
- if (font.is_null()) {
- font = p_base_font;
+ Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
+ RID frid = glyphs[i].font_rid;
+ uint32_t gl = glyphs[i].index;
+
+ //Apply fx.
+ float faded_visibility = 1.0f;
+ if (fade) {
+ if (glyphs[i].start >= fade->starting_index) {
+ faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
+ faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
}
+ font_color.a = faded_visibility;
+ }
- if (p_mode == PROCESS_POINTER && r_click_char) {
- *r_click_char = 0;
+ bool visible = (font_color.a != 0);
+
+ for (int j = 0; j < fx_stack.size(); j++) {
+ ItemFX *item_fx = fx_stack[j];
+ if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
+ ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
+
+ Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
+ Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
+
+ if (!custom_effect.is_null()) {
+ charfx->elapsed_time = item_custom->elapsed_time;
+ charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
+ charfx->visibility = visible;
+ charfx->outline = true;
+ charfx->font = frid;
+ charfx->glpyh_index = gl;
+ charfx->offset = fx_offset;
+ charfx->color = font_color;
+
+ bool effect_status = custom_effect->_process_effect_impl(charfx);
+ custom_fx_ok = effect_status;
+
+ fx_offset += charfx->offset;
+ font_color = charfx->color;
+ frid = charfx->font;
+ gl = charfx->glpyh_index;
+ visible &= charfx->visibility;
+ }
+ } else if (item_fx->type == ITEM_SHAKE) {
+ ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
+
+ uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
+ uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
+ uint64_t max_rand = 2147483647;
+ double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
+ double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
+ double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
+ n_time = (n_time > 1.0) ? 1.0 : n_time;
+ fx_offset += Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
+ } else if (item_fx->type == ITEM_WAVE) {
+ ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
+
+ double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f);
+ fx_offset += Point2(0, 1) * value;
+ } else if (item_fx->type == ITEM_TORNADO) {
+ ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
+
+ double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
+ double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
+ fx_offset += Point2(torn_x, torn_y);
+ } else if (item_fx->type == ITEM_RAINBOW) {
+ ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
+
+ font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + gloff.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
}
+ }
- ENSURE_WIDTH(img->size.width);
+ Point2 shadow_ofs(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y"));
- bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->size.height, img->size.height));
+ // Draw glyph outlines.
+ for (int j = 0; j < glyphs[i].repeat; j++) {
if (visible) {
- line_is_blank = false;
- }
-
- if (p_mode == PROCESS_DRAW && visible) {
- img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size), false, img->color);
+ if (frid != RID()) {
+ if (p_shadow_as_outline) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, shadow_ofs.y), gl, p_font_color_shadow);
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(shadow_ofs.x, -shadow_ofs.y), gl, p_font_color_shadow);
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, -shadow_ofs.y), gl, p_font_color_shadow);
+ }
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
+ }
}
- p_char_count++;
-
- ADVANCE(img->size.width);
- CHECK_HEIGHT((img->size.height + font->get_descent()));
+ gloff.x += glyphs[i].advance;
+ }
+ }
- } break;
- case ITEM_NEWLINE: {
- lh = 0;
+ // Draw main text.
+ Color selection_fg = get_theme_color("font_color_selected");
+ Color selection_bg = get_theme_color("selection_color");
- if (p_mode != PROCESS_CACHE) {
- lh = line < l.height_caches.size() ? l.height_caches[line] : 1;
- line_is_blank = true;
- }
+ int sel_start = -1;
+ int sel_end = -1;
- } break;
- case ITEM_TABLE: {
- lh = 0;
- ItemTable *table = static_cast<ItemTable *>(it);
- int hseparation = get_theme_constant("table_hseparation");
- int vseparation = get_theme_constant("table_vseparation");
- Color ccolor = _find_color(table, p_base_color);
- Vector2 draw_ofs = Point2(wofs, y);
- Color font_color_shadow = get_theme_color("font_color_shadow");
- bool use_outline = get_theme_constant("shadow_as_outline");
- Point2 shadow_ofs2(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y"));
-
- if (p_mode == PROCESS_CACHE) {
- int idx = 0;
- //set minimums to zero
- for (int i = 0; i < table->columns.size(); i++) {
- table->columns.write[i].min_width = 0;
- table->columns.write[i].max_width = 0;
- table->columns.write[i].width = 0;
- }
- //compute minimum width for each cell
- const int available_width = p_width - hseparation * (table->columns.size() - 1) - wofs;
+ if (selection.active && (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) <= (l.char_offset + TS->shaped_text_get_range(rid).y) && (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) >= (l.char_offset + TS->shaped_text_get_range(rid).x)) {
+ sel_start = MAX(TS->shaped_text_get_range(rid).x, (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) - l.char_offset);
+ sel_end = MIN(TS->shaped_text_get_range(rid).y, (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) - l.char_offset);
- for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
- ERR_CONTINUE(E->get()->type != ITEM_FRAME); //children should all be frames
- ItemFrame *frame = static_cast<ItemFrame *>(E->get());
+ Vector<Vector2> sel = TS->shaped_text_get_selection(rid, sel_start, sel_end);
+ for (int i = 0; i < sel.size(); i++) {
+ Rect2 rect = Rect2(sel[i].x + p_ofs.x + off.x, p_ofs.y + off.y - TS->shaped_text_get_ascent(rid), sel[i].y - sel[i].x, TS->shaped_text_get_size(rid).y);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_bg);
+ }
+ }
- int column = idx % table->columns.size();
+ for (int i = 0; i < gl_size; i++) {
+ bool selected = selection.active && (sel_start != -1) && (glyphs[i].start >= sel_start) && (glyphs[i].end <= sel_end);
+ Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
+ Color font_color = _find_color(it, p_base_color);
+ if (_find_underline(it) || (_find_meta(it, &meta) && underline_meta)) {
+ Color uc = font_color;
+ uc.a *= 0.5;
+ float y_off = TS->shaped_text_get_underline_position(rid);
+ float underline_width = TS->shaped_text_get_underline_thickness(rid);
+#ifdef TOOLS_ENABLED
+ underline_width *= EDSCALE;
+#endif
+ draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width);
+ } else if (_find_strikethrough(it)) {
+ Color uc = font_color;
+ uc.a *= 0.5;
+ float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
+ float underline_width = TS->shaped_text_get_underline_thickness(rid);
+#ifdef TOOLS_ENABLED
+ underline_width *= EDSCALE;
+#endif
+ draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width);
+ }
- int ly = 0;
+ // Get FX.
+ ItemFade *fade = nullptr;
+ Item *fade_item = it;
+ while (fade_item) {
+ if (fade_item->type == ITEM_FADE) {
+ fade = static_cast<ItemFade *>(fade_item);
+ break;
+ }
+ fade_item = fade_item->parent;
+ }
- for (int i = 0; i < frame->lines.size(); i++) {
- _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2);
- table->columns.write[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width);
- table->columns.write[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width);
- }
- idx++;
- }
+ Vector<ItemFX *> fx_stack;
+ _fetch_item_fx_stack(it, fx_stack);
+ bool custom_fx_ok = true;
- //compute available width and total ratio (for expanders)
+ Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
+ RID frid = glyphs[i].font_rid;
+ uint32_t gl = glyphs[i].index;
- int total_ratio = 0;
- int remaining_width = available_width;
- table->total_width = hseparation;
+ //Apply fx.
+ float faded_visibility = 1.0f;
+ if (fade) {
+ if (glyphs[i].start >= fade->starting_index) {
+ faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
+ faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
+ }
+ font_color.a = faded_visibility;
+ }
- for (int i = 0; i < table->columns.size(); i++) {
- remaining_width -= table->columns[i].min_width;
- if (table->columns[i].max_width > table->columns[i].min_width) {
- table->columns.write[i].expand = true;
- }
- if (table->columns[i].expand) {
- total_ratio += table->columns[i].expand_ratio;
- }
+ bool visible = (font_color.a != 0);
+
+ for (int j = 0; j < fx_stack.size(); j++) {
+ ItemFX *item_fx = fx_stack[j];
+ if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
+ ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
+
+ Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
+ Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
+
+ if (!custom_effect.is_null()) {
+ charfx->elapsed_time = item_custom->elapsed_time;
+ charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
+ charfx->visibility = visible;
+ charfx->outline = false;
+ charfx->font = frid;
+ charfx->glpyh_index = gl;
+ charfx->offset = fx_offset;
+ charfx->color = font_color;
+
+ bool effect_status = custom_effect->_process_effect_impl(charfx);
+ custom_fx_ok = effect_status;
+
+ fx_offset += charfx->offset;
+ font_color = charfx->color;
+ frid = charfx->font;
+ gl = charfx->glpyh_index;
+ visible &= charfx->visibility;
}
+ } else if (item_fx->type == ITEM_SHAKE) {
+ ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
+
+ uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
+ uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
+ uint64_t max_rand = 2147483647;
+ double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
+ double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
+ double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
+ n_time = (n_time > 1.0) ? 1.0 : n_time;
+ fx_offset += Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
+ } else if (item_fx->type == ITEM_WAVE) {
+ ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
+
+ double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f);
+ fx_offset += Point2(0, 1) * value;
+ } else if (item_fx->type == ITEM_TORNADO) {
+ ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
+
+ double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
+ double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
+ fx_offset += Point2(torn_x, torn_y);
+ } else if (item_fx->type == ITEM_RAINBOW) {
+ ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
+
+ font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + off.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
+ }
+ }
- //assign actual widths
- for (int i = 0; i < table->columns.size(); i++) {
- table->columns.write[i].width = table->columns[i].min_width;
- if (table->columns[i].expand && total_ratio > 0) {
- table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
- }
- table->total_width += table->columns[i].width + hseparation;
- }
+ if (selected) {
+ font_color = override_selected_font_color ? selection_fg : font_color;
+ }
- //resize to max_width if needed and distribute the remaining space
- bool table_need_fit = true;
- while (table_need_fit) {
- table_need_fit = false;
- //fit slim
- for (int i = 0; i < table->columns.size(); i++) {
- if (!table->columns[i].expand) {
- continue;
- }
- int dif = table->columns[i].width - table->columns[i].max_width;
- if (dif > 0) {
- table_need_fit = true;
- table->columns.write[i].width = table->columns[i].max_width;
- table->total_width -= dif;
- total_ratio -= table->columns[i].expand_ratio;
- }
- }
- //grow
- remaining_width = available_width - table->total_width;
- if (remaining_width > 0 && total_ratio > 0) {
- for (int i = 0; i < table->columns.size(); i++) {
- if (table->columns[i].expand) {
- int dif = table->columns[i].max_width - table->columns[i].width;
- if (dif > 0) {
- int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
- int incr = MIN(dif, slice);
- table->columns.write[i].width += incr;
- table->total_width += incr;
- }
- }
- }
- }
+ // Draw glyphs.
+ for (int j = 0; j < glyphs[i].repeat; j++) {
+ if (visible) {
+ if (frid != RID()) {
+ TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
+ } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ TS->draw_hex_code_box(ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
}
+ }
+ off.x += glyphs[i].advance;
+ }
+ }
+ off.y += TS->shaped_text_get_descent(rid);
+ }
- //compute caches properly again with the right width
- idx = 0;
- for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
- ERR_CONTINUE(E->get()->type != ITEM_FRAME); //children should all be frames
- ItemFrame *frame = static_cast<ItemFrame *>(E->get());
+ return off.y;
+}
- int column = idx % table->columns.size();
+void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool *r_outside) {
+ if (r_click_item) {
+ *r_click_item = nullptr;
+ }
+ if (r_click_char != nullptr) {
+ *r_click_char = 0;
+ }
+ if (r_outside != nullptr) {
+ *r_outside = true;
+ }
- for (int i = 0; i < frame->lines.size(); i++) {
- int ly = 0;
- _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs2);
- frame->lines.write[i].height_cache = ly; //actual height
- frame->lines.write[i].height_accum_cache = ly; //actual height
- }
- idx++;
- }
- }
+ Size2 size = get_size();
+ Rect2 text_rect = _get_text_rect();
- Point2 offset(align_ofs + hseparation, vseparation);
+ int vofs = vscroll->get_value();
- int row_height = 0;
- //draw using computed caches
- int idx = 0;
- for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
- ERR_CONTINUE(E->get()->type != ITEM_FRAME); //children should all be frames
- ItemFrame *frame = static_cast<ItemFrame *>(E->get());
+ // Search for the first line.
+ int from_line = 0;
- int column = idx % table->columns.size();
+ //TODO, change to binary search ?
+ while (from_line < main->lines.size()) {
+ if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + _get_text_rect().get_position().y >= vofs) {
+ break;
+ }
+ from_line++;
+ }
- int ly = 0;
- int yofs = 0;
+ if (from_line >= main->lines.size()) {
+ return;
+ }
+
+ Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
+ while (ofs.y < size.height && from_line < main->lines.size()) {
+ ofs.y += _find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char);
+ if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) {
+ if (r_outside != nullptr) {
+ *r_outside = false;
+ }
+ return;
+ }
+ from_line++;
+ }
+}
- int lines_h = frame->lines[frame->lines.size() - 1].height_accum_cache - (frame->lines[0].height_accum_cache - frame->lines[0].height_cache);
- int lines_ofs = p_ofs.y + offset.y + draw_ofs.y;
+float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char) {
+ Vector2 off;
- bool visible = lines_ofs < get_size().height && lines_ofs + lines_h >= 0;
- if (visible) {
- line_is_blank = false;
- }
+ int char_pos = -1;
+ Line &l = p_frame->lines.write[p_line];
+ bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL);
+ bool lrtl = is_layout_rtl();
+ bool table_hit = false;
- for (int i = 0; i < frame->lines.size(); i++) {
- if (visible) {
- if (p_mode == PROCESS_DRAW) {
- nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2);
- } else if (p_mode == PROCESS_POINTER) {
- _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs2, p_click_pos, r_click_item, r_click_char, r_outside);
- if (r_click_item && *r_click_item) {
- RETURN; // exit early
- }
- }
- }
+ for (int line = 0; line < l.text_buf->get_line_count(); line++) {
+ RID rid = l.text_buf->get_line_rid(line);
- yofs += frame->lines[i].height_cache;
- if (p_mode == PROCESS_CACHE) {
- frame->lines.write[i].height_accum_cache = offset.y + draw_ofs.y + frame->lines[i].height_cache;
- }
- }
+ float width = l.text_buf->get_width();
+ float length = TS->shaped_text_get_width(rid);
- row_height = MAX(yofs, row_height);
- offset.x += table->columns[column].width + hseparation;
+ if (rtl) {
+ off.x = p_width - l.offset.x - width;
+ if (!lrtl && p_frame == main) { // Skip Scrollbar.
+ off.x -= scroll_w;
+ }
+ } else {
+ off.x = l.offset.x;
+ if (lrtl && p_frame == main) { // Skip Scrollbar.
+ off.x += scroll_w;
+ }
+ }
- if (column == table->columns.size() - 1) {
- offset.y += row_height + vseparation;
- offset.x = hseparation;
- row_height = 0;
- }
- idx++;
+ switch (l.text_buf->get_align()) {
+ case HALIGN_FILL:
+ case HALIGN_LEFT: {
+ if (rtl) {
+ off.x += width - length;
}
-
- int total_height = offset.y;
- if (row_height) {
- total_height = row_height + vseparation;
+ } break;
+ case HALIGN_CENTER: {
+ off.x += Math::floor((width - length) / 2.0);
+ } break;
+ case HALIGN_RIGHT: {
+ if (!rtl) {
+ off.x += width - length;
}
+ } break;
+ }
- ADVANCE(table->total_width);
- CHECK_HEIGHT(total_height);
+ off.y += TS->shaped_text_get_ascent(rid);
- } break;
+ Array objects = TS->shaped_text_get_objects(rid);
+ for (int i = 0; i < objects.size(); i++) {
+ Item *it = (Item *)(uint64_t)objects[i];
+ if (it != nullptr) {
+ Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
+ if (rect.has_point(p_click - p_ofs - off)) {
+ switch (it->type) {
+ case ITEM_TABLE: {
+ int hseparation = get_theme_constant("table_hseparation");
+ int vseparation = get_theme_constant("table_vseparation");
- default: {
- }
- }
+ ItemTable *table = static_cast<ItemTable *>(it);
+
+ table_hit = true;
- Item *itp = it;
+ int idx = 0;
+ int col_count = table->columns.size();
+ int row_count = table->rows.size();
- it = _get_next_item(it);
+ for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
+ ItemFrame *frame = static_cast<ItemFrame *>(E->get());
- if (it && (p_line + 1 < p_frame->lines.size()) && p_frame->lines[p_line + 1].from == it) {
- if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh) {
- //went to next line, but pointer was on the previous one
- if (r_outside) {
- *r_outside = true;
+ int col = idx % col_count;
+ int row = idx / col_count;
+
+ if (frame->lines.size() != 0 && row < row_count) {
+ Vector2 coff = frame->lines[0].offset;
+ if (rtl) {
+ coff.x = rect.size.width - table->columns[col].width - coff.x;
+ }
+ Rect2 crect = Rect2(p_ofs + off + rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size);
+ if (col == col_count - 1) {
+ if (rtl) {
+ crect.size.x = crect.position.x + crect.size.x;
+ crect.position.x = 0;
+ } else {
+ crect.size.x = get_size().x;
+ }
+ }
+ if (crect.has_point(p_click)) {
+ for (int j = 0; j < frame->lines.size(); j++) {
+ _find_click_in_line(frame, j, p_ofs + off + rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char);
+ if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) {
+ return off.y;
+ }
+ }
+ }
+ }
+ idx++;
+ }
+ } break;
+ default:
+ break;
+ }
}
- *r_click_item = itp;
- *r_click_char = rchar;
- RETURN;
}
+ }
+ Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)), Size2(get_size().x, TS->shaped_text_get_size(rid).y));
- break;
+ if (rect.has_point(p_click) && !table_hit) {
+ char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
}
+ off.y += TS->shaped_text_get_descent(rid);
}
- NEW_LINE;
- RETURN;
+ if (char_pos >= 0) {
+ // Find item.
+ if (r_click_item != nullptr) {
+ Item *it = p_frame->lines[p_line].from;
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ it = _get_item_at_pos(it, it_to, char_pos);
+ *r_click_item = it;
+ }
+
+ if (r_click_frame != nullptr) {
+ *r_click_frame = p_frame;
+ }
-#undef RETURN
-#undef NEW_LINE
-#undef ENSURE_WIDTH
-#undef ADVANCE
-#undef CHECK_HEIGHT
+ if (r_click_line != nullptr) {
+ *r_click_line = p_line;
+ }
+
+ if (r_click_char != nullptr) {
+ *r_click_char = char_pos;
+ }
+ }
+
+ return off.y;
}
void RichTextLabel::_scroll_changed(double) {
@@ -910,7 +1257,7 @@ void RichTextLabel::_update_scroll() {
vscroll->hide();
}
- main->first_invalid_line = 0; //invalidate ALL
+ main->first_resized_line = 0; //invalidate ALL
_validate_line_caches(main);
}
}
@@ -960,10 +1307,11 @@ void RichTextLabel::_notification(int p_what) {
}
} break;
case NOTIFICATION_RESIZED: {
- main->first_invalid_line = 0; //invalidate ALL
+ main->first_resized_line = 0; //invalidate ALL
update();
} break;
+ case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_ENTER_TREE: {
if (bbcode != "") {
set_bbcode(bbcode);
@@ -971,11 +1319,11 @@ void RichTextLabel::_notification(int p_what) {
main->first_invalid_line = 0; //invalidate ALL
update();
-
} break;
- case NOTIFICATION_THEME_CHANGED: {
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ main->first_invalid_line = 0; //invalidate ALL
update();
-
} break;
case NOTIFICATION_DRAW: {
_validate_line_caches(main);
@@ -994,36 +1342,37 @@ void RichTextLabel::_notification(int p_what) {
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
}
- int ofs = vscroll->get_value();
-
- //todo, change to binary search
+ float vofs = vscroll->get_value();
+ // Search for the first line.
int from_line = 0;
- int total_chars = 0;
+
+ //TODO, change to binary search ?
while (from_line < main->lines.size()) {
- if (main->lines[from_line].height_accum_cache + _get_text_rect().get_position().y >= ofs) {
+ if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + _get_text_rect().get_position().y >= vofs) {
break;
}
- total_chars += main->lines[from_line].char_count;
from_line++;
}
if (from_line >= main->lines.size()) {
break; //nothing to draw
}
- int y = (main->lines[from_line].height_accum_cache - main->lines[from_line].height_cache) - ofs;
Ref<Font> base_font = get_theme_font("normal_font");
Color base_color = get_theme_color("default_color");
+ Color outline_color = get_theme_color("outline_color");
+ int outline_size = get_theme_constant("outline_size");
Color font_color_shadow = get_theme_color("font_color_shadow");
bool use_outline = get_theme_constant("shadow_as_outline");
Point2 shadow_ofs(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y"));
visible_line_count = 0;
- while (y < size.height && from_line < main->lines.size()) {
- visible_line_count++;
- _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), nullptr, nullptr, nullptr, total_chars);
- total_chars += main->lines[from_line].char_count;
+ // New cache draw.
+ Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
+ while (ofs.y < size.height && from_line < main->lines.size()) {
+ visible_line_count++;
+ ofs.y += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_color_shadow, use_outline, shadow_ofs);
from_line++;
}
} break;
@@ -1036,50 +1385,12 @@ void RichTextLabel::_notification(int p_what) {
}
}
-void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item, int *r_click_char, bool *r_outside) {
- if (r_click_item) {
- *r_click_item = nullptr;
- }
-
- Rect2 text_rect = _get_text_rect();
- int ofs = vscroll->get_value();
- Color font_color_shadow = get_theme_color("font_color_shadow");
- bool use_outline = get_theme_constant("shadow_as_outline");
- Point2 shadow_ofs(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y"));
-
- //todo, change to binary search
- int from_line = 0;
-
- while (from_line < p_frame->lines.size()) {
- if (p_frame->lines[from_line].height_accum_cache >= ofs) {
- break;
- }
- from_line++;
- }
-
- if (from_line >= p_frame->lines.size()) {
- return;
- }
-
- int y = (p_frame->lines[from_line].height_accum_cache - p_frame->lines[from_line].height_cache) - ofs;
- Ref<Font> base_font = get_theme_font("normal_font");
- Color base_color = get_theme_color("default_color");
-
- while (y < text_rect.get_size().height && from_line < p_frame->lines.size()) {
- _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, p_click, r_click_item, r_click_char, r_outside);
- if (r_click_item && *r_click_item) {
- return;
- }
- from_line++;
- }
-}
-
Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const {
if (!underline_meta) {
return CURSOR_ARROW;
}
- if (selection.click) {
+ if (selection.click_item) {
return CURSOR_IBEAM;
}
@@ -1087,10 +1398,13 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
return CURSOR_ARROW; //invalid
}
- int line = 0;
+ if (main->first_resized_line < main->lines.size()) {
+ return CURSOR_ARROW; //invalid
+ }
+
Item *item = nullptr;
- bool outside;
- ((RichTextLabel *)(this))->_find_click(main, p_pos, &item, &line, &outside);
+ bool outside = true;
+ ((RichTextLabel *)(this))->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside);
if (item && !outside && ((RichTextLabel *)(this))->_find_meta(item, nullptr)) {
return CURSOR_POINTING_HAND;
@@ -1106,27 +1420,37 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
if (main->first_invalid_line < main->lines.size()) {
return;
}
+ if (main->first_resized_line < main->lines.size()) {
+ return;
+ }
if (b->get_button_index() == BUTTON_LEFT) {
if (b->is_pressed() && !b->is_doubleclick()) {
scroll_updated = false;
- int line = 0;
- Item *item = nullptr;
-
+ ItemFrame *c_frame = nullptr;
+ int c_line = 0;
+ Item *c_item = nullptr;
+ int c_index = 0;
bool outside;
- _find_click(main, b->get_position(), &item, &line, &outside);
- if (item) {
+ _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside);
+ if (c_item != nullptr) {
if (selection.enabled) {
- selection.click = item;
- selection.click_char = line;
+ selection.click_frame = c_frame;
+ selection.click_item = c_item;
+ selection.click_line = c_line;
+ selection.click_char = c_index;
// Erase previous selection.
if (selection.active) {
- selection.from = nullptr;
- selection.from_char = '\0';
- selection.to = nullptr;
- selection.to_char = '\0';
+ selection.from_frame = nullptr;
+ selection.from_line = 0;
+ selection.from_item = nullptr;
+ selection.from_char = 0;
+ selection.to_frame = nullptr;
+ selection.to_line = 0;
+ selection.to_item = nullptr;
+ selection.to_char = 0;
selection.active = false;
update();
@@ -1135,44 +1459,49 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
}
} else if (b->is_pressed() && b->is_doubleclick() && selection.enabled) {
//doubleclick: select word
- int line = 0;
- Item *item = nullptr;
+
+ ItemFrame *c_frame = nullptr;
+ int c_line = 0;
+ Item *c_item = nullptr;
+ int c_index = 0;
bool outside;
- _find_click(main, b->get_position(), &item, &line, &outside);
+ _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside);
- while (item && item->type != ITEM_TEXT) {
- item = _get_next_item(item, true);
- }
+ if (c_frame) {
+ const Line &l = c_frame->lines[c_line];
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(l.text_buf->get_rid());
+ for (int i = 0; i < words.size(); i++) {
+ if (c_index >= words[i].x && c_index < words[i].y) {
+ selection.from_frame = c_frame;
+ selection.from_line = c_line;
+ selection.from_item = c_item;
+ selection.from_char = words[i].x;
+
+ selection.to_frame = c_frame;
+ selection.to_line = c_line;
+ selection.to_item = c_item;
+ selection.to_char = words[i].y;
- if (item && item->type == ITEM_TEXT) {
- String itext = static_cast<ItemText *>(item)->text;
-
- int beg, end;
- if (select_word(itext, line, beg, end)) {
- selection.from = item;
- selection.to = item;
- selection.from_char = beg;
- selection.to_char = end - 1;
- selection.active = true;
- update();
+ selection.active = true;
+ update();
+ break;
+ }
}
}
} else if (!b->is_pressed()) {
- selection.click = nullptr;
+ selection.click_item = nullptr;
if (!b->is_doubleclick() && !scroll_updated) {
- int line = 0;
- Item *item = nullptr;
+ Item *c_item = nullptr;
- bool outside;
- _find_click(main, b->get_position(), &item, &line, &outside);
+ bool outside = true;
+ _find_click(main, b->get_position(), nullptr, nullptr, &c_item, nullptr, &outside);
- if (item) {
+ if (c_item) {
Variant meta;
- if (!outside && _find_meta(item, &meta)) {
+ if (!outside && _find_meta(c_item, &meta)) {
//meta clicked
-
emit_signal("meta_clicked", meta);
}
}
@@ -1221,13 +1550,13 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
} break;
case KEY_UP: {
if (vscroll->is_visible_in_tree()) {
- vscroll->set_value(vscroll->get_value() - get_theme_font("normal_font")->get_height());
+ vscroll->set_value(vscroll->get_value() - get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size")));
handled = true;
}
} break;
case KEY_DOWN: {
if (vscroll->is_visible_in_tree()) {
- vscroll->set_value(vscroll->get_value() + get_theme_font("normal_font")->get_height());
+ vscroll->set_value(vscroll->get_value() + get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size")));
handled = true;
}
} break;
@@ -1265,27 +1594,32 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
if (main->first_invalid_line < main->lines.size()) {
return;
}
+ if (main->first_resized_line < main->lines.size()) {
+ return;
+ }
- int line = 0;
- Item *item = nullptr;
+ ItemFrame *c_frame = nullptr;
+ int c_line = 0;
+ Item *c_item = nullptr;
+ int c_index = 0;
bool outside;
- _find_click(main, m->get_position(), &item, &line, &outside);
-
- if (selection.click) {
- if (!item) {
- return; // do not update
- }
- selection.from = selection.click;
+ _find_click(main, m->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside);
+ if (selection.click_item && c_item) {
+ selection.from_frame = selection.click_frame;
+ selection.from_line = selection.click_line;
+ selection.from_item = selection.click_item;
selection.from_char = selection.click_char;
- selection.to = item;
- selection.to_char = line;
+ selection.to_frame = c_frame;
+ selection.to_line = c_line;
+ selection.to_item = c_item;
+ selection.to_char = c_index;
bool swap = false;
- if (selection.from->index > selection.to->index) {
+ if (selection.from_item->index > selection.to_item->index) {
swap = true;
- } else if (selection.from->index == selection.to->index) {
+ } else if (selection.from_item->index == selection.to_item->index) {
if (selection.from_char > selection.to_char) {
swap = true;
} else if (selection.from_char == selection.to_char) {
@@ -1295,7 +1629,9 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
}
if (swap) {
- SWAP(selection.from, selection.to);
+ SWAP(selection.from_frame, selection.to_frame);
+ SWAP(selection.from_line, selection.to_line);
+ SWAP(selection.from_item, selection.to_item);
SWAP(selection.from_char, selection.to_char);
}
@@ -1305,7 +1641,7 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
Variant meta;
ItemMeta *item_meta;
- if (item && !outside && _find_meta(item, &meta, &item_meta)) {
+ if (c_item && !outside && _find_meta(c_item, &meta, &item_meta)) {
if (meta_hovering != item_meta) {
if (meta_hovering) {
emit_signal("meta_hover_ended", current_meta);
@@ -1322,6 +1658,31 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
}
}
+void RichTextLabel::_find_frame(Item *p_item, ItemFrame **r_frame, int *r_line) {
+ if (r_frame != nullptr) {
+ *r_frame = nullptr;
+ }
+ if (r_line != nullptr) {
+ *r_line = 0;
+ }
+
+ Item *item = p_item;
+
+ while (item) {
+ if (item->parent != nullptr && item->parent->type == ITEM_FRAME) {
+ if (r_frame != nullptr) {
+ *r_frame = (ItemFrame *)item->parent;
+ }
+ if (r_line != nullptr) {
+ *r_line = item->line;
+ }
+ return;
+ }
+
+ item = item->parent;
+ }
+}
+
Ref<Font> RichTextLabel::_find_font(Item *p_item) {
Item *fontitem = p_item;
@@ -1337,10 +1698,103 @@ Ref<Font> RichTextLabel::_find_font(Item *p_item) {
return Ref<Font>();
}
-int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font) {
+int RichTextLabel::_find_font_size(Item *p_item) {
+ Item *sizeitem = p_item;
+
+ while (sizeitem) {
+ if (sizeitem->type == ITEM_FONT_SIZE) {
+ ItemFontSize *fi = static_cast<ItemFontSize *>(sizeitem);
+ return fi->font_size;
+ }
+
+ sizeitem = sizeitem->parent;
+ }
+
+ return -1;
+}
+
+int RichTextLabel::_find_outline_size(Item *p_item) {
+ Item *sizeitem = p_item;
+
+ while (sizeitem) {
+ if (sizeitem->type == ITEM_OUTLINE_SIZE) {
+ ItemOutlineSize *fi = static_cast<ItemOutlineSize *>(sizeitem);
+ return fi->outline_size;
+ }
+
+ sizeitem = sizeitem->parent;
+ }
+
+ return 0;
+}
+
+Dictionary RichTextLabel::_find_font_features(Item *p_item) {
+ Item *ffitem = p_item;
+
+ while (ffitem) {
+ if (ffitem->type == ITEM_FONT_FEATURES) {
+ ItemFontFeatures *fi = static_cast<ItemFontFeatures *>(ffitem);
+ return fi->opentype_features;
+ }
+
+ ffitem = ffitem->parent;
+ }
+
+ return Dictionary();
+}
+
+RichTextLabel::ItemList *RichTextLabel::_find_list_item(Item *p_item) {
Item *item = p_item;
- int margin = 0;
+ while (item) {
+ if (item->type == ITEM_LIST) {
+ return static_cast<ItemList *>(item);
+ }
+ item = item->parent;
+ }
+
+ return nullptr;
+}
+
+int RichTextLabel::_find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list) {
+ Item *item = p_item;
+ Item *prev_item = p_item;
+
+ int level = 0;
+
+ while (item) {
+ if (item->type == ITEM_LIST) {
+ ItemList *list = static_cast<ItemList *>(item);
+
+ ItemFrame *frame = nullptr;
+ int line = -1;
+ _find_frame(list, &frame, &line);
+
+ int index = 1;
+ if (frame != nullptr) {
+ for (int i = list->line + 1; i <= prev_item->line; i++) {
+ if (_find_list_item(frame->lines[i].from) == list) {
+ index++;
+ }
+ }
+ }
+
+ r_index.push_back(index);
+ r_list.push_back(list);
+
+ prev_item = item;
+ }
+ level++;
+ item = item->parent;
+ }
+
+ return level;
+}
+
+int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size) {
+ Item *item = p_item;
+
+ float margin = 0;
while (item) {
if (item->type == ITEM_INDENT) {
@@ -1348,16 +1802,22 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font) {
if (font.is_null()) {
font = p_base_font;
}
-
- ItemIndent *indent = static_cast<ItemIndent *>(item);
-
- margin += indent->level * tab_size * font->get_char_size(' ').width;
+ int font_size = _find_font_size(item);
+ if (font_size == -1) {
+ font_size = p_base_font_size;
+ }
+ margin += tab_size * font->get_char_size('m', 0, font_size).width;
} else if (item->type == ITEM_LIST) {
Ref<Font> font = _find_font(item);
if (font.is_null()) {
font = p_base_font;
}
+ int font_size = _find_font_size(item);
+ if (font_size == -1) {
+ font_size = p_base_font_size;
+ }
+ margin += tab_size * font->get_char_size('m', 0, font_size).width;
}
item = item->parent;
@@ -1370,9 +1830,9 @@ RichTextLabel::Align RichTextLabel::_find_align(Item *p_item) {
Item *item = p_item;
while (item) {
- if (item->type == ITEM_ALIGN) {
- ItemAlign *align = static_cast<ItemAlign *>(item);
- return align->align;
+ if (item->type == ITEM_PARAGRAPH) {
+ ItemParagraph *p = static_cast<ItemParagraph *>(item);
+ return p->align;
}
item = item->parent;
@@ -1381,6 +1841,57 @@ RichTextLabel::Align RichTextLabel::_find_align(Item *p_item) {
return default_align;
}
+TextServer::Direction RichTextLabel::_find_direction(Item *p_item) {
+ Item *item = p_item;
+
+ while (item) {
+ if (item->type == ITEM_PARAGRAPH) {
+ ItemParagraph *p = static_cast<ItemParagraph *>(item);
+ if (p->direction != Control::TEXT_DIRECTION_INHERITED) {
+ return (TextServer::Direction)p->direction;
+ }
+ }
+
+ item = item->parent;
+ }
+
+ if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
+ return is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR;
+ } else {
+ return (TextServer::Direction)text_direction;
+ }
+}
+
+Control::StructuredTextParser RichTextLabel::_find_stt(Item *p_item) {
+ Item *item = p_item;
+
+ while (item) {
+ if (item->type == ITEM_PARAGRAPH) {
+ ItemParagraph *p = static_cast<ItemParagraph *>(item);
+ return p->st_parser;
+ }
+
+ item = item->parent;
+ }
+
+ return st_parser;
+}
+
+String RichTextLabel::_find_language(Item *p_item) {
+ Item *item = p_item;
+
+ while (item) {
+ if (item->type == ITEM_PARAGRAPH) {
+ ItemParagraph *p = static_cast<ItemParagraph *>(item);
+ return p->language;
+ }
+
+ item = item->parent;
+ }
+
+ return language;
+}
+
Color RichTextLabel::_find_color(Item *p_item, const Color &p_default_color) {
Item *item = p_item;
@@ -1396,25 +1907,26 @@ Color RichTextLabel::_find_color(Item *p_item, const Color &p_default_color) {
return p_default_color;
}
-bool RichTextLabel::_find_underline(Item *p_item) {
+Color RichTextLabel::_find_outline_color(Item *p_item, const Color &p_default_color) {
Item *item = p_item;
while (item) {
- if (item->type == ITEM_UNDERLINE) {
- return true;
+ if (item->type == ITEM_OUTLINE_COLOR) {
+ ItemOutlineColor *color = static_cast<ItemOutlineColor *>(item);
+ return color->color;
}
item = item->parent;
}
- return false;
+ return p_default_color;
}
-bool RichTextLabel::_find_strikethrough(Item *p_item) {
+bool RichTextLabel::_find_underline(Item *p_item) {
Item *item = p_item;
while (item) {
- if (item->type == ITEM_STRIKETHROUGH) {
+ if (item->type == ITEM_UNDERLINE) {
return true;
}
@@ -1424,17 +1936,17 @@ bool RichTextLabel::_find_strikethrough(Item *p_item) {
return false;
}
-bool RichTextLabel::_find_by_type(Item *p_item, ItemType p_type) {
- ERR_FAIL_INDEX_V((int)p_type, 19, false);
-
+bool RichTextLabel::_find_strikethrough(Item *p_item) {
Item *item = p_item;
while (item) {
- if (item->type == p_type) {
+ if (item->type == ITEM_STRIKETHROUGH) {
return true;
}
+
item = item->parent;
}
+
return false;
}
@@ -1530,38 +2042,67 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) {
void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
if (p_frame->first_invalid_line == p_frame->lines.size()) {
+ if (p_frame->first_resized_line == p_frame->lines.size()) {
+ return;
+ }
+
+ // Resize lines without reshaping.
+ Size2 size = get_size();
+ if (fixed_width != -1) {
+ size.width = fixed_width;
+ }
+ Rect2 text_rect = _get_text_rect();
+
+ Ref<Font> base_font = get_theme_font("normal_font");
+ int base_font_size = get_theme_font_size("normal_font_size");
+
+ for (int i = p_frame->first_resized_line; i < p_frame->lines.size(); i++) {
+ _resize_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w);
+ }
+
+ int total_height = 0;
+ if (p_frame->lines.size()) {
+ total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + get_theme_stylebox("normal")->get_minimum_size().height;
+ }
+
+ p_frame->first_resized_line = p_frame->lines.size();
+
+ updating_scroll = true;
+ vscroll->set_max(total_height);
+ vscroll->set_page(size.height);
+ if (scroll_follow && scroll_following) {
+ vscroll->set_value(total_height - size.height);
+ }
+ updating_scroll = false;
+
+ if (fit_content_height) {
+ minimum_size_changed();
+ }
return;
}
- //validate invalid lines
+ // Shape invalid lines.
Size2 size = get_size();
if (fixed_width != -1) {
size.width = fixed_width;
}
Rect2 text_rect = _get_text_rect();
- Color font_color_shadow = get_theme_color("font_color_shadow");
- bool use_outline = get_theme_constant("shadow_as_outline");
- Point2 shadow_ofs(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y"));
Ref<Font> base_font = get_theme_font("normal_font");
+ int base_font_size = get_theme_font_size("normal_font_size");
+ int total_chars = (p_frame->first_invalid_line == 0) ? 0 : (p_frame->lines[p_frame->first_invalid_line].char_offset + p_frame->lines[p_frame->first_invalid_line].char_count);
for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) {
- int y = 0;
- _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color(), font_color_shadow, use_outline, shadow_ofs);
- p_frame->lines.write[i].height_cache = y;
- p_frame->lines.write[i].height_accum_cache = y;
-
- if (i > 0) {
- p_frame->lines.write[i].height_accum_cache += p_frame->lines[i - 1].height_accum_cache;
- }
+ _shape_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w, &total_chars);
}
int total_height = 0;
if (p_frame->lines.size()) {
- total_height = p_frame->lines[p_frame->lines.size() - 1].height_accum_cache + get_theme_stylebox("normal")->get_minimum_size().height;
+ total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + get_theme_stylebox("normal")->get_minimum_size().height;
}
- main->first_invalid_line = p_frame->lines.size();
+ p_frame->first_invalid_line = p_frame->lines.size();
+ p_frame->first_resized_line = p_frame->lines.size();
updating_scroll = true;
vscroll->set_max(total_height);
@@ -1569,7 +2110,6 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
if (scroll_follow && scroll_following) {
vscroll->set_value(total_height - size.height);
}
-
updating_scroll = false;
if (fit_content_height) {
@@ -1641,6 +2181,13 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline)
p_item->parent = current;
p_item->E = current->subitems.push_back(p_item);
p_item->index = current_idx++;
+ p_item->char_ofs = current_char_ofs;
+ if (p_item->type == ITEM_TEXT) {
+ ItemText *t = (ItemText *)p_item;
+ current_char_ofs += t->text.length();
+ } else if (p_item->type == ITEM_IMAGE) {
+ current_char_ofs++;
+ }
if (p_enter) {
current = p_item;
@@ -1686,7 +2233,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub
}
}
-void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color) {
+void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, VAlign p_align) {
if (current->type == ITEM_TABLE) {
return;
}
@@ -1696,6 +2243,7 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width,
item->image = p_image;
item->color = p_color;
+ item->inline_align = p_align;
if (p_width > 0) {
// custom width
@@ -1764,7 +2312,7 @@ bool RichTextLabel::remove_line(const int p_line) {
main->lines.write[0].from = main;
}
- main->first_invalid_line = 0;
+ main->first_invalid_line = 0; // p_line ???
return true;
}
@@ -1813,6 +2361,30 @@ void RichTextLabel::push_mono() {
push_font(mono_font);
}
+void RichTextLabel::push_font_size(int p_font_size) {
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
+ ItemFontSize *item = memnew(ItemFontSize);
+
+ item->font_size = p_font_size;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_font_features(const Dictionary &p_features) {
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
+ ItemFontFeatures *item = memnew(ItemFontFeatures);
+
+ item->opentype_features = p_features;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_outline_size(int p_font_size) {
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
+ ItemOutlineSize *item = memnew(ItemOutlineSize);
+
+ item->outline_size = p_font_size;
+ _add_item(item, true);
+}
+
void RichTextLabel::push_color(const Color &p_color) {
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemColor *item = memnew(ItemColor);
@@ -1821,6 +2393,14 @@ void RichTextLabel::push_color(const Color &p_color) {
_add_item(item, true);
}
+void RichTextLabel::push_outline_color(const Color &p_color) {
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
+ ItemOutlineColor *item = memnew(ItemOutlineColor);
+
+ item->color = p_color;
+ _add_item(item, true);
+}
+
void RichTextLabel::push_underline() {
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemUnderline *item = memnew(ItemUnderline);
@@ -1835,11 +2415,14 @@ void RichTextLabel::push_strikethrough() {
_add_item(item, true);
}
-void RichTextLabel::push_align(Align p_align) {
+void RichTextLabel::push_paragraph(Align p_align, Control::TextDirection p_direction, const String &p_language, Control::StructuredTextParser p_st_parser) {
ERR_FAIL_COND(current->type == ITEM_TABLE);
- ItemAlign *item = memnew(ItemAlign);
+ ItemParagraph *item = memnew(ItemParagraph);
item->align = p_align;
+ item->direction = p_direction;
+ item->language = p_language;
+ item->st_parser = p_st_parser;
_add_item(item, true, true);
}
@@ -1852,13 +2435,15 @@ void RichTextLabel::push_indent(int p_level) {
_add_item(item, true, true);
}
-void RichTextLabel::push_list(ListType p_list) {
+void RichTextLabel::push_list(int p_level, ListType p_list, bool p_capitalize) {
ERR_FAIL_COND(current->type == ITEM_TABLE);
- ERR_FAIL_INDEX(p_list, 3);
+ ERR_FAIL_COND(p_level < 0);
ItemList *item = memnew(ItemList);
item->list_type = p_list;
+ item->level = p_level;
+ item->capitalize = p_capitalize;
_add_item(item, true, true);
}
@@ -1870,17 +2455,18 @@ void RichTextLabel::push_meta(const Variant &p_meta) {
_add_item(item, true);
}
-void RichTextLabel::push_table(int p_columns) {
+void RichTextLabel::push_table(int p_columns, VAlign p_align) {
ERR_FAIL_COND(p_columns < 1);
ItemTable *item = memnew(ItemTable);
item->columns.resize(p_columns);
item->total_width = 0;
+ item->inline_align = p_align;
for (int i = 0; i < item->columns.size(); i++) {
item->columns.write[i].expand = false;
item->columns.write[i].expand_ratio = 1;
}
- _add_item(item, true, true);
+ _add_item(item, true, false);
}
void RichTextLabel::push_fade(int p_start_index, int p_length) {
@@ -1934,6 +2520,36 @@ void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_r
table->columns.write[p_column].expand_ratio = p_ratio;
}
+void RichTextLabel::set_cell_row_background_color(const Color &p_odd_row_bg, const Color &p_even_row_bg) {
+ ERR_FAIL_COND(current->type != ITEM_FRAME);
+ ItemFrame *cell = static_cast<ItemFrame *>(current);
+ ERR_FAIL_COND(!cell->cell);
+ cell->odd_row_bg = p_odd_row_bg;
+ cell->even_row_bg = p_even_row_bg;
+}
+
+void RichTextLabel::set_cell_border_color(const Color &p_color) {
+ ERR_FAIL_COND(current->type != ITEM_FRAME);
+ ItemFrame *cell = static_cast<ItemFrame *>(current);
+ ERR_FAIL_COND(!cell->cell);
+ cell->border = p_color;
+}
+
+void RichTextLabel::set_cell_size_override(const Size2 &p_min_size, const Size2 &p_max_size) {
+ ERR_FAIL_COND(current->type != ITEM_FRAME);
+ ItemFrame *cell = static_cast<ItemFrame *>(current);
+ ERR_FAIL_COND(!cell->cell);
+ cell->min_size_over = p_min_size;
+ cell->max_size_over = p_max_size;
+}
+
+void RichTextLabel::set_cell_padding(const Rect2 &p_padding) {
+ ERR_FAIL_COND(current->type != ITEM_FRAME);
+ ItemFrame *cell = static_cast<ItemFrame *>(current);
+ ERR_FAIL_COND(!cell->cell);
+ cell->padding = p_padding;
+}
+
void RichTextLabel::push_cell() {
ERR_FAIL_COND(current->type != ITEM_TABLE);
@@ -1942,10 +2558,9 @@ void RichTextLabel::push_cell() {
_add_item(item, true);
current_frame = item;
item->cell = true;
- item->parent_line = item->parent_frame->lines.size() - 1;
item->lines.resize(1);
item->lines.write[0].from = nullptr;
- item->first_invalid_line = 0;
+ item->first_invalid_line = 0; // parent frame last line ???
}
int RichTextLabel::get_current_table_column() const {
@@ -1972,9 +2587,13 @@ void RichTextLabel::clear() {
main->lines.resize(1);
main->first_invalid_line = 0;
update();
- selection.click = nullptr;
+
+ selection.click_frame = nullptr;
+ selection.click_item = nullptr;
selection.active = false;
+
current_idx = 1;
+ current_char_ofs = 0;
if (scroll_follow) {
scroll_following = true;
}
@@ -1986,7 +2605,7 @@ void RichTextLabel::clear() {
void RichTextLabel::set_tab_size(int p_spaces) {
tab_size = p_spaces;
- main->first_invalid_line = 0;
+ main->first_resized_line = 0;
update();
}
@@ -2135,7 +2754,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
if (tag_stack.front()->get() == "i") {
in_italics = false;
}
- if (tag_stack.front()->get() == "indent") {
+ if ((tag_stack.front()->get() == "indent") || (tag_stack.front()->get() == "ol") || (tag_stack.front()->get() == "ul")) {
indent_level--;
}
@@ -2177,12 +2796,24 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag.begins_with("table=")) {
- int columns = tag.substr(6, tag.length()).to_int();
+ Vector<String> subtag = tag.substr(6, tag.length()).split(",");
+ int columns = subtag[0].to_int();
if (columns < 1) {
columns = 1;
}
- push_table(columns);
+ VAlign align = VALIGN_TOP;
+ if (subtag.size() > 1) {
+ if (subtag[1] == "top" || subtag[1] == "t") {
+ align = VALIGN_TOP;
+ } else if (subtag[1] == "center" || subtag[1] == "c") {
+ align = VALIGN_CENTER;
+ } else if (subtag[1] == "bottom" || subtag[1] == "b") {
+ align = VALIGN_BOTTOM;
+ }
+ }
+
+ push_table(columns, align);
pos = brk_end + 1;
tag_stack.push_front("table");
} else if (tag == "cell") {
@@ -2197,6 +2828,42 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
set_table_column_expand(get_current_table_column(), true, ratio);
push_cell();
+
+ pos = brk_end + 1;
+ tag_stack.push_front("cell");
+ } else if (tag.begins_with("cell ")) {
+ Vector<String> subtag = tag.substr(5, tag.length()).split(" ");
+
+ for (int i = 0; i < subtag.size(); i++) {
+ Vector<String> subtag_a = subtag[i].split("=");
+ if (subtag_a.size() == 2) {
+ if (subtag_a[0] == "expand") {
+ int ratio = subtag_a[1].to_int();
+ if (ratio < 1) {
+ ratio = 1;
+ }
+ set_table_column_expand(get_current_table_column(), true, ratio);
+ }
+ }
+ }
+ push_cell();
+ for (int i = 0; i < subtag.size(); i++) {
+ Vector<String> subtag_a = subtag[i].split("=");
+ if (subtag_a.size() == 2) {
+ if (subtag_a[0] == "border") {
+ Color color = _get_color_from_string(subtag_a[1], Color(0, 0, 0, 0));
+ set_cell_border_color(color);
+ } else if (subtag_a[0] == "bg") {
+ Vector<String> subtag_b = subtag_a[1].split(",");
+ if (subtag_b.size() == 2) {
+ Color color1 = _get_color_from_string(subtag_b[0], Color(0, 0, 0, 0));
+ Color color2 = _get_color_from_string(subtag_b[1], Color(0, 0, 0, 0));
+ set_cell_row_background_color(color1, color2);
+ }
+ }
+ }
+ }
+
pos = brk_end + 1;
tag_stack.push_front("cell");
} else if (tag == "u") {
@@ -2209,32 +2876,156 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
push_strikethrough();
pos = brk_end + 1;
tag_stack.push_front(tag);
+ } else if (tag == "lrm") {
+ add_text(String::chr(0x200E));
+ pos = brk_end + 1;
+ } else if (tag == "rlm") {
+ add_text(String::chr(0x200F));
+ pos = brk_end + 1;
+ } else if (tag == "lre") {
+ add_text(String::chr(0x202A));
+ pos = brk_end + 1;
+ } else if (tag == "rle") {
+ add_text(String::chr(0x202B));
+ pos = brk_end + 1;
+ } else if (tag == "lro") {
+ add_text(String::chr(0x202D));
+ pos = brk_end + 1;
+ } else if (tag == "rlo") {
+ add_text(String::chr(0x202E));
+ pos = brk_end + 1;
+ } else if (tag == "pdf") {
+ add_text(String::chr(0x202C));
+ pos = brk_end + 1;
+ } else if (tag == "alm") {
+ add_text(String::chr(0x061c));
+ pos = brk_end + 1;
+ } else if (tag == "lri") {
+ add_text(String::chr(0x2066));
+ pos = brk_end + 1;
+ } else if (tag == "rli") {
+ add_text(String::chr(0x2027));
+ pos = brk_end + 1;
+ } else if (tag == "fsi") {
+ add_text(String::chr(0x2068));
+ pos = brk_end + 1;
+ } else if (tag == "pdi") {
+ add_text(String::chr(0x2069));
+ pos = brk_end + 1;
+ } else if (tag == "zwj") {
+ add_text(String::chr(0x200D));
+ pos = brk_end + 1;
+ } else if (tag == "zwnj") {
+ add_text(String::chr(0x200C));
+ pos = brk_end + 1;
+ } else if (tag == "wj") {
+ add_text(String::chr(0x2060));
+ pos = brk_end + 1;
+ } else if (tag == "shy") {
+ add_text(String::chr(0x00AD));
+ pos = brk_end + 1;
} else if (tag == "center") {
- push_align(ALIGN_CENTER);
+ push_paragraph(ALIGN_CENTER);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "fill") {
- push_align(ALIGN_FILL);
+ push_paragraph(ALIGN_FILL);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "right") {
- push_align(ALIGN_RIGHT);
+ push_paragraph(ALIGN_RIGHT);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "ul") {
- push_list(LIST_DOTS);
+ indent_level++;
+ push_list(indent_level, LIST_DOTS, false);
pos = brk_end + 1;
tag_stack.push_front(tag);
- } else if (tag == "ol") {
- push_list(LIST_NUMBERS);
+ } else if ((tag == "ol") || (tag == "ol type=1")) {
+ indent_level++;
+ push_list(indent_level, LIST_NUMBERS, false);
pos = brk_end + 1;
tag_stack.push_front(tag);
+ } else if (tag == "ol type=a") {
+ indent_level++;
+ push_list(indent_level, LIST_LETTERS, false);
+ pos = brk_end + 1;
+ tag_stack.push_front("ol");
+ } else if (tag == "ol type=A") {
+ indent_level++;
+ push_list(indent_level, LIST_LETTERS, true);
+ pos = brk_end + 1;
+ tag_stack.push_front("ol");
+ } else if (tag == "ol type=i") {
+ indent_level++;
+ push_list(indent_level, LIST_ROMAN, false);
+ pos = brk_end + 1;
+ tag_stack.push_front("ol");
+ } else if (tag == "ol type=I") {
+ indent_level++;
+ push_list(indent_level, LIST_ROMAN, true);
+ pos = brk_end + 1;
+ tag_stack.push_front("ol");
} else if (tag == "indent") {
indent_level++;
push_indent(indent_level);
pos = brk_end + 1;
tag_stack.push_front(tag);
-
+ } else if (tag == "p") {
+ push_paragraph(ALIGN_LEFT);
+ pos = brk_end + 1;
+ tag_stack.push_front("p");
+ } else if (tag.begins_with("p ")) {
+ Vector<String> subtag = tag.substr(2, tag.length()).split(" ");
+ Align align = ALIGN_LEFT;
+ Control::TextDirection dir = Control::TEXT_DIRECTION_INHERITED;
+ String lang;
+ Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
+ for (int i = 0; i < subtag.size(); i++) {
+ Vector<String> subtag_a = subtag[i].split("=");
+ if (subtag_a.size() == 2) {
+ if (subtag_a[0] == "align") {
+ if (subtag_a[1] == "l" || subtag_a[1] == "left") {
+ align = ALIGN_LEFT;
+ } else if (subtag_a[1] == "c" || subtag_a[1] == "center") {
+ align = ALIGN_CENTER;
+ } else if (subtag_a[1] == "r" || subtag_a[1] == "right") {
+ align = ALIGN_RIGHT;
+ } else if (subtag_a[1] == "f" || subtag_a[1] == "fill") {
+ align = ALIGN_FILL;
+ }
+ } else if (subtag_a[0] == "dir" || subtag_a[0] == "direction") {
+ if (subtag_a[1] == "a" || subtag_a[1] == "auto") {
+ dir = Control::TEXT_DIRECTION_AUTO;
+ } else if (subtag_a[1] == "l" || subtag_a[1] == "ltr") {
+ dir = Control::TEXT_DIRECTION_LTR;
+ } else if (subtag_a[1] == "r" || subtag_a[1] == "rtl") {
+ dir = Control::TEXT_DIRECTION_RTL;
+ }
+ } else if (subtag_a[0] == "lang" || subtag_a[0] == "language") {
+ lang = subtag_a[1];
+ } else if (subtag_a[0] == "st" || subtag_a[0] == "bidi_override") {
+ if (subtag_a[1] == "d" || subtag_a[1] == "default") {
+ st_parser = STRUCTURED_TEXT_DEFAULT;
+ } else if (subtag_a[1] == "u" || subtag_a[1] == "uri") {
+ st_parser = STRUCTURED_TEXT_URI;
+ } else if (subtag_a[1] == "f" || subtag_a[1] == "file") {
+ st_parser = STRUCTURED_TEXT_FILE;
+ } else if (subtag_a[1] == "e" || subtag_a[1] == "email") {
+ st_parser = STRUCTURED_TEXT_EMAIL;
+ } else if (subtag_a[1] == "l" || subtag_a[1] == "list") {
+ st_parser = STRUCTURED_TEXT_LIST;
+ } else if (subtag_a[1] == "n" || subtag_a[1] == "none") {
+ st_parser = STRUCTURED_TEXT_NONE;
+ } else if (subtag_a[1] == "c" || subtag_a[1] == "custom") {
+ st_parser = STRUCTURED_TEXT_CUSTOM;
+ }
+ }
+ }
+ }
+ push_paragraph(align, dir, lang, st_parser);
+ pos = brk_end + 1;
+ tag_stack.push_front("p");
} else if (tag == "url") {
int end = p_bbcode.find("[", brk_end);
if (end == -1) {
@@ -2251,7 +3042,19 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
push_meta(url);
pos = brk_end + 1;
tag_stack.push_front("url");
- } else if (bbcode_name == "img") {
+ } else if (tag.begins_with("img")) {
+ VAlign align = VALIGN_TOP;
+ if (tag.begins_with("img=")) {
+ String al = tag.substr(4, tag.length());
+ if (al == "top" || al == "t") {
+ align = VALIGN_TOP;
+ } else if (al == "center" || al == "c") {
+ align = VALIGN_CENTER;
+ } else if (al == "bottom" || al == "b") {
+ align = VALIGN_BOTTOM;
+ }
+ }
+
int end = p_bbcode.find("[", brk_end);
if (end == -1) {
end = p_bbcode.length();
@@ -2289,7 +3092,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
}
}
- add_image(texture, width, height, color);
+ add_image(texture, width, height, color, align);
}
pos = end;
@@ -2301,6 +3104,13 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front("color");
+ } else if (tag.begins_with("outline_color=")) {
+ String color_str = tag.substr(14, tag.length());
+ Color color = _get_color_from_string(color_str, base_color);
+ push_outline_color(color);
+ pos = brk_end + 1;
+ tag_stack.push_front("outline_color");
+
} else if (tag.begins_with("font=")) {
String fnt = tag.substr(5, tag.length());
@@ -2313,6 +3123,54 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front("font");
+ } else if (tag.begins_with("font_size=")) {
+ int fnt_size = tag.substr(10, tag.length()).to_int();
+ push_font_size(fnt_size);
+ pos = brk_end + 1;
+ tag_stack.push_front("font_size");
+ } else if (tag.begins_with("opentype_features=")) {
+ String fnt_ftr = tag.substr(18, tag.length());
+ Vector<String> subtag = fnt_ftr.split(",");
+ Dictionary ftrs;
+ for (int i = 0; i < subtag.size(); i++) {
+ Vector<String> subtag_a = subtag[i].split("=");
+ if (subtag_a.size() == 2) {
+ ftrs[TS->name_to_tag(subtag_a[0])] = subtag_a[1].to_int();
+ } else if (subtag_a.size() == 1) {
+ ftrs[TS->name_to_tag(subtag_a[0])] = 1;
+ }
+ }
+ push_font_features(ftrs);
+ pos = brk_end + 1;
+ tag_stack.push_front("opentype_features");
+ } else if (tag.begins_with("font ")) {
+ Vector<String> subtag = tag.substr(2, tag.length()).split(" ");
+
+ for (int i = 1; i < subtag.size(); i++) {
+ Vector<String> subtag_a = subtag[i].split("=", true, 2);
+ if (subtag_a.size() == 2) {
+ if (subtag_a[0] == "name" || subtag_a[0] == "n") {
+ String fnt = subtag_a[1];
+ Ref<Font> font = ResourceLoader::load(fnt, "Font");
+ if (font.is_valid()) {
+ push_font(font);
+ } else {
+ push_font(normal_font);
+ }
+ } else if (subtag_a[0] == "size" || subtag_a[0] == "s") {
+ int fnt_size = subtag_a[1].to_int();
+ push_font_size(fnt_size);
+ }
+ }
+ }
+
+ pos = brk_end + 1;
+ tag_stack.push_front("font");
+ } else if (tag.begins_with("outline_size=")) {
+ int fnt_size = tag.substr(13, tag.length()).to_int();
+ push_outline_size(fnt_size);
+ pos = brk_end + 1;
+ tag_stack.push_front("outline_size");
} else if (bbcode_name == "fade") {
int start_index = 0;
@@ -2445,7 +3303,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
void RichTextLabel::scroll_to_line(int p_line) {
ERR_FAIL_INDEX(p_line, main->lines.size());
_validate_line_caches(main);
- vscroll->set_value(main->lines[p_line].height_accum_cache - main->lines[p_line].height_cache);
+ vscroll->set_value(main->lines[p_line].offset.y);
}
int RichTextLabel::get_line_count() const {
@@ -2472,94 +3330,165 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) {
}
}
-bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) {
- ERR_FAIL_COND_V(!selection.enabled, false);
- Item *it = main;
- int charidx = 0;
-
- if (p_from_selection && selection.active) {
- it = selection.to;
- charidx = selection.to_char + 1;
- }
-
- while (it) {
- if (it->type == ITEM_TEXT) {
- ItemText *t = static_cast<ItemText *>(it);
- int sp = t->text.findn(p_string, charidx);
- if (sp != -1) {
- selection.from = it;
- selection.from_char = sp;
- selection.to = it;
- selection.to_char = sp + p_string.length() - 1;
- selection.active = true;
- update();
-
- _validate_line_caches(main);
+bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p_string, Item *p_from, Item *p_to) {
+ ERR_FAIL_COND_V(p_frame == nullptr, false);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), false);
- int fh = _find_font(t).is_valid() ? _find_font(t)->get_height() : get_theme_font("normal_font")->get_height();
+ Line &l = p_frame->lines.write[p_line];
- float offset = 0;
+ String text;
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ switch (it->type) {
+ case ITEM_NEWLINE: {
+ text += "\n";
+ } break;
+ case ITEM_TEXT: {
+ ItemText *t = (ItemText *)it;
+ text += t->text;
+ } break;
+ case ITEM_IMAGE: {
+ text += " ";
+ } break;
+ case ITEM_TABLE: {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ int idx = 0;
+ for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
+ ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E->get());
- int line = t->line;
- Item *item = t;
- while (item) {
- if (item->type == ITEM_FRAME) {
- ItemFrame *frame = static_cast<ItemFrame *>(item);
- if (line >= 0 && line < frame->lines.size()) {
- offset += frame->lines[line].height_accum_cache - frame->lines[line].height_cache;
- line = frame->line;
+ for (int i = 0; i < frame->lines.size(); i++) {
+ if (_search_line(frame, i, p_string, p_from, p_to)) {
+ return true;
}
}
- item = item->parent;
+ idx++;
}
- vscroll->set_value(offset - fh);
+ } break;
+ default:
+ break;
+ }
+ }
+ int sp = text.findn(p_string, 0);
+ if (sp != -1) {
+ selection.from_frame = p_frame;
+ selection.from_line = p_line;
+ selection.from_item = _get_item_at_pos(l.from, it_to, sp);
+ selection.from_char = sp;
+ selection.to_frame = p_frame;
+ selection.to_line = p_line;
+ selection.to_item = _get_item_at_pos(l.from, it_to, sp + p_string.length() - 1);
+ selection.to_char = sp + p_string.length() - 1;
+ selection.active = true;
+ return true;
+ }
+ return false;
+}
+
+bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) {
+ ERR_FAIL_COND_V(!selection.enabled, false);
+
+ if (p_from_selection && selection.active) {
+ for (int i = 0; i < main->lines.size(); i++) {
+ if (_search_line(main, i, p_string, selection.from_item, selection.to_item)) {
+ update();
return true;
}
}
-
- if (p_search_previous) {
- it = _get_prev_item(it, true);
- } else {
- it = _get_next_item(it, true);
+ } else {
+ for (int i = 0; i < main->lines.size(); i++) {
+ if (_search_line(main, i, p_string, nullptr, nullptr)) {
+ update();
+ return true;
+ }
}
- charidx = 0;
}
return false;
}
-void RichTextLabel::selection_copy() {
- if (!selection.active || !selection.enabled) {
- return;
- }
-
+String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p_selection) {
String text;
+ ERR_FAIL_COND_V(p_frame == nullptr, text);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), text);
- RichTextLabel::Item *item = selection.from;
-
- while (item) {
- if (item->type == ITEM_TEXT) {
- String itext = static_cast<ItemText *>(item)->text;
- if (item == selection.from && item == selection.to) {
- text += itext.substr(selection.from_char, selection.to_char - selection.from_char + 1);
- } else if (item == selection.from) {
- text += itext.substr(selection.from_char, itext.size());
- } else if (item == selection.to) {
- text += itext.substr(0, selection.to_char + 1);
- } else {
- text += itext;
- }
+ Line &l = p_frame->lines.write[p_line];
- } else if (item->type == ITEM_NEWLINE) {
- text += "\n";
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ int end_idx = 0;
+ if (it_to != nullptr) {
+ end_idx = it_to->index;
+ } else {
+ for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ end_idx = it->index + 1;
+ }
+ }
+ for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ if ((p_selection.to_item != nullptr) && (p_selection.to_item->index < l.from->index)) {
+ break;
}
- if (item == selection.to) {
+ if ((p_selection.from_item != nullptr) && (p_selection.from_item->index >= end_idx)) {
break;
}
+ switch (it->type) {
+ case ITEM_NEWLINE: {
+ text += "\n";
+ } break;
+ case ITEM_TEXT: {
+ ItemText *t = (ItemText *)it;
+ text += t->text;
+ } break;
+ case ITEM_IMAGE: {
+ text += " ";
+ } break;
+ case ITEM_TABLE: {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ int idx = 0;
+ int col_count = table->columns.size();
+ for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
+ ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E->get());
+ int column = idx % col_count;
+
+ for (int i = 0; i < frame->lines.size(); i++) {
+ text += _get_line_text(frame, i, p_selection);
+ }
+ if (column == col_count - 1) {
+ text += "\n";
+ } else {
+ text += " ";
+ }
+ idx++;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ if ((l.from != nullptr) && (p_frame == p_selection.to_frame) && (p_selection.to_item != nullptr) && (p_selection.to_item->index >= l.from->index) && (p_selection.to_item->index < end_idx)) {
+ text = text.substr(0, p_selection.to_char);
+ }
+ if ((l.from != nullptr) && (p_frame == p_selection.from_frame) && (p_selection.from_item != nullptr) && (p_selection.from_item->index >= l.from->index) && (p_selection.from_item->index < end_idx)) {
+ text = text.substr(p_selection.from_char, -1);
+ }
+ return text;
+}
+
+String RichTextLabel::get_selected_text() {
+ if (!selection.active || !selection.enabled) {
+ return "";
+ }
- item = _get_next_item(item, true);
+ String text;
+ for (int i = 0; i < main->lines.size(); i++) {
+ text += _get_line_text(main, i, selection);
}
+ return text;
+}
+
+void RichTextLabel::selection_copy() {
+ String text = get_selected_text();
if (text != "") {
DisplayServer::get_singleton()->clipboard_set(text);
@@ -2605,7 +3534,7 @@ String RichTextLabel::get_text() {
text += t->text;
} else if (it->type == ITEM_NEWLINE) {
text += "\n";
- } else if (it->type == ITEM_INDENT) {
+ } else if (it->type == ITEM_INDENT || it->type == ITEM_LIST) {
text += "\t";
}
it = _get_next_item(it, true);
@@ -2618,18 +3547,73 @@ void RichTextLabel::set_text(const String &p_string) {
add_text(p_string);
}
-void RichTextLabel::set_percent_visible(float p_percent) {
- if (p_percent < 0 || p_percent >= 1) {
- visible_characters = -1;
- percent_visible = 1;
+void RichTextLabel::set_text_direction(Control::TextDirection p_text_direction) {
+ ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
+ if (text_direction != p_text_direction) {
+ text_direction = p_text_direction;
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ update();
+ }
+}
- } else {
- visible_characters = get_total_character_count() * p_percent;
- percent_visible = p_percent;
+void RichTextLabel::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
+ if (st_parser != p_parser) {
+ st_parser = p_parser;
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ update();
}
+}
+
+Control::StructuredTextParser RichTextLabel::get_structured_text_bidi_override() const {
+ return st_parser;
+}
+
+void RichTextLabel::set_structured_text_bidi_override_options(Array p_args) {
+ st_args = p_args;
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
update();
}
+Array RichTextLabel::get_structured_text_bidi_override_options() const {
+ return st_args;
+}
+
+Control::TextDirection RichTextLabel::get_text_direction() const {
+ return text_direction;
+}
+
+void RichTextLabel::set_language(const String &p_language) {
+ if (language != p_language) {
+ language = p_language;
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ update();
+ }
+}
+
+String RichTextLabel::get_language() const {
+ return language;
+}
+
+void RichTextLabel::set_percent_visible(float p_percent) {
+ if (percent_visible != p_percent) {
+ if (p_percent < 0 || p_percent >= 1) {
+ visible_characters = -1;
+ percent_visible = 1;
+
+ } else {
+ visible_characters = get_total_character_count() * p_percent;
+ percent_visible = p_percent;
+ }
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ update();
+ }
+}
+
float RichTextLabel::get_percent_visible() const {
return percent_visible;
}
@@ -2665,7 +3649,7 @@ void RichTextLabel::install_effect(const Variant effect) {
int RichTextLabel::get_content_height() const {
int total_height = 0;
if (main->lines.size()) {
- total_height = main->lines[main->lines.size() - 1].height_accum_cache + get_theme_stylebox("normal")->get_minimum_size().height;
+ total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y + get_theme_stylebox("normal")->get_minimum_size().height;
}
return total_height;
}
@@ -2675,29 +3659,46 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text);
ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text);
ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text);
- ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)));
+ ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(VALIGN_TOP));
ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line);
ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);
+ ClassDB::bind_method(D_METHOD("push_font_size", "font_size"), &RichTextLabel::push_font_size);
+ ClassDB::bind_method(D_METHOD("push_font_features", "opentype_features"), &RichTextLabel::push_font_features);
ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal);
ClassDB::bind_method(D_METHOD("push_bold"), &RichTextLabel::push_bold);
ClassDB::bind_method(D_METHOD("push_bold_italics"), &RichTextLabel::push_bold_italics);
ClassDB::bind_method(D_METHOD("push_italics"), &RichTextLabel::push_italics);
ClassDB::bind_method(D_METHOD("push_mono"), &RichTextLabel::push_mono);
ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color);
- ClassDB::bind_method(D_METHOD("push_align", "align"), &RichTextLabel::push_align);
+ ClassDB::bind_method(D_METHOD("push_outline_size", "outline_size"), &RichTextLabel::push_outline_size);
+ ClassDB::bind_method(D_METHOD("push_outline_color", "color"), &RichTextLabel::push_outline_color);
+ ClassDB::bind_method(D_METHOD("push_paragraph", "align", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(STRUCTURED_TEXT_DEFAULT));
ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent);
- ClassDB::bind_method(D_METHOD("push_list", "type"), &RichTextLabel::push_list);
+ ClassDB::bind_method(D_METHOD("push_list", "level", "type", "capitalize"), &RichTextLabel::push_list);
ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta);
ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline);
ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough);
- ClassDB::bind_method(D_METHOD("push_table", "columns"), &RichTextLabel::push_table);
+ ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(VALIGN_TOP));
ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand);
+ ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color);
+ ClassDB::bind_method(D_METHOD("set_cell_border_color", "color"), &RichTextLabel::set_cell_border_color);
+ ClassDB::bind_method(D_METHOD("set_cell_size_override", "min_size", "max_size"), &RichTextLabel::set_cell_size_override);
+ ClassDB::bind_method(D_METHOD("set_cell_padding", "padding"), &RichTextLabel::set_cell_padding);
ClassDB::bind_method(D_METHOD("push_cell"), &RichTextLabel::push_cell);
ClassDB::bind_method(D_METHOD("pop"), &RichTextLabel::pop);
ClassDB::bind_method(D_METHOD("clear"), &RichTextLabel::clear);
+ ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &RichTextLabel::set_structured_text_bidi_override);
+ ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &RichTextLabel::get_structured_text_bidi_override);
+ ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &RichTextLabel::set_structured_text_bidi_override_options);
+ ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &RichTextLabel::get_structured_text_bidi_override_options);
+ ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &RichTextLabel::set_text_direction);
+ ClassDB::bind_method(D_METHOD("get_text_direction"), &RichTextLabel::get_text_direction);
+ ClassDB::bind_method(D_METHOD("set_language", "language"), &RichTextLabel::set_language);
+ ClassDB::bind_method(D_METHOD("get_language"), &RichTextLabel::get_language);
+
ClassDB::bind_method(D_METHOD("set_meta_underline", "enable"), &RichTextLabel::set_meta_underline);
ClassDB::bind_method(D_METHOD("is_meta_underlined"), &RichTextLabel::is_meta_underlined);
@@ -2772,6 +3773,13 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, "RichTextEffect", (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+
+ ADD_GROUP("Structured Text", "structured_text_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
+
ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
@@ -2783,6 +3791,7 @@ void RichTextLabel::_bind_methods() {
BIND_ENUM_CONSTANT(LIST_NUMBERS);
BIND_ENUM_CONSTANT(LIST_LETTERS);
+ BIND_ENUM_CONSTANT(LIST_ROMAN);
BIND_ENUM_CONSTANT(LIST_DOTS);
BIND_ENUM_CONSTANT(ITEM_FRAME);
@@ -2790,10 +3799,14 @@ void RichTextLabel::_bind_methods() {
BIND_ENUM_CONSTANT(ITEM_IMAGE);
BIND_ENUM_CONSTANT(ITEM_NEWLINE);
BIND_ENUM_CONSTANT(ITEM_FONT);
+ BIND_ENUM_CONSTANT(ITEM_FONT_SIZE);
+ BIND_ENUM_CONSTANT(ITEM_FONT_FEATURES);
BIND_ENUM_CONSTANT(ITEM_COLOR);
+ BIND_ENUM_CONSTANT(ITEM_OUTLINE_SIZE);
+ BIND_ENUM_CONSTANT(ITEM_OUTLINE_COLOR);
BIND_ENUM_CONSTANT(ITEM_UNDERLINE);
BIND_ENUM_CONSTANT(ITEM_STRIKETHROUGH);
- BIND_ENUM_CONSTANT(ITEM_ALIGN);
+ BIND_ENUM_CONSTANT(ITEM_PARAGRAPH);
BIND_ENUM_CONSTANT(ITEM_INDENT);
BIND_ENUM_CONSTANT(ITEM_LIST);
BIND_ENUM_CONSTANT(ITEM_TABLE);
@@ -2923,6 +3936,7 @@ RichTextLabel::RichTextLabel() {
main->lines.resize(1);
main->lines.write[0].from = main;
main->first_invalid_line = 0;
+ main->first_resized_line = 0;
current_frame = main;
tab_size = 4;
default_align = ALIGN_LEFT;
@@ -2948,10 +3962,10 @@ RichTextLabel::RichTextLabel() {
vscroll->connect("value_changed", callable_mp(this, &RichTextLabel::_scroll_changed));
vscroll->set_step(1);
vscroll->hide();
- current_idx = 1;
use_bbcode = false;
- selection.click = nullptr;
+ selection.click_frame = nullptr;
+ selection.click_item = nullptr;
selection.active = false;
selection.enabled = false;
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 2bda7c7fce..93e48dd449 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -33,13 +33,13 @@
#include "rich_text_effect.h"
#include "scene/gui/scroll_bar.h"
+#include "scene/resources/text_paragraph.h"
class RichTextLabel : public Control {
GDCLASS(RichTextLabel, Control);
public:
enum Align {
-
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT,
@@ -47,23 +47,26 @@ public:
};
enum ListType {
-
LIST_NUMBERS,
LIST_LETTERS,
+ LIST_ROMAN,
LIST_DOTS
};
enum ItemType {
-
ITEM_FRAME,
ITEM_TEXT,
ITEM_IMAGE,
ITEM_NEWLINE,
ITEM_FONT,
+ ITEM_FONT_SIZE,
+ ITEM_FONT_FEATURES,
ITEM_COLOR,
+ ITEM_OUTLINE_SIZE,
+ ITEM_OUTLINE_COLOR,
ITEM_UNDERLINE,
ITEM_STRIKETHROUGH,
- ITEM_ALIGN,
+ ITEM_PARAGRAPH,
ITEM_INDENT,
ITEM_LIST,
ITEM_TABLE,
@@ -83,31 +86,25 @@ private:
struct Item;
struct Line {
- Item *from;
- Vector<int> offset_caches;
- Vector<int> height_caches;
- Vector<int> ascent_caches;
- Vector<int> descent_caches;
- Vector<int> space_caches;
- int height_cache;
- int height_accum_cache;
- int char_count;
- int minimum_width;
- int maximum_width;
-
- Line() {
- from = nullptr;
- char_count = 0;
- }
+ Item *from = nullptr;
+
+ Ref<TextParagraph> text_buf;
+
+ Vector2 offset;
+ int char_offset = 0;
+ int char_count = 0;
+
+ Line() { text_buf.instance(); }
};
struct Item {
- int index;
- Item *parent;
- ItemType type;
+ int index = 0;
+ int char_ofs = 0;
+ Item *parent = nullptr;
+ ItemType type = ITEM_FRAME;
List<Item *> subitems;
- List<Item *>::Element *E;
- int line;
+ List<Item *>::Element *E = nullptr;
+ int line = 0;
void _clear_children() {
while (subitems.size()) {
@@ -116,29 +113,26 @@ private:
}
}
- Item() {
- parent = nullptr;
- E = nullptr;
- line = 0;
- index = 0;
- type = ITEM_FRAME;
- }
virtual ~Item() { _clear_children(); }
};
struct ItemFrame : public Item {
- int parent_line;
- bool cell;
+ bool cell = false;
+
Vector<Line> lines;
- int first_invalid_line;
- ItemFrame *parent_frame;
-
- ItemFrame() {
- type = ITEM_FRAME;
- parent_frame = nullptr;
- cell = false;
- parent_line = 0;
- }
+ int first_invalid_line = 0;
+ int first_resized_line = 0;
+
+ ItemFrame *parent_frame = nullptr;
+
+ Color odd_row_bg = Color(0, 0, 0, 0);
+ Color even_row_bg = Color(0, 0, 0, 0);
+ Color border = Color(0, 0, 0, 0);
+ Size2 min_size_over = Size2(-1, -1);
+ Size2 max_size_over = Size2(-1, -1);
+ Rect2 padding;
+
+ ItemFrame() { type = ITEM_FRAME; }
};
struct ItemText : public Item {
@@ -148,6 +142,7 @@ private:
struct ItemImage : public Item {
Ref<Texture2D> image;
+ VAlign inline_align = VALIGN_TOP;
Size2 size;
Color color;
ItemImage() { type = ITEM_IMAGE; }
@@ -158,11 +153,31 @@ private:
ItemFont() { type = ITEM_FONT; }
};
+ struct ItemFontSize : public Item {
+ int font_size = 16;
+ ItemFontSize() { type = ITEM_FONT_SIZE; }
+ };
+
+ struct ItemFontFeatures : public Item {
+ Dictionary opentype_features;
+ ItemFontFeatures() { type = ITEM_FONT_FEATURES; }
+ };
+
struct ItemColor : public Item {
Color color;
ItemColor() { type = ITEM_COLOR; }
};
+ struct ItemOutlineSize : public Item {
+ int outline_size = 0;
+ ItemOutlineSize() { type = ITEM_OUTLINE_SIZE; }
+ };
+
+ struct ItemOutlineColor : public Item {
+ Color color;
+ ItemOutlineColor() { type = ITEM_OUTLINE_COLOR; }
+ };
+
struct ItemUnderline : public Item {
ItemUnderline() { type = ITEM_UNDERLINE; }
};
@@ -176,18 +191,23 @@ private:
ItemMeta() { type = ITEM_META; }
};
- struct ItemAlign : public Item {
- Align align;
- ItemAlign() { type = ITEM_ALIGN; }
+ struct ItemParagraph : public Item {
+ Align align = ALIGN_LEFT;
+ String language;
+ Control::TextDirection direction = Control::TEXT_DIRECTION_AUTO;
+ Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
+ ItemParagraph() { type = ITEM_PARAGRAPH; }
};
struct ItemIndent : public Item {
- int level;
+ int level = 0;
ItemIndent() { type = ITEM_INDENT; }
};
struct ItemList : public Item {
- ListType list_type;
+ ListType list_type = LIST_DOTS;
+ bool capitalize = false;
+ int level = 0;
ItemList() { type = ITEM_LIST; }
};
@@ -205,37 +225,32 @@ private:
};
Vector<Column> columns;
- int total_width;
+ Vector<float> rows;
+
+ int total_width = 0;
+ int total_height = 0;
+ VAlign inline_align = VALIGN_TOP;
ItemTable() { type = ITEM_TABLE; }
};
struct ItemFade : public Item {
- int starting_index;
- int length;
+ int starting_index = 0;
+ int length = 0;
ItemFade() { type = ITEM_FADE; }
};
struct ItemFX : public Item {
- float elapsed_time;
-
- ItemFX() {
- elapsed_time = 0.0f;
- }
+ float elapsed_time = 0.f;
};
struct ItemShake : public ItemFX {
- int strength;
- float rate;
- uint64_t _current_rng;
- uint64_t _previous_rng;
-
- ItemShake() {
- strength = 0;
- rate = 0.0f;
- _current_rng = 0;
- type = ITEM_SHAKE;
- }
+ int strength = 0;
+ float rate = 0.0f;
+ uint64_t _current_rng = 0;
+ uint64_t _previous_rng = 0;
+
+ ItemShake() { type = ITEM_SHAKE; }
void reroll_random() {
_previous_rng = _current_rng;
@@ -254,38 +269,25 @@ private:
};
struct ItemWave : public ItemFX {
- float frequency;
- float amplitude;
+ float frequency = 1.0f;
+ float amplitude = 1.0f;
- ItemWave() {
- frequency = 1.0f;
- amplitude = 1.0f;
- type = ITEM_WAVE;
- }
+ ItemWave() { type = ITEM_WAVE; }
};
struct ItemTornado : public ItemFX {
- float radius;
- float frequency;
+ float radius = 1.0f;
+ float frequency = 1.0f;
- ItemTornado() {
- radius = 1.0f;
- frequency = 1.0f;
- type = ITEM_TORNADO;
- }
+ ItemTornado() { type = ITEM_TORNADO; }
};
struct ItemRainbow : public ItemFX {
- float saturation;
- float value;
- float frequency;
-
- ItemRainbow() {
- saturation = 0.8f;
- value = 0.8f;
- frequency = 1.0f;
- type = ITEM_RAINBOW;
- }
+ float saturation = 0.8f;
+ float value = 0.8f;
+ float frequency = 1.0f;
+
+ ItemRainbow() { type = ITEM_RAINBOW; }
};
struct ItemCustomFX : public ItemFX {
@@ -294,7 +296,6 @@ private:
ItemCustomFX() {
type = ITEM_CUSTOMFX;
-
char_fx_transform.instance();
}
@@ -319,7 +320,8 @@ private:
int scroll_w;
bool scroll_updated;
bool updating_scroll;
- int current_idx;
+ int current_idx = 1;
+ int current_char_ofs = 0;
int visible_line_count;
int tab_size;
@@ -339,24 +341,25 @@ private:
void _add_item(Item *p_item, bool p_enter = false, bool p_ensure_newline = false);
void _remove_item(Item *p_item, const int p_line, const int p_subitem_line);
- struct ProcessState {
- int line_width;
- };
-
- enum ProcessMode {
-
- PROCESS_CACHE,
- PROCESS_DRAW,
- PROCESS_POINTER
- };
+ String language;
+ TextDirection text_direction = TEXT_DIRECTION_AUTO;
+ Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
+ Array st_args;
struct Selection {
- Item *click;
+ ItemFrame *click_frame;
+ int click_line;
+ Item *click_item;
int click_char;
- Item *from;
+ ItemFrame *from_frame;
+ int from_line;
+ Item *from_item;
int from_char;
- Item *to;
+
+ ItemFrame *to_frame;
+ int to_line;
+ Item *to_item;
int to_char;
bool active; // anything selected? i.e. from, to, etc. valid?
@@ -368,18 +371,38 @@ private:
int visible_characters;
float percent_visible;
- int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr, int p_char_count = 0);
- void _find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr);
+ void _find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr);
+
+ String _get_line_text(ItemFrame *p_frame, int p_line, Selection p_sel);
+ bool _search_line(ItemFrame *p_frame, int p_line, const String &p_string, Item *p_from, Item *p_to);
+
+ void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset);
+ void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width);
+ float _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs);
+ float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr);
+
+ String _roman(int p_num, bool p_capitalize) const;
+ String _letters(int p_num, bool p_capitalize) const;
+ Item *_get_item_at_pos(Item *p_item_from, Item *p_item_to, int p_position);
+ void _find_frame(Item *p_item, ItemFrame **r_frame, int *r_line);
Ref<Font> _find_font(Item *p_item);
- int _find_margin(Item *p_item, const Ref<Font> &p_base_font);
+ int _find_font_size(Item *p_item);
+ Dictionary _find_font_features(Item *p_item);
+ int _find_outline_size(Item *p_item);
+ ItemList *_find_list_item(Item *p_item);
+ int _find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list);
+ int _find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size);
Align _find_align(Item *p_item);
+ TextServer::Direction _find_direction(Item *p_item);
+ Control::StructuredTextParser _find_stt(Item *p_item);
+ String _find_language(Item *p_item);
Color _find_color(Item *p_item, const Color &p_default_color);
+ Color _find_outline_color(Item *p_item, const Color &p_default_color);
bool _find_underline(Item *p_item);
bool _find_strikethrough(Item *p_item);
bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = nullptr);
bool _find_layout_subitem(Item *from, Item *to);
- bool _find_by_type(Item *p_item, ItemType p_type);
void _fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack);
static Color _get_color_from_string(const String &p_color_str, const Color &p_default_color);
@@ -399,8 +422,6 @@ private:
bool use_bbcode;
String bbcode;
- void _update_all_lines();
-
int fixed_width;
bool fit_content_height;
@@ -411,23 +432,27 @@ protected:
public:
String get_text();
void add_text(const String &p_text);
- void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0));
+ void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), VAlign p_align = VALIGN_TOP);
void add_newline();
bool remove_line(const int p_line);
void push_font(const Ref<Font> &p_font);
+ void push_font_size(int p_font_size);
+ void push_font_features(const Dictionary &p_features);
+ void push_outline_size(int p_font_size);
void push_normal();
void push_bold();
void push_bold_italics();
void push_italics();
void push_mono();
void push_color(const Color &p_color);
+ void push_outline_color(const Color &p_color);
void push_underline();
void push_strikethrough();
- void push_align(Align p_align);
+ void push_paragraph(Align p_align, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", Control::StructuredTextParser p_st_parser = STRUCTURED_TEXT_DEFAULT);
void push_indent(int p_level);
- void push_list(ListType p_list);
+ void push_list(int p_level, ListType p_list, bool p_capitalize);
void push_meta(const Variant &p_meta);
- void push_table(int p_columns);
+ void push_table(int p_columns, VAlign p_align = VALIGN_TOP);
void push_fade(int p_start_index, int p_length);
void push_shake(int p_strength, float p_rate);
void push_wave(float p_frequency, float p_amplitude);
@@ -435,6 +460,10 @@ public:
void push_rainbow(float p_saturation, float p_value, float p_frequency);
void push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment);
void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1);
+ void set_cell_row_background_color(const Color &p_odd_row_bg, const Color &p_even_row_bg);
+ void set_cell_border_color(const Color &p_color);
+ void set_cell_size_override(const Size2 &p_min_size, const Size2 &p_max_size);
+ void set_cell_padding(const Rect2 &p_padding);
int get_current_table_column() const;
void push_cell();
void pop();
@@ -475,6 +504,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);
@@ -488,6 +518,18 @@ public:
void set_text(const String &p_string);
+ void set_text_direction(TextDirection p_text_direction);
+ TextDirection get_text_direction() const;
+
+ void set_language(const String &p_language);
+ String get_language() const;
+
+ void set_structured_text_bidi_override(Control::StructuredTextParser p_parser);
+ Control::StructuredTextParser get_structured_text_bidi_override() const;
+
+ void set_structured_text_bidi_override_options(Array p_args);
+ Array get_structured_text_bidi_override_options() const;
+
void set_visible_characters(int p_visible);
int get_visible_characters() const;
int get_total_character_count() const;
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 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.h b/scene/gui/shortcut.h
index 0d7809e5cf..176958b397 100644
--- a/scene/gui/shortcut.h
+++ b/scene/gui/shortcut.h
@@ -32,7 +32,7 @@
#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);
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index ae2f99e91d..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;
}
@@ -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 d47f771d1d..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;
@@ -422,6 +512,7 @@ void Tabs::set_tab_title(int p_tab, const String &p_title) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].text = p_title;
tabs.write[p_tab].xl_text = tr(p_title);
+ _shape(p_tab);
update();
minimum_size_changed();
}
@@ -431,6 +522,61 @@ String Tabs::get_tab_title(int p_tab) const {
return tabs[p_tab].text;
}
+void Tabs::set_tab_text_direction(int p_tab, Control::TextDirection p_text_direction) {
+ ERR_FAIL_INDEX(p_tab, tabs.size());
+ ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
+ if (tabs[p_tab].text_direction != p_text_direction) {
+ tabs.write[p_tab].text_direction = p_text_direction;
+ _shape(p_tab);
+ update();
+ }
+}
+
+Control::TextDirection Tabs::get_tab_text_direction(int p_tab) const {
+ ERR_FAIL_INDEX_V(p_tab, tabs.size(), Control::TEXT_DIRECTION_INHERITED);
+ return tabs[p_tab].text_direction;
+}
+
+void Tabs::clear_tab_opentype_features(int p_tab) {
+ ERR_FAIL_INDEX(p_tab, tabs.size());
+ tabs.write[p_tab].opentype_features.clear();
+ _shape(p_tab);
+ update();
+}
+
+void Tabs::set_tab_opentype_feature(int p_tab, const String &p_name, int p_value) {
+ ERR_FAIL_INDEX(p_tab, tabs.size());
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!tabs[p_tab].opentype_features.has(tag) || (int)tabs[p_tab].opentype_features[tag] != p_value) {
+ tabs.write[p_tab].opentype_features[tag] = p_value;
+ _shape(p_tab);
+ update();
+ }
+}
+
+int Tabs::get_tab_opentype_feature(int p_tab, const String &p_name) const {
+ ERR_FAIL_INDEX_V(p_tab, tabs.size(), -1);
+ int32_t tag = TS->name_to_tag(p_name);
+ if (!tabs[p_tab].opentype_features.has(tag)) {
+ return -1;
+ }
+ return tabs[p_tab].opentype_features[tag];
+}
+
+void Tabs::set_tab_language(int p_tab, const String &p_language) {
+ ERR_FAIL_INDEX(p_tab, tabs.size());
+ if (tabs[p_tab].language != p_language) {
+ tabs.write[p_tab].language = p_language;
+ _shape(p_tab);
+ update();
+ }
+}
+
+String Tabs::get_tab_language(int p_tab) const {
+ ERR_FAIL_INDEX_V(p_tab, tabs.size(), "");
+ return tabs[p_tab].language;
+}
+
void Tabs::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].icon = p_icon;
@@ -495,7 +641,7 @@ void Tabs::_update_hover() {
}
if (hover != hover_now) {
hover = hover_now;
- emit_signal("tab_hover", hover);
+ emit_signal("tab_hovered", hover);
}
if (hover_buttons == -1) { // no hover
@@ -508,7 +654,6 @@ void Tabs::_update_cache() {
Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled");
Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg");
Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg");
- Ref<Font> font = get_theme_font("font");
Ref<Texture2D> incr = get_theme_icon("increment");
Ref<Texture2D> decr = get_theme_icon("decrement");
int limit = get_size().width - incr->get_width() - decr->get_width();
@@ -520,7 +665,8 @@ void Tabs::_update_cache() {
for (int i = 0; i < tabs.size(); i++) {
tabs.write[i].ofs_cache = mw;
tabs.write[i].size_cache = get_tab_width(i);
- tabs.write[i].size_text = Math::ceil(font->get_string_size(tabs[i].xl_text).width);
+ tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x);
+ tabs.write[i].text_buf->set_width(-1);
mw += tabs[i].size_cache;
if (tabs[i].size_cache <= min_width || i == current) {
size_fixed += tabs[i].size_cache;
@@ -562,6 +708,7 @@ void Tabs::_update_cache() {
tabs.write[i].ofs_cache = w;
tabs.write[i].size_cache = lsize;
tabs.write[i].size_text = slen;
+ tabs.write[i].text_buf->set_width(slen);
w += lsize;
}
}
@@ -578,6 +725,9 @@ void Tabs::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) {
Tab t;
t.text = p_str;
t.xl_text = tr(p_str);
+ t.text_buf.instance();
+ t.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
+ t.text_buf->add_string(t.xl_text, get_theme_font("font"), get_theme_font_size("font_size"), Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
t.icon = p_icon;
t.disabled = false;
t.ofs_cache = 0;
@@ -771,7 +921,6 @@ int Tabs::get_tab_width(int p_idx) const {
Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg");
Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg");
Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled");
- Ref<Font> font = get_theme_font("font");
int x = 0;
@@ -783,7 +932,7 @@ int Tabs::get_tab_width(int p_idx) const {
}
}
- x += Math::ceil(font->get_string_size(tabs[p_idx].xl_text).width);
+ x += Math::ceil(tabs[p_idx].text_buf->get_size().x);
if (tabs[p_idx].disabled) {
x += tab_disabled->get_minimum_size().width;
@@ -869,7 +1018,11 @@ void Tabs::ensure_tab_visible(int p_idx) {
Rect2 Tabs::get_tab_rect(int p_tab) const {
ERR_FAIL_INDEX_V(p_tab, tabs.size(), Rect2());
- return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height);
+ if (is_layout_rtl()) {
+ return Rect2(get_size().width - tabs[p_tab].ofs_cache - tabs[p_tab].size_cache, 0, tabs[p_tab].size_cache, get_size().height);
+ } else {
+ return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height);
+ }
}
void Tabs::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) {
@@ -927,6 +1080,13 @@ void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_previous_tab"), &Tabs::get_previous_tab);
ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &Tabs::set_tab_title);
ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &Tabs::get_tab_title);
+ ClassDB::bind_method(D_METHOD("set_tab_text_direction", "tab_idx", "direction"), &Tabs::set_tab_text_direction);
+ ClassDB::bind_method(D_METHOD("get_tab_text_direction", "tab_idx"), &Tabs::get_tab_text_direction);
+ ClassDB::bind_method(D_METHOD("set_tab_opentype_feature", "tab_idx", "tag", "values"), &Tabs::set_tab_opentype_feature);
+ ClassDB::bind_method(D_METHOD("get_tab_opentype_feature", "tab_idx", "tag"), &Tabs::get_tab_opentype_feature);
+ ClassDB::bind_method(D_METHOD("clear_tab_opentype_features", "tab_idx"), &Tabs::clear_tab_opentype_features);
+ ClassDB::bind_method(D_METHOD("set_tab_language", "tab_idx", "language"), &Tabs::set_tab_language);
+ ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &Tabs::get_tab_language);
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &Tabs::set_tab_icon);
ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &Tabs::get_tab_icon);
ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &Tabs::set_tab_disabled);
@@ -954,8 +1114,8 @@ void Tabs::_bind_methods() {
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab")));
- ADD_SIGNAL(MethodInfo("tab_close", PropertyInfo(Variant::INT, "tab")));
- ADD_SIGNAL(MethodInfo("tab_hover", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_closed", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("reposition_active_tab_request", PropertyInfo(Variant::INT, "idx_to")));
ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h
index b94c4a37a1..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;
@@ -103,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);
@@ -119,6 +126,16 @@ public:
void set_tab_title(int p_tab, const String &p_title);
String get_tab_title(int p_tab) const;
+ void set_tab_text_direction(int p_tab, TextDirection p_text_direction);
+ TextDirection get_tab_text_direction(int p_tab) const;
+
+ void set_tab_opentype_feature(int p_tab, const String &p_name, int p_value);
+ int get_tab_opentype_feature(int p_tab, const String &p_name) const;
+ void clear_tab_opentype_features(int p_tab);
+
+ void set_tab_language(int p_tab, const String &p_language);
+ String get_tab_language(int p_tab) const;
+
void set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_tab_icon(int p_tab) const;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 957e1c11c7..f3569f9ce3 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
@@ -113,63 +115,118 @@ void TextEdit::Text::set_font(const Ref<Font> &p_font) {
font = p_font;
}
+void TextEdit::Text::set_font_size(int p_font_size) {
+ font_size = p_font_size;
+}
+
void TextEdit::Text::set_indent_size(int p_indent_size) {
indent_size = p_indent_size;
}
-void TextEdit::Text::_update_line_cache(int p_line) const {
- int w = 0;
+void TextEdit::Text::set_font_features(const Dictionary &p_features) {
+ opentype_features = p_features;
+}
- int len = text[p_line].data.length();
- const char32_t *str = text[p_line].data.get_data();
+void TextEdit::Text::set_direction_and_language(TextServer::Direction p_direction, String p_language) {
+ direction = p_direction;
+ language = p_language;
+}
- // Update width.
+void TextEdit::Text::set_draw_control_chars(bool p_draw_control_chars) {
+ draw_control_chars = p_draw_control_chars;
+}
- for (int i = 0; i < len; i++) {
- w += get_char_width(str[i], str[i + 1], w);
- }
+int TextEdit::Text::get_line_width(int p_line) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), 0);
+ return text[p_line].data_buf->get_size().x;
+}
- 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;
+}
+
+int TextEdit::Text::get_line_wrap_amount(int p_line) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), 0);
- if (text[p_line].width_cache == -1) {
- _update_line_cache(p_line);
+ return text[p_line].data_buf->get_line_count() - 1;
+}
+
+Vector<Vector2i> TextEdit::Text::get_line_wrap_ranges(int p_line) const {
+ Vector<Vector2i> ret;
+ ERR_FAIL_INDEX_V(p_line, text.size(), ret);
+
+ for (int i = 0; i < text[p_line].data_buf->get_line_count(); i++) {
+ ret.push_back(text[p_line].data_buf->get_line_range(i));
}
+ return ret;
+}
- return text[p_line].width_cache;
+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;
}
-void TextEdit::Text::set_line_wrap_amount(int p_line, int p_wrap_amount) const {
+_FORCE_INLINE_ const String &TextEdit::Text::operator[](int p_line) const {
+ return text[p_line].data;
+}
+
+void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Vector<Vector2i> &p_bidi_override) {
ERR_FAIL_INDEX(p_line, text.size());
- text.write[p_line].wrap_amount_cache = p_wrap_amount;
-}
+ if (font.is_null() || font_size <= 0) {
+ return; // Not in tree?
+ }
-int TextEdit::Text::get_line_wrap_amount(int p_line) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), -1);
+ text.write[p_line].data_buf->clear();
+ text.write[p_line].data_buf->set_width(width);
+ text.write[p_line].data_buf->set_direction((TextServer::Direction)direction);
+ text.write[p_line].data_buf->set_preserve_control(draw_control_chars);
+ if (p_ime_text.length() > 0) {
+ text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language);
+ if (!p_bidi_override.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);
+ }
+ }
- return text[p_line].wrap_amount_cache;
+ // Apply tab align.
+ if (indent_size > 0) {
+ Vector<float> tabs;
+ tabs.push_back(font->get_char_size('m', 0, font_size).width * indent_size);
+ text.write[p_line].data_buf->tab_align(tabs);
+ }
}
-void TextEdit::Text::clear_width_cache() {
+void TextEdit::Text::invalidate_all_lines() {
for (int i = 0; i < text.size(); i++) {
- text.write[i].width_cache = -1;
+ text.write[i].data_buf->set_width(width);
+ if (indent_size > 0) {
+ Vector<float> tabs;
+ tabs.push_back(font->get_char_size('m', 0, font_size).width * indent_size);
+ text.write[i].data_buf->tab_align(tabs);
+ }
}
}
-void TextEdit::Text::clear_wrap_cache() {
+void TextEdit::Text::invalidate_all() {
for (int i = 0; i < text.size(); i++) {
- text.write[i].wrap_amount_cache = -1;
+ invalidate_cache(i);
}
}
void TextEdit::Text::clear() {
text.clear();
- insert(0, "");
+ insert(0, "", Vector<Vector2i>());
}
int TextEdit::Text::get_max_width(bool p_exclude_hidden) const {
@@ -184,46 +241,30 @@ int TextEdit::Text::get_max_width(bool p_exclude_hidden) const {
return max;
}
-void TextEdit::Text::set(int p_line, const String &p_text) {
+void TextEdit::Text::set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override) {
ERR_FAIL_INDEX(p_line, text.size());
- text.write[p_line].width_cache = -1;
- text.write[p_line].wrap_amount_cache = -1;
text.write[p_line].data = p_text;
+ text.write[p_line].bidi_override = p_bidi_override;
+ invalidate_cache(p_line);
}
-void TextEdit::Text::insert(int p_at, const String &p_text) {
+void TextEdit::Text::insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override) {
Line line;
line.gutters.resize(gutter_count);
line.marked = false;
line.hidden = false;
- line.width_cache = -1;
- line.wrap_amount_cache = -1;
line.data = p_text;
+ line.bidi_override = p_bidi_override;
text.insert(p_at, line);
+
+ invalidate_cache(p_at);
}
void TextEdit::Text::remove(int p_at) {
text.remove(p_at);
}
-int TextEdit::Text::get_char_width(char32_t c, char32_t next_c, int px) const {
- int tab_w = font->get_char_size(' ').width * indent_size;
- int w = 0;
-
- if (c == '\t') {
- int left = px % tab_w;
- if (left == 0) {
- w = tab_w;
- } else {
- w = tab_w - px % tab_w; // Is right.
- }
- } else {
- w = font->get_char_size(c, next_c).width;
- }
- return w;
-}
-
void TextEdit::Text::add_gutter(int p_at) {
for (int i = 0; i < text.size(); i++) {
if (p_at < 0 || p_at > gutter_count) {
@@ -318,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: {
@@ -338,9 +379,17 @@ void TextEdit::_click_selection_held() {
}
}
+Point2 TextEdit::_get_local_mouse_pos() const {
+ Point2 mp = get_local_mouse_position();
+ if (is_layout_rtl()) {
+ mp.x = get_size().width - mp.x;
+ }
+ return mp;
+}
+
void TextEdit::_update_selection_mode_pointer() {
dragging_selection = true;
- Point2 mp = get_local_mouse_position();
+ Point2 mp = _get_local_mouse_pos();
int row, col;
_get_mouse_pos(Point2i(mp.x, mp.y), row, col);
@@ -356,7 +405,7 @@ void TextEdit::_update_selection_mode_pointer() {
void TextEdit::_update_selection_mode_word() {
dragging_selection = true;
- Point2 mp = get_local_mouse_position();
+ Point2 mp = _get_local_mouse_pos();
int row, col;
_get_mouse_pos(Point2i(mp.x, mp.y), row, col);
@@ -413,7 +462,7 @@ void TextEdit::_update_selection_mode_word() {
void TextEdit::_update_selection_mode_line() {
dragging_selection = true;
- Point2 mp = get_local_mouse_position();
+ Point2 mp = _get_local_mouse_pos();
int row, col;
_get_mouse_pos(Point2i(mp.x, mp.y), row, col);
@@ -438,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)) {
@@ -479,7 +528,8 @@ void TextEdit::_update_minimap_drag() {
control_height = scroll_height;
}
- Point2 mp = get_local_mouse_position();
+ Point2 mp = _get_local_mouse_pos();
+
double diff = (mp.y - minimap_scroll_click_pos) / control_height;
v_scroll->set_as_ratio(minimap_scroll_ratio + diff);
}
@@ -494,7 +544,7 @@ void TextEdit::_notification(int p_what) {
if (text_changed_dirty) {
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
}
- _update_wrap_at();
+ _update_wrap_at(true);
} break;
case NOTIFICATION_RESIZED: {
_update_scrollbars();
@@ -506,9 +556,11 @@ void TextEdit::_notification(int p_what) {
call_deferred("_update_wrap_at");
}
} break;
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
_update_caches();
- _update_wrap_at();
+ _update_wrap_at(true);
} break;
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
window_has_focus = true;
@@ -556,6 +608,7 @@ void TextEdit::_notification(int p_what) {
}
Size2 size = get_size();
+ bool rtl = is_layout_rtl();
if ((!has_focus() && !menu->has_focus()) || !window_has_focus) {
draw_caret = false;
}
@@ -582,8 +635,6 @@ void TextEdit::_notification(int p_what) {
cache.style_focus->draw(ci, Rect2(Point2(), size));
}
- int ascent = cache.font->get_ascent();
-
int visible_rows = get_visible_rows() + 1;
Color color = readonly ? cache.font_color_readonly : cache.font_color;
@@ -593,17 +644,25 @@ void TextEdit::_notification(int p_what) {
}
if (line_length_guidelines) {
- const int hard_x = xmargin_beg + (int)cache.font->get_char_size('0').width * line_length_guideline_hard_col - cursor.x_ofs;
+ const int hard_x = xmargin_beg + (int)cache.font->get_char_size('0', 0, cache.font_size).width * line_length_guideline_hard_col - cursor.x_ofs;
if (hard_x > xmargin_beg && hard_x < xmargin_end) {
- RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(hard_x, 0), Point2(hard_x, size.height), cache.line_length_guideline_color);
+ if (rtl) {
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(size.width - hard_x, 0), Point2(size.width - hard_x, size.height), cache.line_length_guideline_color);
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(hard_x, 0), Point2(hard_x, size.height), cache.line_length_guideline_color);
+ }
}
// Draw a "Soft" line length guideline, less visible than the hard line length guideline.
// It's usually set to a lower column compared to the hard line length guideline.
// Only drawn if its column differs from the hard line length guideline.
- const int soft_x = xmargin_beg + (int)cache.font->get_char_size('0').width * line_length_guideline_soft_col - cursor.x_ofs;
+ const int soft_x = xmargin_beg + (int)cache.font->get_char_size('0', 0, cache.font_size).width * line_length_guideline_soft_col - cursor.x_ofs;
if (hard_x != soft_x && soft_x > xmargin_beg && soft_x < xmargin_end) {
- RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(soft_x, 0), Point2(soft_x, size.height), cache.line_length_guideline_color * Color(1, 1, 1, 0.5));
+ if (rtl) {
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(size.width - soft_x, 0), Point2(size.width - soft_x, size.height), cache.line_length_guideline_color * Color(1, 1, 1, 0.5));
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(soft_x, 0), Point2(soft_x, size.height), cache.line_length_guideline_color * Color(1, 1, 1, 0.5));
+ }
}
}
@@ -762,7 +821,7 @@ void TextEdit::_notification(int p_what) {
int cursor_wrap_index = get_cursor_wrap_index();
- FontDrawer drawer(cache.font, Color(1, 1, 1));
+ //FontDrawer drawer(cache.font, Color(1, 1, 1));
int first_visible_line = get_first_visible_line() - 1;
int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0);
@@ -791,7 +850,11 @@ void TextEdit::_notification(int p_what) {
// draw the minimap
Color viewport_color = (cache.background_color.get_v() < 0.5) ? Color(1, 1, 1, 0.1) : Color(0, 0, 0, 0.1);
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), viewport_offset_y, cache.minimap_width, viewport_height), viewport_color);
+ if (rtl) {
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - cache.minimap_width, viewport_offset_y, cache.minimap_width, viewport_height), viewport_color);
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), viewport_offset_y, cache.minimap_width, viewport_height), viewport_color);
+ }
for (int i = 0; i < minimap_draw_amount; i++) {
minimap_line++;
@@ -841,7 +904,11 @@ void TextEdit::_notification(int p_what) {
}
if (minimap_line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), i * 3, cache.minimap_width, 2), cache.current_line_color);
+ if (rtl) {
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - cache.minimap_width, i * 3, cache.minimap_width, 2), cache.current_line_color);
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), i * 3, cache.minimap_width, 2), cache.current_line_color);
+ }
}
Color previous_color;
@@ -888,7 +955,11 @@ void TextEdit::_notification(int p_what) {
// take one for zero indexing, and if we hit whitespace / the end of a word.
int chars = MAX(0, (j - (characters - 1)) - (is_whitespace ? 1 : 0)) + 1;
int char_x_ofs = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * chars)) + tabs;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_x_ofs, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color);
+ if (rtl) {
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(size.width - char_x_ofs - minimap_char_size.x * characters, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color);
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_x_ofs, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color);
+ }
}
if (out_of_bounds) {
@@ -907,6 +978,7 @@ void TextEdit::_notification(int p_what) {
}
// draw main text
+ int row_height = get_row_height();
int line = first_visible_line;
for (int i = 0; i < draw_amount; i++) {
line++;
@@ -926,20 +998,17 @@ void TextEdit::_notification(int p_what) {
continue;
}
- const String &fullstr = text[line];
-
Dictionary color_map = _get_line_syntax_highlighting(line);
// Ensure we at least use the font color.
Color current_color = readonly ? cache.font_color_readonly : cache.font_color;
- bool underlined = false;
+ const Ref<TextParagraph> ldata = text.get_line_data(line);
Vector<String> wrap_rows = get_wrap_rows_text(line);
int line_wrap_amount = times_line_wraps(line);
- int last_wrap_column = 0;
- for (int line_wrap_index = 0; line_wrap_index < line_wrap_amount + 1; line_wrap_index++) {
+ for (int line_wrap_index = 0; line_wrap_index <= line_wrap_amount; line_wrap_index++) {
if (line_wrap_index != 0) {
i++;
if (i >= draw_amount) {
@@ -948,18 +1017,7 @@ void TextEdit::_notification(int p_what) {
}
const String &str = wrap_rows[line_wrap_index];
- int indent_px = line_wrap_index != 0 ? get_indent_level(line) * cache.font->get_char_size(' ').width : 0;
- if (indent_px >= wrap_at) {
- indent_px = 0;
- }
-
- if (line_wrap_index > 0) {
- last_wrap_column += wrap_rows[line_wrap_index - 1].length();
- }
-
int char_margin = xmargin_beg - cursor.x_ofs;
- char_margin += indent_px;
- int char_ofs = 0;
int ofs_readonly = 0;
int ofs_x = 0;
@@ -968,48 +1026,45 @@ 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);
+ }
}
}
@@ -1031,8 +1086,12 @@ void TextEdit::_notification(int p_what) {
break;
}
- int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
- cache.font->draw(ci, Point2(gutter_offset + ofs_x, yofs + cache.font->get_ascent()), text, get_line_gutter_item_color(line, g));
+ Ref<TextLine> tl;
+ tl.instance();
+ tl->add_string(text, cache.font, cache.font_size);
+
+ int yofs = ofs_y + (row_height - tl->get_size().y) / 2;
+ tl->draw(ci, Point2(gutter_offset + ofs_x, yofs), get_line_gutter_item_color(line, g));
} break;
case GUTTER_TPYE_ICON: {
const Ref<Texture2D> icon = get_line_gutter_icon(line, g);
@@ -1040,7 +1099,7 @@ void TextEdit::_notification(int p_what) {
break;
}
- Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, get_row_height()));
+ Rect2 gutter_rect = Rect2(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, row_height));
int horizontal_padding = gutter_rect.size.x / 6;
int vertical_padding = gutter_rect.size.y / 6;
@@ -1048,13 +1107,28 @@ void TextEdit::_notification(int p_what) {
gutter_rect.position += Point2(horizontal_padding, vertical_padding);
gutter_rect.size -= Point2(horizontal_padding, vertical_padding) * 2;
+ // Correct icon aspect ratio.
+ float icon_ratio = icon->get_width() / icon->get_height();
+ float gutter_ratio = gutter_rect.size.x / gutter_rect.size.y;
+ if (gutter_ratio > icon_ratio) {
+ gutter_rect.size.x = floor(icon->get_width() * (gutter_rect.size.y / icon->get_height()));
+ } else {
+ gutter_rect.size.y = floor(icon->get_height() * (gutter_rect.size.x / icon->get_width()));
+ }
+ if (rtl) {
+ gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x;
+ }
+
icon->draw_rect(ci, gutter_rect, false, get_line_gutter_item_color(line, g));
} break;
case GUTTER_TPYE_CUSTOM: {
if (gutter.custom_draw_obj.is_valid()) {
Object *cdo = ObjectDB::get_instance(gutter.custom_draw_obj);
if (cdo) {
- Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, get_row_height()));
+ Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, row_height));
+ if (rtl) {
+ gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x;
+ }
cdo->call(gutter.custom_draw_callback, line, g, Rect2(gutter_rect));
}
}
@@ -1065,296 +1139,303 @@ void TextEdit::_notification(int p_what) {
}
}
- // Loop through characters in one line.
- int j = 0;
- for (; j < str.length(); j++) {
- if (color_map.has(last_wrap_column + j)) {
- current_color = color_map[last_wrap_column + j].get("color");
- if (readonly && current_color.a > cache.font_color_readonly.a) {
- current_color.a = cache.font_color_readonly.a;
- }
- }
- color = current_color;
-
- int char_w;
+ // 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);
- // 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);
- }
-
- bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col + highlighted_text.length());
+ ofs_y += (row_height - text_height) / 2;
- // If this is the original highlighted text we don't want to highlight it again.
- if (cursor.line == line && cursor_wrap_index == line_wrap_index && (cursor.column >= highlighted_text_col && cursor.column <= highlighted_text_col + highlighted_text.length())) {
- in_highlighted_word = false;
- }
+ const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid);
+ const TextServer::Glyph *glyphs = visual.ptr();
+ int gl_size = visual.size();
- if (in_highlighted_word) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.word_highlighted_color);
- }
+ ofs_y += ldata->get_line_ascent(line_wrap_index);
+ 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;
- }
-
- char32_t cchar = ime_text[ofs];
- char32_t next = ime_text[ofs + 1];
- int im_char_width = cache.font->get_char_size(cchar, next).width;
-
- if ((char_ofs + char_margin + im_char_width) >= xmargin_end) {
- break;
- }
-
- bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y;
- if (selected) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color);
- } else {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
- }
-
- drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
+ if (line_wrap_index == line_wrap_amount && is_folded(line)) {
+ int yofs = (text_height - cache.folded_eol_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index);
+ int xofs = cache.folded_eol_icon->get_width() / 2;
+ Color eol_color = cache.code_folding_color;
+ eol_color.a = 1;
+ cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs + ofs_x, ofs_y + yofs), eol_color);
+ }
- char_ofs += im_char_width;
- ofs++;
- }
- }
- if (ime_text.length() == 0) {
- if (draw_caret) {
- if (insert_mode) {
-#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);
- char32_t cchar = ime_text[ofs];
- char32_t next = ime_text[ofs + 1];
- int im_char_width = cache.font->get_char_size(cchar, next).width;
+ t_caret.position += Vector2(char_margin + ofs_x, ofs_y);
+ t_caret.size.x = caret_width;
- if ((char_ofs + char_margin + im_char_width) >= xmargin_end) {
- break;
+ draw_rect(t_caret, cache.caret_color);
}
-
- bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y;
- if (selected) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color);
- } else {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
+ }
+ } else {
+ {
+ // IME intermidiet text range.
+ Vector<Vector2> sel = TS->shaped_text_get_selection(rid, cursor.column, cursor.column + ime_text.length());
+ for (int j = 0; j < sel.size(); j++) {
+ Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, text_height);
+ if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) {
+ continue;
+ }
+ if (rect.position.x < xmargin_beg) {
+ rect.size.x -= (xmargin_beg - rect.position.x);
+ rect.position.x = xmargin_beg;
+ } else if (rect.position.x + rect.size.x > xmargin_end) {
+ rect.size.x = xmargin_end - rect.position.x;
+ }
+ rect.size.y = caret_width;
+ draw_rect(rect, cache.caret_color);
+ cursor_pos.x = rect.position.x;
}
-
- drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
-
- char_ofs += im_char_width;
- ofs++;
}
- }
- if (ime_text.length() == 0) {
- if (draw_caret) {
- if (insert_mode) {
- int char_w = cache.font->get_char_size(' ').width;
-#ifdef TOOLS_ENABLED
- int caret_h = (block_caret) ? 4 : 2 * EDSCALE;
-#else
- int caret_h = (block_caret) ? 4 : 2;
-#endif
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(char_w, caret_h)), cache.caret_color);
- } else {
- int char_w = cache.font->get_char_size(' ').width;
-#ifdef TOOLS_ENABLED
- int caret_w = (block_caret) ? char_w : 2 * EDSCALE;
-#else
- int caret_w = (block_caret) ? char_w : 2;
-#endif
-
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, cache.font->get_height())), cache.caret_color);
+ {
+ // IME caret.
+ Vector<Vector2> sel = TS->shaped_text_get_selection(rid, cursor.column + ime_selection.x, cursor.column + ime_selection.x + ime_selection.y);
+ for (int j = 0; j < sel.size(); j++) {
+ Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, text_height);
+ if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) {
+ continue;
+ }
+ if (rect.position.x < xmargin_beg) {
+ rect.size.x -= (xmargin_beg - rect.position.x);
+ rect.position.x = xmargin_beg;
+ } else if (rect.position.x + rect.size.x > xmargin_end) {
+ rect.size.x = xmargin_end - rect.position.x;
+ }
+ rect.size.y = caret_width * 3;
+ draw_rect(rect, cache.caret_color);
+ cursor_pos.x = rect.position.x;
}
}
}
}
+ ofs_y += ldata->get_line_descent(line_wrap_index);
}
}
@@ -1363,19 +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;
}
@@ -1386,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;
}
@@ -1415,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;
@@ -1437,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) {
@@ -1475,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) {
@@ -1494,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;
}
@@ -1508,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;
@@ -1526,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;
@@ -1539,12 +1650,26 @@ void TextEdit::_notification(int p_what) {
if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
- Point2 cursor_pos = Point2(cursor_get_column(), cursor_get_line()) * get_row_height();
- DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id());
+ DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + _get_cursor_pixel_pos(false), get_viewport()->get_window_id());
}
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
- DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true);
+ int cursor_start = -1;
+ int cursor_end = -1;
+
+ if (!selection.active) {
+ String full_text = _base_get_text(0, 0, cursor.line, cursor.column);
+
+ cursor_start = full_text.length();
+ } else {
+ String pre_text = _base_get_text(0, 0, selection.from_line, selection.from_column);
+ String post_text = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
+
+ cursor_start = pre_text.length();
+ cursor_end = cursor_start + post_text.length();
+ }
+
+ DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true, -1, cursor_start, cursor_end);
}
} break;
case NOTIFICATION_FOCUS_EXIT: {
@@ -1558,6 +1683,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();
@@ -1567,6 +1693,15 @@ void TextEdit::_notification(int p_what) {
if (has_focus()) {
ime_text = DisplayServer::get_singleton()->ime_get_text();
ime_selection = DisplayServer::get_singleton()->ime_get_selection();
+
+ String t;
+ if (cursor.column >= 0) {
+ t = text[cursor.line].substr(0, cursor.column) + ime_text + text[cursor.line].substr(cursor.column, text[cursor.line].length());
+ } else {
+ t = ime_text;
+ }
+
+ text.invalidate_cache(cursor.line, cursor.column, t, structured_text_parser(st_parser, st_args, t));
update();
}
} break;
@@ -1917,7 +2052,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;
@@ -1928,58 +2063,43 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
} else {
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;
r_col = col;
}
-Vector2i TextEdit::_get_cursor_pixel_pos() {
- adjust_viewport_to_cursor();
- int row = (cursor.line - get_first_visible_line() - cursor.wrap_ofs);
- // Correct for hidden and wrapped lines
- for (int i = get_first_visible_line(); i < cursor.line; i++) {
- if (is_line_hidden(i)) {
- row -= 1;
- continue;
- }
- row += times_line_wraps(i);
+Vector2i TextEdit::_get_cursor_pixel_pos(bool p_adjust_viewport) {
+ if (p_adjust_viewport) {
+ adjust_viewport_to_cursor();
}
- // Row might be wrapped. Adjust row and r_column
- Vector<String> rows2 = get_wrap_rows_text(cursor.line);
- while (rows2.size() > 1) {
- if (cursor.column >= rows2[0].length()) {
- cursor.column -= rows2[0].length();
- rows2.remove(0);
- row++;
- } else {
- break;
+ int row = 1;
+ for (int i = get_first_visible_line(); i < cursor.line; i++) {
+ if (!is_line_hidden(i)) {
+ row += times_line_wraps(i) + 1;
}
}
+ row += cursor.wrap_ofs;
// Calculate final pixel position
- int y = (row - get_v_scroll_offset() + 1 /*Bottom of line*/) * get_row_height();
+ int 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;
- 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++;
+
+ 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);
}
@@ -2044,7 +2164,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;
}
@@ -2065,7 +2193,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();
@@ -2104,7 +2232,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);
int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
for (int i = 0; i < gutters.size(); i++) {
@@ -2112,7 +2240,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
continue;
}
- if (mb->get_position().x > left_margin && mb->get_position().x <= (left_margin + gutters[i].width) - 3) {
+ if (mpos.x > left_margin && mpos.x <= (left_margin + gutters[i].width) - 3) {
emit_signal("gutter_clicked", row, i);
return;
}
@@ -2123,7 +2251,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
// Unfold on folded icon click.
if (is_folded(row)) {
left_margin += gutter_padding + text.get_line_width(row) - cursor.x_ofs;
- if (mb->get_position().x > left_margin && mb->get_position().x <= left_margin + cache.folded_eol_icon->get_width() + 3) {
+ if (mpos.x > left_margin && mpos.x <= left_margin + cache.folded_eol_icon->get_width() + 3) {
unfold_line(row);
return;
}
@@ -2146,7 +2274,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;
@@ -2190,19 +2318,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();
}
@@ -2214,7 +2342,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()) {
@@ -2234,7 +2362,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();
@@ -2244,7 +2372,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;
@@ -2280,9 +2408,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);
}
@@ -2302,13 +2434,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: {
@@ -2336,7 +2468,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());
}
@@ -2585,7 +2718,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) {
@@ -2674,7 +2807,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)) {
@@ -2817,34 +2950,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);
@@ -2914,17 +3025,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);
}
@@ -2935,7 +3041,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()) {
@@ -2977,16 +3087,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);
}
@@ -2997,7 +3103,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()) {
@@ -3136,34 +3246,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;
@@ -3174,7 +3262,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);
@@ -3404,6 +3496,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;
@@ -3491,9 +3597,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();
}
@@ -3613,17 +3718,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();
}
@@ -3687,7 +3792,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
/* STEP 2: Add spaces if the char is greater than the end of the line. */
while (p_char > text[p_line].length()) {
- text.set(p_line, text[p_line] + String::chr(' '));
+ text.set(p_line, text[p_line] + String::chr(' '), structured_text_parser(st_parser, st_args, text[p_line] + String::chr(' ')));
}
/* STEP 3: Separate dest string in pre and post text. */
@@ -3699,13 +3804,13 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
// Insert the substrings.
if (j == 0) {
- text.set(p_line, preinsert_text + substrings[j]);
+ text.set(p_line, preinsert_text + substrings[j], structured_text_parser(st_parser, st_args, preinsert_text + substrings[j]));
} else {
- text.insert(p_line + j, substrings[j]);
+ text.insert(p_line + j, substrings[j], structured_text_parser(st_parser, st_args, substrings[j]));
}
if (j == substrings.size() - 1) {
- text.set(p_line + j, text[p_line + j] + postinsert_text);
+ text.set(p_line + j, text[p_line + j] + postinsert_text, structured_text_parser(st_parser, st_args, text[p_line + j] + postinsert_text));
}
}
@@ -3716,11 +3821,16 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
text.set_hidden(p_line, false);
}
- text.set_line_wrap_amount(p_line, -1);
+ text.invalidate_cache(p_line);
r_end_line = p_line + substrings.size() - 1;
r_end_column = text[r_end_line].length() - postinsert_text.length();
+ TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text.get_line_data(r_end_line)->get_rid(), (r_end_line == p_line) ? cursor.column : 0, r_end_column);
+ if (dir != TextServer::DIRECTION_AUTO) {
+ input_direction = (TextDirection)dir;
+ }
+
if (!text_changed_dirty && !setting_text) {
if (is_inside_tree()) {
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
@@ -3767,9 +3877,10 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
for (int i = p_from_line; i < p_to_line; i++) {
text.remove(p_from_line + 1);
}
- text.set(p_from_line, pre_text + post_text);
+ text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text));
- text.set_line_wrap_amount(p_from_line, -1);
+ //text.set_line_wrap_amount(p_from_line, -1);
+ text.invalidate_cache(p_from_line);
if (!text_changed_dirty && !setting_text) {
if (is_inside_tree()) {
@@ -3943,6 +4054,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 {
@@ -3970,19 +4088,27 @@ int TextEdit::get_total_visible_rows() const {
return total_rows;
}
-void TextEdit::_update_wrap_at() {
- wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding - cache.minimap_width - wrap_right_offset;
- update_cursor_wrap_offset();
- text.clear_wrap_cache();
+void TextEdit::_update_wrap_at(bool p_force) {
+ int new_wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding;
+ if (draw_minimap) {
+ new_wrap_at -= minimap_width;
+ }
+ if (v_scroll->is_visible_in_tree()) {
+ new_wrap_at -= v_scroll->get_combined_minimum_size().width;
+ }
+ new_wrap_at -= wrap_right_offset; // Give it a little more space.
- for (int i = 0; i < text.size(); i++) {
- // Update all values that wrap.
- if (!line_wraps(i)) {
- continue;
+ if ((wrap_at != new_wrap_at) || p_force) {
+ wrap_at = new_wrap_at;
+ if (wrap_enabled) {
+ text.set_width(wrap_at);
+ } else {
+ text.set_width(-1);
}
- Vector<String> rows = get_wrap_rows_text(i);
- text.set_line_wrap_amount(i, rows.size() - 1);
+ text.invalidate_all_lines();
}
+
+ update_cursor_wrap_offset();
}
void TextEdit::adjust_viewport_to_cursor() {
@@ -4014,14 +4140,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;
@@ -4049,14 +4193,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);
+ }
+
+ // 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 (cursor_x < cursor.x_ofs) {
- cursor.x_ofs = cursor_x;
+ if (MIN(cursor_pos.x, cursor_pos.y) < cursor.x_ofs) {
+ cursor.x_ofs = MIN(cursor_pos.x, cursor_pos.y);
}
} else {
cursor.x_ofs = 0;
@@ -4081,24 +4244,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 {
@@ -4110,66 +4266,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()) {
- char32_t c = line_text[col];
- int w = text.get_char_width(c, line_text[col + 1], px + word_px);
-
- int indent_ofs = (cur_wrap_index != 0 ? tab_offset_px : 0);
-
- if (indent_ofs + word_px + w > wrap_at) {
- // Not enough space to add this char; start next line.
- wrap_substring += word_str;
- lines.push_back(wrap_substring);
- cur_wrap_index++;
- wrap_substring = "";
- px = 0;
-
- word_str = "";
- word_str += c;
- word_px = w;
- } else {
- word_str += c;
- word_px += w;
- if (c == ' ') {
- // End of a word; add this word to the substring.
- wrap_substring += word_str;
- px += word_px;
- word_str = "";
- word_px = 0;
- }
-
- if (indent_ofs + px + word_px > wrap_at) {
- // This word will be moved to the next line.
- lines.push_back(wrap_substring);
- // Reset for next wrap.
- cur_wrap_index++;
- wrap_substring = "";
- px = 0;
- }
- }
- col++;
- }
- // 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;
}
@@ -4199,6 +4301,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;
@@ -4333,6 +4443,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;
@@ -4372,101 +4506,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) {
@@ -4476,7 +4556,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);
@@ -4552,7 +4632,7 @@ void TextEdit::set_text(String p_text) {
update();
setting_text = false;
-};
+}
String TextEdit::get_text() {
String longthing;
@@ -4565,11 +4645,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();
@@ -4644,32 +4837,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();
}
@@ -4678,7 +4845,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 {
@@ -4720,6 +4890,7 @@ void TextEdit::_update_caches() {
cache.completion_existing_color = get_theme_color("completion_existing_color");
cache.completion_font_color = get_theme_color("completion_font_color");
cache.font = get_theme_font("font");
+ cache.font_size = get_theme_font_size("font_size");
cache.caret_color = get_theme_color("caret_color");
cache.caret_background_color = get_theme_color("caret_background_color");
cache.font_color = get_theme_color("font_color");
@@ -4740,12 +4911,22 @@ void TextEdit::_update_caches() {
#else
cache.line_spacing = get_theme_constant("line_spacing");
#endif
- cache.row_height = cache.font->get_height() + cache.line_spacing;
cache.tab_icon = get_theme_icon("tab");
cache.space_icon = get_theme_icon("space");
cache.folded_eol_icon = get_theme_icon("GuiEllipsis", "EditorIcons");
+
+ TextServer::Direction dir;
+ if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
+ dir = is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR;
+ } else {
+ dir = (TextServer::Direction)text_direction;
+ }
+ text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text.set_font_features(opentype_features);
+ text.set_draw_control_chars(draw_control_chars);
text.set_font(cache.font);
- text.clear_width_cache();
+ text.set_font_size(cache.font_size);
+ text.invalidate_all();
if (syntax_highlighter.is_valid()) {
syntax_highlighter->set_text_edit(this);
@@ -4985,7 +5166,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 = "";
}
@@ -5011,7 +5192,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);
@@ -5043,7 +5224,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);
@@ -5152,27 +5333,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) {
@@ -5468,7 +5635,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;
@@ -5483,7 +5650,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;
@@ -5858,8 +6025,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++) {
@@ -5884,6 +6054,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 {
@@ -6406,7 +6577,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());
@@ -6453,7 +6624,10 @@ void TextEdit::set_line_length_guideline_hard_column(int p_column) {
}
void TextEdit::set_draw_minimap(bool p_draw) {
- draw_minimap = p_draw;
+ if (draw_minimap != p_draw) {
+ draw_minimap = p_draw;
+ _update_wrap_at();
+ }
update();
}
@@ -6462,7 +6636,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();
}
@@ -6523,6 +6700,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));
+ }
}
}
}
@@ -6584,21 +6856,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);
@@ -6607,6 +6948,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));
@@ -6620,9 +6966,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);
@@ -6739,6 +7093,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_minimap_width"), &TextEdit::get_minimap_width);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "readonly"), "set_readonly", "is_readonly");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs");
@@ -6767,6 +7124,11 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_moving_by_right_click"), "set_right_click_moves_caret", "is_right_click_moving_caret");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_mid_grapheme_caret_enabled", "get_mid_grapheme_caret_enabled");
+
+ ADD_GROUP("Structured Text", "structured_text_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
ADD_SIGNAL(MethodInfo("cursor_changed"));
ADD_SIGNAL(MethodInfo("text_changed"));
@@ -6785,6 +7147,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);
@@ -6806,8 +7189,8 @@ TextEdit::TextEdit() {
wrap_right_offset = 10;
set_focus_mode(FOCUS_ALL);
_update_caches();
- cache.row_height = 1;
cache.line_spacing = 1;
+ cache.font_size = 16;
set_default_cursor_shape(CURSOR_IBEAM);
indent_size = 4;
@@ -6831,7 +7214,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;
@@ -6907,9 +7290,43 @@ TextEdit::TextEdit() {
shortcut_keys_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
+
+ menu_dir = memnew(PopupMenu);
+ menu_dir->set_name("DirMenu");
+ menu_dir->add_radio_check_item(RTR("Same as layout direction"), MENU_DIR_INHERITED);
+ menu_dir->add_radio_check_item(RTR("Auto-detect direction"), MENU_DIR_AUTO);
+ menu_dir->add_radio_check_item(RTR("Left-to-right"), MENU_DIR_LTR);
+ menu_dir->add_radio_check_item(RTR("Right-to-left"), MENU_DIR_RTL);
+ menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), true);
+ menu->add_child(menu_dir);
+
+ menu_ctl = memnew(PopupMenu);
+ menu_ctl->set_name("CTLMenu");
+ menu_ctl->add_item(RTR("Left-to-right mark (LRM)"), MENU_INSERT_LRM);
+ menu_ctl->add_item(RTR("Right-to-left mark (RLM)"), MENU_INSERT_RLM);
+ menu_ctl->add_item(RTR("Start of left-to-right embedding (LRE)"), MENU_INSERT_LRE);
+ menu_ctl->add_item(RTR("Start of right-to-left embedding (RLE)"), MENU_INSERT_RLE);
+ menu_ctl->add_item(RTR("Start of left-to-right override (LRO)"), MENU_INSERT_LRO);
+ menu_ctl->add_item(RTR("Start of right-to-left override (RLO)"), MENU_INSERT_RLO);
+ menu_ctl->add_item(RTR("Pop direction formatting (PDF)"), MENU_INSERT_PDF);
+ menu_ctl->add_separator();
+ menu_ctl->add_item(RTR("Arabic letter mark (ALM)"), MENU_INSERT_ALM);
+ menu_ctl->add_item(RTR("Left-to-right isolate (LRI)"), MENU_INSERT_LRI);
+ menu_ctl->add_item(RTR("Right-to-left isolate (RLI)"), MENU_INSERT_RLI);
+ menu_ctl->add_item(RTR("First strong isolate (FSI)"), MENU_INSERT_FSI);
+ menu_ctl->add_item(RTR("Pop direction isolate (PDI)"), MENU_INSERT_PDI);
+ menu_ctl->add_separator();
+ menu_ctl->add_item(RTR("Zero width joiner (ZWJ)"), MENU_INSERT_ZWJ);
+ menu_ctl->add_item(RTR("Zero width non-joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
+ menu_ctl->add_item(RTR("Word joiner (WJ)"), MENU_INSERT_WJ);
+ menu_ctl->add_item(RTR("Soft hyphen (SHY)"), MENU_INSERT_SHY);
+ menu->add_child(menu_ctl);
+
readonly = true; // Initialise to opposite first, so we get past the early-out in set_readonly.
set_readonly(false);
menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
+ menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
+ menu_ctl->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
first_draw = true;
}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 562f4768ae..3f16ed1366 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -36,6 +36,7 @@
#include "scene/gui/scroll_bar.h"
#include "scene/main/timer.h"
#include "scene/resources/syntax_highlighter.h"
+#include "scene/resources/text_paragraph.h"
class TextEdit : public Control {
GDCLASS(TextEdit, Control);
@@ -47,6 +48,14 @@ public:
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;
@@ -79,47 +88,68 @@ private:
struct Line {
Vector<Gutter> gutters;
- int32_t width_cache;
+ String data;
+ Vector<Vector2i> bidi_override;
+ Ref<TextParagraph> data_buf;
+
bool marked;
bool hidden;
- int32_t wrap_amount_cache;
- String data;
+
Line() {
- width_cache = 0;
+ data_buf.instance();
+
marked = false;
hidden = false;
- wrap_amount_cache = 0;
}
};
private:
mutable Vector<Line> text;
Ref<Font> font;
+ int font_size = -1;
+
+ Dictionary opentype_features;
+ String language;
+ TextServer::Direction direction = TextServer::DIRECTION_AUTO;
+ bool draw_control_chars = false;
+
+ int width = -1;
+
int indent_size = 4;
int gutter_count = 0;
- void _update_line_cache(int p_line) const;
-
public:
void set_indent_size(int p_indent_size);
void set_font(const Ref<Font> &p_font);
+ void set_font_size(int p_font_size);
+ void set_font_features(const Dictionary &p_features);
+ void set_direction_and_language(TextServer::Direction p_direction, String p_language);
+ void set_draw_control_chars(bool p_draw_control_chars);
+
+ int get_line_height(int p_line, int p_wrap_index) const;
int get_line_width(int p_line) const;
int get_max_width(bool p_exclude_hidden = false) const;
- int get_char_width(char32_t c, char32_t next_c, int px) const;
- void set_line_wrap_amount(int p_line, int p_wrap_amount) const;
+
+ void set_width(float p_width);
int get_line_wrap_amount(int p_line) const;
- void set(int p_line, const String &p_text);
+ Vector<Vector2i> get_line_wrap_ranges(int p_line) const;
+ const Ref<TextParagraph> get_line_data(int p_line) const;
+
+ void set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override);
void set_marked(int p_line, bool p_marked) { text.write[p_line].marked = p_marked; }
bool is_marked(int p_line) const { return text[p_line].marked; }
void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; }
bool is_hidden(int p_line) const { return text[p_line].hidden; }
- void insert(int p_at, const String &p_text);
+ void insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override);
void remove(int p_at);
int size() const { return text.size(); }
void clear();
- void clear_width_cache();
- void clear_wrap_cache();
- _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
+
+ void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Vector<Vector2i> &p_bidi_override = Vector<Vector2i>());
+ void invalidate_all();
+ void invalidate_all_lines();
+
+ _FORCE_INLINE_ const String &operator[](int p_line) const;
/* Gutters. */
void add_gutter(int p_at);
@@ -157,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;
@@ -178,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;
@@ -261,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;
@@ -276,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;
@@ -357,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;
@@ -377,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;
@@ -406,6 +434,8 @@ private:
Size2 get_minimum_size() const override;
int _get_control_height() const;
+ Point2 _get_local_mouse_pos() const;
+
void _reset_caret_blink_timer();
void _toggle_draw_caret();
@@ -426,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();
@@ -445,6 +477,7 @@ protected:
Ref<StyleBox> style_focus;
Ref<StyleBox> style_readonly;
Ref<Font> font;
+ int font_size;
Color completion_background_color;
Color completion_selected_color;
Color completion_existing_color;
@@ -465,11 +498,9 @@ protected:
Color search_result_border_color;
Color background_color;
- int row_height;
int line_spacing;
int minimap_width;
Cache() {
- row_height = 0;
line_spacing = 0;
minimap_width = 0;
}
@@ -488,6 +519,10 @@ protected:
static void _bind_methods();
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
public:
/* Syntax Highlighting. */
Ref<SyntaxHighlighter> get_syntax_highlighter();
@@ -542,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
};
@@ -565,6 +621,25 @@ public:
bool is_insert_text_operation();
+ void set_text_direction(TextDirection p_text_direction);
+ TextDirection get_text_direction() const;
+
+ void set_opentype_feature(const String &p_name, int p_value);
+ int get_opentype_feature(const String &p_name) const;
+ void clear_opentype_features();
+
+ void set_language(const String &p_language);
+ String get_language() const;
+
+ void set_draw_control_chars(bool p_draw_control_chars);
+ bool get_draw_control_chars() const;
+
+ void set_structured_text_bidi_override(Control::StructuredTextParser p_parser);
+ Control::StructuredTextParser get_structured_text_bidi_override() const;
+
+ void set_structured_text_bidi_override_options(Array p_args);
+ Array get_structured_text_bidi_override_options() const;
+
void set_highlighted_word(const String &new_word);
void set_text(String p_text);
void insert_text_at_cursor(const String &p_text);
@@ -617,12 +692,15 @@ public:
void center_viewport_to_cursor();
+ void set_mid_grapheme_caret_enabled(const bool p_enabled);
+ bool get_mid_grapheme_caret_enabled() const;
+
void cursor_set_column(int p_col, bool p_adjust_viewport = true);
void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true, int p_wrap_index = 0);
int cursor_get_column() const;
int cursor_get_line() const;
- Vector2i _get_cursor_pixel_pos();
+ Vector2i _get_cursor_pixel_pos(bool p_adjust_viewport = true);
bool cursor_get_blink_enabled() const;
void cursor_set_blink_enabled(const bool p_enabled);
@@ -636,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;
@@ -761,6 +844,7 @@ public:
};
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_bar.cpp
index 484b14d11c..b5115690ac 100644
--- a/scene/gui/texture_progress.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* texture_progress.cpp */
+/* texture_progress_bar.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,21 +28,21 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "texture_progress.h"
+#include "texture_progress_bar.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
-void TextureProgress::set_under_texture(const Ref<Texture2D> &p_texture) {
+void TextureProgressBar::set_under_texture(const Ref<Texture2D> &p_texture) {
under = p_texture;
update();
minimum_size_changed();
}
-Ref<Texture2D> TextureProgress::get_under_texture() const {
+Ref<Texture2D> TextureProgressBar::get_under_texture() const {
return under;
}
-void TextureProgress::set_over_texture(const Ref<Texture2D> &p_texture) {
+void TextureProgressBar::set_over_texture(const Ref<Texture2D> &p_texture) {
over = p_texture;
update();
if (under.is_null()) {
@@ -50,33 +50,33 @@ void TextureProgress::set_over_texture(const Ref<Texture2D> &p_texture) {
}
}
-Ref<Texture2D> TextureProgress::get_over_texture() const {
+Ref<Texture2D> TextureProgressBar::get_over_texture() const {
return over;
}
-void TextureProgress::set_stretch_margin(Margin p_margin, int p_size) {
+void TextureProgressBar::set_stretch_margin(Margin p_margin, int p_size) {
ERR_FAIL_INDEX((int)p_margin, 4);
stretch_margin[p_margin] = p_size;
update();
minimum_size_changed();
}
-int TextureProgress::get_stretch_margin(Margin p_margin) const {
+int TextureProgressBar::get_stretch_margin(Margin p_margin) const {
ERR_FAIL_INDEX_V((int)p_margin, 4, 0);
return stretch_margin[p_margin];
}
-void TextureProgress::set_nine_patch_stretch(bool p_stretch) {
+void TextureProgressBar::set_nine_patch_stretch(bool p_stretch) {
nine_patch_stretch = p_stretch;
update();
minimum_size_changed();
}
-bool TextureProgress::get_nine_patch_stretch() const {
+bool TextureProgressBar::get_nine_patch_stretch() const {
return nine_patch_stretch;
}
-Size2 TextureProgress::get_minimum_size() const {
+Size2 TextureProgressBar::get_minimum_size() const {
if (nine_patch_stretch) {
return Size2(stretch_margin[MARGIN_LEFT] + stretch_margin[MARGIN_RIGHT], stretch_margin[MARGIN_TOP] + stretch_margin[MARGIN_BOTTOM]);
} else if (under.is_valid()) {
@@ -90,44 +90,44 @@ Size2 TextureProgress::get_minimum_size() const {
return Size2(1, 1);
}
-void TextureProgress::set_progress_texture(const Ref<Texture2D> &p_texture) {
+void TextureProgressBar::set_progress_texture(const Ref<Texture2D> &p_texture) {
progress = p_texture;
update();
minimum_size_changed();
}
-Ref<Texture2D> TextureProgress::get_progress_texture() const {
+Ref<Texture2D> TextureProgressBar::get_progress_texture() const {
return progress;
}
-void TextureProgress::set_tint_under(const Color &p_tint) {
+void TextureProgressBar::set_tint_under(const Color &p_tint) {
tint_under = p_tint;
update();
}
-Color TextureProgress::get_tint_under() const {
+Color TextureProgressBar::get_tint_under() const {
return tint_under;
}
-void TextureProgress::set_tint_progress(const Color &p_tint) {
+void TextureProgressBar::set_tint_progress(const Color &p_tint) {
tint_progress = p_tint;
update();
}
-Color TextureProgress::get_tint_progress() const {
+Color TextureProgressBar::get_tint_progress() const {
return tint_progress;
}
-void TextureProgress::set_tint_over(const Color &p_tint) {
+void TextureProgressBar::set_tint_over(const Color &p_tint) {
tint_over = p_tint;
update();
}
-Color TextureProgress::get_tint_over() const {
+Color TextureProgressBar::get_tint_over() const {
return tint_over;
}
-Point2 TextureProgress::unit_val_to_uv(float val) {
+Point2 TextureProgressBar::unit_val_to_uv(float val) {
if (progress.is_null()) {
return Point2();
}
@@ -191,7 +191,7 @@ Point2 TextureProgress::unit_val_to_uv(float val) {
return (p + t1 * dir);
}
-Point2 TextureProgress::get_relative_center() {
+Point2 TextureProgressBar::get_relative_center() {
if (progress.is_null()) {
return Point2();
}
@@ -204,7 +204,7 @@ Point2 TextureProgress::get_relative_center() {
return p;
}
-void TextureProgress::draw_nine_patch_stretched(const Ref<Texture2D> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate) {
+void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate) {
Vector2 texture_size = p_texture->get_size();
Vector2 topleft = Vector2(stretch_margin[MARGIN_LEFT], stretch_margin[MARGIN_TOP]);
Vector2 bottomright = Vector2(stretch_margin[MARGIN_RIGHT], stretch_margin[MARGIN_BOTTOM]);
@@ -306,7 +306,7 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture2D> &p_texture,
RS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright, RS::NINE_PATCH_STRETCH, RS::NINE_PATCH_STRETCH, true, p_modulate);
}
-void TextureProgress::_notification(int p_what) {
+void TextureProgressBar::_notification(int p_what) {
const float corners[12] = { -0.125, -0.375, -0.625, -0.875, 0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875 };
switch (p_what) {
case NOTIFICATION_DRAW: {
@@ -428,17 +428,17 @@ void TextureProgress::_notification(int p_what) {
}
}
-void TextureProgress::set_fill_mode(int p_fill) {
+void TextureProgressBar::set_fill_mode(int p_fill) {
ERR_FAIL_INDEX(p_fill, 9);
mode = (FillMode)p_fill;
update();
}
-int TextureProgress::get_fill_mode() {
+int TextureProgressBar::get_fill_mode() {
return mode;
}
-void TextureProgress::set_radial_initial_angle(float p_angle) {
+void TextureProgressBar::set_radial_initial_angle(float p_angle) {
while (p_angle > 360) {
p_angle -= 360;
}
@@ -449,64 +449,64 @@ void TextureProgress::set_radial_initial_angle(float p_angle) {
update();
}
-float TextureProgress::get_radial_initial_angle() {
+float TextureProgressBar::get_radial_initial_angle() {
return rad_init_angle;
}
-void TextureProgress::set_fill_degrees(float p_angle) {
+void TextureProgressBar::set_fill_degrees(float p_angle) {
rad_max_degrees = CLAMP(p_angle, 0, 360);
update();
}
-float TextureProgress::get_fill_degrees() {
+float TextureProgressBar::get_fill_degrees() {
return rad_max_degrees;
}
-void TextureProgress::set_radial_center_offset(const Point2 &p_off) {
+void TextureProgressBar::set_radial_center_offset(const Point2 &p_off) {
rad_center_off = p_off;
update();
}
-Point2 TextureProgress::get_radial_center_offset() {
+Point2 TextureProgressBar::get_radial_center_offset() {
return rad_center_off;
}
-void TextureProgress::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_under_texture", "tex"), &TextureProgress::set_under_texture);
- ClassDB::bind_method(D_METHOD("get_under_texture"), &TextureProgress::get_under_texture);
+void TextureProgressBar::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_under_texture", "tex"), &TextureProgressBar::set_under_texture);
+ ClassDB::bind_method(D_METHOD("get_under_texture"), &TextureProgressBar::get_under_texture);
- ClassDB::bind_method(D_METHOD("set_progress_texture", "tex"), &TextureProgress::set_progress_texture);
- ClassDB::bind_method(D_METHOD("get_progress_texture"), &TextureProgress::get_progress_texture);
+ ClassDB::bind_method(D_METHOD("set_progress_texture", "tex"), &TextureProgressBar::set_progress_texture);
+ ClassDB::bind_method(D_METHOD("get_progress_texture"), &TextureProgressBar::get_progress_texture);
- ClassDB::bind_method(D_METHOD("set_over_texture", "tex"), &TextureProgress::set_over_texture);
- ClassDB::bind_method(D_METHOD("get_over_texture"), &TextureProgress::get_over_texture);
+ ClassDB::bind_method(D_METHOD("set_over_texture", "tex"), &TextureProgressBar::set_over_texture);
+ ClassDB::bind_method(D_METHOD("get_over_texture"), &TextureProgressBar::get_over_texture);
- ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &TextureProgress::set_fill_mode);
- ClassDB::bind_method(D_METHOD("get_fill_mode"), &TextureProgress::get_fill_mode);
+ ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &TextureProgressBar::set_fill_mode);
+ ClassDB::bind_method(D_METHOD("get_fill_mode"), &TextureProgressBar::get_fill_mode);
- ClassDB::bind_method(D_METHOD("set_tint_under", "tint"), &TextureProgress::set_tint_under);
- ClassDB::bind_method(D_METHOD("get_tint_under"), &TextureProgress::get_tint_under);
+ ClassDB::bind_method(D_METHOD("set_tint_under", "tint"), &TextureProgressBar::set_tint_under);
+ ClassDB::bind_method(D_METHOD("get_tint_under"), &TextureProgressBar::get_tint_under);
- ClassDB::bind_method(D_METHOD("set_tint_progress", "tint"), &TextureProgress::set_tint_progress);
- ClassDB::bind_method(D_METHOD("get_tint_progress"), &TextureProgress::get_tint_progress);
+ ClassDB::bind_method(D_METHOD("set_tint_progress", "tint"), &TextureProgressBar::set_tint_progress);
+ ClassDB::bind_method(D_METHOD("get_tint_progress"), &TextureProgressBar::get_tint_progress);
- ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgress::set_tint_over);
- ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgress::get_tint_over);
+ ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgressBar::set_tint_over);
+ ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgressBar::get_tint_over);
- ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgress::set_radial_initial_angle);
- ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgress::get_radial_initial_angle);
+ ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgressBar::set_radial_initial_angle);
+ ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgressBar::get_radial_initial_angle);
- ClassDB::bind_method(D_METHOD("set_radial_center_offset", "mode"), &TextureProgress::set_radial_center_offset);
- ClassDB::bind_method(D_METHOD("get_radial_center_offset"), &TextureProgress::get_radial_center_offset);
+ ClassDB::bind_method(D_METHOD("set_radial_center_offset", "mode"), &TextureProgressBar::set_radial_center_offset);
+ ClassDB::bind_method(D_METHOD("get_radial_center_offset"), &TextureProgressBar::get_radial_center_offset);
- ClassDB::bind_method(D_METHOD("set_fill_degrees", "mode"), &TextureProgress::set_fill_degrees);
- ClassDB::bind_method(D_METHOD("get_fill_degrees"), &TextureProgress::get_fill_degrees);
+ ClassDB::bind_method(D_METHOD("set_fill_degrees", "mode"), &TextureProgressBar::set_fill_degrees);
+ ClassDB::bind_method(D_METHOD("get_fill_degrees"), &TextureProgressBar::get_fill_degrees);
- ClassDB::bind_method(D_METHOD("set_stretch_margin", "margin", "value"), &TextureProgress::set_stretch_margin);
- ClassDB::bind_method(D_METHOD("get_stretch_margin", "margin"), &TextureProgress::get_stretch_margin);
+ ClassDB::bind_method(D_METHOD("set_stretch_margin", "margin", "value"), &TextureProgressBar::set_stretch_margin);
+ ClassDB::bind_method(D_METHOD("get_stretch_margin", "margin"), &TextureProgressBar::get_stretch_margin);
- ClassDB::bind_method(D_METHOD("set_nine_patch_stretch", "stretch"), &TextureProgress::set_nine_patch_stretch);
- ClassDB::bind_method(D_METHOD("get_nine_patch_stretch"), &TextureProgress::get_nine_patch_stretch);
+ ClassDB::bind_method(D_METHOD("set_nine_patch_stretch", "stretch"), &TextureProgressBar::set_nine_patch_stretch);
+ ClassDB::bind_method(D_METHOD("get_nine_patch_stretch"), &TextureProgressBar::get_nine_patch_stretch);
ADD_GROUP("Textures", "texture_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_under_texture", "get_under_texture");
@@ -539,7 +539,7 @@ void TextureProgress::_bind_methods() {
BIND_ENUM_CONSTANT(FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE);
}
-TextureProgress::TextureProgress() {
+TextureProgressBar::TextureProgressBar() {
mode = FILL_LEFT_TO_RIGHT;
rad_init_angle = 0;
rad_center_off = Point2();
diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress_bar.h
index 5e29fca21f..342e719a59 100644
--- a/scene/gui/texture_progress.h
+++ b/scene/gui/texture_progress_bar.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* texture_progress.h */
+/* texture_progress_bar.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef TEXTURE_PROGRESS_H
-#define TEXTURE_PROGRESS_H
+#ifndef TEXTURE_PROGRESS_BAR_H
+#define TEXTURE_PROGRESS_BAR_H
#include "scene/gui/range.h"
-class TextureProgress : public Range {
- GDCLASS(TextureProgress, Range);
+class TextureProgressBar : public Range {
+ GDCLASS(TextureProgressBar, Range);
Ref<Texture2D> under;
Ref<Texture2D> progress;
@@ -95,7 +95,7 @@ public:
Size2 get_minimum_size() const override;
- TextureProgress();
+ TextureProgressBar();
private:
FillMode mode;
@@ -111,6 +111,6 @@ private:
void draw_nine_patch_stretched(const Ref<Texture2D> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate);
};
-VARIANT_ENUM_CAST(TextureProgress::FillMode);
+VARIANT_ENUM_CAST(TextureProgressBar::FillMode);
-#endif // TEXTURE_PROGRESS_H
+#endif // TEXTURE_PROGRESS_BAR_H
diff --git a/scene/gui/texture_rect.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 f6636cf392..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) {
@@ -2428,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;
@@ -2459,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();
@@ -2492,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");
}
@@ -2537,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();
@@ -2577,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) {
@@ -2640,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();
@@ -2787,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) {
@@ -2916,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) {
@@ -2944,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);
}
@@ -3019,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 {
@@ -3027,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();
}
@@ -3228,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;
}
}
@@ -3384,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();
}
@@ -3393,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()) {
@@ -3558,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) {
@@ -3585,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) {
@@ -3612,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) {
@@ -3732,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();
@@ -3823,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);
@@ -3891,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 796408a38d..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.get_data()[0], Color(1, 1, 1), true);
- }
- return p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.get_data()[0], p_modulate);
+ return p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.get_data()[0], p_size, p_modulate, p_outline_size, p_outline_modulate);
}
void CanvasItem::_notify_transform(CanvasItem *p_node) {
@@ -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);
}
}
@@ -1386,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);
@@ -1408,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);
}
}
@@ -1425,6 +1389,22 @@ void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) {
_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 {
return texture_repeat;
}
@@ -1437,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;
@@ -1459,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 3f32df87a7..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;
}
@@ -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 da0169b60b..f484d25dc1 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -30,7 +30,7 @@
#include "http_request.h"
#include "core/io/compression.h"
-#include "core/ustring.h"
+#include "core/string/ustring.h"
void HTTPRequest::_redirect_request(const String &p_new_url) {
}
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index e7753089c7..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"
@@ -2873,6 +2873,7 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_APPLICATION_PAUSED);
BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_IN);
BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_OUT);
+ BIND_CONSTANT(NOTIFICATION_TEXT_SERVER_CHANGED);
BIND_ENUM_CONSTANT(PAUSE_MODE_INHERIT);
BIND_ENUM_CONSTANT(PAUSE_MODE_STOP);
@@ -2889,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..1df61daa2c 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,35 @@ 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);
+
+ float lod_threshold = GLOBAL_DEF("rendering/quality/mesh_lod/threshold_pixels", 1.0);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/mesh_lod/threshold_pixels", PropertyInfo(Variant::FLOAT, "rendering/quality/mesh_lod/threshold_pixels", PROPERTY_HINT_RANGE, "0,1024,0.1"));
+ root->set_lod_threshold(lod_threshold);
+
+ bool snap_2d_transforms = GLOBAL_DEF("rendering/quality/2d/snap_2d_transforms_to_pixel", false);
+ root->set_snap_2d_transforms_to_pixel(snap_2d_transforms);
+
+ 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 e5ab4f9958..3e5802ce2e 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -34,7 +34,7 @@
#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"
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 b29b40ea5f..c96dd4ad35 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.last_mouse_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());
@@ -2351,19 +2392,19 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (!mods && p_event->is_action_pressed("ui_up") && input->is_action_just_pressed("ui_up")) {
- next = from->_get_focus_neighbour(MARGIN_TOP);
+ next = from->_get_focus_neighbor(MARGIN_TOP);
}
if (!mods && p_event->is_action_pressed("ui_left") && input->is_action_just_pressed("ui_left")) {
- next = from->_get_focus_neighbour(MARGIN_LEFT);
+ next = from->_get_focus_neighbor(MARGIN_LEFT);
}
if (!mods && p_event->is_action_pressed("ui_right") && input->is_action_just_pressed("ui_right")) {
- next = from->_get_focus_neighbour(MARGIN_RIGHT);
+ next = from->_get_focus_neighbor(MARGIN_RIGHT);
}
if (!mods && p_event->is_action_pressed("ui_down") && input->is_action_just_pressed("ui_down")) {
- next = from->_get_focus_neighbour(MARGIN_BOTTOM);
+ next = from->_get_focus_neighbor(MARGIN_BOTTOM);
}
if (next) {
@@ -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,25 @@ 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_lod_threshold(float p_pixels) {
+ lod_threshold = p_pixels;
+ RS::get_singleton()->viewport_set_lod_threshold(viewport, lod_threshold);
+}
+float Viewport::get_lod_threshold() const {
+ return lod_threshold;
+}
+
void Viewport::set_debug_draw(DebugDraw p_debug_draw) {
debug_draw = p_debug_draw;
RS::get_singleton()->viewport_set_debug_draw(viewport, RS::ViewportDebugDraw(p_debug_draw));
@@ -3154,6 +3221,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 +3293,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 +3322,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 +3390,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 +3433,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 +3482,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 +3507,27 @@ 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);
+
+ ClassDB::bind_method(D_METHOD("set_lod_threshold", "pixels"), &Viewport::set_lod_threshold);
+ ClassDB::bind_method(D_METHOD("get_lod_threshold"), &Viewport::get_lod_threshold);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", 0), "set_world_2d", "get_world_2d");
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::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
ADD_GROUP("Canvas Items", "canvas_item_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,MipmapLinear,MipmapNearest"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter");
@@ -3405,6 +3541,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);
@@ -3463,6 +3602,7 @@ void Viewport::_bind_methods() {
BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI);
BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI_PROBES);
BIND_ENUM_CONSTANT(DEBUG_DRAW_GI_BUFFER);
+ BIND_ENUM_CONSTANT(DEBUG_DRAW_DISABLE_LOD);
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR);
@@ -3474,6 +3614,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() {
@@ -3514,6 +3665,8 @@ Viewport::Viewport() {
set_shadow_atlas_quadrant_subdiv(2, SHADOW_ATLAS_QUADRANT_SUBDIV_16);
set_shadow_atlas_quadrant_subdiv(3, SHADOW_ATLAS_QUADRANT_SUBDIV_64);
+ set_lod_threshold(lod_threshold);
+
String id = itos(get_instance_id());
input_group = "_vp_input" + id;
gui_input_group = "_vp_gui_input" + id;
@@ -3522,14 +3675,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 +3698,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 +3713,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() {
@@ -3680,7 +3839,7 @@ void SubViewport::_bind_methods() {
BIND_ENUM_CONSTANT(CLEAR_MODE_ALWAYS);
BIND_ENUM_CONSTANT(CLEAR_MODE_NEVER);
- BIND_ENUM_CONSTANT(CLEAR_MODE_ONLY_NEXT_FRAME);
+ BIND_ENUM_CONSTANT(CLEAR_MODE_ONCE);
BIND_ENUM_CONSTANT(UPDATE_DISABLED);
BIND_ENUM_CONSTANT(UPDATE_ONCE);
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 52145a7761..ffbc3c782a 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -142,6 +142,7 @@ public:
DEBUG_DRAW_SDFGI,
DEBUG_DRAW_SDFGI_PROBES,
DEBUG_DRAW_GI_BUFFER,
+ DEBUG_DRAW_DISABLE_LOD,
};
enum DefaultCanvasItemTextureFilter {
@@ -159,6 +160,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 +241,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 +297,15 @@ private:
MSAA msaa;
ScreenSpaceAA screen_space_aa;
+ bool use_debanding = false;
+ float lod_threshold = 1.0;
+
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 +344,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 +380,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 +403,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 +542,12 @@ 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;
+
+ void set_lod_threshold(float p_pixels);
+ float get_lod_threshold() const;
+
Vector2 get_camera_coords(const Vector2 &p_viewport_coords) const;
Vector2 get_camera_rect_size() const;
@@ -555,6 +582,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 +596,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,10 +628,9 @@ class SubViewport : public Viewport {
public:
enum ClearMode {
-
CLEAR_MODE_ALWAYS,
CLEAR_MODE_NEVER,
- CLEAR_MODE_ONLY_NEXT_FRAME
+ CLEAR_MODE_ONCE
};
enum UpdateMode {
@@ -642,6 +680,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 7c2350d1c0..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) {
@@ -659,9 +660,8 @@ void Window::_update_viewport_size() {
if (!use_font_oversampling) {
font_oversampling = 1.0;
}
- if (DynamicFontAtSize::font_oversampling != font_oversampling) {
- DynamicFontAtSize::font_oversampling = font_oversampling;
- DynamicFont::update_oversampling();
+ if (TS->font_get_oversampling() != font_oversampling) {
+ TS->font_set_oversampling(font_oversampling);
}
}
@@ -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 820513c53d..30077aa642 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,6 +77,7 @@
#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"
@@ -116,7 +118,7 @@
#include "scene/gui/tabs.h"
#include "scene/gui/text_edit.h"
#include "scene/gui/texture_button.h"
-#include "scene/gui/texture_progress.h"
+#include "scene/gui/texture_progress_bar.h"
#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
#include "scene/gui/video_player.h"
@@ -142,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"
@@ -166,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"
@@ -194,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"
@@ -227,14 +232,16 @@
static Ref<ResourceFormatSaverText> resource_saver_text;
static Ref<ResourceFormatLoaderText> resource_loader_text;
-static Ref<ResourceFormatLoaderDynamicFont> resource_loader_dynamic_font;
+static Ref<ResourceFormatLoaderFont> resource_loader_font;
+
+#ifndef DISABLE_DEPRECATED
+static Ref<ResourceFormatLoaderCompatFont> resource_loader_compat_font;
+#endif /* DISABLE_DEPRECATED */
static Ref<ResourceFormatLoaderStreamTexture2D> resource_loader_stream_texture;
static Ref<ResourceFormatLoaderStreamTextureLayered> resource_loader_texture_layered;
static Ref<ResourceFormatLoaderStreamTexture3D> resource_loader_texture_3d;
-static Ref<ResourceFormatLoaderBMFont> resource_loader_bmfont;
-
static Ref<ResourceFormatSaverShader> resource_saver_shader;
static Ref<ResourceFormatLoaderShader> resource_loader_shader;
@@ -245,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);
@@ -269,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>();
@@ -322,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>();
@@ -339,7 +349,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
- ClassDB::register_class<TextureProgress>();
+ ClassDB::register_class<TextureProgressBar>();
ClassDB::register_class<ItemList>();
ClassDB::register_class<LineEdit>();
@@ -450,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>();
@@ -512,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>();
@@ -553,6 +573,7 @@ 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>();
@@ -582,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();
@@ -589,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>();
@@ -613,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>();
@@ -654,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>();
@@ -721,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>();
@@ -786,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.
@@ -822,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");
@@ -889,6 +915,7 @@ void register_scene_types() {
ClassDB::add_compatibility_class("SpringArm", "SpringArm3D");
ClassDB::add_compatibility_class("Sprite", "Sprite2D");
ClassDB::add_compatibility_class("StaticBody", "StaticBody3D");
+ ClassDB::add_compatibility_class("TextureProgress", "TextureProgressBar");
ClassDB::add_compatibility_class("VehicleBody", "VehicleBody3D");
ClassDB::add_compatibility_class("VehicleWheel", "VehicleWheel3D");
ClassDB::add_compatibility_class("ViewportContainer", "SubViewportContainer");
@@ -902,8 +929,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
@@ -952,8 +980,13 @@ void unregister_scene_types() {
SceneDebugger::deinitialize();
clear_default_theme();
- ResourceLoader::remove_resource_format_loader(resource_loader_dynamic_font);
- resource_loader_dynamic_font.unref();
+ ResourceLoader::remove_resource_format_loader(resource_loader_font);
+ resource_loader_font.unref();
+
+#ifndef DISABLE_DEPRECATED
+ ResourceLoader::remove_resource_format_loader(resource_loader_compat_font);
+ resource_loader_compat_font.unref();
+#endif /* DISABLE_DEPRECATED */
ResourceLoader::remove_resource_format_loader(resource_loader_texture_layered);
resource_loader_texture_layered.unref();
@@ -964,8 +997,6 @@ void unregister_scene_types() {
ResourceLoader::remove_resource_format_loader(resource_loader_stream_texture);
resource_loader_stream_texture.unref();
- DynamicFont::finish_dynamic_fonts();
-
ResourceSaver::remove_resource_format_saver(resource_saver_text);
resource_saver_text.unref();
@@ -978,9 +1009,6 @@ void unregister_scene_types() {
ResourceLoader::remove_resource_format_loader(resource_loader_shader);
resource_loader_shader.unref();
- ResourceLoader::remove_resource_format_loader(resource_loader_bmfont);
- resource_loader_bmfont.unref();
-
//StandardMaterial3D is not initialised when 3D is disabled, so it shouldn't be cleaned up either
#ifndef _3D_DISABLED
BaseMaterial3D::finish_shaders();
diff --git a/scene/resources/SCsub b/scene/resources/SCsub
index 3a86b22835..f4dc7a46fb 100644
--- a/scene/resources/SCsub
+++ b/scene/resources/SCsub
@@ -2,6 +2,25 @@
Import("env")
-env.add_source_files(env.scene_sources, "*.cpp")
+# Thirdparty code
+
+thirdparty_obj = []
+
+thirdparty_sources = "#thirdparty/misc/mikktspace.c"
+
+env_thirdparty = env.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.scene_sources += thirdparty_obj
+
+# Godot source files
+
+scene_obj = []
+
+env.add_source_files(scene_obj, "*.cpp")
+env.scene_sources += scene_obj
+
+# Needed to force rebuilding the scene files when the thirdparty code is updated.
+env.Depends(scene_obj, thirdparty_obj)
SConscript("default_theme/SCsub")
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index b8edd70712..f4670ca850 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -33,8 +33,6 @@
#include "core/math/geometry_3d.h"
-#define ANIM_MIN_LENGTH 0.001
-
bool Animation::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
@@ -721,7 +719,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 +731,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--;
}
-
}
*/
@@ -813,8 +808,8 @@ int Animation::transform_track_insert_key(int p_track, float p_time, const Vecto
return ret;
}
-void Animation::track_remove_key_at_position(int p_track, float p_pos) {
- int idx = track_find_key(p_track, p_pos, true);
+void Animation::track_remove_key_at_time(int p_track, float p_time) {
+ int idx = track_find_key(p_track, p_time, true);
ERR_FAIL_COND(idx < 0);
track_remove_key(p_track, idx);
}
@@ -2611,7 +2606,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key);
ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key);
- ClassDB::bind_method(D_METHOD("track_remove_key_at_position", "track_idx", "position"), &Animation::track_remove_key_at_position);
+ ClassDB::bind_method(D_METHOD("track_remove_key_at_time", "track_idx", "time"), &Animation::track_remove_key_at_time);
ClassDB::bind_method(D_METHOD("track_set_key_value", "track_idx", "key", "value"), &Animation::track_set_key_value);
ClassDB::bind_method(D_METHOD("track_set_key_transition", "track_idx", "key_idx", "transition"), &Animation::track_set_key_transition);
ClassDB::bind_method(D_METHOD("track_set_key_time", "track_idx", "key_idx", "time"), &Animation::track_set_key_time);
@@ -2633,6 +2628,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..650a54ebfc 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -31,7 +31,9 @@
#ifndef ANIMATION_H
#define ANIMATION_H
-#include "core/resource.h"
+#include "core/io/resource.h"
+
+#define ANIM_MIN_LENGTH 0.001
class Animation : public Resource {
GDCLASS(Animation, Resource);
@@ -293,7 +295,7 @@ public:
void track_set_key_time(int p_track, int p_key_idx, float p_time);
int track_find_key(int p_track, float p_time, bool p_exact = false) const;
void track_remove_key(int p_track, int p_idx);
- void track_remove_key_at_position(int p_track, float p_pos);
+ void track_remove_key_at_time(int p_track, float p_time);
int track_get_key_count(int p_track) const;
Variant track_get_key_value(int p_track, int p_key_idx) const;
float track_get_key_time(int p_track, int p_key_idx) const;
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index 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.cpp b/scene/resources/bit_map.cpp
index 10f0de8ff8..d07447179d 100644
--- a/scene/resources/bit_map.cpp
+++ b/scene/resources/bit_map.cpp
@@ -63,7 +63,7 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshol
}
void BitMap::set_bit_rect(const Rect2 &p_rect, bool p_value) {
- Rect2i current = Rect2i(0, 0, width, height).clip(p_rect);
+ Rect2i current = Rect2i(0, 0, width, height).intersection(p_rect);
uint8_t *data = bitmask.ptrw();
for (int i = current.position.x; i < current.position.x + current.size.x; i++) {
@@ -482,7 +482,7 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_
}
Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon) const {
- Rect2i r = Rect2i(0, 0, width, height).clip(p_rect);
+ Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect);
print_verbose("BitMap: Rect: " + r);
Point2i from;
@@ -522,7 +522,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) {
bool bit_value = p_pixels > 0;
p_pixels = Math::abs(p_pixels);
- Rect2i r = Rect2i(0, 0, width, height).clip(p_rect);
+ Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect);
Ref<BitMap> copy;
copy.instance();
diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h
index 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/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 9d1355eec6..5da7f682e5 100644
--- a/scene/resources/capsule_shape_3d.cpp
+++ b/scene/resources/capsule_shape_3d.cpp
@@ -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/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 ad64541247..bb8c27a60d 100644
--- a/scene/resources/cylinder_shape_3d.cpp
+++ b/scene/resources/cylinder_shape_3d.cpp
@@ -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/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/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index f65f78b332..2de28b5e5c 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;
@@ -75,6 +77,17 @@ static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, fl
return style;
}
+static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
+ Ref<StyleBoxFlat> style(memnew(StyleBoxFlat));
+ style->set_bg_color(p_color);
+ style->set_default_margin(MARGIN_LEFT, p_margin_left * scale);
+ style->set_default_margin(MARGIN_RIGHT, p_margin_right * scale);
+ style->set_default_margin(MARGIN_BOTTOM, p_margin_bottom * scale);
+ style->set_default_margin(MARGIN_TOP, p_margin_top * scale);
+
+ return style;
+}
+
static Ref<StyleBoxTexture> sb_expand(Ref<StyleBoxTexture> p_sbox, float p_left, float p_top, float p_right, float p_botton) {
p_sbox->set_expand_margin_size(MARGIN_LEFT, p_left * scale);
p_sbox->set_expand_margin_size(MARGIN_TOP, p_top * scale);
@@ -95,38 +108,23 @@ static Ref<Texture2D> make_icon(T p_src) {
return texture;
}
-static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_charcount, const int *p_char_rects, int p_kerning_count, const int *p_kernings, int p_w, int p_h, const unsigned char *p_img) {
- Ref<BitmapFont> font(memnew(BitmapFont));
-
- Ref<Image> image = memnew(Image(p_img));
- Ref<ImageTexture> tex = memnew(ImageTexture);
- tex->create_from_image(image);
-
- font->add_texture(tex);
-
- for (int i = 0; i < p_charcount; i++) {
- const int *c = &p_char_rects[i * 8];
+static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, bool p_flip_x = false) {
+ if (!p_flip_y && !p_flip_x) {
+ return p_texture;
+ }
- int chr = c[0];
- Rect2 frect;
- frect.position.x = c[1];
- frect.position.y = c[2];
- frect.size.x = c[3];
- frect.size.y = c[4];
- Point2 align(c[6], c[5]);
- int advance = c[7];
+ Ref<ImageTexture> texture(memnew(ImageTexture));
+ Ref<Image> img = p_texture->get_data();
- font->add_char(chr, 0, frect, align, advance);
+ if (p_flip_y) {
+ img->flip_y();
}
-
- for (int i = 0; i < p_kerning_count; i++) {
- font->add_kerning_pair(p_kernings[i * 3 + 0], p_kernings[i * 3 + 1], p_kernings[i * 3 + 2]);
+ if (p_flip_x) {
+ img->flip_x();
}
- font->set_height(p_height);
- font->set_ascent(p_ascent);
-
- return font;
+ texture->create_from_image(img);
+ return texture;
}
static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_botton = -1) {
@@ -182,11 +180,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("focus", "Button", sb_button_focus);
theme->set_font("font", "Button", Ref<Font>());
+ theme->set_font_size("font_size", "Button", -1);
+ theme->set_constant("outline_size", "Button", 0 * scale);
theme->set_color("font_color", "Button", control_font_color);
theme->set_color("font_color_pressed", "Button", control_font_color_pressed);
theme->set_color("font_color_hover", "Button", control_font_color_hover);
theme->set_color("font_color_disabled", "Button", control_font_color_disabled);
+ theme->set_color("font_outline_modulate", "Button", Color(1, 1, 1));
theme->set_constant("hseparation", "Button", 2 * scale);
@@ -195,6 +196,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("focus", "LinkButton", focus);
theme->set_font("font", "LinkButton", Ref<Font>());
+ theme->set_font_size("font_size", "LinkButton", -1);
theme->set_color("font_color", "LinkButton", control_font_color);
theme->set_color("font_color_pressed", "LinkButton", control_font_color_pressed);
@@ -211,6 +213,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus);
theme->set_font("font", "ColorPickerButton", Ref<Font>());
+ theme->set_font_size("font_size", "ColorPickerButton", -1);
theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1));
theme->set_color("font_color_pressed", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1));
@@ -221,21 +224,33 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// OptionButton
+ Ref<StyleBox> sb_optbutton_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
+ theme->set_stylebox("focus", "OptionButton", sb_optbutton_focus);
+
Ref<StyleBox> sb_optbutton_normal = sb_expand(make_stylebox(option_button_normal_png, 4, 4, 21, 4, 6, 3, 9, 3), 2, 2, 2, 2);
Ref<StyleBox> sb_optbutton_pressed = sb_expand(make_stylebox(option_button_pressed_png, 4, 4, 21, 4, 6, 3, 9, 3), 2, 2, 2, 2);
Ref<StyleBox> sb_optbutton_hover = sb_expand(make_stylebox(option_button_hover_png, 4, 4, 21, 4, 6, 2, 9, 2), 2, 2, 2, 2);
Ref<StyleBox> sb_optbutton_disabled = sb_expand(make_stylebox(option_button_disabled_png, 4, 4, 21, 4, 6, 2, 9, 2), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
theme->set_stylebox("normal", "OptionButton", sb_optbutton_normal);
theme->set_stylebox("pressed", "OptionButton", sb_optbutton_pressed);
theme->set_stylebox("hover", "OptionButton", sb_optbutton_hover);
theme->set_stylebox("disabled", "OptionButton", sb_optbutton_disabled);
- theme->set_stylebox("focus", "OptionButton", sb_optbutton_focus);
+
+ Ref<StyleBox> sb_optbutton_normal_mirrored = sb_expand(make_stylebox(option_button_normal_mirrored_png, 21, 4, 4, 4, 9, 3, 6, 3), 2, 2, 2, 2);
+ Ref<StyleBox> sb_optbutton_pressed_mirrored = sb_expand(make_stylebox(option_button_pressed_mirrored_png, 21, 4, 4, 4, 9, 3, 6, 3), 2, 2, 2, 2);
+ Ref<StyleBox> sb_optbutton_hover_mirrored = sb_expand(make_stylebox(option_button_hover_mirrored_png, 21, 4, 4, 4, 9, 2, 6, 2), 2, 2, 2, 2);
+ Ref<StyleBox> sb_optbutton_disabled_mirrored = sb_expand(make_stylebox(option_button_disabled_mirrored_png, 21, 4, 4, 4, 9, 2, 6, 2), 2, 2, 2, 2);
+
+ theme->set_stylebox("normal_mirrored", "OptionButton", sb_optbutton_normal_mirrored);
+ theme->set_stylebox("pressed_mirrored", "OptionButton", sb_optbutton_pressed_mirrored);
+ theme->set_stylebox("hover_mirrored", "OptionButton", sb_optbutton_hover_mirrored);
+ theme->set_stylebox("disabled_mirrored", "OptionButton", sb_optbutton_disabled_mirrored);
theme->set_icon("arrow", "OptionButton", make_icon(option_arrow_png));
theme->set_font("font", "OptionButton", Ref<Font>());
+ theme->set_font_size("font_size", "OptionButton", -1);
theme->set_color("font_color", "OptionButton", control_font_color);
theme->set_color("font_color_pressed", "OptionButton", control_font_color_pressed);
@@ -254,6 +269,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("focus", "MenuButton", sb_button_focus);
theme->set_font("font", "MenuButton", Ref<Font>());
+ theme->set_font_size("font_size", "MenuButton", -1);
theme->set_color("font_color", "MenuButton", control_font_color);
theme->set_color("font_color_pressed", "MenuButton", control_font_color_pressed);
@@ -288,6 +304,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("radio_unchecked", "CheckBox", make_icon(radio_unchecked_png));
theme->set_font("font", "CheckBox", Ref<Font>());
+ theme->set_font_size("font_size", "CheckBox", -1);
theme->set_color("font_color", "CheckBox", control_font_color);
theme->set_color("font_color_pressed", "CheckBox", control_font_color_pressed);
@@ -318,7 +335,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("off", "CheckButton", make_icon(toggle_off_png));
theme->set_icon("off_disabled", "CheckButton", make_icon(toggle_off_disabled_png));
+ theme->set_icon("on_mirrored", "CheckButton", make_icon(toggle_on_mirrored_png));
+ theme->set_icon("on_disabled_mirrored", "CheckButton", make_icon(toggle_on_disabled_mirrored_png));
+ theme->set_icon("off_mirrored", "CheckButton", make_icon(toggle_off_mirrored_png));
+ theme->set_icon("off_disabled_mirrored", "CheckButton", make_icon(toggle_off_disabled_mirrored_png));
+
theme->set_font("font", "CheckButton", Ref<Font>());
+ theme->set_font_size("font_size", "CheckButton", -1);
theme->set_color("font_color", "CheckButton", control_font_color);
theme->set_color("font_color_pressed", "CheckButton", control_font_color_pressed);
@@ -333,6 +356,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("normal", "Label", memnew(StyleBoxEmpty));
theme->set_font("font", "Label", Ref<Font>());
+ theme->set_font_size("font_size", "Label", -1);
theme->set_color("font_color", "Label", Color(1, 1, 1));
theme->set_color("font_color_shadow", "Label", Color(0, 0, 0, 0));
@@ -340,7 +364,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("shadow_offset_x", "Label", 1 * scale);
theme->set_constant("shadow_offset_y", "Label", 1 * scale);
- theme->set_constant("shadow_as_outline", "Label", 0 * scale);
+ theme->set_constant("outline_size", "Label", 0 * scale);
+ theme->set_constant("shadow_outline_size", "Label", 1 * scale);
theme->set_constant("line_spacing", "Label", 3 * scale);
// LineEdit
@@ -350,6 +375,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("read_only", "LineEdit", make_stylebox(line_edit_disabled_png, 6, 6, 6, 6));
theme->set_font("font", "LineEdit", Ref<Font>());
+ theme->set_font_size("font_size", "LineEdit", -1);
theme->set_color("font_color", "LineEdit", control_font_color);
theme->set_color("font_color_selected", "LineEdit", Color(0, 0, 0));
@@ -369,6 +395,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("fg", "ProgressBar", make_stylebox(progress_fill_png, 6, 6, 6, 6, 2, 1, 2, 1));
theme->set_font("font", "ProgressBar", Ref<Font>());
+ theme->set_font_size("font_size", "ProgressBar", -1);
theme->set_color("font_color", "ProgressBar", control_font_color_hover);
theme->set_color("font_color_shadow", "ProgressBar", Color(0, 0, 0));
@@ -384,6 +411,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("space", "TextEdit", make_icon(space_png));
theme->set_font("font", "TextEdit", Ref<Font>());
+ theme->set_font_size("font_size", "TextEdit", -1);
theme->set_color("background_color", "TextEdit", Color(0, 0, 0, 0));
theme->set_color("completion_background_color", "TextEdit", Color(0.17, 0.16, 0.2));
@@ -423,6 +451,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("folded", "CodeEdit", make_icon(arrow_right_png));
theme->set_font("font", "CodeEdit", Ref<Font>());
+ theme->set_font_size("font_size", "CodeEdit", -1);
theme->set_color("background_color", "CodeEdit", Color(0, 0, 0, 0));
theme->set_color("completion_background_color", "CodeEdit", Color(0.17, 0.16, 0.2));
@@ -563,13 +592,16 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("radio_checked", "PopupMenu", make_icon(radio_checked_png));
theme->set_icon("radio_unchecked", "PopupMenu", make_icon(radio_unchecked_png));
theme->set_icon("submenu", "PopupMenu", make_icon(submenu_png));
+ theme->set_icon("submenu_mirrored", "PopupMenu", make_icon(submenu_mirrored_png));
theme->set_font("font", "PopupMenu", Ref<Font>());
+ theme->set_font_size("font_size", "PopupMenu", -1);
theme->set_color("font_color", "PopupMenu", control_font_color);
theme->set_color("font_color_accel", "PopupMenu", Color(0.7, 0.7, 0.7, 0.8));
theme->set_color("font_color_disabled", "PopupMenu", Color(0.4, 0.4, 0.4, 0.8));
theme->set_color("font_color_hover", "PopupMenu", control_font_color);
+ theme->set_color("font_color_separator", "PopupMenu", control_font_color);
theme->set_constant("hseparation", "PopupMenu", 4 * scale);
theme->set_constant("vseparation", "PopupMenu", 4 * scale);
@@ -632,9 +664,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("select_arrow", "Tree", make_icon(dropdown_png));
theme->set_icon("arrow", "Tree", make_icon(arrow_down_png));
theme->set_icon("arrow_collapsed", "Tree", make_icon(arrow_right_png));
+ theme->set_icon("arrow_collapsed_mirrored", "Tree", make_icon(arrow_left_png));
theme->set_font("title_button_font", "Tree", Ref<Font>());
theme->set_font("font", "Tree", Ref<Font>());
+ theme->set_font_size("font_size", "Tree", -1);
theme->set_color("title_button_color", "Tree", control_font_color);
theme->set_color("font_color", "Tree", control_font_color_low);
@@ -663,7 +697,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("vseparation", "ItemList", 2);
theme->set_constant("icon_margin", "ItemList", 4);
theme->set_constant("line_separation", "ItemList", 2 * scale);
+
theme->set_font("font", "ItemList", Ref<Font>());
+ theme->set_font_size("font_size", "ItemList", -1);
+
theme->set_color("font_color", "ItemList", control_font_color_lower);
theme->set_color("font_color_selected", "ItemList", control_font_color_pressed);
theme->set_color("guide_color", "ItemList", Color(0, 0, 0, 0.1));
@@ -692,6 +729,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("menu_highlight", "TabContainer", make_icon(tab_menu_hl_png));
theme->set_font("font", "TabContainer", Ref<Font>());
+ theme->set_font_size("font_size", "TabContainer", -1);
theme->set_color("font_color_fg", "TabContainer", control_font_color_hover);
theme->set_color("font_color_bg", "TabContainer", control_font_color_low);
@@ -716,6 +754,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("close", "Tabs", make_icon(tab_close_png));
theme->set_font("font", "Tabs", Ref<Font>());
+ theme->set_font_size("font_size", "Tabs", -1);
theme->set_color("font_color_fg", "Tabs", control_font_color_hover);
theme->set_color("font_color_bg", "Tabs", control_font_color_low);
@@ -775,6 +814,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));
@@ -793,6 +833,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_font("bold_italics_font", "RichTextLabel", Ref<Font>());
theme->set_font("mono_font", "RichTextLabel", Ref<Font>());
+ theme->set_font_size("normal_font_size", "RichTextLabel", -1);
+ theme->set_font_size("bold_font_size", "RichTextLabel", -1);
+ theme->set_font_size("italics_font_size", "RichTextLabel", -1);
+ theme->set_font_size("bold_italics_font_size", "RichTextLabel", -1);
+ theme->set_font_size("mono_font_size", "RichTextLabel", -1);
+
theme->set_color("default_color", "RichTextLabel", Color(1, 1, 1));
theme->set_color("font_color_selected", "RichTextLabel", font_color_selection);
theme->set_color("selection_color", "RichTextLabel", Color(0.1, 0.1, 1, 0.8));
@@ -807,6 +853,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale);
theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale);
+ theme->set_color("table_odd_row_bg", "RichTextLabel", Color(0, 0, 0, 0));
+ theme->set_color("table_even_row_bg", "RichTextLabel", Color(0, 0, 0, 0));
+ theme->set_color("table_border", "RichTextLabel", Color(0, 0, 0, 0));
// Containers
theme->set_stylebox("bg", "VSplitContainer", make_stylebox(vsplit_bg_png, 1, 1, 1, 1));
@@ -835,6 +884,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("reset", "GraphEdit", make_icon(icon_zoom_reset_png));
theme->set_icon("more", "GraphEdit", make_icon(icon_zoom_more_png));
theme->set_icon("snap", "GraphEdit", make_icon(icon_snap_grid_png));
+ theme->set_icon("minimap", "GraphEdit", make_icon(icon_grid_minimap_png));
theme->set_stylebox("bg", "GraphEdit", make_stylebox(tree_bg_png, 4, 4, 4, 5));
theme->set_color("grid_minor", "GraphEdit", Color(1, 1, 1, 0.05));
theme->set_color("grid_major", "GraphEdit", Color(1, 1, 1, 0.2));
@@ -848,6 +898,19 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale);
theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale);
+ theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0));
+ Ref<StyleBoxFlat> style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0);
+ style_minimap_camera->set_border_color(Color(0.65, 0.65, 0.65, 0.45));
+ style_minimap_camera->set_border_width_all(1);
+ theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera);
+ Ref<StyleBoxFlat> style_minimap_node = make_flat_stylebox(Color(1, 1, 1), 0, 0, 0, 0);
+ style_minimap_node->set_corner_radius_all(2);
+ theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node);
+
+ Ref<Texture2D> resizer_icon = make_icon(window_resizer_png);
+ theme->set_icon("resizer", "GraphEditMinimap", flip_icon(resizer_icon, true, true));
+ theme->set_color("resizer_color", "GraphEditMinimap", Color(1, 1, 1, 0.85));
+
// Theme
default_icon = make_icon(error_icon_png);
@@ -863,12 +926,47 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
Ref<StyleBox> default_style;
Ref<Texture2D> default_icon;
Ref<Font> default_font;
+ int default_font_size = 16;
if (p_font.is_valid()) {
default_font = p_font;
} else if (p_hidpi) {
- default_font = make_font(_hidpi_font_height, _hidpi_font_ascent, _hidpi_font_charcount, &_hidpi_font_charrects[0][0], _hidpi_font_kerning_pair_count, &_hidpi_font_kerning_pairs[0][0], _hidpi_font_img_width, _hidpi_font_img_height, _hidpi_font_img_data);
+ TextServer::BitmapFontData data;
+ data.height = _hidpi_font_height;
+ data.ascent = _hidpi_font_ascent;
+ data.charcount = _hidpi_font_charcount;
+ data.char_rects = &_hidpi_font_charrects[0][0];
+ data.kerning_count = _hidpi_font_kerning_pair_count;
+ data.kernings = &_hidpi_font_kerning_pairs[0][0];
+ data.w = _hidpi_font_img_width;
+ data.h = _hidpi_font_img_height;
+ data.img = _hidpi_font_img_data;
+
+ Ref<FontData> font_data;
+ font_data.instance();
+ font_data->load_memory((const uint8_t *)&data, sizeof(data), "fnt");
+ default_font_size = font_data->get_base_size();
+
+ default_font.instance();
+ default_font->add_data(font_data);
} else {
- default_font = make_font(_lodpi_font_height, _lodpi_font_ascent, _lodpi_font_charcount, &_lodpi_font_charrects[0][0], _lodpi_font_kerning_pair_count, &_lodpi_font_kerning_pairs[0][0], _lodpi_font_img_width, _lodpi_font_img_height, _lodpi_font_img_data);
+ TextServer::BitmapFontData data;
+ data.height = _lodpi_font_height;
+ data.ascent = _lodpi_font_ascent;
+ data.charcount = _lodpi_font_charcount;
+ data.char_rects = &_lodpi_font_charrects[0][0];
+ data.kerning_count = _lodpi_font_kerning_pair_count;
+ data.kernings = &_lodpi_font_kerning_pairs[0][0];
+ data.w = _lodpi_font_img_width;
+ data.h = _lodpi_font_img_height;
+ data.img = _lodpi_font_img_data;
+
+ Ref<FontData> font_data;
+ font_data.instance();
+ font_data->load_memory((const uint8_t *)&data, sizeof(data), "fnt");
+ default_font_size = font_data->get_base_size();
+
+ default_font.instance();
+ default_font->add_data(font_data);
}
Ref<Font> large_font = default_font;
fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0);
@@ -877,6 +975,7 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
Theme::set_default_icon(default_icon);
Theme::set_default_style(default_style);
Theme::set_default_font(default_font);
+ Theme::set_default_font_size(default_font_size);
}
void clear_default_theme() {
diff --git a/scene/resources/default_theme/font_lodpi.inc b/scene/resources/default_theme/font_lodpi.inc
index 959e2c1d7b..d2f5851224 100644
--- a/scene/resources/default_theme/font_lodpi.inc
+++ b/scene/resources/default_theme/font_lodpi.inc
@@ -8,7 +8,7 @@ static const int _lodpi_font_charrects[191][8]={
{224,85,180,5,11,0,1,7},
{192,32,16,11,13,-2,-1,9},
{96,2,216,3,2,0,3,8},
-{160,1734439808,0,0,0,11,0,4},
+{160,0,0,0,0,11,0,4},
{32,0,0,0,0,11,0,4},
{33,65,234,2,10,1,1,4},
{225,112,169,5,11,0,1,7},
diff --git a/scene/resources/default_theme/icon_grid_minimap.png b/scene/resources/default_theme/icon_grid_minimap.png
new file mode 100644
index 0000000000..00a6179d5e
--- /dev/null
+++ b/scene/resources/default_theme/icon_grid_minimap.png
Binary files differ
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 a15efb593a..b905c9db69 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -6,6 +6,10 @@ static const unsigned char arrow_down_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x34, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x32, 0x78, 0xf0, 0x1f, 0x15, 0x52, 0x20, 0xf1, 0x30, 0xee, 0xc1, 0x17, 0xb8, 0xf0, 0xb7, 0x87, 0x69, 0x48, 0xb6, 0xdc, 0xd7, 0xb8, 0x7f, 0x9, 0x2c, 0x7c, 0xfd, 0xb1, 0x2e, 0x9a, 0x3, 0x5e, 0x70, 0x3f, 0x9c, 0xff, 0x70, 0xfe, 0xb, 0x6e, 0x6, 0xea, 0x3, 0x0, 0xfb, 0x81, 0x48, 0xb8, 0x4d, 0xe4, 0x75, 0xd9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char arrow_left_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0xa0, 0x0, 0xdc, 0x4f, 0x78, 0xf0, 0xff, 0xc1, 0xff, 0x7, 0xff, 0x21, 0x3c, 0x46, 0x98, 0xf0, 0x43, 0xed, 0xff, 0x27, 0x19, 0xb8, 0x19, 0x18, 0x18, 0x18, 0x14, 0x18, 0x19, 0x18, 0x18, 0x18, 0x98, 0x20, 0xc2, 0x2f, 0xb8, 0xff, 0xaf, 0x82, 0x8, 0xc3, 0x0, 0x54, 0xe2, 0xe7, 0x14, 0x6, 0x2d, 0x54, 0x83, 0x99, 0x70, 0xd9, 0x8, 0x95, 0x60, 0xcf, 0x61, 0xb8, 0x86, 0x55, 0x42, 0xe2, 0x2b, 0x63, 0x18, 0xc3, 0x57, 0xac, 0x46, 0xc9, 0x5f, 0xfd, 0x9f, 0x43, 0x89, 0x67, 0x19, 0x18, 0x0, 0xf4, 0x30, 0x14, 0x49, 0xef, 0xe6, 0x74, 0x60, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char arrow_right_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x17, 0x3c, 0xf8, 0xf, 0x82, 0xf7, 0x13, 0x70, 0x48, 0x3c, 0xf8, 0xf2, 0x50, 0x1b, 0x43, 0x2, 0xa, 0xaf, 0xbe, 0xe0, 0xc6, 0x2e, 0xf1, 0xff, 0xe1, 0x7c, 0x12, 0x24, 0x10, 0x46, 0x11, 0xb6, 0x1c, 0xe1, 0x5c, 0xa, 0x0, 0x0, 0xe0, 0x14, 0x48, 0xb1, 0x3d, 0x1b, 0x7a, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -162,6 +166,10 @@ static const unsigned char icon_folder_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x6, 0x78, 0x70, 0xf4, 0xc1, 0x7f, 0x24, 0x78, 0x18, 0x53, 0xc1, 0x7f, 0x54, 0x48, 0x50, 0xc1, 0x43, 0x1b, 0xbc, 0xa, 0x50, 0xad, 0x23, 0xa4, 0xe0, 0xff, 0x70, 0x52, 0x70, 0x18, 0x97, 0xf4, 0xfd, 0x43, 0xd4, 0x88, 0x4a, 0x0, 0x5a, 0xcb, 0x18, 0xab, 0x5e, 0xd9, 0x1a, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char icon_grid_minimap_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x2, 0xd, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x75, 0x93, 0x31, 0x68, 0x14, 0x51, 0x10, 0x86, 0xbf, 0xd9, 0xd, 0xbb, 0xde, 0x76, 0x82, 0x21, 0xf8, 0xe0, 0xbc, 0x5d, 0x8b, 0x80, 0x69, 0x6c, 0xd2, 0x5a, 0x6a, 0x91, 0xc3, 0xd2, 0x46, 0x22, 0x8, 0x9, 0x89, 0x70, 0x85, 0x10, 0x41, 0xd, 0x24, 0x45, 0xb0, 0xb, 0x68, 0x11, 0x14, 0x24, 0x10, 0x22, 0x62, 0x21, 0x41, 0xe, 0x4b, 0x21, 0xa4, 0xb7, 0x49, 0x17, 0xb1, 0x8, 0xb9, 0xdd, 0xc7, 0x86, 0x33, 0x21, 0xe1, 0x3a, 0x8f, 0x64, 0x61, 0x6f, 0x2c, 0xbc, 0x3b, 0x36, 0xb9, 0xdc, 0xc0, 0x2b, 0xde, 0xcc, 0xfc, 0xf3, 0xff, 0xfc, 0xcc, 0x48, 0xa3, 0xd1, 0x78, 0x20, 0x22, 0x13, 0xbe, 0xef, 0xaf, 0xdf, 0xac, 0xd7, 0x1f, 0xe1, 0x38, 0xd3, 0xa8, 0x2a, 0xf0, 0x45, 0x6a, 0xb5, 0xcf, 0x5c, 0x11, 0xcd, 0x66, 0x33, 0x38, 0x3f, 0x3f, 0x9f, 0x13, 0x91, 0x7d, 0xb1, 0xd6, 0x6e, 0xaa, 0xea, 0xd3, 0xe0, 0xe8, 0xe8, 0xde, 0xe8, 0xee, 0xee, 0x37, 0xc0, 0xe9, 0xf6, 0x75, 0xf0, 0xfd, 0x9, 0x99, 0x9d, 0x6d, 0x15, 0x81, 0x59, 0x96, 0x3d, 0x3, 0x5e, 0x2, 0x63, 0x22, 0xf2, 0x69, 0xa4, 0x57, 0x1c, 0xdd, 0xdb, 0xfb, 0x5b, 0x0, 0x3, 0x38, 0x67, 0x41, 0x30, 0x11, 0xc7, 0xf1, 0x13, 0x0, 0x11, 0x71, 0xb2, 0x2c, 0x7b, 0xd8, 0xad, 0xad, 0x2, 0x6f, 0xb9, 0x0, 0x38, 0x3c, 0xfc, 0x5, 0x9c, 0xf6, 0xff, 0x22, 0x27, 0x27, 0xe3, 0xe3, 0x7f, 0xa, 0x3, 0x67, 0x45, 0xe4, 0xbb, 0xe7, 0x79, 0xb7, 0xc3, 0x30, 0x7c, 0xd7, 0x67, 0xe9, 0xe3, 0x67, 0x66, 0x5c, 0x60, 0x1, 0x50, 0x40, 0x51, 0x7d, 0x71, 0x6b, 0x72, 0xf2, 0x20, 0x8a, 0xa2, 0xf9, 0x28, 0x8a, 0xe6, 0x1, 0x3a, 0x9d, 0xce, 0x4f, 0x63, 0x4c, 0x3b, 0x4d, 0xd3, 0xd2, 0xc0, 0x80, 0x3c, 0xcf, 0xf, 0x92, 0xa9, 0xa9, 0x31, 0x60, 0x5, 0x58, 0x91, 0x5a, 0xed, 0xc7, 0x15, 0xfe, 0x95, 0xac, 0xb5, 0xcf, 0xf3, 0x3c, 0x3f, 0xe8, 0x25, 0x46, 0xa, 0xc5, 0xd, 0x11, 0x59, 0xb3, 0xd5, 0xea, 0x1b, 0xa0, 0x95, 0x54, 0xab, 0x5b, 0x97, 0xd1, 0x22, 0xb2, 0xa6, 0xaa, 0x6d, 0x60, 0xd, 0x58, 0xba, 0xa0, 0x20, 0xc, 0xc3, 0x65, 0xd7, 0x75, 0x23, 0xe0, 0x2e, 0xb0, 0x1, 0x5c, 0xbf, 0xf4, 0x0, 0xbe, 0xba, 0xae, 0x1b, 0x85, 0x61, 0xb8, 0x3c, 0xa0, 0x20, 0x4d, 0xd3, 0x52, 0xb9, 0x5c, 0x6e, 0xc5, 0x71, 0xbc, 0x23, 0x22, 0xd3, 0x61, 0x18, 0xde, 0x2f, 0xb2, 0x27, 0x49, 0xa2, 0xaa, 0xba, 0x53, 0x2e, 0x97, 0x5b, 0x69, 0x9a, 0x96, 0xf2, 0x3c, 0x1f, 0xf0, 0xc0, 0x5a, 0x6b, 0x5f, 0x1, 0x25, 0x86, 0x84, 0xe3, 0x38, 0x9e, 0xb5, 0x76, 0x2e, 0xcf, 0xf3, 0xfd, 0x1, 0x5, 0x22, 0xb2, 0xa1, 0xaa, 0x4b, 0x22, 0x72, 0xad, 0xcb, 0x38, 0xe0, 0x81, 0xaa, 0x7e, 0x0, 0xce, 0x44, 0xe4, 0xbd, 0xaa, 0xbe, 0xbe, 0xa0, 0xa0, 0x52, 0xa9, 0x2c, 0x7a, 0x9e, 0x17, 0x1, 0x3d, 0xe0, 0x55, 0x1e, 0x6c, 0x79, 0x9e, 0x17, 0x55, 0x2a, 0x95, 0xc5, 0x1, 0x5, 0xcd, 0x66, 0x33, 0x30, 0xc6, 0x9c, 0xc6, 0x71, 0xbc, 0x2d, 0x22, 0x8f, 0x87, 0x78, 0xb0, 0x6d, 0x8c, 0x39, 0xed, 0xae, 0x74, 0xdf, 0x83, 0x3a, 0x70, 0x9c, 0x65, 0x59, 0x23, 0x49, 0x92, 0x5, 0x11, 0x9, 0x86, 0x79, 0x20, 0x22, 0x41, 0x92, 0x24, 0xb, 0x59, 0x96, 0x35, 0x80, 0x63, 0xa0, 0x2e, 0x3d, 0xf6, 0xc2, 0x91, 0xdc, 0x0, 0x5c, 0x55, 0x5d, 0xbf, 0x4, 0x9e, 0x3, 0x72, 0xfe, 0xaf, 0xfb, 0xaa, 0xe7, 0x79, 0x1f, 0x8d, 0x31, 0x6d, 0x29, 0x36, 0xf5, 0xce, 0x14, 0xb8, 0x33, 0x44, 0xc4, 0x6f, 0xdf, 0xf7, 0xd7, 0x8d, 0x31, 0xed, 0x5e, 0xe2, 0x1f, 0xb, 0x5c, 0xe2, 0xcb, 0xd, 0x9b, 0x69, 0xcb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char icon_parent_folder_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x68, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x33, 0xb8, 0x27, 0xfe, 0xe0, 0xfc, 0x83, 0x73, 0xf7, 0xc4, 0x71, 0x48, 0xdf, 0x11, 0x7b, 0x78, 0xe9, 0xc1, 0x3f, 0x20, 0xbc, 0xfe, 0x40, 0x12, 0x8f, 0x34, 0x4c, 0x9, 0xa6, 0xe1, 0x57, 0x80, 0x12, 0x17, 0x81, 0xf8, 0x2f, 0x58, 0xe1, 0x15, 0x34, 0x8b, 0x1e, 0x9c, 0x5, 0xa, 0x5e, 0xb8, 0x23, 0x6, 0x52, 0x70, 0x5b, 0x14, 0xac, 0xf0, 0xc, 0xaa, 0x82, 0x7d, 0xf, 0x8e, 0xde, 0x14, 0xf9, 0xcf, 0x8, 0x52, 0xc0, 0xc0, 0x70, 0x5b, 0xf4, 0xe1, 0xc9, 0x7, 0x47, 0xb1, 0xb8, 0x3, 0xaa, 0x0, 0xa, 0x48, 0x52, 0x80, 0xb0, 0xea, 0xc8, 0xc3, 0x83, 0xc, 0x83, 0xe, 0x0, 0x0, 0xb8, 0x27, 0x55, 0x4c, 0xbe, 0xc0, 0xd2, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -214,18 +222,34 @@ static const unsigned char option_button_disabled_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x2f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x3f, 0x3f, 0x5a, 0x5a, 0x5a, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x59, 0x59, 0x59, 0x2a, 0x2a, 0x30, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x27, 0x35, 0x35, 0x35, 0x4a, 0x4a, 0x4a, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x56, 0x56, 0x56, 0x62, 0x62, 0x62, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x48, 0x48, 0x48, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, 0x54, 0x54, 0x54, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x49, 0x49, 0x49, 0x3a, 0x3a, 0x3a, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x39, 0x39, 0x39, 0x47, 0x47, 0x47, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x46, 0x46, 0x46, 0xd3, 0xa7, 0xd4, 0x88, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xec, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0x8e, 0x5, 0x4e, 0x0, 0x31, 0x10, 0x45, 0xff, 0xef, 0x56, 0xc2, 0xe2, 0xee, 0x4e, 0x3c, 0xc8, 0xd, 0xb8, 0x38, 0xa7, 0xc0, 0xdd, 0xdd, 0x5d, 0xbb, 0x94, 0x4c, 0xd2, 0xc1, 0xdf, 0x4a, 0x47, 0x5e, 0x27, 0xc3, 0xc, 0x80, 0x64, 0x24, 0xb8, 0xc7, 0x4f, 0x2c, 0x6b, 0xd5, 0x90, 0xff, 0xdd, 0x23, 0x7e, 0xc1, 0xa2, 0xb6, 0x64, 0xad, 0x26, 0x82, 0xc6, 0xef, 0x45, 0xbc, 0xe3, 0x1, 0x2c, 0x69, 0x9b, 0xc8, 0x7f, 0x5, 0x36, 0x1e, 0x41, 0x84, 0xd6, 0x4, 0x25, 0x90, 0xb7, 0x39, 0x6c, 0x4c, 0x2f, 0xbe, 0x68, 0xdf, 0x13, 0xa1, 0x9e, 0xc8, 0xa4, 0xb7, 0xe7, 0x47, 0x93, 0x63, 0x1b, 0xf9, 0xdc, 0x8, 0x88, 0x60, 0xbf, 0x4, 0xff, 0xd2, 0x56, 0x93, 0xe3, 0xb7, 0xb2, 0xf6, 0x2c, 0x36, 0x1, 0x16, 0xf4, 0x5f, 0x42, 0xc4, 0x17, 0x8f, 0xb5, 0xc0, 0xa5, 0x8, 0x30, 0x5f, 0xc2, 0x5d, 0xcf, 0xc9, 0xd, 0x74, 0x87, 0x8b, 0x5e, 0x56, 0x22, 0x24, 0xf7, 0x6d, 0xc2, 0xd1, 0x80, 0x26, 0x27, 0x5d, 0x5b, 0x67, 0x7d, 0x2f, 0x80, 0x4d, 0xc9, 0x7f, 0x9, 0x63, 0xa7, 0x61, 0x23, 0xc7, 0x74, 0x3d, 0xf, 0xae, 0x41, 0x84, 0x6f, 0x13, 0xe6, 0x26, 0xfa, 0x36, 0x46, 0x73, 0xbc, 0xba, 0x3b, 0xd6, 0xca, 0x8, 0xd0, 0xd6, 0x36, 0x4e, 0x35, 0xeb, 0xad, 0xd7, 0xb0, 0x60, 0x72, 0xdc, 0xda, 0x9c, 0x2, 0x67, 0x76, 0x64, 0x82, 0x99, 0x9b, 0xb6, 0x80, 0xb0, 0x7e, 0x8d, 0xf6, 0x5a, 0xdd, 0xe1, 0xb5, 0xba, 0xba, 0xb1, 0x0, 0xcd, 0xc7, 0x50, 0x23, 0xeb, 0xfb, 0x7f, 0xb4, 0xc8, 0x22, 0x18, 0xdd, 0x0, 0xd5, 0xec, 0x4e, 0x53, 0xc6, 0x18, 0x44, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char option_button_disabled_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x2f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x5a, 0x5a, 0x5a, 0x3e, 0x3e, 0x3e, 0x2a, 0x2a, 0x30, 0x59, 0x59, 0x59, 0x22, 0x22, 0x27, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x4a, 0x4a, 0x4a, 0x36, 0x36, 0x36, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x62, 0x62, 0x62, 0x56, 0x56, 0x56, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x54, 0x54, 0x54, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x48, 0x48, 0x48, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x3a, 0x3a, 0x3a, 0x49, 0x49, 0x49, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x47, 0x47, 0x47, 0x39, 0x39, 0x39, 0x46, 0x46, 0x46, 0xda, 0x9d, 0x96, 0x5c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0xf, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0xef, 0x50, 0x45, 0x4b, 0x6d, 0xe, 0x95, 0xa6, 0xa5, 0x40, 0xab, 0x15, 0x5, 0xdc, 0xc0, 0x8a, 0xa, 0xb8, 0xa1, 0xef, 0xff, 0xc, 0x86, 0x90, 0x30, 0xc, 0x17, 0xfa, 0x5d, 0x26, 0xff, 0x4c, 0x4e, 0x12, 0x41, 0x98, 0x32, 0x33, 0x1b, 0x98, 0x9b, 0x17, 0x83, 0x84, 0x18, 0xa, 0x47, 0xa2, 0xb1, 0xe9, 0x7d, 0x21, 0x16, 0x58, 0x58, 0x5c, 0x5a, 0x96, 0x24, 0x29, 0x9e, 0x48, 0xa6, 0xd2, 0x2b, 0x51, 0xb2, 0xb4, 0xba, 0x96, 0xc9, 0xca, 0x87, 0x47, 0x8c, 0x9c, 0xc9, 0x1d, 0x9f, 0x30, 0xeb, 0x1b, 0xe9, 0x8, 0x9, 0x36, 0x15, 0x25, 0xaf, 0x9e, 0x6a, 0x8c, 0x8a, 0xa0, 0xa0, 0x8f, 0x9c, 0x15, 0xb7, 0x52, 0x61, 0x12, 0xa8, 0x6, 0x56, 0x4d, 0x2b, 0xcb, 0x98, 0xb8, 0xc4, 0x3, 0x5d, 0x2f, 0x24, 0x43, 0xc3, 0xc0, 0x6, 0xcd, 0x2a, 0xcb, 0x4c, 0xe, 0x4a, 0x95, 0x2a, 0x57, 0x49, 0x88, 0x24, 0x70, 0xc, 0x70, 0xcf, 0xcb, 0x17, 0x8c, 0x5, 0x8e, 0x77, 0xc9, 0x79, 0xf1, 0x20, 0x9, 0x80, 0x4, 0xd6, 0x64, 0x50, 0xbb, 0xe2, 0x6a, 0xd2, 0x30, 0xc0, 0xd7, 0xf5, 0x89, 0x19, 0xb4, 0x46, 0xbe, 0x79, 0xc3, 0x35, 0x69, 0x80, 0x6e, 0x15, 0xb8, 0x33, 0xef, 0x99, 0x7, 0x84, 0x5a, 0x6d, 0xae, 0x45, 0x8f, 0xb0, 0x1f, 0xed, 0x86, 0x3f, 0xbe, 0xa6, 0x6f, 0x3f, 0x75, 0x9e, 0xb9, 0xe, 0x1d, 0xd2, 0x78, 0x79, 0xed, 0x62, 0xf0, 0x19, 0xdc, 0xeb, 0x17, 0xdf, 0xb8, 0x77, 0x7a, 0xcd, 0xed, 0x8f, 0xcf, 0x5e, 0xb7, 0x8e, 0x19, 0xe5, 0xab, 0x3f, 0xf8, 0x66, 0xda, 0x3b, 0xf4, 0xa1, 0x76, 0xf7, 0x90, 0xe3, 0x8e, 0x67, 0x70, 0x1, 0xbc, 0x9f, 0x91, 0xc1, 0xfe, 0x1, 0x7d, 0xea, 0x7f, 0x3f, 0xeb, 0xef, 0xef, 0xfe, 0x5, 0xd6, 0xe3, 0x56, 0x89, 0xd8, 0x62, 0xb6, 0x83, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char option_button_hover_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0x4b, 0x5f, 0x5a, 0x6c, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x5f, 0x5a, 0x6b, 0x2a, 0x2a, 0x30, 0x56, 0x53, 0x64, 0x22, 0x22, 0x27, 0x3e, 0x3b, 0x46, 0x57, 0x53, 0x63, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x5b, 0x57, 0x68, 0x5a, 0x56, 0x67, 0x67, 0x63, 0x76, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x4d, 0x4a, 0x57, 0x49, 0x46, 0x52, 0x48, 0x45, 0x51, 0x5a, 0x56, 0x65, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x5b, 0x57, 0x66, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x43, 0x40, 0x4c, 0x54, 0x50, 0x5f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x47, 0x43, 0x51, 0x43, 0x3f, 0x4d, 0x42, 0x3f, 0x4c, 0x53, 0x4f, 0x5f, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x53, 0x50, 0x5f, 0x53, 0x4f, 0x5e, 0x5f, 0x5a, 0x6c, 0xd3, 0x26, 0x54, 0x35, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe5, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x85, 0x91, 0x43, 0x62, 0xc5, 0x60, 0x18, 0x45, 0xef, 0x8d, 0x51, 0xdb, 0xee, 0x46, 0xca, 0x6d, 0x77, 0x58, 0xce, 0x6b, 0xdb, 0x7c, 0x8a, 0xad, 0xea, 0x44, 0x1f, 0x4e, 0x92, 0x1f, 0x4c, 0x0, 0xe0, 0x9, 0x61, 0xf0, 0x89, 0x32, 0x12, 0xcd, 0xd4, 0x8, 0xef, 0x1f, 0x35, 0x54, 0xa0, 0x68, 0x1a, 0x34, 0x89, 0x8, 0x86, 0xa4, 0xd, 0x57, 0xb4, 0xdf, 0x79, 0x5, 0x89, 0x94, 0xba, 0xf9, 0xb3, 0xc0, 0xce, 0xeb, 0xf0, 0x17, 0x1c, 0xab, 0x11, 0x9, 0x1a, 0xf9, 0x96, 0x84, 0x5d, 0x5e, 0x43, 0x15, 0x7, 0x2e, 0x42, 0x41, 0x51, 0xd3, 0xbe, 0x67, 0xd5, 0x6b, 0x42, 0x3a, 0x38, 0x9b, 0xf5, 0x2e, 0x20, 0xfa, 0x5, 0x33, 0x41, 0x69, 0xf4, 0xeb, 0x49, 0x6c, 0x19, 0xe6, 0xbd, 0xdd, 0xd, 0x48, 0xa0, 0x92, 0xb, 0x36, 0x72, 0x6a, 0x26, 0xf0, 0x14, 0xa, 0x10, 0x72, 0xe1, 0x7d, 0xf4, 0xf6, 0x35, 0x1b, 0xc3, 0xe3, 0x18, 0x9d, 0x50, 0xf0, 0xe4, 0xc2, 0x17, 0xae, 0x27, 0xd3, 0xe4, 0x6e, 0xe8, 0xf8, 0x7e, 0xbc, 0x1, 0x48, 0x9e, 0x57, 0xf8, 0xc5, 0xfc, 0xbd, 0x7a, 0x98, 0xc4, 0x94, 0x47, 0xbf, 0xe4, 0xce, 0x48, 0x8, 0x6e, 0x69, 0x91, 0x63, 0x47, 0x73, 0x49, 0xbc, 0x77, 0x3e, 0xd7, 0x4b, 0x3b, 0x12, 0x36, 0x16, 0xb3, 0x85, 0x6a, 0x68, 0xce, 0x39, 0x62, 0xc6, 0xba, 0x3d, 0x8d, 0xe7, 0x91, 0x20, 0xac, 0xad, 0xa6, 0xc2, 0xe, 0x6, 0xcc, 0x74, 0xc, 0x2d, 0xe7, 0xf9, 0x55, 0x2, 0x28, 0x94, 0x37, 0xab, 0xee, 0xa1, 0xcc, 0xbf, 0xdb, 0xed, 0x3, 0x70, 0xe6, 0x4f, 0x4a, 0xc3, 0xed, 0xed, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char option_button_hover_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x5f, 0x5a, 0x6c, 0x42, 0x40, 0x4b, 0x2a, 0x2a, 0x30, 0x5f, 0x5a, 0x6b, 0x22, 0x22, 0x27, 0x56, 0x53, 0x64, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x57, 0x53, 0x63, 0x3e, 0x3c, 0x47, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x67, 0x63, 0x76, 0x5a, 0x56, 0x67, 0x5b, 0x57, 0x68, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x5a, 0x56, 0x65, 0x48, 0x45, 0x51, 0x49, 0x46, 0x52, 0x4d, 0x4a, 0x57, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x5b, 0x57, 0x66, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x54, 0x50, 0x5f, 0x43, 0x40, 0x4c, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x53, 0x4f, 0x5f, 0x42, 0x3f, 0x4c, 0x43, 0x3f, 0x4d, 0x47, 0x43, 0x51, 0x5f, 0x5a, 0x6c, 0x53, 0x4f, 0x5e, 0x53, 0x50, 0x5f, 0x68, 0x6, 0xa3, 0x65, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x15, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0x8b, 0xb, 0x54, 0x51, 0x4b, 0x6d, 0x8f, 0x60, 0xd3, 0x62, 0xc1, 0x56, 0x11, 0x5, 0xdc, 0xd0, 0x5a, 0x14, 0x70, 0x5f, 0xdf, 0xff, 0x1, 0xc, 0x69, 0xc2, 0x30, 0x5c, 0xe8, 0x77, 0x99, 0xfc, 0x33, 0x39, 0x49, 0x38, 0x6e, 0xc8, 0xc8, 0xa8, 0x6f, 0x6c, 0x9c, 0xf7, 0x63, 0x7c, 0x20, 0x18, 0xa, 0x47, 0x86, 0xf7, 0xb9, 0x88, 0x6f, 0x62, 0x72, 0x6a, 0x5a, 0x10, 0x84, 0x68, 0x2c, 0x9e, 0x48, 0xce, 0x84, 0xf1, 0xd2, 0xec, 0x5c, 0x3a, 0x23, 0x6e, 0x6c, 0x52, 0x62, 0x3a, 0xbb, 0xb5, 0xed, 0xd9, 0x99, 0x5f, 0x48, 0x86, 0x70, 0xb0, 0x28, 0x49, 0x39, 0x79, 0x57, 0xa1, 0x64, 0x15, 0xf6, 0xf2, 0x9e, 0xc2, 0xfe, 0x52, 0x22, 0x88, 0x3, 0x59, 0x43, 0xb2, 0x6e, 0x64, 0x28, 0x1d, 0x15, 0x59, 0x90, 0x2f, 0x1c, 0xc4, 0x3, 0xbd, 0xc0, 0x4, 0xc5, 0x28, 0x89, 0x54, 0x16, 0x8a, 0xe5, 0x43, 0xa6, 0x1c, 0xe3, 0x71, 0x60, 0x69, 0x60, 0x1f, 0x95, 0x8e, 0x29, 0x3, 0xac, 0xca, 0x9, 0x53, 0x89, 0xfa, 0x71, 0x0, 0x38, 0x30, 0x6, 0x83, 0xea, 0x29, 0x53, 0x15, 0x7a, 0x1, 0x3a, 0xab, 0xd, 0xcc, 0xa0, 0xd4, 0x73, 0x8d, 0x73, 0xa6, 0x41, 0x2, 0xf5, 0x42, 0x82, 0x4b, 0xfd, 0x8a, 0xba, 0x56, 0xd5, 0xe6, 0xd, 0xd3, 0x24, 0x47, 0x98, 0xb7, 0x66, 0xdd, 0xe9, 0x5f, 0xd3, 0x31, 0xef, 0xdc, 0x16, 0xe3, 0x92, 0x21, 0xb5, 0xfb, 0x87, 0x36, 0x2, 0x87, 0x42, 0x9d, 0xee, 0xe3, 0x13, 0xd5, 0x72, 0xc9, 0x35, 0x97, 0x9f, 0x5f, 0x3a, 0xed, 0x1a, 0xa2, 0xa4, 0xd7, 0xee, 0xdb, 0xbb, 0xe7, 0xe3, 0x33, 0x45, 0x1e, 0x6a, 0x65, 0x55, 0xb5, 0xec, 0xfe, 0xc, 0x36, 0xc0, 0xd7, 0xb7, 0xe7, 0x67, 0x6d, 0x9d, 0x3c, 0xf5, 0xbf, 0x9f, 0xf5, 0xf7, 0x77, 0xff, 0x2, 0xa7, 0xc5, 0x58, 0xc8, 0x6e, 0x59, 0x8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char option_button_normal_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x56, 0x52, 0x60, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x52, 0x22, 0x22, 0x27, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x5d, 0x5a, 0x6a, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x46, 0x42, 0x4f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x4e, 0x4b, 0x58, 0x8, 0xd9, 0x10, 0xcb, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xd1, 0x55, 0x5a, 0xc4, 0x30, 0x14, 0x5, 0xe0, 0x73, 0xea, 0x82, 0xbb, 0x3b, 0x6c, 0x80, 0x45, 0xb0, 0x70, 0x5e, 0xb1, 0x37, 0xdc, 0x75, 0xdc, 0x3d, 0x43, 0x2a, 0x5f, 0x3a, 0x7e, 0x6a, 0xc9, 0xbd, 0x7f, 0x9d, 0x2a, 0x0, 0xa4, 0x16, 0xd, 0xaa, 0x18, 0x8e, 0x41, 0x3f, 0x11, 0xd1, 0xbe, 0x52, 0xc7, 0x48, 0xa8, 0xfb, 0x5e, 0x68, 0xd4, 0x24, 0x96, 0x6a, 0xfc, 0xaf, 0x8b, 0x32, 0xbf, 0x61, 0x90, 0xc6, 0x3c, 0x27, 0x3, 0xce, 0xfe, 0x20, 0x2, 0x4b, 0xd2, 0x45, 0x1f, 0x14, 0xd5, 0x78, 0x5e, 0x36, 0x1c, 0x7d, 0xe5, 0x33, 0x2, 0xb3, 0x84, 0x8a, 0xec, 0xd4, 0xeb, 0x9a, 0x1a, 0x1b, 0x82, 0xf5, 0x79, 0x20, 0x2, 0x46, 0x1f, 0x58, 0x8d, 0x95, 0xe4, 0x6a, 0x1d, 0xcf, 0x4f, 0x89, 0x85, 0x10, 0x80, 0x56, 0x1f, 0x8, 0xf4, 0x53, 0xf7, 0x81, 0x6c, 0x4, 0xa0, 0xf5, 0x41, 0x69, 0xeb, 0xb7, 0xd0, 0x7b, 0x86, 0xcc, 0x36, 0xbb, 0x11, 0x90, 0x66, 0x1f, 0x88, 0xef, 0xbd, 0x64, 0x92, 0x5a, 0x7b, 0x4e, 0xed, 0x34, 0x42, 0x20, 0xa5, 0xd5, 0x7, 0x27, 0x69, 0xfb, 0x51, 0x8d, 0x69, 0x6e, 0xd5, 0xcc, 0xb9, 0x18, 0xf4, 0xaf, 0x40, 0x6e, 0x3f, 0x1d, 0xab, 0xf1, 0xdd, 0xc7, 0xf1, 0x12, 0x45, 0xc, 0xce, 0x4f, 0x17, 0x12, 0xd0, 0xb6, 0xc4, 0x87, 0x1a, 0x6f, 0x2f, 0x48, 0x8b, 0xef, 0x31, 0xd0, 0x6e, 0xce, 0xa8, 0xc0, 0x25, 0x56, 0x7d, 0x5, 0x52, 0xed, 0x6e, 0xae, 0x68, 0x0, 0xd4, 0x86, 0x7f, 0x56, 0x43, 0x62, 0x38, 0xc, 0x46, 0x28, 0xba, 0x1, 0x7a, 0xad, 0x4f, 0x59, 0x90, 0xab, 0xbf, 0xa4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char option_button_normal_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x56, 0x53, 0x61, 0x3c, 0x3a, 0x45, 0x2a, 0x2a, 0x30, 0x56, 0x52, 0x60, 0x22, 0x22, 0x27, 0x47, 0x44, 0x52, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x47, 0x44, 0x50, 0x33, 0x31, 0x3a, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x5d, 0x5a, 0x6a, 0x51, 0x4f, 0x5d, 0x52, 0x50, 0x5d, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x51, 0x4e, 0x5b, 0x41, 0x3e, 0x49, 0x42, 0x3e, 0x4a, 0x46, 0x42, 0x4e, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x50, 0x4e, 0x5a, 0x40, 0x3e, 0x48, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x47, 0x43, 0x50, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x44, 0x41, 0x4e, 0x36, 0x34, 0x3e, 0x4e, 0x4b, 0x58, 0x44, 0x41, 0x4c, 0x44, 0x42, 0x4d, 0x7d, 0x2e, 0xcf, 0xc5, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x13, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0x8b, 0xb, 0x54, 0x51, 0x4b, 0x6d, 0xf, 0x68, 0xd3, 0xa2, 0x5, 0x5b, 0x40, 0x14, 0x70, 0x3, 0x2b, 0x2a, 0xe0, 0xbe, 0xbd, 0xff, 0x3, 0x18, 0xd2, 0x84, 0x61, 0xb8, 0xd0, 0xef, 0x32, 0xf9, 0x67, 0x72, 0x92, 0x70, 0xdc, 0x88, 0xb1, 0x71, 0xcf, 0xc4, 0x24, 0xef, 0xc5, 0x78, 0x9f, 0x3f, 0x10, 0xc, 0x8d, 0xee, 0x73, 0x21, 0xcf, 0xd4, 0xf4, 0xcc, 0xac, 0x20, 0x8, 0xe1, 0x48, 0x34, 0x16, 0x9f, 0xb, 0xe2, 0xa5, 0xf9, 0x85, 0x64, 0x4a, 0xdc, 0xda, 0xa6, 0xc4, 0x64, 0x7a, 0x67, 0xd7, 0xb5, 0xb7, 0xb8, 0x14, 0xf, 0xe0, 0x60, 0x59, 0x92, 0x32, 0xf2, 0xbe, 0x42, 0xc9, 0x2a, 0x64, 0x73, 0xae, 0x83, 0xc3, 0x95, 0x98, 0x1f, 0x7, 0xb2, 0x86, 0x64, 0xdd, 0x48, 0x51, 0x3a, 0xca, 0x1f, 0x1d, 0x53, 0xb9, 0x6c, 0xd4, 0xd7, 0xf, 0x4c, 0x50, 0x8c, 0x82, 0x48, 0xa5, 0x21, 0x5f, 0x3c, 0x61, 0x8a, 0x11, 0x1e, 0x7, 0x96, 0x6, 0x76, 0xa9, 0x50, 0xa6, 0xc, 0xb0, 0x2a, 0xa7, 0x4c, 0x25, 0xec, 0xc5, 0x1, 0xe0, 0xc0, 0x18, 0xe, 0xaa, 0x67, 0x4c, 0x55, 0xe8, 0x7, 0xe8, 0xbc, 0x36, 0x34, 0x83, 0x52, 0xcf, 0x34, 0x2e, 0x98, 0x6, 0x9, 0xd4, 0x4b, 0x9, 0xae, 0xf4, 0x6b, 0xea, 0x46, 0x55, 0x9b, 0x2d, 0xa6, 0x49, 0x8e, 0x30, 0x6f, 0xcd, 0xba, 0x33, 0xb8, 0xa6, 0x63, 0xde, 0xb5, 0xef, 0x99, 0x36, 0x19, 0x52, 0x7b, 0x78, 0xec, 0x20, 0x70, 0x28, 0xd4, 0xed, 0x3d, 0x3d, 0x33, 0x2f, 0xe4, 0x9a, 0xab, 0xaf, 0x6f, 0xdd, 0x4e, 0xd, 0x51, 0xd2, 0x7b, 0xef, 0xe3, 0x93, 0x6a, 0x25, 0xc8, 0x43, 0xad, 0xad, 0xab, 0x96, 0x3d, 0x98, 0xc1, 0x6, 0xf8, 0xfa, 0x76, 0xfd, 0x6c, 0x6c, 0x92, 0xa7, 0xfe, 0xf7, 0xb3, 0xfe, 0xfe, 0xee, 0x5f, 0xa1, 0x5f, 0x59, 0xbd, 0x75, 0x41, 0x2b, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char option_button_pressed_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x2f, 0x37, 0x46, 0x43, 0x4f, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x44, 0x50, 0x2a, 0x2a, 0x30, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x27, 0x3d, 0x3a, 0x45, 0x56, 0x52, 0x60, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x43, 0x40, 0x4c, 0x42, 0x40, 0x4b, 0x4c, 0x49, 0x56, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x3a, 0x38, 0x41, 0x36, 0x34, 0x3d, 0x44, 0x41, 0x4c, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x44, 0x42, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x50, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x3f, 0x3d, 0x47, 0x4f, 0x4c, 0x59, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x45, 0x42, 0x4d, 0x41, 0x3e, 0x49, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x52, 0x4e, 0x5c, 0x51, 0x4e, 0x5b, 0x5d, 0x59, 0x69, 0x10, 0x9d, 0xe0, 0x3c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xcf, 0x3, 0x62, 0x4, 0x51, 0x10, 0x4, 0xd0, 0xaa, 0x31, 0x62, 0xdb, 0xb8, 0x49, 0x2e, 0x9e, 0x3b, 0xc4, 0xb6, 0x9d, 0xc5, 0x58, 0x1f, 0xc1, 0xd6, 0xe8, 0x77, 0xf7, 0x1b, 0x59, 0x6c, 0x2, 0x20, 0x37, 0xaa, 0xc5, 0x17, 0x7e, 0xc7, 0x62, 0x28, 0x45, 0x75, 0xfe, 0x6c, 0xe1, 0x4f, 0x68, 0x86, 0x41, 0x69, 0x44, 0x51, 0x4b, 0xb1, 0xce, 0xcc, 0xe4, 0x83, 0xd7, 0xb0, 0x48, 0x6b, 0x98, 0xe8, 0x9, 0x38, 0x70, 0x8b, 0xa, 0xcc, 0x12, 0x1a, 0xf0, 0x4d, 0xac, 0x87, 0xf3, 0x96, 0x6f, 0x8e, 0x5f, 0x56, 0xc0, 0x53, 0x20, 0x8f, 0xbf, 0xdb, 0x86, 0x58, 0x5b, 0x9, 0xbf, 0x47, 0x80, 0xa, 0x58, 0x1a, 0x38, 0xad, 0x9, 0x5f, 0xac, 0xe3, 0x20, 0xbc, 0x4b, 0x46, 0x4b, 0x0, 0x3a, 0x1a, 0x24, 0xd0, 0x69, 0x85, 0xc0, 0x63, 0x5, 0x60, 0x68, 0xf0, 0x36, 0x7f, 0xf3, 0xaa, 0xbe, 0xe1, 0x61, 0x81, 0x69, 0x5, 0x72, 0x5b, 0x83, 0xe4, 0x6a, 0x59, 0x16, 0xf7, 0x53, 0x47, 0x77, 0x8b, 0xad, 0x12, 0xe4, 0xb9, 0xa3, 0xc1, 0xe6, 0x83, 0x7b, 0x20, 0xd6, 0xb4, 0xe7, 0xbf, 0xed, 0xe1, 0x1a, 0xd8, 0xfa, 0xdf, 0xb9, 0x70, 0xb8, 0x21, 0xd6, 0xbb, 0x17, 0x1b, 0xe3, 0x4c, 0x6a, 0xb0, 0xbd, 0x25, 0x5, 0x3b, 0x5e, 0x7c, 0x21, 0xc0, 0xc2, 0x68, 0xee, 0xf1, 0xbc, 0x6, 0x46, 0xb1, 0xbd, 0x5e, 0x30, 0x5, 0x27, 0x19, 0x24, 0xb8, 0x61, 0x6e, 0xf8, 0xf5, 0xf7, 0xcd, 0x47, 0x16, 0xa0, 0x18, 0x13, 0x6a, 0x64, 0x7d, 0xff, 0x8f, 0x1e, 0x59, 0x84, 0xa2, 0x1b, 0x0, 0xe5, 0xe0, 0x4e, 0x46, 0x1d, 0x98, 0x92, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char option_button_pressed_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x46, 0x43, 0x4f, 0x31, 0x2f, 0x38, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x50, 0x22, 0x22, 0x27, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x56, 0x52, 0x60, 0x3c, 0x3a, 0x45, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x4c, 0x49, 0x56, 0x42, 0x40, 0x4b, 0x43, 0x40, 0x4c, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x44, 0x41, 0x4c, 0x36, 0x34, 0x3d, 0x3a, 0x38, 0x41, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x44, 0x41, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4e, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x3f, 0x3d, 0x48, 0x50, 0x4d, 0x5a, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x50, 0x4e, 0x5a, 0x40, 0x3e, 0x48, 0x41, 0x3e, 0x49, 0x45, 0x42, 0x4d, 0x5d, 0x59, 0x69, 0x51, 0x4e, 0x5b, 0x52, 0x4e, 0x5c, 0x49, 0x7e, 0x80, 0x9, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x14, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x40, 0x3, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x40, 0xc0, 0xc6, 0xc1, 0xc9, 0xc5, 0xcd, 0x83, 0x2e, 0xcf, 0xc0, 0xc3, 0xcc, 0xcb, 0xc7, 0x2f, 0x20, 0x28, 0x28, 0x28, 0x24, 0x2c, 0x22, 0x2a, 0x26, 0xce, 0xd, 0x14, 0x92, 0x90, 0x54, 0x51, 0x55, 0x53, 0xd7, 0x80, 0x2, 0x35, 0x15, 0x4d, 0x2d, 0x6d, 0x8, 0xd0, 0x91, 0x92, 0x16, 0xe3, 0x2, 0x2a, 0x90, 0xd1, 0xd5, 0xd5, 0xd3, 0x37, 0x30, 0x84, 0x2, 0x7d, 0x23, 0x63, 0x13, 0x53, 0x28, 0x30, 0x93, 0x15, 0xe5, 0x4, 0x2a, 0xd0, 0x37, 0xb7, 0xd0, 0xb7, 0xb4, 0x52, 0x85, 0x2, 0x4b, 0xb, 0x6b, 0x1b, 0x5b, 0x18, 0xb0, 0x13, 0xe1, 0x0, 0x29, 0xb0, 0x37, 0x36, 0xb4, 0x72, 0x50, 0x83, 0x2, 0x4d, 0x63, 0x6b, 0x47, 0x27, 0x18, 0x70, 0x14, 0x66, 0x3, 0x2a, 0x70, 0x36, 0x37, 0x76, 0x71, 0x75, 0x70, 0x83, 0x2, 0x2b, 0x63, 0x67, 0x77, 0xf, 0x18, 0x70, 0x17, 0x62, 0x7, 0x2a, 0x30, 0x6, 0x2a, 0xb0, 0x42, 0x56, 0xe0, 0xe9, 0x5, 0x3, 0x9e, 0x82, 0x20, 0x5, 0x16, 0xde, 0x3e, 0x48, 0x6e, 0x30, 0xf4, 0xd5, 0xf3, 0xf3, 0x87, 0x1, 0x3f, 0xb0, 0x2, 0xa3, 0x0, 0x5d, 0xe3, 0x40, 0xcb, 0x20, 0x28, 0x8, 0x36, 0x32, 0xa, 0x9, 0x85, 0x81, 0x10, 0xb0, 0x15, 0xf6, 0x61, 0xf6, 0xbe, 0xe1, 0x70, 0x6f, 0x86, 0xdb, 0x47, 0x44, 0x46, 0xc1, 0x40, 0x24, 0xd8, 0x91, 0xe6, 0xd1, 0x31, 0xb1, 0x16, 0xc6, 0xe1, 0x50, 0x60, 0x11, 0x17, 0x9f, 0x90, 0x8, 0x5, 0x49, 0xc9, 0x60, 0x6f, 0xca, 0xa5, 0xa4, 0xc6, 0xc5, 0xfa, 0x58, 0x40, 0x81, 0x6e, 0x5a, 0x7c, 0x7a, 0x6, 0x4, 0x64, 0x66, 0xc9, 0x83, 0x3, 0x4a, 0x41, 0xd1, 0xc8, 0xd9, 0x5, 0xee, 0x6, 0x17, 0x63, 0xe3, 0xec, 0x1c, 0x8, 0xc8, 0x55, 0x52, 0x6, 0x7, 0x35, 0xc1, 0xc8, 0xc2, 0x1f, 0xdd, 0x0, 0xa5, 0xe, 0x59, 0xe5, 0x7f, 0xe9, 0xa4, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char overbright_indicator_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x1, 0x85, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x0, 0x0, 0x78, 0x9c, 0x7d, 0x91, 0x3d, 0x48, 0xc3, 0x40, 0x1c, 0xc5, 0x5f, 0x53, 0xa5, 0x2a, 0x2d, 0xe, 0x16, 0x11, 0x75, 0xc8, 0x50, 0x9d, 0x2c, 0x8a, 0x8a, 0x38, 0x6a, 0x15, 0x8a, 0x50, 0x21, 0xd4, 0xa, 0xad, 0x3a, 0x98, 0x5c, 0xfa, 0x21, 0x34, 0x69, 0x48, 0x52, 0x5c, 0x1c, 0x5, 0xd7, 0x82, 0x83, 0x1f, 0x8b, 0x55, 0x7, 0x17, 0x67, 0x5d, 0x1d, 0x5c, 0x5, 0x41, 0xf0, 0x3, 0xc4, 0xc5, 0xd5, 0x49, 0xd1, 0x45, 0x4a, 0xfc, 0x5f, 0x5a, 0x68, 0x11, 0xe3, 0xc1, 0x71, 0x3f, 0xde, 0xdd, 0x7b, 0xdc, 0xbd, 0x3, 0x84, 0x6a, 0x91, 0x69, 0x56, 0xdb, 0x18, 0xa0, 0xe9, 0xb6, 0x99, 0x8c, 0xc7, 0xc4, 0x74, 0x66, 0x45, 0xc, 0xbc, 0xa2, 0x13, 0x3, 0x8, 0xa1, 0x17, 0xa3, 0x32, 0xb3, 0x8c, 0x59, 0x49, 0x4a, 0xc0, 0x73, 0x7c, 0xdd, 0xc3, 0xc7, 0xd7, 0xbb, 0x28, 0xcf, 0xf2, 0x3e, 0xf7, 0xe7, 0x8, 0xa9, 0x59, 0x8b, 0x1, 0x3e, 0x91, 0x78, 0x86, 0x19, 0xa6, 0x4d, 0xbc, 0x4e, 0x3c, 0xb5, 0x69, 0x1b, 0x9c, 0xf7, 0x89, 0xc3, 0xac, 0x20, 0xab, 0xc4, 0xe7, 0xc4, 0x23, 0x26, 0x5d, 0x90, 0xf8, 0x91, 0xeb, 0x4a, 0x9d, 0xdf, 0x38, 0xe7, 0x5d, 0x16, 0x78, 0x66, 0xd8, 0x4c, 0x25, 0xe7, 0x88, 0xc3, 0xc4, 0x62, 0xbe, 0x85, 0x95, 0x16, 0x66, 0x5, 0x53, 0x23, 0x9e, 0x24, 0x8e, 0xa8, 0x9a, 0x4e, 0xf9, 0x42, 0xba, 0xce, 0x2a, 0xe7, 0x2d, 0xce, 0x5a, 0xb1, 0xcc, 0x1a, 0xf7, 0xe4, 0x2f, 0xc, 0x66, 0xf5, 0xe5, 0x25, 0xae, 0xd3, 0x1c, 0x44, 0x1c, 0xb, 0x58, 0x84, 0x4, 0x11, 0xa, 0xca, 0xd8, 0x40, 0x11, 0x36, 0xa2, 0xb4, 0xea, 0xa4, 0x58, 0x48, 0xd2, 0x7e, 0xcc, 0xc3, 0xdf, 0xef, 0xfa, 0x25, 0x72, 0x29, 0xe4, 0xda, 0x0, 0x23, 0xc7, 0x3c, 0x4a, 0xd0, 0x20, 0xbb, 0x7e, 0xf0, 0x3f, 0xf8, 0xdd, 0xad, 0x95, 0x9b, 0x18, 0xaf, 0x27, 0x5, 0x63, 0x40, 0xfb, 0x8b, 0xe3, 0x7c, 0xc, 0x1, 0x81, 0x5d, 0xa0, 0x56, 0x71, 0x9c, 0xef, 0x63, 0xc7, 0xa9, 0x9d, 0x0, 0xfe, 0x67, 0xe0, 0x4a, 0x6f, 0xfa, 0x4b, 0x55, 0x60, 0xfa, 0x93, 0xf4, 0x4a, 0x53, 0x8b, 0x1c, 0x1, 0xdd, 0xdb, 0xc0, 0xc5, 0x75, 0x53, 0x53, 0xf6, 0x80, 0xcb, 0x1d, 0xa0, 0xef, 0xc9, 0x90, 0x4d, 0xd9, 0x95, 0xfc, 0x34, 0x85, 0x5c, 0xe, 0x78, 0x3f, 0xa3, 0x6f, 0xca, 0x0, 0x3d, 0xb7, 0x40, 0xd7, 0x6a, 0xbd, 0xb7, 0xc6, 0x3e, 0x4e, 0x1f, 0x80, 0x14, 0x75, 0x95, 0xb8, 0x1, 0xe, 0xe, 0x81, 0xe1, 0x3c, 0x65, 0xaf, 0x79, 0xbc, 0xbb, 0xa3, 0xb5, 0xb7, 0x7f, 0xcf, 0x34, 0xfa, 0xfb, 0x1, 0x8e, 0x80, 0x72, 0xb2, 0xed, 0x78, 0xfa, 0x7b, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x15, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0x63, 0x63, 0x66, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4c, 0x39, 0x3a, 0xe, 0x0, 0x0, 0x0, 0x6, 0x74, 0x52, 0x4e, 0x53, 0xff, 0xff, 0xff, 0x7f, 0x0, 0x80, 0x2c, 0x16, 0xc1, 0x6d, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x6, 0x61, 0x66, 0xb8, 0x7d, 0x0, 0x0, 0x0, 0x32, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x0, 0x1, 0x46, 0x65, 0x17, 0x17, 0x30, 0x43, 0xc8, 0x4, 0x50, 0x88, 0x1c, 0x52, 0x1, 0x0, 0x2, 0x40, 0x14, 0xbb, 0x70, 0x8b, 0x40, 0xff, 0x2c, 0x18, 0xbe, 0xc6, 0xed, 0x8d, 0x42, 0xa1, 0x50, 0x28, 0x14, 0xa, 0x85, 0xbd, 0xb0, 0x13, 0xfc, 0x71, 0x1, 0xca, 0xf, 0x19, 0x62, 0x24, 0xd6, 0x8, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -314,6 +338,10 @@ static const unsigned char submenu_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x78, 0xc0, 0xf0, 0xe0, 0x3f, 0x8, 0xde, 0x4f, 0x60, 0x0, 0x3, 0xb8, 0xc0, 0x83, 0x2f, 0xf, 0xb5, 0xe1, 0x2, 0x50, 0x78, 0xf5, 0x5, 0x37, 0xaa, 0xc0, 0xff, 0x87, 0xf3, 0x31, 0x4, 0x30, 0xb5, 0x60, 0x1a, 0x8a, 0x61, 0x2d, 0x0, 0xa6, 0x55, 0x4f, 0xb1, 0x91, 0xd6, 0xa7, 0xae, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char submenu_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x55, 0xcb, 0xa1, 0x11, 0x83, 0x40, 0x14, 0x45, 0xd1, 0xb3, 0x6b, 0x10, 0x34, 0x10, 0x47, 0x34, 0xf4, 0x13, 0x9d, 0x2, 0x28, 0x87, 0x2a, 0xe8, 0x84, 0x2, 0x82, 0x65, 0x71, 0x69, 0x0, 0x11, 0xf5, 0x11, 0x4c, 0x66, 0xd8, 0xe7, 0xee, 0x99, 0x79, 0x80, 0xed, 0x5d, 0xa2, 0x44, 0x9, 0x12, 0xec, 0x43, 0x2c, 0x5a, 0x78, 0xa6, 0xcc, 0xb7, 0x8d, 0xf9, 0x4a, 0xc8, 0xfc, 0x26, 0x3d, 0x37, 0xa8, 0x97, 0x69, 0x46, 0x6b, 0x5, 0x8f, 0x23, 0xbd, 0x1c, 0xd5, 0xa5, 0xfb, 0xc4, 0xf8, 0x87, 0x13, 0xd2, 0x2f, 0x14, 0x49, 0x6f, 0xb1, 0x11, 0xe1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char tab_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x19, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xc0, 0x2, 0xfe, 0x47, 0xfe, 0x17, 0x1, 0xc2, 0x48, 0xd2, 0x84, 0x10, 0x2, 0x84, 0xb9, 0x98, 0x0, 0x0, 0xbf, 0x67, 0x1d, 0x5, 0x89, 0x9b, 0x48, 0x90, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -354,6 +382,14 @@ static const unsigned char toggle_off_disabled_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0xfc, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x24, 0x24, 0x28, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x19, 0x1c, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0x4e, 0x4e, 0x52, 0x1a, 0x1a, 0x1d, 0x32, 0x32, 0x37, 0x2c, 0x26, 0x2c, 0x26, 0x25, 0x2a, 0x27, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x2f, 0x26, 0x2d, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x15, 0x18, 0x20, 0x20, 0x25, 0x20, 0x20, 0x24, 0x5b, 0x5b, 0x5f, 0x84, 0x84, 0x87, 0x77, 0x77, 0x7a, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x69, 0x69, 0x6c, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x27, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1e, 0x0, 0x0, 0x0, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71, 0xb, 0x1b, 0xbb, 0x0, 0x0, 0x0, 0x54, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x98, 0xe5, 0xfa, 0xfe, 0xff, 0xff, 0x8, 0x17, 0x35, 0x86, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x7, 0x3a, 0xb4, 0xff, 0xff, 0xff, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xb, 0x28, 0x8a, 0xff, 0x8b, 0xf6, 0x45, 0x5, 0x9b, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x37, 0xf, 0xff, 0xfb, 0x4c, 0xfe, 0x4e, 0x4f, 0x50, 0xfb, 0x9c, 0xf6, 0x8c, 0x3b, 0xbb, 0x3c, 0x87, 0xf3, 0x53, 0x14, 0xd4, 0x6d, 0x6c, 0xf9, 0x0, 0x0, 0x2, 0x3, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x55, 0x85, 0x9a, 0xe2, 0x30, 0x18, 0xbc, 0x7a, 0x8b, 0xbb, 0x7b, 0x2, 0xbb, 0x4d, 0x36, 0xb8, 0x3b, 0xeb, 0xae, 0xef, 0xff, 0x2e, 0x47, 0x48, 0x3f, 0xa0, 0x7a, 0xae, 0x83, 0x56, 0xfe, 0xe9, 0xfc, 0xfe, 0xe9, 0x6f, 0x2, 0xc7, 0xb, 0x82, 0xf8, 0x45, 0x8, 0x2, 0xcf, 0x39, 0x9a, 0xf3, 0xa2, 0x24, 0x2b, 0xaa, 0xe6, 0xfb, 0x2, 0x34, 0x55, 0x91, 0x25, 0x91, 0xb7, 0x3f, 0x5d, 0xf4, 0xab, 0x81, 0x60, 0x28, 0x1c, 0x89, 0xc6, 0x3c, 0x11, 0x8d, 0x84, 0x43, 0xc1, 0x80, 0xea, 0x17, 0x2d, 0x2a, 0xf8, 0x78, 0x22, 0x99, 0x4a, 0x67, 0xb2, 0xb9, 0x7c, 0xe1, 0xb, 0xc8, 0xe7, 0xb2, 0x99, 0x74, 0x2a, 0x99, 0x88, 0xf3, 0x26, 0xfb, 0x62, 0xa9, 0x5c, 0xa9, 0xd6, 0x0, 0x80, 0x10, 0xb2, 0xfb, 0x20, 0x5, 0x80, 0x75, 0xe8, 0x48, 0x52, 0xad, 0x94, 0x4b, 0xc5, 0x23, 0x6, 0xae, 0xa1, 0x9d, 0x9c, 0xd6, 0x80, 0x8e, 0xf0, 0x1e, 0x48, 0xdf, 0xb1, 0xd4, 0x81, 0x8b, 0x8e, 0xd3, 0x13, 0xad, 0xc1, 0x1d, 0xfc, 0x57, 0x82, 0x67, 0x35, 0x48, 0x30, 0x6a, 0xb6, 0x76, 0x97, 0x5b, 0x3a, 0x41, 0x98, 0xb4, 0x77, 0x4a, 0xdc, 0x3c, 0x39, 0xb, 0x2a, 0xfb, 0x38, 0xf0, 0x9d, 0x6e, 0xaf, 0x6, 0x11, 0xea, 0x1f, 0xdf, 0x41, 0x10, 0x6a, 0x7b, 0xc6, 0x62, 0xd0, 0xed, 0xf0, 0x6, 0x81, 0x58, 0xa, 0xd, 0x1, 0xa1, 0xa2, 0x8f, 0xa1, 0x23, 0xd2, 0xf2, 0x62, 0x18, 0x8e, 0x4a, 0x63, 0x26, 0x81, 0x93, 0x2, 0x13, 0xa0, 0xe3, 0xbe, 0xf5, 0xe, 0x82, 0x3d, 0x25, 0x14, 0x26, 0x1, 0x89, 0x11, 0xf0, 0x72, 0x70, 0xba, 0x15, 0x60, 0xbf, 0x3, 0x11, 0xe3, 0xcf, 0x6c, 0xbe, 0x58, 0xa2, 0x42, 0x7f, 0xb1, 0xc5, 0xee, 0x8b, 0x9d, 0x5f, 0xad, 0x37, 0xcc, 0x7, 0x41, 0x9, 0x65, 0x21, 0xbd, 0xd9, 0x8a, 0x3d, 0xe9, 0xf9, 0x7c, 0x46, 0x16, 0xb3, 0x3e, 0x35, 0xa4, 0x5f, 0x6, 0x2e, 0x46, 0x8a, 0xc0, 0x8, 0xd4, 0xcb, 0x2b, 0x88, 0x75, 0x3b, 0x81, 0x8e, 0xd, 0x1, 0xd4, 0x68, 0x8e, 0xfa, 0xe7, 0x94, 0xe0, 0x7c, 0x4f, 0x90, 0xbf, 0x56, 0x45, 0x46, 0x50, 0xba, 0xa9, 0x41, 0xec, 0x10, 0xb0, 0x96, 0x41, 0xd0, 0x3f, 0xdf, 0x7e, 0xe1, 0x79, 0xff, 0xfc, 0xfc, 0x9c, 0x6c, 0xbf, 0xf6, 0x14, 0xb7, 0x25, 0x83, 0x40, 0xd, 0xd7, 0x3c, 0x15, 0x90, 0x9d, 0x2, 0xec, 0xae, 0x40, 0x9, 0xdd, 0x1, 0xef, 0x18, 0xa0, 0x2, 0x59, 0xda, 0x5c, 0xd8, 0xc7, 0x80, 0x97, 0xd7, 0x5f, 0xc8, 0x42, 0x7f, 0x79, 0xbe, 0x9c, 0x17, 0x2c, 0x4, 0xfb, 0x2c, 0xd0, 0x3a, 0xb8, 0xff, 0x42, 0x1d, 0x10, 0x5a, 0x95, 0x33, 0x44, 0xd8, 0x97, 0x81, 0xfb, 0xa4, 0xc4, 0xed, 0x2b, 0xf1, 0x1, 0xfe, 0x40, 0x25, 0xd2, 0x5e, 0x18, 0x7c, 0x7b, 0x2f, 0x3c, 0xd2, 0x5e, 0xf8, 0x72, 0x37, 0x16, 0xbe, 0xdc, 0x8d, 0x6c, 0x1e, 0x3c, 0x3d, 0xd7, 0x40, 0xdb, 0x32, 0xf, 0xbc, 0xec, 0x9f, 0x5f, 0xf6, 0xf3, 0x80, 0x4d, 0x24, 0x2d, 0xf8, 0xfa, 0x6, 0xea, 0x80, 0xd, 0x24, 0x68, 0x0, 0x6c, 0x5f, 0x8e, 0xf6, 0xd5, 0xd7, 0xa0, 0x56, 0x34, 0xcf, 0xb4, 0x86, 0x92, 0xc, 0xa5, 0x33, 0x17, 0xf9, 0xc2, 0x17, 0x91, 0xbf, 0xc8, 0xa4, 0x43, 0x49, 0xa5, 0xc1, 0x5b, 0xa7, 0x72, 0x47, 0xd, 0xac, 0x47, 0xd7, 0xef, 0xb1, 0x2f, 0xe0, 0xfd, 0x7a, 0xb4, 0xe, 0xa8, 0x1d, 0x91, 0xb3, 0x6f, 0x95, 0xb1, 0xb4, 0xf9, 0x28, 0x7d, 0x79, 0x2f, 0x94, 0x3e, 0x36, 0xd2, 0x98, 0xe7, 0x5c, 0x36, 0x93, 0xf8, 0x15, 0xa0, 0x9b, 0xe9, 0xff, 0xc2, 0x67, 0x14, 0xf4, 0xa5, 0xb3, 0x35, 0x5e, 0x63, 0x97, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char toggle_off_disabled_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x9c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x1d, 0x1d, 0x21, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x2e, 0x2e, 0x2e, 0x46, 0x46, 0x46, 0x57, 0x57, 0x57, 0x60, 0x60, 0x60, 0x62, 0x62, 0x62, 0x5e, 0x5e, 0x5e, 0x4a, 0x4a, 0x4a, 0x12, 0x12, 0x15, 0x25, 0x27, 0x2d, 0x42, 0x42, 0x42, 0x59, 0x59, 0x59, 0x32, 0x32, 0x32, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x39, 0x39, 0x39, 0x49, 0x49, 0x49, 0x5a, 0x5a, 0x5a, 0x48, 0x48, 0x48, 0x54, 0x54, 0x54, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x1e, 0x1e, 0x22, 0x25, 0x26, 0x2b, 0x3f, 0x3f, 0x3f, 0xe, 0xe, 0x10, 0x2c, 0x2c, 0x2c, 0x58, 0x58, 0x58, 0x5d, 0x5d, 0x5f, 0x80, 0x80, 0x80, 0x79, 0x79, 0x79, 0x40, 0x40, 0x44, 0x32, 0x32, 0x37, 0x41, 0x41, 0x41, 0x1a, 0x1a, 0x1d, 0x6a, 0x6a, 0x6d, 0x52, 0x52, 0x52, 0x3a, 0x3a, 0x3a, 0x5b, 0x5b, 0x5b, 0x2f, 0x2f, 0x2f, 0x50, 0x50, 0x51, 0x13, 0x13, 0x15, 0x2b, 0xcd, 0x4, 0x96, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x1, 0x29, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x94, 0xdb, 0x76, 0x83, 0x20, 0x10, 0x45, 0x8d, 0xe8, 0x24, 0x98, 0xda, 0x1a, 0x93, 0xda, 0x5b, 0xac, 0xb9, 0x50, 0x14, 0x52, 0x72, 0xfd, 0xff, 0x7f, 0xab, 0x68, 0x49, 0x28, 0x5a, 0x35, 0xf5, 0xa5, 0xf, 0x39, 0xf, 0xb3, 0x96, 0xe3, 0xda, 0x7, 0x98, 0x81, 0xb1, 0xac, 0x9b, 0xfe, 0x97, 0x6, 0x36, 0x72, 0x5c, 0x68, 0x91, 0xeb, 0x20, 0x7b, 0x50, 0xcf, 0xf, 0x5b, 0xe1, 0xb3, 0xc9, 0xb0, 0x6, 0x1f, 0xe1, 0x46, 0xc6, 0x1b, 0xdf, 0xf9, 0xf7, 0xa5, 0x1e, 0x2, 0xf, 0xf0, 0xc8, 0xe4, 0x27, 0x8d, 0xcb, 0x87, 0xd3, 0xd9, 0xf8, 0x31, 0x7a, 0x92, 0x7a, 0xf6, 0x5e, 0x5e, 0xdf, 0xa6, 0xa1, 0x3b, 0x31, 0xc, 0x1a, 0xd7, 0xf, 0xe7, 0xf1, 0xbb, 0xfe, 0x9d, 0xc4, 0xf3, 0x10, 0xff, 0xe4, 0x17, 0x4d, 0xfc, 0x72, 0x15, 0x7b, 0xc6, 0x81, 0xe2, 0xd5, 0x72, 0xa1, 0xf3, 0xeb, 0xc6, 0xf3, 0x93, 0x8f, 0xc4, 0x4c, 0x25, 0x33, 0x2, 0x6b, 0xcd, 0xc0, 0x56, 0x3f, 0x10, 0x4d, 0x33, 0x6, 0x24, 0xcd, 0x55, 0x4, 0x2e, 0x93, 0x9b, 0xa0, 0x6a, 0x1a, 0x6c, 0xe0, 0x53, 0x33, 0x40, 0x2a, 0x2f, 0x28, 0xe2, 0x29, 0x22, 0x12, 0x24, 0x25, 0x9d, 0x6b, 0xbb, 0xab, 0x1a, 0xec, 0xb6, 0x80, 0x34, 0x3, 0x47, 0x6d, 0x40, 0x42, 0x94, 0x11, 0x21, 0xd, 0x84, 0x32, 0xd8, 0x1f, 0xaa, 0x6, 0x87, 0x3d, 0xe8, 0x65, 0x54, 0x3d, 0x24, 0x22, 0xf, 0x47, 0x4a, 0x84, 0x10, 0xbc, 0x8, 0x45, 0xd6, 0x8f, 0xaa, 0x6, 0x91, 0xf, 0x50, 0xd3, 0x44, 0x5e, 0xec, 0xe0, 0x78, 0xfd, 0xe, 0x2e, 0x35, 0x60, 0xc0, 0x33, 0xf3, 0x8, 0x1d, 0x6a, 0x70, 0xee, 0x2, 0xc9, 0x44, 0x46, 0xc1, 0x30, 0xe8, 0xd0, 0x85, 0xcb, 0x3d, 0xe0, 0x4c, 0xd6, 0x92, 0xf1, 0xef, 0xd0, 0xf5, 0x1e, 0xf4, 0xbe, 0x89, 0xfd, 0xdf, 0x42, 0xff, 0xd7, 0x68, 0x9d, 0xae, 0x9b, 0x7, 0xa7, 0xba, 0x89, 0x4, 0x9d, 0x55, 0x37, 0x91, 0xca, 0x99, 0x88, 0xdb, 0x61, 0xfc, 0xeb, 0x4c, 0xbc, 0xe9, 0xaf, 0xfa, 0x2, 0xdc, 0x1a, 0x30, 0x60, 0x4e, 0xef, 0xb8, 0xbb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
+static const unsigned char toggle_off_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0xaa, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x24, 0x24, 0x28, 0x20, 0x20, 0x25, 0x14, 0x14, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x27, 0x10, 0x10, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x19, 0x19, 0x1c, 0x12, 0x12, 0x15, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x11, 0x11, 0x14, 0x1e, 0x1e, 0x22, 0x23, 0x23, 0x27, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x25, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x24, 0x24, 0x27, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x1a, 0x1a, 0x1e, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x26, 0x11, 0x11, 0x13, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x24, 0x24, 0x28, 0x2d, 0x26, 0x2c, 0x51, 0x2c, 0x39, 0x6c, 0x31, 0x42, 0x71, 0x32, 0x44, 0x6e, 0x31, 0x43, 0x63, 0x2f, 0x3f, 0x4d, 0x2b, 0x37, 0x27, 0x25, 0x2a, 0x47, 0x2a, 0x35, 0x68, 0x30, 0x40, 0x52, 0x2c, 0x39, 0x3c, 0x28, 0x31, 0x2e, 0x25, 0x2c, 0x26, 0x25, 0x2a, 0x32, 0x26, 0x2e, 0x4d, 0x2b, 0x38, 0x66, 0x30, 0x40, 0x50, 0x2c, 0x38, 0x5e, 0x2e, 0x3d, 0x38, 0x27, 0x30, 0x35, 0x27, 0x2f, 0x5f, 0x2e, 0x3d, 0x44, 0x2a, 0x34, 0x5f, 0x2f, 0x3e, 0x2f, 0x25, 0x2c, 0x43, 0x2a, 0x34, 0x2c, 0x26, 0x2c, 0x66, 0x2f, 0x40, 0x37, 0x27, 0x30, 0x36, 0x27, 0x30, 0x64, 0x2f, 0x3f, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x32, 0x32, 0x37, 0x46, 0x2a, 0x35, 0x53, 0x2c, 0x39, 0xc9, 0xc9, 0xca, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x5d, 0x2e, 0x3d, 0x3e, 0x29, 0x32, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x69, 0x30, 0x41, 0x2f, 0x26, 0x2d, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x4e, 0x4e, 0x52, 0x48, 0x2b, 0x36, 0x2c, 0x26, 0x2b, 0x25, 0x25, 0x27, 0xea, 0xac, 0x78, 0x5d, 0x0, 0x0, 0x0, 0x51, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xfa, 0xe5, 0x98, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xf3, 0x86, 0x7, 0x3a, 0x96, 0xf9, 0xb4, 0x9a, 0xb9, 0xb, 0x28, 0x76, 0xfb, 0x8a, 0xde, 0xf6, 0x82, 0x9b, 0xc6, 0xe6, 0x4c, 0xfe, 0x4f, 0xe9, 0xfb, 0x37, 0x83, 0x9c, 0xf6, 0x77, 0x8b, 0x3b, 0x9c, 0xbb, 0x74, 0xda, 0xf3, 0x87, 0xfb, 0x45, 0x4e, 0x53, 0x5, 0xf, 0x14, 0xc7, 0x22, 0x44, 0x61, 0x0, 0x0, 0x2, 0x45, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xd5, 0x55, 0xe7, 0x5f, 0xd3, 0x40, 0x18, 0x26, 0x3b, 0x91, 0x24, 0x65, 0x95, 0xd, 0xd, 0x8e, 0x22, 0x6a, 0x71, 0x2b, 0x82, 0x75, 0xe1, 0x66, 0xa6, 0x97, 0xa0, 0x56, 0x45, 0xa4, 0x2a, 0x2e, 0xdc, 0x76, 0x25, 0x18, 0x84, 0x6, 0x9c, 0xff, 0xb3, 0xb9, 0x26, 0x17, 0xda, 0xa4, 0x49, 0xd4, 0x4f, 0xfa, 0x7c, 0xb8, 0xdf, 0xe5, 0xcd, 0xef, 0x79, 0xee, 0xbd, 0xf7, 0xde, 0xd1, 0xd4, 0xf4, 0xf, 0x1, 0xc3, 0x9, 0x82, 0x8c, 0x4, 0x41, 0xe0, 0x58, 0x43, 0x3a, 0x4e, 0x52, 0x34, 0xc3, 0x72, 0xbb, 0x22, 0xc0, 0xb1, 0xc, 0x4d, 0x91, 0xb8, 0xff, 0x74, 0xb2, 0x99, 0xe5, 0x5, 0x31, 0xd6, 0xd2, 0xda, 0x16, 0x8a, 0xd6, 0x96, 0x98, 0x28, 0xf0, 0x6c, 0x33, 0xe9, 0xf1, 0x2, 0x6f, 0xef, 0x88, 0x77, 0x76, 0x75, 0xf7, 0xf4, 0xf6, 0xc9, 0x11, 0xc8, 0xf4, 0xf5, 0xf, 0xc, 0x76, 0xc6, 0x3b, 0xda, 0xeb, 0x9c, 0xc0, 0x13, 0xd2, 0xd0, 0xee, 0x3d, 0x99, 0x0, 0xe, 0x50, 0x20, 0xec, 0xbd, 0xb5, 0x1, 0x40, 0xdd, 0xbb, 0x6f, 0x48, 0x4a, 0xd4, 0x28, 0x60, 0x49, 0x6e, 0x78, 0xff, 0x48, 0xe0, 0xa1, 0x90, 0xbb, 0x70, 0xeb, 0xf6, 0x1d, 0x1b, 0xd9, 0xbb, 0xf7, 0x16, 0x80, 0x7a, 0x60, 0x98, 0x4b, 0x62, 0x3b, 0xf7, 0x67, 0x84, 0x83, 0xa1, 0xbe, 0xdf, 0x5f, 0x7c, 0xb0, 0xf4, 0x70, 0x39, 0x57, 0xdd, 0xe7, 0x1e, 0x3d, 0x7e, 0xb2, 0xa8, 0xa8, 0x87, 0x4, 0xc6, 0x8d, 0x3, 0x9e, 0x1a, 0x3d, 0x7c, 0x24, 0x94, 0xbf, 0xf2, 0xf4, 0x59, 0xed, 0xf7, 0xf3, 0x17, 0x2b, 0x8a, 0x7a, 0x74, 0x34, 0x85, 0x2e, 0x41, 0x4a, 0xe2, 0xb1, 0x30, 0x7e, 0xee, 0xe5, 0xea, 0xab, 0x7a, 0xcb, 0xf2, 0xea, 0x6b, 0x70, 0x5c, 0x94, 0x48, 0xe7, 0x6, 0x14, 0x7f, 0x22, 0x34, 0xf0, 0x6f, 0xde, 0xbe, 0xf3, 0x9a, 0xde, 0x7f, 0xf8, 0x8, 0x4e, 0xf2, 0x94, 0x73, 0x3, 0x5a, 0x38, 0x85, 0x7e, 0xe4, 0xb, 0xc5, 0x52, 0x59, 0xd6, 0x8a, 0x16, 0xaa, 0x8b, 0xe, 0x8d, 0x6b, 0x9f, 0xfc, 0xa2, 0x4b, 0x6b, 0xe0, 0xb4, 0x40, 0xdb, 0x2, 0x4, 0x23, 0xf6, 0x20, 0xbb, 0xb1, 0x9e, 0xff, 0x5c, 0xcc, 0x6b, 0x90, 0xa8, 0xd9, 0x6c, 0xb, 0x1b, 0x9b, 0x7e, 0x81, 0xcd, 0xd, 0xa5, 0x5f, 0x64, 0x1c, 0x1, 0x76, 0xac, 0x17, 0x39, 0x0, 0x49, 0xeb, 0x15, 0xcd, 0x84, 0x2, 0x26, 0x12, 0xd8, 0xda, 0xf6, 0xb, 0x6c, 0x6f, 0x29, 0x67, 0xc6, 0x58, 0x47, 0x40, 0x1a, 0x47, 0x6f, 0xa8, 0x99, 0xd6, 0xf2, 0xa5, 0xa0, 0x19, 0x86, 0xa1, 0x57, 0x97, 0xaa, 0x35, 0x9b, 0x6b, 0x10, 0xd8, 0xac, 0xa2, 0x8e, 0x4b, 0xc8, 0x83, 0x18, 0x4a, 0x22, 0x1d, 0x7a, 0x50, 0xf8, 0xaa, 0x41, 0xa6, 0x66, 0x44, 0x78, 0xa0, 0xc6, 0x58, 0x37, 0x6, 0x13, 0xc8, 0x6e, 0x56, 0x64, 0xbd, 0x54, 0xf6, 0x8, 0x34, 0x8e, 0x1, 0x38, 0x8b, 0x62, 0x80, 0xd3, 0x69, 0xf7, 0x15, 0xb4, 0x92, 0x59, 0x2a, 0xc8, 0x1e, 0x81, 0xa0, 0x57, 0x48, 0xd3, 0x6e, 0x1e, 0x9c, 0x73, 0x7f, 0xe8, 0x95, 0x6f, 0x56, 0x2c, 0xcb, 0xba, 0xb3, 0x84, 0xe5, 0xc1, 0x79, 0x94, 0x7, 0x7f, 0x97, 0x89, 0xca, 0x5, 0x37, 0x13, 0x61, 0x2d, 0x5c, 0xfc, 0xf3, 0x5a, 0xb8, 0xb4, 0x53, 0xb, 0xbf, 0x51, 0x8d, 0xdf, 0x43, 0xab, 0x11, 0xf6, 0x83, 0xc9, 0xcb, 0xa1, 0x3e, 0xd4, 0xf7, 0x83, 0x1f, 0x40, 0xbd, 0x32, 0x59, 0xd3, 0xf, 0x60, 0x47, 0xe2, 0x84, 0xab, 0xd7, 0x82, 0xc8, 0x76, 0x47, 0x72, 0x9a, 0x92, 0xd5, 0x91, 0x7e, 0x82, 0xeb, 0x37, 0x4, 0x2e, 0x51, 0xdf, 0xd3, 0x92, 0x4c, 0x5c, 0xec, 0xea, 0x9e, 0x18, 0x91, 0x23, 0x91, 0xb9, 0x39, 0x30, 0x28, 0xc6, 0x99, 0xa4, 0xa7, 0x31, 0x63, 0x64, 0x8a, 0xe5, 0xd3, 0x53, 0xd3, 0x33, 0x6d, 0x11, 0x98, 0x99, 0x9e, 0x4a, 0xf3, 0x6c, 0x8a, 0xf4, 0xcd, 0x6, 0xc, 0x9f, 0xa5, 0xe6, 0xe6, 0xa5, 0xe8, 0xb9, 0x20, 0xcd, 0xcf, 0x51, 0xb3, 0x8d, 0x67, 0x8b, 0x35, 0x99, 0xa2, 0x7, 0x93, 0x35, 0x9a, 0x2, 0x26, 0xd3, 0xff, 0x8b, 0x5f, 0x3d, 0xdc, 0x7c, 0xb4, 0x8c, 0xb2, 0xd8, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char toggle_on_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x74, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x12, 0x12, 0x15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x1a, 0x1a, 0x1e, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x2c, 0x36, 0x27, 0x49, 0x65, 0x29, 0x5d, 0x85, 0x2a, 0x66, 0x95, 0x2a, 0x68, 0x99, 0x29, 0x64, 0x92, 0x28, 0x4c, 0x6b, 0x25, 0x27, 0x2d, 0x27, 0x43, 0x5c, 0x29, 0x5f, 0x89, 0x27, 0x49, 0x66, 0x25, 0x30, 0x3e, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x25, 0x2d, 0x38, 0x25, 0x3a, 0x4c, 0x27, 0x4d, 0x6b, 0x29, 0x60, 0x8c, 0x27, 0x44, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x59, 0x7f, 0x25, 0x34, 0x43, 0x25, 0x35, 0x45, 0x28, 0x58, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x40, 0x57, 0x27, 0x41, 0x57, 0x25, 0x2a, 0x33, 0x29, 0x5d, 0x87, 0x25, 0x34, 0x44, 0x25, 0x2b, 0x34, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x27, 0x43, 0x5b, 0x27, 0x4d, 0x6c, 0x27, 0x4e, 0x6d, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x28, 0x56, 0x7b, 0x26, 0x3b, 0x4e, 0x26, 0x3a, 0x4e, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0xe4, 0xe4, 0xe5, 0x27, 0x44, 0x5d, 0xdd, 0xc9, 0xf2, 0x7e, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0x8, 0x17, 0x35, 0x73, 0xd9, 0x7, 0x3a, 0x96, 0xf9, 0x9a, 0xb, 0x28, 0x76, 0xfb, 0x77, 0xde, 0x45, 0x5, 0x82, 0xc6, 0xc6, 0x37, 0xf, 0xe9, 0x4c, 0x4e, 0x4f, 0x50, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x37, 0x21, 0x5a, 0x6c, 0x0, 0x0, 0x2, 0x4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x95, 0x63, 0x83, 0xdc, 0x60, 0x10, 0xc7, 0x1b, 0x3c, 0xc1, 0xda, 0x3e, 0xdb, 0x36, 0xe7, 0x6c, 0xdb, 0xa, 0xcf, 0xc6, 0x7e, 0xf8, 0x2a, 0x6d, 0xb8, 0xac, 0x7b, 0xbf, 0xf7, 0xf3, 0x1f, 0xcf, 0x7c, 0xf8, 0x97, 0xc0, 0x70, 0x82, 0x20, 0xb3, 0x42, 0x10, 0x38, 0x96, 0xd2, 0x1c, 0x27, 0x11, 0x45, 0x33, 0xac, 0x2d, 0xb, 0x2c, 0x43, 0x53, 0x88, 0xc4, 0xad, 0xde, 0x49, 0x3b, 0xe3, 0x70, 0xba, 0xdc, 0x1e, 0xaf, 0x2f, 0x23, 0x5e, 0x8f, 0xdb, 0xe5, 0x74, 0x30, 0x76, 0xd2, 0x14, 0x5, 0xee, 0xf, 0x4, 0x43, 0xe1, 0x48, 0x34, 0x16, 0x87, 0x2c, 0xc4, 0x63, 0xd1, 0x48, 0x38, 0x14, 0xc, 0xf8, 0x71, 0x83, 0x7d, 0xa2, 0xa0, 0xb0, 0xa8, 0x78, 0x4, 0x72, 0x64, 0xa4, 0xb8, 0xa8, 0xb0, 0x20, 0xa1, 0x53, 0xc0, 0x4a, 0xd8, 0xd2, 0xb2, 0x72, 0xc8, 0xc4, 0xe8, 0xd8, 0xf8, 0xc4, 0xa4, 0xc2, 0xd4, 0xf4, 0x28, 0x94, 0x97, 0x95, 0xb2, 0x25, 0x98, 0x96, 0x3f, 0xed, 0xac, 0xc8, 0x18, 0xfb, 0xcc, 0xec, 0xdc, 0xfc, 0xc2, 0xe2, 0xd2, 0x17, 0x96, 0x57, 0x56, 0xd7, 0xd6, 0x37, 0x66, 0xe2, 0x15, 0x4e, 0x5a, 0xad, 0x3, 0x5e, 0x59, 0x55, 0x5d, 0x93, 0xd1, 0x7e, 0x73, 0x6b, 0x1b, 0x74, 0xec, 0xec, 0x6e, 0xce, 0xd4, 0xd4, 0x56, 0x55, 0x7e, 0x4f, 0x82, 0x2c, 0x70, 0xd5, 0x41, 0x6, 0xf6, 0xf6, 0xb7, 0x56, 0xc0, 0xc0, 0xca, 0xd6, 0xc1, 0x5e, 0x5d, 0x7d, 0x41, 0x83, 0x12, 0x2, 0x86, 0x1c, 0x8d, 0x90, 0x89, 0xc3, 0xa3, 0x63, 0x30, 0xb1, 0x33, 0x77, 0x2, 0x8d, 0xe, 0xa4, 0x8, 0xe0, 0x94, 0xb3, 0x9, 0x54, 0x4e, 0xcf, 0x38, 0x5e, 0x0, 0x91, 0x93, 0x40, 0x94, 0x41, 0xe1, 0xfc, 0x2, 0x2c, 0x5c, 0x9e, 0x43, 0x73, 0x4b, 0xab, 0x92, 0x3, 0x41, 0xbb, 0xa2, 0xa0, 0x22, 0x5f, 0x9d, 0x5e, 0x73, 0xa7, 0x22, 0x27, 0x6b, 0x2, 0x37, 0xb7, 0x60, 0xe1, 0xee, 0x6, 0xda, 0xea, 0x69, 0x42, 0x11, 0x60, 0xda, 0x63, 0x5a, 0x0, 0xdc, 0x3d, 0xc0, 0xd5, 0x83, 0xf8, 0xc8, 0x8b, 0xaa, 0xc0, 0xd3, 0x33, 0x58, 0x78, 0x7e, 0x82, 0xf2, 0xe, 0x86, 0x54, 0x4, 0xa, 0x3a, 0xb5, 0x1e, 0x8a, 0x8f, 0x0, 0xf0, 0x72, 0x26, 0xca, 0x2f, 0x8f, 0xaa, 0xc0, 0xc4, 0x22, 0x58, 0x58, 0x9c, 0x0, 0xe8, 0x2a, 0xf8, 0x26, 0xc0, 0xb8, 0xb5, 0x21, 0xba, 0xff, 0x12, 0xc1, 0xd9, 0xeb, 0x67, 0xe3, 0xb7, 0xb3, 0x9c, 0x23, 0xa0, 0x5d, 0x6d, 0xa0, 0xf2, 0xf8, 0x0, 0xf7, 0xbc, 0xf0, 0x59, 0x40, 0xe0, 0x72, 0xad, 0x1, 0x4e, 0xb5, 0xe8, 0xba, 0x20, 0xf2, 0x8f, 0xfc, 0xd9, 0xd7, 0x2, 0x3e, 0xe6, 0xda, 0x5, 0xc, 0x39, 0xba, 0x41, 0xe3, 0xfe, 0x41, 0x2, 0x38, 0x15, 0x0, 0x24, 0x21, 0xf3, 0x1c, 0x74, 0x7, 0x11, 0xf6, 0xd3, 0x93, 0xa8, 0xee, 0x42, 0x6d, 0xfe, 0xbb, 0xd0, 0xa3, 0xed, 0x42, 0xe, 0xdb, 0xb8, 0x61, 0xdc, 0xc6, 0xa4, 0xba, 0x8d, 0xea, 0x3d, 0xe8, 0xed, 0xab, 0xc9, 0xe7, 0x1e, 0xd4, 0xf4, 0xf5, 0xab, 0xf7, 0x40, 0xb9, 0x48, 0xac, 0x73, 0x60, 0x10, 0x72, 0x66, 0x70, 0xc0, 0xc9, 0x26, 0x8c, 0x37, 0xad, 0x84, 0xe, 0xba, 0xc2, 0x91, 0xb6, 0x72, 0xc8, 0x4a, 0x79, 0x5b, 0x24, 0xec, 0xa, 0xd2, 0x25, 0xb8, 0xf9, 0x2a, 0x57, 0x32, 0x8e, 0x96, 0xfa, 0x8e, 0x21, 0x5f, 0x16, 0x86, 0x3a, 0xea, 0x5b, 0x1c, 0x4c, 0x25, 0x89, 0x59, 0xbf, 0x4a, 0x3, 0x6a, 0x1d, 0x2e, 0xc8, 0xfe, 0x17, 0xa, 0x86, 0x5b, 0x51, 0x3, 0x8e, 0xa5, 0xf9, 0x4c, 0x64, 0xe, 0x68, 0x9f, 0xe9, 0xbd, 0xf0, 0x9, 0xb7, 0x71, 0x36, 0xc6, 0x9b, 0x3d, 0x7f, 0x21, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -362,6 +398,14 @@ static const unsigned char toggle_on_disabled_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x53, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x2e, 0x2e, 0x2e, 0x46, 0x46, 0x46, 0x57, 0x57, 0x57, 0x60, 0x60, 0x60, 0x62, 0x62, 0x62, 0x5e, 0x5e, 0x5e, 0x4a, 0x4a, 0x4a, 0x12, 0x12, 0x15, 0x25, 0x27, 0x2d, 0x42, 0x42, 0x42, 0x59, 0x59, 0x59, 0x32, 0x32, 0x32, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x39, 0x39, 0x39, 0x49, 0x49, 0x49, 0x5a, 0x5a, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x48, 0x48, 0x48, 0x54, 0x54, 0x54, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x25, 0x26, 0x2b, 0x3f, 0x3f, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x2c, 0x2c, 0x2c, 0x58, 0x58, 0x58, 0x1a, 0x1a, 0x1e, 0x40, 0x40, 0x44, 0x56, 0x56, 0x58, 0x80, 0x80, 0x80, 0x79, 0x79, 0x79, 0x3c, 0x3c, 0x3d, 0x2e, 0x2e, 0x30, 0x27, 0x27, 0x29, 0x64, 0x64, 0x66, 0x41, 0x41, 0x41, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0x5d, 0x5f, 0x34, 0x34, 0x36, 0x52, 0x52, 0x52, 0x3a, 0x3a, 0x3a, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x32, 0x32, 0x37, 0x42, 0x42, 0x44, 0x6a, 0x6a, 0x6d, 0x5b, 0x5b, 0x5b, 0x2f, 0x2f, 0x2f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x49, 0x4a, 0x0, 0x0, 0x0, 0x50, 0x50, 0x51, 0x70, 0x70, 0x74, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbd, 0xb, 0x85, 0x35, 0x0, 0x0, 0x0, 0x71, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xff, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xff, 0x7, 0x3a, 0x96, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb, 0x28, 0x76, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x77, 0xde, 0xff, 0xff, 0x45, 0x5, 0x82, 0xff, 0xff, 0xc6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x37, 0xf, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x4c, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4e, 0x4f, 0xff, 0x50, 0xff, 0xff, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x49, 0x96, 0x6e, 0xf, 0x0, 0x0, 0x1, 0xfa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x18, 0x5e, 0x0, 0xd0, 0x5c, 0x39, 0x28, 0x49, 0x12, 0xc0, 0x60, 0xb8, 0xda, 0xdd, 0xcb, 0x31, 0x33, 0xb6, 0x6d, 0xdb, 0x5e, 0xdb, 0xd6, 0xfb, 0x17, 0x4e, 0x7d, 0x37, 0xe6, 0xf9, 0x2b, 0x23, 0x7f, 0x9c, 0x20, 0x28, 0x86, 0xe1, 0xb, 0xc1, 0x30, 0x14, 0x99, 0x6a, 0x8e, 0xe2, 0x4, 0x49, 0xd1, 0xcc, 0xda, 0x2, 0x18, 0x9a, 0x22, 0x9, 0x1c, 0x9d, 0xf4, 0x8e, 0xaf, 0xd3, 0x1b, 0x9b, 0x5b, 0xdb, 0x1c, 0x2e, 0x6f, 0x2e, 0x5c, 0xce, 0xf6, 0xd6, 0xe6, 0x6, 0xbd, 0x8e, 0x8f, 0x45, 0x81, 0xf2, 0x5, 0x42, 0x91, 0x58, 0x22, 0x95, 0xc9, 0x61, 0x1, 0x72, 0x99, 0x54, 0x22, 0x16, 0x9, 0x5, 0x7c, 0x74, 0xc4, 0x5e, 0xa1, 0x54, 0xa9, 0x35, 0x5a, 0x58, 0x12, 0xad, 0x46, 0xad, 0x52, 0x2a, 0x86, 0x14, 0x10, 0x1d, 0xa3, 0x37, 0x18, 0x61, 0x1e, 0x26, 0xb3, 0xc5, 0x6a, 0x63, 0xb1, 0x3b, 0x4c, 0x60, 0x34, 0xe8, 0x19, 0x1d, 0x32, 0xc8, 0x9f, 0xda, 0x74, 0xce, 0x8d, 0xdd, 0xe5, 0xf6, 0x98, 0xbd, 0x3e, 0xff, 0x57, 0x2, 0xa6, 0x60, 0x28, 0xec, 0x76, 0xc9, 0x9d, 0x9b, 0x54, 0xbf, 0xe, 0x68, 0x24, 0x1a, 0x8b, 0xcf, 0xb5, 0x4f, 0x24, 0x53, 0x30, 0x44, 0x3a, 0x99, 0x70, 0xc5, 0x33, 0xd1, 0xc8, 0x8f, 0x24, 0x70, 0xe5, 0x56, 0x16, 0xe6, 0x90, 0xcb, 0x27, 0x4d, 0x63, 0x9, 0x25, 0xf3, 0xb9, 0x6c, 0x41, 0x59, 0x64, 0x43, 0x40, 0x88, 0x8d, 0x12, 0xcc, 0xa3, 0x5c, 0x49, 0xc3, 0x18, 0x69, 0x4f, 0x19, 0x4a, 0x1b, 0x4, 0x2b, 0x80, 0x92, 0x9b, 0x55, 0xe8, 0x53, 0xab, 0x37, 0x9a, 0x2d, 0x68, 0x37, 0x3a, 0xd0, 0xee, 0x2, 0x4b, 0xcf, 0x1, 0x13, 0x38, 0x7a, 0xb0, 0xb3, 0xbb, 0xc7, 0xe6, 0x80, 0x51, 0x5b, 0x52, 0xe8, 0xd3, 0xdd, 0xaf, 0x1d, 0x34, 0x6a, 0xed, 0x46, 0x77, 0x20, 0x70, 0x78, 0x4, 0x13, 0x1c, 0x1d, 0xc2, 0x71, 0x81, 0xc2, 0x58, 0x1, 0xfa, 0x44, 0x36, 0x8, 0xa0, 0x71, 0xa, 0xb0, 0x7f, 0xd6, 0x3e, 0x6f, 0xb6, 0xfb, 0x2, 0x17, 0x97, 0x30, 0xc1, 0xe5, 0x5, 0x18, 0xaf, 0x68, 0x9c, 0x15, 0x50, 0x5e, 0xf, 0x7a, 0xd8, 0x3e, 0x7, 0x80, 0x9b, 0x7a, 0xbb, 0x7b, 0x73, 0xde, 0x17, 0xb0, 0xfa, 0x60, 0x2, 0x9f, 0x15, 0xe0, 0x56, 0xf9, 0x5d, 0x80, 0xde, 0x1e, 0xc, 0xd1, 0xe9, 0xd7, 0x8, 0xea, 0x77, 0xed, 0x2e, 0xdc, 0xd7, 0x97, 0x8e, 0x80, 0xda, 0x3a, 0x86, 0x3e, 0xe7, 0x67, 0x70, 0xda, 0x6c, 0xb5, 0xbb, 0xd0, 0x6a, 0x2c, 0x5b, 0x3, 0x94, 0xdc, 0xad, 0x42, 0x9f, 0x76, 0xf3, 0xbc, 0x59, 0x87, 0xaf, 0xe1, 0x9f, 0x2f, 0xd5, 0x5, 0x76, 0xe, 0x1e, 0x60, 0xc0, 0xe9, 0x59, 0x7, 0xa0, 0xd6, 0x2, 0xe8, 0xb4, 0xe6, 0xcf, 0xc1, 0x83, 0x90, 0x40, 0x7e, 0x79, 0x12, 0xfb, 0xbb, 0x90, 0x59, 0x7d, 0x17, 0x1e, 0xd9, 0x5d, 0xf8, 0xd5, 0x6d, 0xec, 0xdf, 0x83, 0xa7, 0xe7, 0xf8, 0x2a, 0xf7, 0x20, 0xfe, 0xfc, 0xc2, 0xde, 0x83, 0xfe, 0x45, 0x62, 0x36, 0x5f, 0xdf, 0x60, 0x69, 0xde, 0x5e, 0x37, 0x19, 0xc5, 0xe8, 0x4d, 0xd3, 0x51, 0xc2, 0x2d, 0xb1, 0xe4, 0xd8, 0x8, 0xb, 0x31, 0x1e, 0x4b, 0xc4, 0x5b, 0x42, 0x4a, 0x87, 0x8e, 0x5f, 0xe5, 0x8, 0xbd, 0xb1, 0x5b, 0xb8, 0x7a, 0xe7, 0x2d, 0xe0, 0xfd, 0xaa, 0xb0, 0xbb, 0x41, 0x47, 0x70, 0x64, 0xf2, 0xab, 0x14, 0x89, 0xbd, 0xf, 0xe5, 0xe2, 0xbf, 0xa0, 0xfc, 0xd8, 0x23, 0x8a, 0x28, 0x32, 0xe3, 0x33, 0xe1, 0x4b, 0xc0, 0x7e, 0xa6, 0xff, 0x87, 0xcf, 0xb, 0x94, 0xb9, 0x37, 0x3c, 0xc6, 0xd8, 0xcd, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char toggle_on_disabled_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x69, 0x50, 0x4c, 0x54, 0x45, 0x93, 0x7f, 0x2b, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x19, 0x19, 0x1c, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0x4e, 0x4e, 0x52, 0x1a, 0x1a, 0x1d, 0x32, 0x32, 0x37, 0x2c, 0x26, 0x2c, 0x26, 0x25, 0x2a, 0x27, 0x25, 0x2a, 0x11, 0x11, 0x14, 0x2f, 0x26, 0x2d, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x15, 0x15, 0x18, 0x5b, 0x5b, 0x5f, 0x84, 0x84, 0x87, 0x77, 0x77, 0x7a, 0x69, 0x69, 0x6c, 0x20, 0x20, 0x24, 0x24, 0x24, 0x27, 0x23, 0x23, 0x28, 0x1a, 0x1a, 0x1e, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0xd7, 0x77, 0xc6, 0x92, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x1, 0x35, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x94, 0xd1, 0x72, 0x83, 0x20, 0x10, 0x45, 0x4d, 0x90, 0xb8, 0x18, 0x96, 0x6c, 0xb5, 0x24, 0x41, 0x68, 0x9b, 0xf4, 0xff, 0x3f, 0xb2, 0x84, 0xb4, 0x8a, 0xa, 0x3a, 0x9d, 0x74, 0xa6, 0x2f, 0xb9, 0xbe, 0x38, 0xca, 0x3d, 0x5c, 0x16, 0xd8, 0xa2, 0x78, 0xea, 0x2f, 0xb5, 0xd9, 0xb2, 0xb2, 0xe4, 0x2b, 0x2a, 0x4b, 0xb6, 0xdd, 0x24, 0xed, 0xbb, 0x8a, 0x1, 0x8, 0x21, 0xee, 0xe3, 0xc4, 0x4d, 0x20, 0x6a, 0x91, 0x84, 0x54, 0xbb, 0xb9, 0x7f, 0xcf, 0x40, 0xa2, 0xea, 0x85, 0x32, 0x50, 0x6a, 0xc8, 0xe4, 0xd8, 0x4f, 0xfd, 0x7, 0x26, 0x48, 0xe1, 0x4b, 0x13, 0x7e, 0x37, 0x92, 0x50, 0x51, 0x1b, 0x92, 0xe4, 0x56, 0x72, 0x18, 0xfb, 0x5f, 0x99, 0x40, 0xd4, 0xf1, 0x8, 0x42, 0x6c, 0x17, 0x6b, 0x71, 0x1c, 0x1, 0x4e, 0x40, 0x21, 0x74, 0x24, 0x89, 0xd4, 0x2c, 0x11, 0x4e, 0xb1, 0xff, 0xc, 0x52, 0xe9, 0xe9, 0x8, 0x52, 0x8b, 0x11, 0xf8, 0x39, 0x2, 0x6c, 0x7d, 0x80, 0xf9, 0x8, 0xa4, 0xe1, 0xd5, 0x74, 0x16, 0xb9, 0xee, 0x5a, 0xae, 0xdd, 0xcf, 0xb7, 0xb7, 0x8, 0xe0, 0x2b, 0x40, 0x73, 0x40, 0x4, 0x75, 0x6, 0xa9, 0x43, 0xdd, 0xb9, 0x8, 0xc0, 0x46, 0x0, 0x25, 0xe7, 0x0, 0xa9, 0xfa, 0x0, 0x9d, 0xe7, 0x1b, 0xd4, 0xce, 0xea, 0x1, 0x50, 0x8e, 0x1, 0x89, 0x82, 0x35, 0x3d, 0x20, 0xb8, 0x94, 0xd1, 0x4e, 0xb9, 0x1, 0xc0, 0x7f, 0x91, 0x80, 0x42, 0x2, 0xe5, 0xcd, 0xd6, 0x24, 0x13, 0xbc, 0xc3, 0x5a, 0xd, 0x90, 0x93, 0xf5, 0x4b, 0xf0, 0x8b, 0x49, 0xd6, 0x60, 0x75, 0x17, 0xb4, 0x75, 0xd6, 0x84, 0x95, 0xb8, 0xe4, 0x2e, 0xac, 0x9f, 0x3, 0xba, 0x9d, 0x4b, 0xf4, 0xb3, 0xb4, 0xfd, 0x4c, 0xf1, 0x39, 0x28, 0x3e, 0xc4, 0x63, 0x27, 0xb1, 0x38, 0x3e, 0x7a, 0x17, 0xb2, 0xb7, 0x31, 0xeb, 0x9f, 0xdc, 0xc6, 0xa2, 0xb8, 0x30, 0x68, 0xa7, 0xfd, 0x60, 0xc1, 0x7f, 0x99, 0x77, 0x94, 0xeb, 0x27, 0xd4, 0x70, 0x6f, 0x48, 0xe2, 0x5b, 0xe0, 0x9f, 0xa4, 0xbf, 0xba, 0x66, 0x7b, 0x22, 0x5f, 0x55, 0xb6, 0x27, 0x3e, 0xf5, 0x8f, 0xfa, 0x2, 0xa0, 0x14, 0x20, 0xeb, 0xde, 0xb1, 0x8c, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
+static const unsigned char toggle_on_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x9b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x24, 0x24, 0x28, 0x20, 0x20, 0x25, 0x14, 0x14, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x27, 0x10, 0x10, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x19, 0x19, 0x1c, 0x12, 0x12, 0x15, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0x1e, 0x22, 0x23, 0x23, 0x27, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1d, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0xb, 0xb, 0xd, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x1a, 0x1a, 0x1e, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x11, 0x11, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x25, 0x2c, 0x36, 0x28, 0x4c, 0x6b, 0x29, 0x64, 0x92, 0x2a, 0x68, 0x99, 0x2a, 0x66, 0x95, 0x29, 0x5d, 0x85, 0x27, 0x49, 0x65, 0x25, 0x25, 0x28, 0x25, 0x27, 0x2d, 0x27, 0x44, 0x5c, 0x29, 0x60, 0x8c, 0x27, 0x4d, 0x6b, 0x25, 0x3a, 0x4c, 0x25, 0x2d, 0x38, 0x25, 0x26, 0x2c, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2d, 0x25, 0x30, 0x3e, 0x27, 0x49, 0x66, 0x29, 0x5f, 0x89, 0x27, 0x43, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x58, 0x7f, 0x25, 0x35, 0x45, 0x25, 0x34, 0x43, 0x28, 0x59, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x41, 0x57, 0x27, 0x40, 0x57, 0x25, 0x2b, 0x34, 0x25, 0x34, 0x44, 0x29, 0x5d, 0x87, 0x25, 0x2a, 0x33, 0x27, 0x43, 0x5b, 0x27, 0x4e, 0x6d, 0x27, 0x4d, 0x6c, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x28, 0x56, 0x7b, 0x26, 0x3a, 0x4e, 0x26, 0x3b, 0x4e, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x27, 0x44, 0x5d, 0xa6, 0xa2, 0x25, 0x5b, 0x0, 0x0, 0x0, 0x4c, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xfa, 0xe5, 0x98, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xf3, 0x86, 0x7, 0x3a, 0x96, 0xf9, 0xb4, 0x9a, 0xb9, 0xb, 0x28, 0x77, 0xfb, 0x8b, 0x5, 0x45, 0xde, 0xf6, 0x82, 0x9b, 0xf, 0x37, 0xc6, 0xe6, 0xe9, 0xfb, 0x4e, 0x50, 0x83, 0x9c, 0x78, 0x8c, 0x3c, 0x9c, 0xbb, 0x74, 0xda, 0x87, 0x53, 0x14, 0xd0, 0x92, 0x4e, 0x2c, 0x0, 0x0, 0x2, 0x35, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0x63, 0x60, 0x18, 0x44, 0x80, 0x91, 0x89, 0x99, 0x99, 0x85, 0x20, 0x60, 0x66, 0x66, 0x62, 0xc4, 0xaa, 0x9d, 0x89, 0x85, 0x95, 0x8d, 0x9d, 0x83, 0x93, 0x8b, 0x0, 0xe0, 0xe4, 0x60, 0x67, 0x63, 0x65, 0x61, 0xc2, 0xb4, 0x9d, 0x85, 0x9b, 0x83, 0x87, 0x97, 0x8f, 0x5f, 0x40, 0x50, 0x8, 0x2f, 0x10, 0x14, 0xe0, 0xe7, 0xe3, 0xe5, 0xe1, 0xe0, 0x66, 0x41, 0x73, 0x5, 0x93, 0xb0, 0x88, 0xa8, 0x98, 0xb8, 0x84, 0xa4, 0x94, 0xb4, 0xf, 0x1, 0xe0, 0x2b, 0x2d, 0x23, 0x2b, 0x27, 0x26, 0x2a, 0x22, 0x8c, 0xe2, 0x8, 0x26, 0x79, 0x5, 0x45, 0x25, 0x65, 0x5f, 0xc, 0xd5, 0x7e, 0xfe, 0x7e, 0x58, 0xd, 0x51, 0x51, 0x55, 0x54, 0x90, 0x47, 0x32, 0x81, 0x51, 0x8d, 0x53, 0x5d, 0x43, 0xd3, 0x27, 0x20, 0x30, 0x28, 0x18, 0x2, 0x42, 0x42, 0xc3, 0x2, 0x20, 0x6, 0x84, 0xe3, 0x70, 0x87, 0x96, 0x3a, 0xa7, 0x1a, 0x23, 0xc2, 0xff, 0xec, 0xbc, 0xda, 0xd2, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x20, 0x90, 0x90, 0x98, 0x94, 0x9c, 0x12, 0x1, 0x36, 0x2, 0x97, 0x4f, 0x74, 0x78, 0xd9, 0xe1, 0xe1, 0xc0, 0xa4, 0xab, 0xa7, 0x6f, 0x10, 0x91, 0x9a, 0x96, 0x8e, 0xac, 0x22, 0x23, 0x33, 0x35, 0x2, 0x6f, 0x58, 0x18, 0xea, 0xe9, 0x42, 0x3d, 0xc1, 0x68, 0xa4, 0x60, 0x6c, 0x92, 0x95, 0x9d, 0x19, 0x8b, 0xaa, 0x22, 0x36, 0x33, 0x27, 0xb, 0x9f, 0x9, 0xa6, 0xc6, 0xa, 0x46, 0x10, 0x27, 0x30, 0xb1, 0xf2, 0x98, 0xf9, 0xe4, 0x26, 0xa7, 0xa3, 0xab, 0xc8, 0xcb, 0x2f, 0xc0, 0x1b, 0x1f, 0xe6, 0x3c, 0xac, 0x10, 0x3, 0x58, 0x2c, 0x2c, 0xad, 0x7c, 0xa, 0x8b, 0x30, 0x55, 0x14, 0x17, 0xc2, 0x99, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x3e, 0x95, 0x65, 0x55, 0x3e, 0x95, 0xd5, 0x30, 0x31, 0x6b, 0x4b, 0xb, 0x88, 0x1f, 0x84, 0xd9, 0x8d, 0x6d, 0x7c, 0x6a, 0x6a, 0x31, 0xd, 0xa8, 0xab, 0x81, 0x33, 0xab, 0xeb, 0x4b, 0x1a, 0xca, 0x4a, 0x2a, 0xcb, 0xaa, 0x91, 0xc, 0xb0, 0x35, 0x66, 0x67, 0x6, 0x1b, 0xa0, 0xc6, 0x61, 0xa7, 0xe9, 0xd3, 0xd8, 0x84, 0x69, 0x40, 0x53, 0x23, 0xdc, 0x1, 0x65, 0xcd, 0x3e, 0x3e, 0xf5, 0x2d, 0x95, 0xad, 0xe5, 0x95, 0x8, 0x3, 0x7c, 0xed, 0x38, 0x58, 0x20, 0x6, 0x28, 0xd8, 0xfb, 0xf8, 0x84, 0x24, 0x60, 0x1a, 0x90, 0x10, 0x2, 0x63, 0x55, 0xb6, 0x2, 0x89, 0xb6, 0xd2, 0xca, 0xea, 0xb6, 0x56, 0x84, 0x1, 0x3e, 0xf6, 0xa, 0x2c, 0xc4, 0xba, 0xa0, 0x19, 0xe4, 0x82, 0xd2, 0x76, 0xa0, 0xe6, 0xf2, 0x52, 0x4c, 0x17, 0x10, 0x13, 0x6, 0xad, 0x2d, 0x3e, 0xcd, 0xe5, 0x15, 0x40, 0x3, 0x2a, 0xca, 0x30, 0xc3, 0x80, 0x98, 0x58, 0xa8, 0x2c, 0x6f, 0x2d, 0x2f, 0x5, 0x7, 0x60, 0x2b, 0x66, 0x2c, 0x30, 0xb1, 0x8a, 0x3a, 0x10, 0x4c, 0x7, 0xcd, 0x2d, 0x55, 0xc0, 0xb0, 0xac, 0xf0, 0xf1, 0xa9, 0xaa, 0x80, 0x9, 0x39, 0x8a, 0x42, 0xd3, 0x1, 0xc5, 0x29, 0x11, 0x94, 0x17, 0x9c, 0x48, 0xcf, 0xb, 0xce, 0xf0, 0xbc, 0x0, 0xcb, 0x8d, 0x1d, 0xa8, 0xb9, 0x31, 0x12, 0xbf, 0x7e, 0xe4, 0xdc, 0x8, 0x2a, 0xf, 0x5c, 0x5c, 0xd, 0xb0, 0x94, 0x7, 0xb8, 0xf5, 0xbb, 0xb9, 0x20, 0x95, 0x7, 0xa0, 0x12, 0x89, 0x93, 0xd7, 0xdd, 0x3, 0x53, 0x9d, 0x5f, 0x38, 0xf6, 0xf2, 0x40, 0xc5, 0x93, 0x97, 0x53, 0x1e, 0xb5, 0x4c, 0x53, 0x63, 0x17, 0xe5, 0x13, 0x97, 0xb0, 0xd1, 0xf4, 0x21, 0x8, 0x7c, 0x6d, 0x65, 0xe5, 0xf8, 0x44, 0xd9, 0xd5, 0xd0, 0xa, 0x66, 0x46, 0x16, 0x5d, 0xe, 0x1e, 0x4b, 0x63, 0x3b, 0x2f, 0x21, 0x2, 0xc0, 0xcb, 0xce, 0xd8, 0x92, 0x87, 0x43, 0x97, 0x5, 0xa3, 0x6e, 0x60, 0x64, 0x32, 0x62, 0xb5, 0xf0, 0x56, 0x20, 0x5c, 0x2f, 0x28, 0x78, 0x5b, 0xb0, 0x1a, 0x61, 0xaf, 0x5b, 0x80, 0x35, 0x13, 0xe1, 0x8a, 0x9, 0x58, 0x35, 0xe1, 0xa8, 0x99, 0x86, 0x2e, 0x0, 0x0, 0x69, 0x2c, 0x6b, 0xc2, 0xf1, 0x2f, 0x53, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char tooltip_bg_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0xdd, 0xdd, 0xdd, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0xbc, 0x3, 0x4f, 0xe9, 0x0, 0x0, 0x0, 0xd, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0xf5, 0xfe, 0xcc, 0xff, 0xb7, 0x4a, 0xbe, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x54, 0x76, 0x1, 0x2, 0x23, 0x1, 0x6, 0xd1, 0xf4, 0xe, 0x20, 0x28, 0xb, 0x64, 0xd0, 0x5c, 0x7d, 0x17, 0x8, 0x76, 0x4d, 0x62, 0x70, 0x7f, 0x7f, 0x6, 0x8, 0xfe, 0x95, 0x30, 0x78, 0xdc, 0x1, 0x31, 0xce, 0xb6, 0x50, 0xc8, 0x80, 0x1b, 0x8, 0xb7, 0x2, 0x6e, 0x29, 0xdc, 0x19, 0x0, 0xcf, 0x24, 0x4d, 0xb3, 0xd0, 0x4d, 0xb9, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -426,4 +470,8 @@ static const unsigned char window_resizer_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0xbc, 0xe0, 0x45, 0x3f, 0x1, 0xe9, 0xec, 0xfe, 0x81, 0x94, 0x86, 0xb1, 0x70, 0x48, 0x23, 0x58, 0x84, 0xa4, 0x7, 0x15, 0x0, 0x0, 0xed, 0x9f, 0x18, 0xe8, 0xcd, 0x91, 0xd8, 0xe, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char window_resizer_mirrored_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x27, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x18, 0x44, 0xe0, 0x45, 0x3f, 0x76, 0x71, 0x26, 0x18, 0xa3, 0x19, 0xa7, 0x12, 0x38, 0xc8, 0xee, 0xa7, 0xb1, 0x12, 0x98, 0x4, 0x4e, 0x25, 0x8, 0x9, 0x4a, 0x94, 0xc, 0x10, 0x0, 0x0, 0x9d, 0x84, 0x18, 0x73, 0x33, 0x1c, 0x96, 0xd6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
// shaders block
diff --git a/scene/resources/default_theme/toggle_off_disabled_mirrored.png b/scene/resources/default_theme/toggle_off_disabled_mirrored.png
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 bc983c1d7e..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(char32_t p_char, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const {
- const Character *chr = char_map.getptr(p_char);
- ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(nullptr, nullptr)));
-
- if (!chr->found) {
- //not found, try in fallbacks
- for (int i = 0; i < p_fallbacks.size(); i++) {
- DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr());
- if (!fb->valid) {
- continue;
- }
-
- fb->_update_char(p_char);
- const Character *fallback_chr = fb->char_map.getptr(p_char);
- ERR_CONTINUE(!fallback_chr);
-
- if (!fallback_chr->found) {
- continue;
- }
-
- return Pair<const Character *, DynamicFontAtSize *>(fallback_chr, fb);
- }
-
- //not found, try 0xFFFD to display 'not found'.
- const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD);
- chr = char_map.getptr(0xFFFD);
- ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(nullptr, nullptr)));
- }
-
- return Pair<const Character *, DynamicFontAtSize *>(chr, const_cast<DynamicFontAtSize *>(this));
-}
-
-Size2 DynamicFontAtSize::get_char_size(char32_t p_char, char32_t p_next, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const {
- if (!valid) {
- return Size2(1, 1);
- }
- const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
-
- Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks);
- const Character *ch = char_pair_with_font.first;
- ERR_FAIL_COND_V(!ch, Size2());
-
- Size2 ret(0, get_height());
-
- if (ch->found) {
- ret.x = ch->advance;
- }
-
- return ret;
-}
-
-String DynamicFontAtSize::get_available_chars() const {
- String chars;
-
- FT_UInt gindex;
- FT_ULong charcode = FT_Get_First_Char(face, &gindex);
- while (gindex != 0) {
- if (charcode != 0) {
- chars += char32_t(charcode);
- }
- charcode = FT_Get_Next_Char(face, charcode, &gindex);
- }
-
- return chars;
-}
-
-float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks, bool p_advance_only, bool p_outline) const {
- if (!valid) {
- return 0;
- }
-
- const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
-
- Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks);
- const Character *ch = char_pair_with_font.first;
- DynamicFontAtSize *font = char_pair_with_font.second;
-
- ERR_FAIL_COND_V(!ch, 0.0);
-
- float advance = 0.0;
-
- // use normal character size if there's no outline character
- if (p_outline && !ch->found) {
- FT_GlyphSlot slot = face->glyph;
- int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT);
- if (!error) {
- error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
- if (!error) {
- Character character = Character::not_found();
- character = const_cast<DynamicFontAtSize *>(this)->_bitmap_to_character(slot->bitmap, slot->bitmap_top, slot->bitmap_left, slot->advance.x / 64.0);
- advance = character.advance;
- }
- }
- }
-
- if (ch->found) {
- ERR_FAIL_COND_V(ch->texture_idx < -1 || ch->texture_idx >= font->textures.size(), 0);
-
- if (!p_advance_only && ch->texture_idx != -1) {
- Point2 cpos = p_pos;
- cpos.x += ch->h_align;
- cpos.y -= font->get_ascent();
- cpos.y += ch->v_align;
- Color modulate = p_modulate;
- if (FT_HAS_COLOR(face)) {
- modulate.r = modulate.g = modulate.b = 1.0;
- }
- RID texture = font->textures[ch->texture_idx].texture->get_rid();
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, ch->rect.size), texture, ch->rect_uv, modulate, false, 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(char32_t p_char) {
- Character ret = Character::not_found();
-
- if (FT_Load_Char(face, p_char, FT_LOAD_NO_BITMAP | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)) != 0) {
- return ret;
- }
-
- FT_Stroker stroker;
- if (FT_Stroker_New(library, &stroker) != 0) {
- return ret;
- }
-
- FT_Stroker_Set(stroker, (int)(id.outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
- FT_Glyph glyph;
- FT_BitmapGlyph glyph_bitmap;
-
- if (FT_Get_Glyph(face->glyph, &glyph) != 0) {
- goto cleanup_stroker;
- }
- if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {
- goto cleanup_glyph;
- }
- if (FT_Glyph_To_Bitmap(&glyph, font->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) {
- goto cleanup_glyph;
- }
-
- glyph_bitmap = (FT_BitmapGlyph)glyph;
- ret = _bitmap_to_character(glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, glyph->advance.x / 65536.0);
-
-cleanup_glyph:
- FT_Done_Glyph(glyph);
-cleanup_stroker:
- FT_Stroker_Done(stroker);
- return ret;
-}
-
-void DynamicFontAtSize::_update_char(char32_t p_char) {
- if (char_map.has(p_char)) {
- return;
- }
-
- _THREAD_SAFE_METHOD_
-
- Character character = Character::not_found();
-
- FT_GlyphSlot slot = face->glyph;
-
- if (FT_Get_Char_Index(face, p_char) == 0) {
- char_map[p_char] = character;
- return;
- }
-
- int ft_hinting;
-
- switch (font->hinting) {
- case DynamicFontData::HINTING_NONE:
- ft_hinting = FT_LOAD_NO_HINTING;
- break;
- case DynamicFontData::HINTING_LIGHT:
- ft_hinting = FT_LOAD_TARGET_LIGHT;
- break;
- default:
- ft_hinting = FT_LOAD_TARGET_NORMAL;
- break;
- }
-
- int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting);
- if (error) {
- char_map[p_char] = character;
- return;
- }
-
- if (id.outline_size > 0) {
- character = _make_outline_char(p_char);
- } else {
- error = FT_Render_Glyph(face->glyph, font->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
- if (!error) {
- character = _bitmap_to_character(slot->bitmap, slot->bitmap_top, slot->bitmap_left, slot->advance.x / 64.0);
- }
- }
-
- char_map[p_char] = character;
-}
-
-void DynamicFontAtSize::update_oversampling() {
- if (oversampling == font_oversampling || !valid) {
- return;
- }
-
- FT_Done_FreeType(library);
- textures.clear();
- char_map.clear();
- oversampling = font_oversampling;
- valid = false;
- _load();
-}
-
-DynamicFontAtSize::DynamicFontAtSize() {
- valid = false;
- rect_margin = 1;
- ascent = 1;
- descent = 1;
- linegap = 1;
- oversampling = font_oversampling;
- scale_color_font = 1;
-}
-
-DynamicFontAtSize::~DynamicFontAtSize() {
- if (valid) {
- FT_Done_FreeType(library);
- }
- font->size_cache.erase(id);
- font.unref();
-}
-
-/////////////////////////
-
-void DynamicFont::_reload_cache() {
- ERR_FAIL_COND(cache_id.size < 1);
- if (!data.is_valid()) {
- data_at_size.unref();
- outline_data_at_size.unref();
- fallbacks.resize(0);
- fallback_data_at_size.resize(0);
- fallback_outline_data_at_size.resize(0);
- return;
- }
-
- data_at_size = data->_get_dynamic_font_at_size(cache_id);
- if (outline_cache_id.outline_size > 0) {
- outline_data_at_size = data->_get_dynamic_font_at_size(outline_cache_id);
- fallback_outline_data_at_size.resize(fallback_data_at_size.size());
- } else {
- outline_data_at_size.unref();
- fallback_outline_data_at_size.resize(0);
- }
-
- for (int i = 0; i < fallbacks.size(); i++) {
- fallback_data_at_size.write[i] = fallbacks.write[i]->_get_dynamic_font_at_size(cache_id);
- if (outline_cache_id.outline_size > 0) {
- fallback_outline_data_at_size.write[i] = fallbacks.write[i]->_get_dynamic_font_at_size(outline_cache_id);
- }
- }
-
- emit_changed();
- _change_notify();
-}
-
-void DynamicFont::set_font_data(const Ref<DynamicFontData> &p_data) {
- data = p_data;
- _reload_cache();
-
- emit_changed();
- _change_notify();
-}
-
-Ref<DynamicFontData> DynamicFont::get_font_data() const {
- return data;
-}
-
-void DynamicFont::set_size(int p_size) {
- if (cache_id.size == p_size) {
- return;
- }
- cache_id.size = p_size;
- outline_cache_id.size = p_size;
- _reload_cache();
-}
-
-int DynamicFont::get_size() const {
- return cache_id.size;
-}
-
-void DynamicFont::set_outline_size(int p_size) {
- if (outline_cache_id.outline_size == p_size) {
- return;
- }
- ERR_FAIL_COND(p_size < 0 || p_size > UINT8_MAX);
- outline_cache_id.outline_size = p_size;
- _reload_cache();
-}
-
-int DynamicFont::get_outline_size() const {
- return outline_cache_id.outline_size;
-}
-
-void DynamicFont::set_outline_color(Color p_color) {
- if (p_color != outline_color) {
- outline_color = p_color;
- emit_changed();
- _change_notify();
- }
-}
-
-Color DynamicFont::get_outline_color() const {
- return outline_color;
-}
-
-bool DynamicFontData::is_antialiased() const {
- return antialiased;
-}
-
-void DynamicFontData::set_antialiased(bool p_antialiased) {
- if (antialiased == p_antialiased) {
- return;
- }
- antialiased = p_antialiased;
-}
-
-DynamicFontData::Hinting DynamicFontData::get_hinting() const {
- return hinting;
-}
-
-void DynamicFontData::set_hinting(Hinting p_hinting) {
- if (hinting == p_hinting) {
- return;
- }
- hinting = p_hinting;
-}
-
-int DynamicFont::get_spacing(int p_type) const {
- if (p_type == SPACING_TOP) {
- return spacing_top;
- } else if (p_type == SPACING_BOTTOM) {
- return spacing_bottom;
- } else if (p_type == SPACING_CHAR) {
- return spacing_char;
- } else if (p_type == SPACING_SPACE) {
- return spacing_space;
- }
-
- return 0;
-}
-
-void DynamicFont::set_spacing(int p_type, int p_value) {
- if (p_type == SPACING_TOP) {
- spacing_top = p_value;
- } else if (p_type == SPACING_BOTTOM) {
- spacing_bottom = p_value;
- } else if (p_type == SPACING_CHAR) {
- spacing_char = p_value;
- } else if (p_type == SPACING_SPACE) {
- spacing_space = p_value;
- }
-
- emit_changed();
- _change_notify();
-}
-
-float DynamicFont::get_height() const {
- if (!data_at_size.is_valid()) {
- return 1;
- }
-
- return data_at_size->get_height() + spacing_top + spacing_bottom;
-}
-
-float DynamicFont::get_ascent() const {
- if (!data_at_size.is_valid()) {
- return 1;
- }
-
- return data_at_size->get_ascent() + spacing_top;
-}
-
-float DynamicFont::get_descent() const {
- if (!data_at_size.is_valid()) {
- return 1;
- }
-
- return data_at_size->get_descent() + spacing_bottom;
-}
-
-float DynamicFont::get_underline_position() const {
- if (!data_at_size.is_valid()) {
- return 2;
- }
-
- return data_at_size->get_underline_position();
-}
-
-float DynamicFont::get_underline_thickness() const {
- if (!data_at_size.is_valid()) {
- return 1;
- }
-
- return data_at_size->get_underline_thickness();
-}
-
-Size2 DynamicFont::get_char_size(char32_t p_char, char32_t p_next) const {
- if (!data_at_size.is_valid()) {
- return Size2(1, 1);
- }
-
- Size2 ret = data_at_size->get_char_size(p_char, p_next, fallback_data_at_size);
- if (p_char == ' ') {
- ret.width += spacing_space + spacing_char;
- } else if (p_next) {
- ret.width += spacing_char;
- }
-
- return ret;
-}
-
-String DynamicFont::get_available_chars() const {
- if (!data_at_size.is_valid()) {
- return "";
- }
-
- String chars = data_at_size->get_available_chars();
-
- for (int i = 0; i < fallback_data_at_size.size(); i++) {
- String fallback_chars = fallback_data_at_size[i]->get_available_chars();
- for (int j = 0; j < fallback_chars.length(); j++) {
- if (chars.find_char(fallback_chars[j]) == -1) {
- chars += fallback_chars[j];
- }
- }
- }
-
- return chars;
-}
-
-bool DynamicFont::is_distance_field_hint() const {
- return false;
-}
-
-bool DynamicFont::has_outline() const {
- return outline_cache_id.outline_size > 0;
-}
-
-float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, bool p_outline) const {
- const Ref<DynamicFontAtSize> &font_at_size = p_outline && outline_cache_id.outline_size > 0 ? outline_data_at_size : data_at_size;
-
- if (!font_at_size.is_valid()) {
- return 0;
- }
-
- const Vector<Ref<DynamicFontAtSize>> &fallbacks = p_outline && outline_cache_id.outline_size > 0 ? fallback_outline_data_at_size : fallback_data_at_size;
- Color color = p_outline && outline_cache_id.outline_size > 0 ? p_modulate * outline_color : p_modulate;
-
- // If requested outline draw, but no outline is present, simply return advance without drawing anything
- bool advance_only = p_outline && outline_cache_id.outline_size == 0;
- return font_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, color, fallbacks, advance_only, p_outline) + spacing_char;
-}
-
-void DynamicFont::set_fallback(int p_idx, const Ref<DynamicFontData> &p_data) {
- ERR_FAIL_COND(p_data.is_null());
- ERR_FAIL_INDEX(p_idx, fallbacks.size());
- fallbacks.write[p_idx] = p_data;
- fallback_data_at_size.write[p_idx] = fallbacks.write[p_idx]->_get_dynamic_font_at_size(cache_id);
-}
-
-void DynamicFont::add_fallback(const Ref<DynamicFontData> &p_data) {
- ERR_FAIL_COND(p_data.is_null());
- fallbacks.push_back(p_data);
- fallback_data_at_size.push_back(fallbacks.write[fallbacks.size() - 1]->_get_dynamic_font_at_size(cache_id)); //const..
- if (outline_cache_id.outline_size > 0) {
- fallback_outline_data_at_size.push_back(fallbacks.write[fallbacks.size() - 1]->_get_dynamic_font_at_size(outline_cache_id));
- }
-
- _change_notify();
- emit_changed();
- _change_notify();
-}
-
-int DynamicFont::get_fallback_count() const {
- return fallbacks.size();
-}
-
-Ref<DynamicFontData> DynamicFont::get_fallback(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, fallbacks.size(), Ref<DynamicFontData>());
-
- return fallbacks[p_idx];
-}
-
-void DynamicFont::remove_fallback(int p_idx) {
- ERR_FAIL_INDEX(p_idx, fallbacks.size());
- fallbacks.remove(p_idx);
- fallback_data_at_size.remove(p_idx);
- emit_changed();
- _change_notify();
-}
-
-bool DynamicFont::_set(const StringName &p_name, const Variant &p_value) {
- String str = p_name;
- if (str.begins_with("fallback/")) {
- int idx = str.get_slicec('/', 1).to_int();
- Ref<DynamicFontData> fd = p_value;
-
- if (fd.is_valid()) {
- if (idx == fallbacks.size()) {
- add_fallback(fd);
- return true;
- } else if (idx >= 0 && idx < fallbacks.size()) {
- set_fallback(idx, fd);
- return true;
- } else {
- return false;
- }
- } else if (idx >= 0 && idx < fallbacks.size()) {
- remove_fallback(idx);
- return true;
- }
- }
-
- return false;
-}
-
-bool DynamicFont::_get(const StringName &p_name, Variant &r_ret) const {
- String str = p_name;
- if (str.begins_with("fallback/")) {
- int idx = str.get_slicec('/', 1).to_int();
-
- if (idx == fallbacks.size()) {
- r_ret = Ref<DynamicFontData>();
- return true;
- } else if (idx >= 0 && idx < fallbacks.size()) {
- r_ret = get_fallback(idx);
- return true;
- }
- }
-
- return false;
-}
-
-void DynamicFont::_get_property_list(List<PropertyInfo> *p_list) const {
- for (int i = 0; i < fallbacks.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "fallback/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "DynamicFontData"));
- }
-
- p_list->push_back(PropertyInfo(Variant::OBJECT, "fallback/" + itos(fallbacks.size()), PROPERTY_HINT_RESOURCE_TYPE, "DynamicFontData"));
-}
-
-void DynamicFont::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_font_data", "data"), &DynamicFont::set_font_data);
- ClassDB::bind_method(D_METHOD("get_font_data"), &DynamicFont::get_font_data);
-
- ClassDB::bind_method(D_METHOD("get_available_chars"), &DynamicFont::get_available_chars);
-
- ClassDB::bind_method(D_METHOD("set_size", "data"), &DynamicFont::set_size);
- ClassDB::bind_method(D_METHOD("get_size"), &DynamicFont::get_size);
-
- ClassDB::bind_method(D_METHOD("set_outline_size", "size"), &DynamicFont::set_outline_size);
- ClassDB::bind_method(D_METHOD("get_outline_size"), &DynamicFont::get_outline_size);
-
- ClassDB::bind_method(D_METHOD("set_outline_color", "color"), &DynamicFont::set_outline_color);
- ClassDB::bind_method(D_METHOD("get_outline_color"), &DynamicFont::get_outline_color);
-
- ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &DynamicFont::set_spacing);
- ClassDB::bind_method(D_METHOD("get_spacing", "type"), &DynamicFont::get_spacing);
-
- ClassDB::bind_method(D_METHOD("add_fallback", "data"), &DynamicFont::add_fallback);
- ClassDB::bind_method(D_METHOD("set_fallback", "idx", "data"), &DynamicFont::set_fallback);
- ClassDB::bind_method(D_METHOD("get_fallback", "idx"), &DynamicFont::get_fallback);
- ClassDB::bind_method(D_METHOD("remove_fallback", "idx"), &DynamicFont::remove_fallback);
- ClassDB::bind_method(D_METHOD("get_fallback_count"), &DynamicFont::get_fallback_count);
-
- ADD_GROUP("Settings", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "size", PROPERTY_HINT_RANGE, "1,1024,1"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,1024,1"), "set_outline_size", "get_outline_size");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color");
- ADD_GROUP("Extra Spacing", "extra_spacing");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_top"), "set_spacing", "get_spacing", SPACING_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_bottom"), "set_spacing", "get_spacing", SPACING_BOTTOM);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_char"), "set_spacing", "get_spacing", SPACING_CHAR);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_space"), "set_spacing", "get_spacing", SPACING_SPACE);
- ADD_GROUP("Font", "");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font_data", PROPERTY_HINT_RESOURCE_TYPE, "DynamicFontData"), "set_font_data", "get_font_data");
-
- BIND_ENUM_CONSTANT(SPACING_TOP);
- BIND_ENUM_CONSTANT(SPACING_BOTTOM);
- BIND_ENUM_CONSTANT(SPACING_CHAR);
- BIND_ENUM_CONSTANT(SPACING_SPACE);
-}
-
-Mutex DynamicFont::dynamic_font_mutex;
-
-SelfList<DynamicFont>::List *DynamicFont::dynamic_fonts = nullptr;
-
-DynamicFont::DynamicFont() :
- font_list(this) {
- valid = false;
- cache_id.size = 16;
- outline_cache_id.size = 16;
- spacing_top = 0;
- spacing_bottom = 0;
- spacing_char = 0;
- spacing_space = 0;
- outline_color = Color(1, 1, 1);
-
- MutexLock lock(dynamic_font_mutex);
- dynamic_fonts->add(&font_list);
-}
-
-DynamicFont::~DynamicFont() {
- MutexLock lock(dynamic_font_mutex);
- dynamic_fonts->remove(&font_list);
-}
-
-void DynamicFont::initialize_dynamic_fonts() {
- dynamic_fonts = memnew(SelfList<DynamicFont>::List());
-}
-
-void DynamicFont::finish_dynamic_fonts() {
- memdelete(dynamic_fonts);
- dynamic_fonts = nullptr;
-}
-
-void DynamicFont::update_oversampling() {
- Vector<Ref<DynamicFont>> changed;
- {
- MutexLock lock(dynamic_font_mutex);
-
- SelfList<DynamicFont> *E = dynamic_fonts->first();
- while (E) {
- if (E->self()->data_at_size.is_valid()) {
- E->self()->data_at_size->update_oversampling();
-
- if (E->self()->outline_data_at_size.is_valid()) {
- E->self()->outline_data_at_size->update_oversampling();
- }
-
- for (int i = 0; i < E->self()->fallback_data_at_size.size(); i++) {
- if (E->self()->fallback_data_at_size[i].is_valid()) {
- E->self()->fallback_data_at_size.write[i]->update_oversampling();
-
- if (E->self()->has_outline() && E->self()->fallback_outline_data_at_size[i].is_valid()) {
- E->self()->fallback_outline_data_at_size.write[i]->update_oversampling();
- }
- }
- }
-
- changed.push_back(Ref<DynamicFont>(E->self()));
- }
-
- E = E->next();
- }
- }
-
- for (int i = 0; i < changed.size(); i++) {
- changed.write[i]->emit_changed();
- }
-}
-
-/////////////////////////
-
-RES ResourceFormatLoaderDynamicFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
- if (r_error) {
- *r_error = ERR_FILE_CANT_OPEN;
- }
-
- Ref<DynamicFontData> dfont;
- dfont.instance();
- dfont->set_font_path(p_path);
-
- if (r_error) {
- *r_error = OK;
- }
-
- return dfont;
-}
-
-void ResourceFormatLoaderDynamicFont::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("ttf");
- p_extensions->push_back("otf");
-}
-
-bool ResourceFormatLoaderDynamicFont::handles_type(const String &p_type) const {
- return (p_type == "DynamicFontData");
-}
-
-String ResourceFormatLoaderDynamicFont::get_resource_type(const String &p_path) const {
- String el = p_path.get_extension().to_lower();
- if (el == "ttf" || el == "otf") {
- return "DynamicFontData";
- }
- return "";
-}
-
-#endif
diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h
deleted file mode 100644
index a881e21da8..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(char32_t p_char, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const;
- Character _make_outline_char(char32_t p_char);
- TexturePosition _find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height);
- Character _bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance);
-
- static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count);
- static void _ft_stream_close(FT_Stream stream);
-
- HashMap<char32_t, Character> char_map;
-
- _FORCE_INLINE_ void _update_char(char32_t p_char);
-
- friend class DynamicFontData;
- Ref<DynamicFontData> font;
- DynamicFontData::CacheID id;
-
- static HashMap<String, Vector<uint8_t>> _fontdata;
- Error _load();
-
-public:
- static float font_oversampling;
-
- float get_height() const;
-
- float get_ascent() const;
- float get_descent() const;
- float get_underline_position() const;
- float get_underline_thickness() const;
-
- Size2 get_char_size(char32_t p_char, char32_t p_next, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks) const;
- String get_available_chars() const;
-
- float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize>> &p_fallbacks, bool p_advance_only = false, bool p_outline = false) const;
-
- void set_texture_flags(uint32_t p_flags);
- void update_oversampling();
-
- DynamicFontAtSize();
- ~DynamicFontAtSize();
-};
-
-///////////////
-
-class DynamicFont : public Font {
- GDCLASS(DynamicFont, Font);
-
-public:
- enum SpacingType {
- SPACING_TOP,
- SPACING_BOTTOM,
- SPACING_CHAR,
- SPACING_SPACE
- };
-
-private:
- Ref<DynamicFontData> data;
- Ref<DynamicFontAtSize> data_at_size;
- Ref<DynamicFontAtSize> outline_data_at_size;
-
- Vector<Ref<DynamicFontData>> fallbacks;
- Vector<Ref<DynamicFontAtSize>> fallback_data_at_size;
- Vector<Ref<DynamicFontAtSize>> fallback_outline_data_at_size;
-
- DynamicFontData::CacheID cache_id;
- DynamicFontData::CacheID outline_cache_id;
-
- bool valid;
- int spacing_top;
- int spacing_bottom;
- int spacing_char;
- int spacing_space;
-
- Color outline_color;
-
-protected:
- void _reload_cache();
-
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
- static void _bind_methods();
-
-public:
- void set_font_data(const Ref<DynamicFontData> &p_data);
- Ref<DynamicFontData> get_font_data() const;
-
- void set_size(int p_size);
- int get_size() const;
-
- void set_outline_size(int p_size);
- int get_outline_size() const;
-
- void set_outline_color(Color p_color);
- Color get_outline_color() const;
-
- bool get_use_mipmaps() const;
- void set_use_mipmaps(bool p_enable);
-
- bool get_use_filter() const;
- void set_use_filter(bool p_enable);
-
- int get_spacing(int p_type) const;
- void set_spacing(int p_type, int p_value);
-
- void add_fallback(const Ref<DynamicFontData> &p_data);
- void set_fallback(int p_idx, const Ref<DynamicFontData> &p_data);
- int get_fallback_count() const;
- Ref<DynamicFontData> get_fallback(int p_idx) const;
- void remove_fallback(int p_idx);
-
- virtual float get_height() const override;
-
- virtual float get_ascent() const override;
- virtual float get_descent() const override;
- virtual float get_underline_position() const override;
- virtual float get_underline_thickness() const override;
-
- virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const override;
- String get_available_chars() const;
-
- virtual bool is_distance_field_hint() const override;
-
- virtual bool has_outline() const override;
-
- virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const override;
-
- SelfList<DynamicFont> font_list;
-
- static Mutex dynamic_font_mutex;
- static SelfList<DynamicFont>::List *dynamic_fonts;
-
- static void initialize_dynamic_fonts();
- static void finish_dynamic_fonts();
- static void update_oversampling();
-
- DynamicFont();
- ~DynamicFont();
-};
-
-VARIANT_ENUM_CAST(DynamicFont::SpacingType);
-
-/////////////
-
-class ResourceFormatLoaderDynamicFont : public ResourceFormatLoader {
-public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
-};
-
-#endif
-
-#endif
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index ee8e63266d..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 {
@@ -578,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;
- return glow_levels & (1 << p_level);
+ _update_glow();
+}
+
+bool Environment::is_glow_normalized() const {
+ return glow_normalize_levels;
}
void Environment::set_glow_intensity(float p_intensity) {
@@ -670,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,
@@ -739,6 +763,14 @@ float Environment::get_fog_height_density() const {
return fog_height_density;
}
+void Environment::set_fog_aerial_perspective(float p_aerial_perspective) {
+ fog_aerial_perspective = p_aerial_perspective;
+ _update_fog();
+}
+float Environment::get_fog_aerial_perspective() const {
+ return fog_aerial_perspective;
+}
+
void Environment::_update_fog() {
RS::get_singleton()->environment_set_fog(
environment,
@@ -748,7 +780,8 @@ void Environment::_update_fog() {
fog_sun_scatter,
fog_density,
fog_height,
- fog_height_density);
+ fog_height_density,
+ fog_aerial_perspective);
}
// Volumetric Fog
@@ -859,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
@@ -887,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;
}
@@ -1162,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);
@@ -1183,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");
@@ -1219,6 +1276,9 @@ void Environment::_bind_methods() {
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_light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_fog_light_color", "get_fog_light_color");
@@ -1226,6 +1286,7 @@ void Environment::_bind_methods() {
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");
@@ -1274,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
@@ -1329,6 +1390,16 @@ 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();
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index d4d84f31aa..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"
@@ -171,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;
@@ -190,6 +191,7 @@ private:
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;
void _update_fog();
@@ -209,7 +211,8 @@ private:
float adjustment_brightness = 1.0;
float adjustment_contrast = 1.0;
float adjustment_saturation = 1.0;
- Ref<Texture2D> adjustment_color_correction;
+ bool use_1d_color_correction = true;
+ Ref<Texture> adjustment_color_correction;
void _update_adjustment();
protected:
@@ -332,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);
@@ -368,6 +373,8 @@ public:
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);
@@ -396,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();
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 7cc39f661d..791f260c0e 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -31,640 +31,1017 @@
#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));
- float ofs = 0.f;
- switch (p_align) {
- case HALIGN_LEFT: {
- ofs = 0;
- } break;
- case HALIGN_CENTER: {
- ofs = Math::floor((p_width - length) / 2.0);
- } break;
- case HALIGN_RIGHT: {
- ofs = p_width - length;
- } break;
- default: {
- ERR_PRINT("Unknown halignment type");
- } break;
- }
- draw(p_canvas_item, p_pos + Point2(ofs, 0), p_text, p_modulate, p_width, p_outline_modulate);
-}
+ ClassDB::bind_method(D_METHOD("set_data_path", "path"), &FontData::set_data_path);
+ ClassDB::bind_method(D_METHOD("get_data_path"), &FontData::get_data_path);
-void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w, const Color &p_outline_modulate) const {
- Vector2 ofs;
+ ClassDB::bind_method(D_METHOD("get_height", "size"), &FontData::get_height);
+ ClassDB::bind_method(D_METHOD("get_ascent", "size"), &FontData::get_ascent);
+ ClassDB::bind_method(D_METHOD("get_descent", "size"), &FontData::get_descent);
- int chars_drawn = 0;
- bool with_outline = has_outline();
- for (int i = 0; i < p_text.length(); i++) {
- int width = get_char_size(p_text[i]).width;
+ ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &FontData::get_underline_position);
+ ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &FontData::get_underline_thickness);
- if (p_clip_w >= 0 && (ofs.x + width) > p_clip_w) {
- break; //clip
- }
+ ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
+ ClassDB::bind_method(D_METHOD("get_antialiased"), &FontData::get_antialiased);
- ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], with_outline ? p_outline_modulate : p_modulate, with_outline);
- ++chars_drawn;
- }
+ ClassDB::bind_method(D_METHOD("get_variation_list"), &FontData::get_variation_list);
+
+ ClassDB::bind_method(D_METHOD("set_variation", "tag", "value"), &FontData::set_variation);
+ ClassDB::bind_method(D_METHOD("get_variation", "tag"), &FontData::get_variation);
+
+ ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting);
+ ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting);
+
+ ClassDB::bind_method(D_METHOD("set_force_autohinter", "enabled"), &FontData::set_force_autohinter);
+ ClassDB::bind_method(D_METHOD("get_force_autohinter"), &FontData::get_force_autohinter);
+
+ ClassDB::bind_method(D_METHOD("set_distance_field_hint", "distance_field"), &FontData::set_distance_field_hint);
+ ClassDB::bind_method(D_METHOD("get_distance_field_hint"), &FontData::get_distance_field_hint);
+
+ ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char);
+ ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars);
+
+ ClassDB::bind_method(D_METHOD("get_glyph_advance", "index", "size"), &FontData::get_glyph_advance);
+ ClassDB::bind_method(D_METHOD("get_glyph_kerning", "index_a", "index_b", "size"), &FontData::get_glyph_kerning);
+
+ ClassDB::bind_method(D_METHOD("get_base_size"), &FontData::get_base_size);
+
+ ClassDB::bind_method(D_METHOD("has_outline"), &FontData::has_outline);
+
+ ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &FontData::is_language_supported);
+ ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontData::set_language_support_override);
+ ClassDB::bind_method(D_METHOD("get_language_support_override", "language"), &FontData::get_language_support_override);
+ ClassDB::bind_method(D_METHOD("remove_language_support_override", "language"), &FontData::remove_language_support_override);
+ ClassDB::bind_method(D_METHOD("get_language_support_overrides"), &FontData::get_language_support_overrides);
+
+ ClassDB::bind_method(D_METHOD("is_script_supported", "script"), &FontData::is_script_supported);
+ ClassDB::bind_method(D_METHOD("set_script_support_override", "script", "supported"), &FontData::set_script_support_override);
+ ClassDB::bind_method(D_METHOD("get_script_support_override", "script"), &FontData::get_script_support_override);
+ ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontData::remove_script_support_override);
+ ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontData::get_script_support_overrides);
+
+ ClassDB::bind_method(D_METHOD("get_glyph_index", "char", "variation_selector"), &FontData::get_glyph_index, DEFVAL(0x0000));
+ ClassDB::bind_method(D_METHOD("draw_glyph", "canvas", "size", "pos", "index", "color"), &FontData::draw_glyph, DEFVAL(Color(1, 1, 1)));
+ ClassDB::bind_method(D_METHOD("draw_glyph_outline", "canvas", "size", "outline_size", "pos", "index", "color"), &FontData::draw_glyph_outline, DEFVAL(Color(1, 1, 1)));
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "data_path", PROPERTY_HINT_FILE, "*.ttf,*.otf,*.woff,*.fnt,*.font"), "set_data_path", "get_data_path");
- if (has_outline()) {
- ofs = Vector2(0, 0);
- for (int i = 0; i < chars_drawn; i++) {
- ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], p_modulate, false);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "get_antialiased");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "get_force_autohinter");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_field_hint"), "set_distance_field_hint", "get_distance_field_hint");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
+}
+
+bool FontData::_set(const StringName &p_name, const Variant &p_value) {
+ String str = p_name;
+ if (str.begins_with("language_support_override/")) {
+ String lang = str.get_slicec('/', 1);
+ if (lang == "_new") {
+ return false;
}
+ set_language_support_override(lang, p_value);
+ return true;
+ }
+ if (str.begins_with("script_support_override/")) {
+ String scr = str.get_slicec('/', 1);
+ if (scr == "_new") {
+ return false;
+ }
+ set_script_support_override(scr, p_value);
+ return true;
+ }
+ if (str.begins_with("variation/")) {
+ String name = str.get_slicec('/', 1);
+ set_variation(name, p_value);
+ return true;
}
-}
-void Font::update_changes() {
- emit_changed();
+ return false;
}
-void Font::_bind_methods() {
- ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "string", "modulate", "clip_w", "outline_modulate"), &Font::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(-1), DEFVAL(Color(1, 1, 1)));
- ClassDB::bind_method(D_METHOD("get_ascent"), &Font::get_ascent);
- ClassDB::bind_method(D_METHOD("get_descent"), &Font::get_descent);
- ClassDB::bind_method(D_METHOD("get_height"), &Font::get_height);
- ClassDB::bind_method(D_METHOD("is_distance_field_hint"), &Font::is_distance_field_hint);
- ClassDB::bind_method(D_METHOD("get_char_size", "char", "next"), &Font::get_char_size, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("get_string_size", "string"), &Font::get_string_size);
- ClassDB::bind_method(D_METHOD("get_wordwrap_string_size", "string", "width"), &Font::get_wordwrap_string_size);
- ClassDB::bind_method(D_METHOD("has_outline"), &Font::has_outline);
- ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "position", "char", "next", "modulate", "outline"), &Font::draw_char, DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
-}
+bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
+ String str = p_name;
+ if (str.begins_with("language_support_override/")) {
+ String lang = str.get_slicec('/', 1);
+ if (lang == "_new") {
+ return true;
+ }
+ r_ret = get_language_support_override(lang);
+ return true;
+ }
+ if (str.begins_with("script_support_override/")) {
+ String scr = str.get_slicec('/', 1);
+ if (scr == "_new") {
+ return true;
+ }
+ r_ret = get_script_support_override(scr);
+ return true;
+ }
+ if (str.begins_with("variation/")) {
+ String name = str.get_slicec('/', 1);
-Font::Font() {
+ r_ret = get_variation(name);
+ return true;
+ }
+
+ return false;
}
-/////////////////////////////////////////////////////////////////
+void FontData::_get_property_list(List<PropertyInfo> *p_list) const {
+ Vector<String> lang_over = get_language_support_overrides();
+ for (int i = 0; i < lang_over.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE));
+ }
+ p_list->push_back(PropertyInfo(Variant::NIL, "language_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
-void BitmapFont::_set_chars(const Vector<int> &p_chars) {
- int len = p_chars.size();
- //char 1 charsize 1 texture, 4 rect, 2 align, advance 1
- ERR_FAIL_COND(len % 9);
- if (!len) {
- return; //none to do
+ Vector<String> scr_over = get_script_support_overrides();
+ for (int i = 0; i < scr_over.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE));
}
- int chars = len / 9;
+ p_list->push_back(PropertyInfo(Variant::NIL, "script_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
- const int *r = p_chars.ptr();
- for (int i = 0; i < chars; i++) {
- const int *data = &r[i * 9];
- add_char(data[0], data[1], Rect2(data[2], data[3], data[4], data[5]), Size2(data[6], data[7]), data[8]);
+ Dictionary variations = get_variation_list();
+ for (const Variant *ftr = variations.next(nullptr); ftr != nullptr; ftr = variations.next(ftr)) {
+ Vector3i v = variations[*ftr];
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "variation/" + TS->tag_to_name(*ftr), PROPERTY_HINT_RANGE, itos(v.x) + "," + itos(v.y), PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE));
}
}
-Vector<int> BitmapFont::_get_chars() const {
- Vector<int> chars;
+RID FontData::get_rid() const {
+ return rid;
+}
- const char32_t *key = nullptr;
+void FontData::load_resource(const String &p_filename, int p_base_size) {
+ if (rid != RID()) {
+ TS->free(rid);
+ }
+ rid = TS->create_font_resource(p_filename, p_base_size);
+ path = p_filename;
+ base_size = TS->font_get_base_size(rid);
+ emit_changed();
+}
- while ((key = char_map.next(key))) {
- const Character *c = char_map.getptr(*key);
- ERR_FAIL_COND_V(!c, Vector<int>());
- chars.push_back(*key);
- chars.push_back(c->texture_idx);
- chars.push_back(c->rect.position.x);
- chars.push_back(c->rect.position.y);
+void FontData::_load_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size) {
+ if (rid != RID()) {
+ TS->free(rid);
+ }
+ rid = TS->create_font_memory(p_data.ptr(), p_data.size(), p_type, p_base_size);
+ path = TTR("(Memory: " + p_type.to_upper() + " @ 0x" + String::num_int64((uint64_t)p_data.ptr(), 16, true) + ")");
+ base_size = TS->font_get_base_size(rid);
+ emit_changed();
+}
- chars.push_back(c->rect.size.x);
- chars.push_back(c->rect.size.y);
- chars.push_back(c->h_align);
- chars.push_back(c->v_align);
- chars.push_back(c->advance);
+void FontData::load_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) {
+ if (rid != RID()) {
+ TS->free(rid);
}
+ rid = TS->create_font_memory(p_data, p_size, p_type, p_base_size);
+ path = TTR("(Memory: " + p_type.to_upper() + " @ 0x" + String::num_int64((uint64_t)p_data, 16, true) + ")");
+ base_size = TS->font_get_base_size(rid);
+ emit_changed();
+}
- return chars;
+void FontData::set_data_path(const String &p_path) {
+ load_resource(p_path, base_size);
}
-void BitmapFont::_set_kernings(const Vector<int> &p_kernings) {
- int len = p_kernings.size();
- ERR_FAIL_COND(len % 3);
- if (!len) {
- return;
- }
- const int *r = p_kernings.ptr();
+String FontData::get_data_path() const {
+ return path;
+}
- for (int i = 0; i < len / 3; i++) {
- const int *data = &r[i * 3];
- add_kerning_pair(data[0], data[1], data[2]);
+float FontData::get_height(int p_size) const {
+ if (rid == RID()) {
+ return 0.f; // Do not raise errors in getters, to prevent editor from spamming errors on incomplete (without data_path set) fonts.
}
+ return TS->font_get_height(rid, (p_size < 0) ? base_size : p_size);
}
-Vector<int> BitmapFont::_get_kernings() const {
- Vector<int> kernings;
-
- for (Map<KerningPairKey, int>::Element *E = kerning_map.front(); E; E = E->next()) {
- kernings.push_back(E->key().A);
- kernings.push_back(E->key().B);
- kernings.push_back(E->get());
+float FontData::get_ascent(int p_size) const {
+ if (rid == RID()) {
+ return 0.f;
}
+ return TS->font_get_ascent(rid, (p_size < 0) ? base_size : p_size);
+}
- return kernings;
+float FontData::get_descent(int p_size) const {
+ if (rid == RID()) {
+ return 0.f;
+ }
+ return TS->font_get_descent(rid, (p_size < 0) ? base_size : p_size);
}
-void BitmapFont::_set_textures(const Vector<Variant> &p_textures) {
- textures.clear();
- for (int i = 0; i < p_textures.size(); i++) {
- Ref<Texture2D> tex = p_textures[i];
- ERR_CONTINUE(!tex.is_valid());
- add_texture(tex);
+float FontData::get_underline_position(int p_size) const {
+ if (rid == RID()) {
+ return 0.f;
}
+ return TS->font_get_underline_position(rid, (p_size < 0) ? base_size : p_size);
}
-Vector<Variant> BitmapFont::_get_textures() const {
- Vector<Variant> rtextures;
- for (int i = 0; i < textures.size(); i++) {
- rtextures.push_back(textures[i]);
+Dictionary FontData::get_feature_list() const {
+ if (rid == RID()) {
+ return Dictionary();
}
- return rtextures;
+ return TS->font_get_feature_list(rid);
}
-Error BitmapFont::create_from_fnt(const String &p_file) {
- //fnt format used by angelcode bmfont
- //http://www.angelcode.com/products/bmfont/
+float FontData::get_underline_thickness(int p_size) const {
+ if (rid == RID()) {
+ return 0.f;
+ }
+ return TS->font_get_underline_thickness(rid, (p_size < 0) ? base_size : p_size);
+}
- FileAccess *f = FileAccess::open(p_file, FileAccess::READ);
+Dictionary FontData::get_variation_list() const {
+ if (rid == RID()) {
+ return Dictionary();
+ }
+ return TS->font_get_variation_list(rid);
+}
- ERR_FAIL_COND_V_MSG(!f, ERR_FILE_NOT_FOUND, "Can't open font: " + p_file + ".");
+void FontData::set_variation(const String &p_name, double p_value) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_set_variation(rid, p_name, p_value);
+ emit_changed();
+}
- clear();
+double FontData::get_variation(const String &p_name) const {
+ if (rid == RID()) {
+ return 0;
+ }
+ return TS->font_get_variation(rid, p_name);
+}
- while (true) {
- String line = f->get_line();
+void FontData::set_antialiased(bool p_antialiased) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_set_antialiased(rid, p_antialiased);
+ emit_changed();
+}
- int delimiter = line.find(" ");
- String type = line.substr(0, delimiter);
- int pos = delimiter + 1;
- Map<String, String> keys;
+bool FontData::get_antialiased() const {
+ if (rid == RID()) {
+ return false;
+ }
+ return TS->font_get_antialiased(rid);
+}
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
+void FontData::set_distance_field_hint(bool p_distance_field) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_set_distance_field_hint(rid, p_distance_field);
+ emit_changed();
+}
- while (pos < line.size()) {
- int eq = line.find("=", pos);
- if (eq == -1) {
- break;
- }
- String key = line.substr(pos, eq - pos);
- int end = -1;
- String value;
- if (line[eq + 1] == '"') {
- end = line.find("\"", eq + 2);
- if (end == -1) {
- break;
- }
- value = line.substr(eq + 2, end - 1 - eq - 1);
- pos = end + 1;
- } else {
- end = line.find(" ", eq + 1);
- if (end == -1) {
- end = line.size();
- }
+bool FontData::get_distance_field_hint() const {
+ if (rid == RID()) {
+ return false;
+ }
+ return TS->font_get_distance_field_hint(rid);
+}
- value = line.substr(eq + 1, end - eq);
+void FontData::set_hinting(TextServer::Hinting p_hinting) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_set_hinting(rid, p_hinting);
+ emit_changed();
+}
- pos = end;
- }
+TextServer::Hinting FontData::get_hinting() const {
+ if (rid == RID()) {
+ return TextServer::HINTING_NONE;
+ }
+ return TS->font_get_hinting(rid);
+}
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
+void FontData::set_force_autohinter(bool p_enabeld) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_set_force_autohinter(rid, p_enabeld);
+ emit_changed();
+}
- keys[key] = value;
- }
+bool FontData::get_force_autohinter() const {
+ if (rid == RID()) {
+ return false;
+ }
+ return TS->font_get_force_autohinter(rid);
+}
- if (type == "info") {
- if (keys.has("face")) {
- set_name(keys["face"]);
- }
- /*
- if (keys.has("size"))
- font->set_height(keys["size"].to_int());
- */
-
- } else if (type == "common") {
- if (keys.has("lineHeight")) {
- set_height(keys["lineHeight"].to_int());
- }
- if (keys.has("base")) {
- set_ascent(keys["base"].to_int());
- }
+bool FontData::has_char(char32_t p_char) const {
+ if (rid == RID()) {
+ return false;
+ }
+ return TS->font_has_char(rid, p_char);
+}
- } else if (type == "page") {
- if (keys.has("file")) {
- String base_dir = p_file.get_base_dir();
- String file = base_dir.plus_file(keys["file"]);
- Ref<Texture2D> tex = ResourceLoader::load(file);
- if (tex.is_null()) {
- ERR_PRINT("Can't load font texture!");
- } else {
- add_texture(tex);
- }
- }
- } else if (type == "char") {
- char32_t idx = 0;
- if (keys.has("id")) {
- idx = keys["id"].to_int();
- }
+String FontData::get_supported_chars() const {
+ ERR_FAIL_COND_V(rid == RID(), String());
+ return TS->font_get_supported_chars(rid);
+}
- Rect2 rect;
+Vector2 FontData::get_glyph_advance(uint32_t p_index, int p_size) const {
+ ERR_FAIL_COND_V(rid == RID(), Vector2());
+ return TS->font_get_glyph_advance(rid, p_index, (p_size < 0) ? base_size : p_size);
+}
- if (keys.has("x")) {
- rect.position.x = keys["x"].to_int();
- }
- if (keys.has("y")) {
- rect.position.y = keys["y"].to_int();
- }
- if (keys.has("width")) {
- rect.size.width = keys["width"].to_int();
- }
- if (keys.has("height")) {
- rect.size.height = keys["height"].to_int();
- }
+Vector2 FontData::get_glyph_kerning(uint32_t p_index_a, uint32_t p_index_b, int p_size) const {
+ ERR_FAIL_COND_V(rid == RID(), Vector2());
+ return TS->font_get_glyph_kerning(rid, p_index_a, p_index_b, (p_size < 0) ? base_size : p_size);
+}
- Point2 ofs;
+bool FontData::has_outline() const {
+ if (rid == RID()) {
+ return false;
+ }
+ return TS->font_has_outline(rid);
+}
- if (keys.has("xoffset")) {
- ofs.x = keys["xoffset"].to_int();
- }
- if (keys.has("yoffset")) {
- ofs.y = keys["yoffset"].to_int();
- }
+float FontData::get_base_size() const {
+ return base_size;
+}
- int texture = 0;
- if (keys.has("page")) {
- texture = keys["page"].to_int();
- }
- int advance = -1;
- if (keys.has("xadvance")) {
- advance = keys["xadvance"].to_int();
- }
+bool FontData::is_language_supported(const String &p_language) const {
+ if (rid == RID()) {
+ return false;
+ }
+ return TS->font_is_language_supported(rid, p_language);
+}
- add_char(idx, texture, rect, ofs, advance);
+void FontData::set_language_support_override(const String &p_language, bool p_supported) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_set_language_support_override(rid, p_language, p_supported);
+ emit_changed();
+}
- } else if (type == "kerning") {
- char32_t first = 0, second = 0;
- int k = 0;
+bool FontData::get_language_support_override(const String &p_language) const {
+ if (rid == RID()) {
+ return false;
+ }
+ return TS->font_get_language_support_override(rid, p_language);
+}
- if (keys.has("first")) {
- first = keys["first"].to_int();
- }
- if (keys.has("second")) {
- second = keys["second"].to_int();
- }
- if (keys.has("amount")) {
- k = keys["amount"].to_int();
- }
+void FontData::remove_language_support_override(const String &p_language) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_remove_language_support_override(rid, p_language);
+ emit_changed();
+}
- add_kerning_pair(first, second, -k);
- }
+Vector<String> FontData::get_language_support_overrides() const {
+ if (rid == RID()) {
+ return Vector<String>();
+ }
+ return TS->font_get_language_support_overrides(rid);
+}
- if (f->eof_reached()) {
- break;
- }
+bool FontData::is_script_supported(const String &p_script) const {
+ if (rid == RID()) {
+ return false;
}
+ return TS->font_is_script_supported(rid, p_script);
+}
- memdelete(f);
+void FontData::set_script_support_override(const String &p_script, bool p_supported) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_set_script_support_override(rid, p_script, p_supported);
+ emit_changed();
+}
- return OK;
+bool FontData::get_script_support_override(const String &p_script) const {
+ if (rid == RID()) {
+ return false;
+ }
+ return TS->font_get_script_support_override(rid, p_script);
}
-void BitmapFont::set_height(float p_height) {
- height = p_height;
+void FontData::remove_script_support_override(const String &p_script) {
+ ERR_FAIL_COND(rid == RID());
+ TS->font_remove_script_support_override(rid, p_script);
+ emit_changed();
}
-float BitmapFont::get_height() const {
- return height;
+Vector<String> FontData::get_script_support_overrides() const {
+ if (rid == RID()) {
+ return Vector<String>();
+ }
+ return TS->font_get_script_support_overrides(rid);
}
-void BitmapFont::set_ascent(float p_ascent) {
- ascent = p_ascent;
+uint32_t FontData::get_glyph_index(char32_t p_char, char32_t p_variation_selector) const {
+ ERR_FAIL_COND_V(rid == RID(), 0);
+ return TS->font_get_glyph_index(rid, p_char, p_variation_selector);
}
-float BitmapFont::get_ascent() const {
- return ascent;
+Vector2 FontData::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ ERR_FAIL_COND_V(rid == RID(), Vector2());
+ return TS->font_draw_glyph(rid, p_canvas, (p_size <= 0) ? base_size : p_size, p_pos, p_index, p_color);
}
-float BitmapFont::get_descent() const {
- return height - ascent;
+Vector2 FontData::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const {
+ ERR_FAIL_COND_V(rid == RID(), Vector2());
+ return TS->font_draw_glyph_outline(rid, p_canvas, (p_size <= 0) ? base_size : p_size, p_outline_size, p_pos, p_index, p_color);
}
-float BitmapFont::get_underline_position() const {
- return 2;
+FontData::FontData() {}
+
+FontData::FontData(const String &p_filename, int p_base_size) {
+ load_resource(p_filename, p_base_size);
}
-float BitmapFont::get_underline_thickness() const {
- return 1;
+FontData::FontData(const PackedByteArray &p_data, const String &p_type, int p_base_size) {
+ _load_memory(p_data, p_type, p_base_size);
}
-void BitmapFont::add_texture(const Ref<Texture2D> &p_texture) {
- ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object.");
- textures.push_back(p_texture);
+FontData::~FontData() {
+ if (rid != RID()) {
+ TS->free(rid);
+ }
}
-int BitmapFont::get_texture_count() const {
- return textures.size();
-};
+/*************************************************************************/
-Ref<Texture2D> BitmapFont::get_texture(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, textures.size(), Ref<Texture2D>());
- return textures[p_idx];
-};
+void Font::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_data", "data"), &Font::add_data);
+ ClassDB::bind_method(D_METHOD("set_data", "idx", "data"), &Font::set_data);
+ ClassDB::bind_method(D_METHOD("get_data_count"), &Font::get_data_count);
+ ClassDB::bind_method(D_METHOD("get_data", "idx"), &Font::get_data);
+ ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data);
-int BitmapFont::get_character_count() const {
- return char_map.size();
-};
+ ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(-1));
-Vector<char32_t> BitmapFont::get_char_keys() const {
- Vector<char32_t> chars;
- chars.resize(char_map.size());
- const char32_t *ct = nullptr;
- int count = 0;
- while ((ct = char_map.next(ct))) {
- chars.write[count++] = *ct;
- };
+ ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(-1));
- return chars;
-};
+ ClassDB::bind_method(D_METHOD("get_spacing", "type"), &Font::get_spacing);
+ ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &Font::set_spacing);
-BitmapFont::Character BitmapFont::get_character(char32_t p_char) const {
- if (!char_map.has(p_char)) {
- ERR_FAIL_V(Character());
- };
+ ClassDB::bind_method(D_METHOD("get_string_size", "text", "size"), &Font::get_string_size, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND));
- return char_map[p_char];
-};
+ ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
-void BitmapFont::add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) {
- if (p_advance < 0) {
- p_advance = p_rect.size.width;
- }
+ ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char);
+ ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars);
+
+ ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
+
+ ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
- Character c;
- c.rect = p_rect;
- c.texture_idx = p_texture_idx;
- c.v_align = p_align.y;
- c.advance = p_advance;
- c.h_align = p_align.x;
+ ADD_GROUP("Extra Spacing", "extra_spacing");
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_top"), "set_spacing", "get_spacing", SPACING_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_bottom"), "set_spacing", "get_spacing", SPACING_BOTTOM);
- char_map[p_char] = c;
+ BIND_ENUM_CONSTANT(SPACING_TOP);
+ BIND_ENUM_CONSTANT(SPACING_BOTTOM);
}
-void BitmapFont::add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) {
- KerningPairKey kpk;
- kpk.A = p_A;
- kpk.B = p_B;
+void Font::_data_changed() {
+ cache.clear();
+ cache_wrap.clear();
- if (p_kerning == 0 && kerning_map.has(kpk)) {
- kerning_map.erase(kpk);
- } else {
- kerning_map[kpk] = p_kerning;
+ emit_changed();
+ _change_notify();
+}
+
+bool Font::_set(const StringName &p_name, const Variant &p_value) {
+ String str = p_name;
+#ifndef DISABLE_DEPRECATED
+ if (str == "font_data") { // Compatibility, DynamicFont main data
+ Ref<FontData> fd = p_value;
+ if (fd.is_valid()) {
+ add_data(fd);
+ return true;
+ }
+ return false;
+ } else if (str.begins_with("fallback/")) { // Compatibility, DynamicFont fallback data
+ Ref<FontData> fd = p_value;
+ if (fd.is_valid()) {
+ add_data(fd);
+ return true;
+ }
+ return false;
+ } else if (str == "fallback") { // Compatibility, BitmapFont fallback
+ Ref<Font> f = p_value;
+ if (f.is_valid()) {
+ for (int i = 0; i < f->get_data_count(); i++) {
+ add_data(f->get_data(i));
+ }
+ return true;
+ }
+ return false;
}
+#endif /* DISABLE_DEPRECATED */
+ if (str.begins_with("data/")) {
+ int idx = str.get_slicec('/', 1).to_int();
+ Ref<FontData> fd = p_value;
+
+ if (fd.is_valid()) {
+ if (idx == data.size()) {
+ add_data(fd);
+ return true;
+ } else if (idx >= 0 && idx < data.size()) {
+ set_data(idx, fd);
+ return true;
+ } else {
+ return false;
+ }
+ } else if (idx >= 0 && idx < data.size()) {
+ remove_data(idx);
+ return true;
+ }
+ }
+
+ return false;
}
-Vector<BitmapFont::KerningPairKey> BitmapFont::get_kerning_pair_keys() const {
- Vector<BitmapFont::KerningPairKey> ret;
- ret.resize(kerning_map.size());
- int i = 0;
+bool Font::_get(const StringName &p_name, Variant &r_ret) const {
+ String str = p_name;
+ if (str.begins_with("data/")) {
+ int idx = str.get_slicec('/', 1).to_int();
+
+ if (idx == data.size()) {
+ r_ret = Ref<FontData>();
+ return true;
+ } else if (idx >= 0 && idx < data.size()) {
+ r_ret = get_data(idx);
+ return true;
+ }
+ }
- for (Map<KerningPairKey, int>::Element *E = kerning_map.front(); E; E = E->next()) {
- ret.write[i++] = E->key();
+ return false;
+}
+
+void Font::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < data.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "FontData"));
}
- return ret;
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(data.size()), PROPERTY_HINT_RESOURCE_TYPE, "FontData"));
}
-int BitmapFont::get_kerning_pair(char32_t p_A, char32_t p_B) const {
- KerningPairKey kpk;
- kpk.A = p_A;
- kpk.B = p_B;
+void Font::add_data(const Ref<FontData> &p_data) {
+ ERR_FAIL_COND(p_data.is_null());
+ data.push_back(p_data);
- const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk);
- if (E) {
- return E->get();
+ if (data[data.size() - 1].is_valid()) {
+ data.write[data.size() - 1]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
- return 0;
+ cache.clear();
+ cache_wrap.clear();
+
+ emit_changed();
+ _change_notify();
}
-void BitmapFont::set_distance_field_hint(bool p_distance_field) {
- distance_field_hint = p_distance_field;
+void Font::set_data(int p_idx, const Ref<FontData> &p_data) {
+ ERR_FAIL_COND(p_data.is_null());
+ ERR_FAIL_INDEX(p_idx, data.size());
+
+ if (data[p_idx].is_valid()) {
+ data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed));
+ }
+
+ data.write[p_idx] = p_data;
+
+ if (data[p_idx].is_valid()) {
+ data.write[p_idx]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+
+ cache.clear();
+ cache_wrap.clear();
+
emit_changed();
+ _change_notify();
}
-bool BitmapFont::is_distance_field_hint() const {
- return distance_field_hint;
+int Font::get_data_count() const {
+ return data.size();
}
-void BitmapFont::clear() {
- height = 1;
- ascent = 0;
- char_map.clear();
- textures.clear();
- kerning_map.clear();
- distance_field_hint = false;
+Ref<FontData> Font::get_data(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, data.size(), Ref<FontData>());
+ return data[p_idx];
}
-Size2 Font::get_string_size(const String &p_string) const {
- float w = 0;
+void Font::remove_data(int p_idx) {
+ ERR_FAIL_INDEX(p_idx, data.size());
+
+ if (data[p_idx].is_valid()) {
+ data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed));
+ }
+
+ data.remove(p_idx);
+
+ cache.clear();
+ cache_wrap.clear();
+
+ emit_changed();
+ _change_notify();
+}
+
+Dictionary Font::get_feature_list() const {
+ Dictionary out;
+ for (int i = 0; i < data.size(); i++) {
+ Dictionary data_ftrs = data[i]->get_feature_list();
+ for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) {
+ out[*ftr] = data_ftrs[*ftr];
+ }
+ }
+ return out;
+}
- int l = p_string.length();
- if (l == 0) {
- return Size2(0, get_height());
+float Font::get_height(int p_size) const {
+ float ret = 0.f;
+ for (int i = 0; i < data.size(); i++) {
+ ret = MAX(ret, data[i]->get_height(p_size));
}
- const char32_t *sptr = &p_string[0];
+ return ret + spacing_top + spacing_bottom;
+}
- for (int i = 0; i < l; i++) {
- w += get_char_size(sptr[i], sptr[i + 1]).width;
+float Font::get_ascent(int p_size) const {
+ float ret = 0.f;
+ for (int i = 0; i < data.size(); i++) {
+ ret = MAX(ret, data[i]->get_ascent(p_size));
}
+ return ret + spacing_top;
+}
- return Size2(w, get_height());
+float Font::get_descent(int p_size) const {
+ float ret = 0.f;
+ for (int i = 0; i < data.size(); i++) {
+ ret = MAX(ret, data[i]->get_descent(p_size));
+ }
+ return ret + spacing_bottom;
}
-Size2 Font::get_wordwrap_string_size(const String &p_string, float p_width) const {
- ERR_FAIL_COND_V(p_width <= 0, Vector2(0, get_height()));
+float Font::get_underline_position(int p_size) const {
+ float ret = 0.f;
+ for (int i = 0; i < data.size(); i++) {
+ ret = MAX(ret, data[i]->get_underline_position(p_size));
+ }
+ return ret;
+}
- int l = p_string.length();
- if (l == 0) {
- return Size2(p_width, get_height());
+float Font::get_underline_thickness(int p_size) const {
+ float ret = 0.f;
+ for (int i = 0; i < data.size(); i++) {
+ ret = MAX(ret, data[i]->get_underline_thickness(p_size));
}
+ return ret;
+}
- float line_w = 0;
- float h = 0;
- float space_w = get_char_size(' ').width;
- Vector<String> lines = p_string.split("\n");
- for (int i = 0; i < lines.size(); i++) {
- h += get_height();
- String t = lines[i];
- line_w = 0;
- Vector<String> words = t.split(" ");
- for (int j = 0; j < words.size(); j++) {
- line_w += get_string_size(words[j]).x;
- if (line_w > p_width) {
- h += get_height();
- line_w = get_string_size(words[j]).x;
- } else {
- line_w += space_w;
- }
- }
+int Font::get_spacing(int p_type) const {
+ if (p_type == SPACING_TOP) {
+ return spacing_top;
+ } else if (p_type == SPACING_BOTTOM) {
+ return spacing_bottom;
}
- return Size2(p_width, h);
+ return 0;
}
-void BitmapFont::set_fallback(const Ref<BitmapFont> &p_fallback) {
- for (Ref<BitmapFont> fallback_child = p_fallback; fallback_child != nullptr; fallback_child = fallback_child->get_fallback()) {
- ERR_FAIL_COND_MSG(fallback_child == this, "Can't set as fallback one of its parents to prevent crashes due to recursive loop.");
+void Font::set_spacing(int p_type, int p_value) {
+ if (p_type == SPACING_TOP) {
+ spacing_top = p_value;
+ } else if (p_type == SPACING_BOTTOM) {
+ spacing_bottom = p_value;
}
- fallback = p_fallback;
+ emit_changed();
+ _change_notify();
}
-Ref<BitmapFont> BitmapFont::get_fallback() const {
- return fallback;
+// Drawing string and string sizes, cached.
+
+Size2 Font::get_string_size(const String &p_text, int p_size) const {
+ ERR_FAIL_COND_V(data.empty(), Size2());
+
+ uint64_t hash = p_text.hash64();
+ hash = hash_djb2_one_64(p_size, hash);
+
+ Ref<TextLine> buffer;
+ if (cache.has(hash)) {
+ buffer = cache.get(hash);
+ } else {
+ buffer.instance();
+ int size = p_size <= 0 ? data[0]->get_base_size() : p_size;
+ buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ cache.insert(hash, buffer);
+ }
+ if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
+ return buffer->get_size() + Vector2(0, spacing_top + spacing_bottom);
+ } else {
+ return buffer->get_size() + Vector2(spacing_top + spacing_bottom, 0);
+ }
}
-float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, const Color &p_modulate, bool p_outline) const {
- const Character *c = char_map.getptr(p_char);
+Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p_size, uint8_t p_flags) const {
+ ERR_FAIL_COND_V(data.empty(), Size2());
+
+ uint64_t hash = p_text.hash64();
+ hash = hash_djb2_one_64(p_size, hash);
- if (!c) {
- if (fallback.is_valid()) {
- return fallback->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, p_outline);
+ uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
+ wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
+
+ Ref<TextParagraph> lines_buffer;
+ if (cache_wrap.has(wrp_hash)) {
+ lines_buffer = cache_wrap.get(wrp_hash);
+ } else {
+ lines_buffer.instance();
+ int size = p_size <= 0 ? data[0]->get_base_size() : p_size;
+ lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ lines_buffer->set_width(p_width);
+ lines_buffer->set_flags(p_flags);
+ cache_wrap.insert(wrp_hash, lines_buffer);
+ }
+
+ Size2 ret;
+ for (int i = 0; i < lines_buffer->get_line_count(); i++) {
+ Size2 line_size = lines_buffer->get_line_size(i);
+ if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
+ ret.x = MAX(ret.x, line_size.x);
+ ret.y += line_size.y + spacing_top + spacing_bottom;
+ } else {
+ ret.y = MAX(ret.y, line_size.y);
+ ret.x += line_size.x + spacing_top + spacing_bottom;
}
- return 0;
+ }
+ return ret;
+}
+
+void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+ uint64_t hash = p_text.hash64();
+ hash = hash_djb2_one_64(p_size, hash);
+
+ Ref<TextLine> buffer;
+ if (cache.has(hash)) {
+ buffer = cache.get(hash);
+ } else {
+ buffer.instance();
+ int size = p_size <= 0 ? data[0]->get_base_size() : p_size;
+ buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ cache.insert(hash, buffer);
}
- ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0);
- if (!p_outline && c->texture_idx != -1) {
- Point2 cpos = p_pos;
- cpos.x += c->h_align;
- cpos.y -= ascent;
- cpos.y += c->v_align;
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx]->get_rid(), c->rect, p_modulate, false, RID(), RID(), Color(1, 1, 1, 1), false);
+ Vector2 ofs = p_pos;
+ if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.y += spacing_top - buffer->get_line_ascent();
+ } else {
+ ofs.x += spacing_top - buffer->get_line_ascent();
}
- return get_char_size(p_char, p_next).width;
+ buffer->set_width(p_width);
+ buffer->set_align(p_align);
+
+ if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
+ buffer->draw_outline(p_canvas_item, ofs, p_outline_size, p_outline_modulate);
+ }
+ buffer->draw(p_canvas_item, ofs, p_modulate);
}
-Size2 BitmapFont::get_char_size(char32_t p_char, char32_t p_next) const {
- const Character *c = char_map.getptr(p_char);
+void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+ uint64_t hash = p_text.hash64();
+ hash = hash_djb2_one_64(p_size, hash);
- if (!c) {
- if (fallback.is_valid()) {
- return fallback->get_char_size(p_char, p_next);
- }
- return Size2();
+ uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
+ wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
+
+ Ref<TextParagraph> lines_buffer;
+ if (cache_wrap.has(wrp_hash)) {
+ lines_buffer = cache_wrap.get(wrp_hash);
+ } else {
+ lines_buffer.instance();
+ int size = p_size <= 0 ? data[0]->get_base_size() : p_size;
+ lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ lines_buffer->set_width(p_width);
+ lines_buffer->set_flags(p_flags);
+ cache_wrap.insert(wrp_hash, lines_buffer);
}
- Size2 ret(c->advance, c->rect.size.y);
+ lines_buffer->set_align(p_align);
+
+ Vector2 lofs = p_pos;
+ for (int i = 0; i < lines_buffer->get_line_count(); i++) {
+ if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
+ lofs.y += spacing_top;
+ if (i == 0) {
+ lofs.y -= lines_buffer->get_line_ascent(0);
+ }
+ } else {
+ lofs.x += spacing_top;
+ if (i == 0) {
+ lofs.x -= lines_buffer->get_line_ascent(0);
+ }
+ }
+ if (p_width > 0) {
+ lines_buffer->set_align(p_align);
+ }
+
+ if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
+ lines_buffer->draw_line_outline(p_canvas_item, lofs, i, p_outline_size, p_outline_modulate);
+ }
+ lines_buffer->draw_line(p_canvas_item, lofs, i, p_modulate);
- if (p_next) {
- KerningPairKey kpk;
- kpk.A = p_char;
- kpk.B = p_next;
+ Size2 line_size = lines_buffer->get_line_size(i);
+ if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) {
+ lofs.y += line_size.y + spacing_bottom;
+ } else {
+ lofs.x += line_size.x + spacing_bottom;
+ }
- const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk);
- if (E) {
- ret.width -= E->get();
+ if ((p_max_lines > 0) && (i >= p_max_lines)) {
+ return;
}
}
+}
- return ret;
+bool Font::has_char(char32_t p_char) const {
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i]->has_char(p_char)) {
+ return true;
+ }
+ }
+ return false;
}
-void BitmapFont::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_from_fnt", "path"), &BitmapFont::create_from_fnt);
- ClassDB::bind_method(D_METHOD("set_height", "px"), &BitmapFont::set_height);
+String Font::get_supported_chars() const {
+ String chars;
+ for (int i = 0; i < data.size(); i++) {
+ String data_chars = data[i]->get_supported_chars();
+ for (int j = 0; j < data_chars.length(); j++) {
+ if (chars.find_char(data_chars[j]) == -1) {
+ chars += data_chars[j];
+ }
+ }
+ }
+ return chars;
+}
- ClassDB::bind_method(D_METHOD("set_ascent", "px"), &BitmapFont::set_ascent);
+Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const {
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i]->has_char(p_char)) {
+ int size = p_size <= 0 ? data[i]->get_base_size() : p_size;
+ uint32_t glyph_a = data[i]->get_glyph_index(p_char);
+ Size2 ret = Size2(data[i]->get_glyph_advance(glyph_a, size).x, data[i]->get_height(size));
+ if ((p_next != 0) && data[i]->has_char(p_next)) {
+ uint32_t glyph_b = data[i]->get_glyph_index(p_next);
+ ret.x -= data[i]->get_glyph_kerning(glyph_a, glyph_b, size).x;
+ }
+ return ret;
+ }
+ }
+ return Size2();
+}
- ClassDB::bind_method(D_METHOD("add_kerning_pair", "char_a", "char_b", "kerning"), &BitmapFont::add_kerning_pair);
- ClassDB::bind_method(D_METHOD("get_kerning_pair", "char_a", "char_b"), &BitmapFont::get_kerning_pair);
+float Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const {
+ for (int i = 0; i < data.size(); i++) {
+ if (data[i]->has_char(p_char)) {
+ int size = p_size <= 0 ? data[i]->get_base_size() : p_size;
+ uint32_t glyph_a = data[i]->get_glyph_index(p_char);
+ float ret = data[i]->get_glyph_advance(glyph_a, size).x;
+ if ((p_next != 0) && data[i]->has_char(p_next)) {
+ uint32_t glyph_b = data[i]->get_glyph_index(p_next);
+ ret -= data[i]->get_glyph_kerning(glyph_a, glyph_b, size).x;
+ }
+ if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
+ data[i]->draw_glyph_outline(p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate);
+ }
+ data[i]->draw_glyph(p_canvas_item, size, p_pos, glyph_a, p_modulate);
+ return ret;
+ }
+ }
+ return 0;
+}
- ClassDB::bind_method(D_METHOD("add_texture", "texture"), &BitmapFont::add_texture);
- ClassDB::bind_method(D_METHOD("add_char", "character", "texture", "rect", "align", "advance"), &BitmapFont::add_char, DEFVAL(Point2()), DEFVAL(-1));
+Vector<RID> Font::get_rids() const {
+ Vector<RID> ret;
+ for (int i = 0; i < data.size(); i++) {
+ RID rid = data[i]->get_rid();
+ if (rid != RID()) {
+ ret.push_back(rid);
+ }
+ }
+ return ret;
+}
- ClassDB::bind_method(D_METHOD("get_texture_count"), &BitmapFont::get_texture_count);
- ClassDB::bind_method(D_METHOD("get_texture", "idx"), &BitmapFont::get_texture);
+void Font::update_changes() {
+ emit_changed();
+}
- ClassDB::bind_method(D_METHOD("set_distance_field_hint", "enable"), &BitmapFont::set_distance_field_hint);
+Font::Font() {
+ cache.set_capacity(128);
+ cache_wrap.set_capacity(32);
+}
- ClassDB::bind_method(D_METHOD("clear"), &BitmapFont::clear);
+Font::~Font() {
+ cache.clear();
+ cache_wrap.clear();
+}
- ClassDB::bind_method(D_METHOD("_set_chars"), &BitmapFont::_set_chars);
- ClassDB::bind_method(D_METHOD("_get_chars"), &BitmapFont::_get_chars);
+/*************************************************************************/
- ClassDB::bind_method(D_METHOD("_set_kernings"), &BitmapFont::_set_kernings);
- ClassDB::bind_method(D_METHOD("_get_kernings"), &BitmapFont::_get_kernings);
+RES ResourceFormatLoaderFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+ if (r_error) {
+ *r_error = ERR_FILE_CANT_OPEN;
+ }
- ClassDB::bind_method(D_METHOD("_set_textures"), &BitmapFont::_set_textures);
- ClassDB::bind_method(D_METHOD("_get_textures"), &BitmapFont::_get_textures);
+ Ref<FontData> dfont;
+ dfont.instance();
+ dfont->load_resource(p_path);
- ClassDB::bind_method(D_METHOD("set_fallback", "fallback"), &BitmapFont::set_fallback);
- ClassDB::bind_method(D_METHOD("get_fallback"), &BitmapFont::get_fallback);
+ if (r_error) {
+ *r_error = OK;
+ }
+
+ return dfont;
+}
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_textures", "_get_textures");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "chars", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_chars", "_get_chars");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "kernings", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_kernings", "_get_kernings");
+void ResourceFormatLoaderFont::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
+#ifndef DISABLE_DEPRECATED
+ if (p_type == "DynamicFontData") {
+ p_extensions->push_back("ttf");
+ p_extensions->push_back("otf");
+ p_extensions->push_back("woff");
+ return;
+ }
+ if (p_type == "BitmapFont") { // BitmapFont (*.font, *fnt) is handled by ResourceFormatLoaderCompatFont
+ return;
+ }
+#endif /* DISABLE_DEPRECATED */
+ if (p_type == "" || handles_type(p_type)) {
+ get_recognized_extensions(p_extensions);
+ }
+}
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "1,1024,1"), "set_height", "get_height");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ascent", PROPERTY_HINT_RANGE, "0,1024,1"), "set_ascent", "get_ascent");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_field"), "set_distance_field_hint", "is_distance_field_hint");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback", PROPERTY_HINT_RESOURCE_TYPE, "BitmapFont"), "set_fallback", "get_fallback");
+void ResourceFormatLoaderFont::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("ttf");
+ p_extensions->push_back("otf");
+ p_extensions->push_back("woff");
+ p_extensions->push_back("font");
+ p_extensions->push_back("fnt");
}
-BitmapFont::BitmapFont() {
- clear();
+bool ResourceFormatLoaderFont::handles_type(const String &p_type) const {
+ return (p_type == "FontData");
}
-BitmapFont::~BitmapFont() {
- clear();
+String ResourceFormatLoaderFont::get_resource_type(const String &p_path) const {
+ String el = p_path.get_extension().to_lower();
+ if (el == "ttf" || el == "otf" || el == "woff" || el == "font" || el == "fnt") {
+ return "FontData";
+ }
+ return "";
}
-////////////
+#ifndef DISABLE_DEPRECATED
-RES ResourceFormatLoaderBMFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderCompatFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
- Ref<BitmapFont> font;
- font.instance();
+ Ref<FontData> dfont;
+ dfont.instance();
+ dfont->load_resource(p_path);
- Error err = font->create_from_fnt(p_path);
+ Ref<Font> font;
+ font.instance();
+ font->add_data(dfont);
- if (err) {
- if (r_error) {
- *r_error = err;
- }
- return RES();
+ if (r_error) {
+ *r_error = OK;
}
return font;
}
-void ResourceFormatLoaderBMFont::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("fnt");
+void ResourceFormatLoaderCompatFont::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
+ if (p_type == "BitmapFont") {
+ p_extensions->push_back("font");
+ p_extensions->push_back("fnt");
+ }
}
-bool ResourceFormatLoaderBMFont::handles_type(const String &p_type) const {
- return (p_type == "BitmapFont");
+void ResourceFormatLoaderCompatFont::get_recognized_extensions(List<String> *p_extensions) const {
}
-String ResourceFormatLoaderBMFont::get_resource_type(const String &p_path) const {
- String el = p_path.get_extension().to_lower();
- if (el == "fnt") {
- return "BitmapFont";
- }
+bool ResourceFormatLoaderCompatFont::handles_type(const String &p_type) const {
+ return (p_type == "Font");
+}
+
+String ResourceFormatLoaderCompatFont::get_resource_type(const String &p_path) const {
return "";
}
+
+#endif /* DISABLE_DEPRECATED */
diff --git a/scene/resources/font.h b/scene/resources/font.h
index c739520da3..fe28e1aea5 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -31,179 +31,194 @@
#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(char32_t p_char, char32_t p_next = 0) const = 0;
- Size2 get_string_size(const String &p_string) const;
- Size2 get_wordwrap_string_size(const String &p_string, float p_width) const;
+ void set_data_path(const String &p_path);
+ String get_data_path() const;
- virtual bool is_distance_field_hint() const = 0;
+ float get_height(int p_size) const;
+ float get_ascent(int p_size) const;
+ float get_descent(int p_size) const;
- void draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1, const Color &p_outline_modulate = Color(1, 1, 1)) const;
- void draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate = Color(1, 1, 1), const Color &p_outline_modulate = Color(1, 1, 1)) const;
+ Dictionary get_feature_list() const;
+ Dictionary get_variation_list() const;
- virtual bool has_outline() const { return false; }
- virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const = 0;
+ void set_variation(const String &p_name, double p_value);
+ double get_variation(const String &p_name) const;
- void update_changes();
- Font();
-};
+ float get_underline_position(int p_size) const;
+ float get_underline_thickness(int p_size) const;
-// Helper class to that draws outlines immediately and draws characters in its destructor.
-class FontDrawer {
- const Ref<Font> &font;
- Color outline_color;
- bool has_outline;
-
- struct PendingDraw {
- RID canvas_item;
- Point2 pos;
- char32_t chr;
- char32_t next;
- Color modulate;
- };
+ void set_antialiased(bool p_antialiased);
+ bool get_antialiased() const;
- Vector<PendingDraw> pending_draws;
+ void set_distance_field_hint(bool p_distance_field);
+ bool get_distance_field_hint() const;
-public:
- FontDrawer(const Ref<Font> &p_font, const Color &p_outline_color) :
- font(p_font),
- outline_color(p_outline_color) {
- has_outline = p_font->has_outline();
- }
-
- float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1)) {
- if (has_outline) {
- PendingDraw draw = { p_canvas_item, p_pos, p_char, p_next, p_modulate };
- pending_draws.push_back(draw);
- }
- return font->draw_char(p_canvas_item, p_pos, p_char, p_next, has_outline ? outline_color : p_modulate, has_outline);
- }
-
- ~FontDrawer() {
- for (int i = 0; i < pending_draws.size(); ++i) {
- const PendingDraw &draw = pending_draws[i];
- font->draw_char(draw.canvas_item, draw.pos, draw.chr, draw.next, draw.modulate, false);
- }
- }
-};
+ void set_force_autohinter(bool p_enabeld);
+ bool get_force_autohinter() const;
-class BitmapFont : public Font {
- GDCLASS(BitmapFont, Font);
- RES_BASE_EXTENSION("font");
+ void set_hinting(TextServer::Hinting p_hinting);
+ TextServer::Hinting get_hinting() const;
- Vector<Ref<Texture2D>> textures;
+ bool has_char(char32_t p_char) const;
+ String get_supported_chars() const;
-public:
- struct Character {
- int texture_idx;
- Rect2 rect;
- float v_align;
- float h_align;
- float advance;
-
- Character() {
- texture_idx = 0;
- v_align = 0;
- }
- };
+ Vector2 get_glyph_advance(uint32_t p_index, int p_size) const;
+ Vector2 get_glyph_kerning(uint32_t p_index_a, uint32_t p_index_b, int p_size) const;
+
+ bool has_outline() const;
+ float get_base_size() const;
+
+ bool is_language_supported(const String &p_language) const;
+ void set_language_support_override(const String &p_language, bool p_supported);
+ bool get_language_support_override(const String &p_language) const;
+ void remove_language_support_override(const String &p_language);
+ Vector<String> get_language_support_overrides() const;
+
+ bool is_script_supported(const String &p_script) const;
+ void set_script_support_override(const String &p_script, bool p_supported);
+ bool get_script_support_override(const String &p_script) const;
+ void remove_script_support_override(const String &p_script);
+ Vector<String> get_script_support_overrides() const;
- struct KerningPairKey {
- union {
- struct {
- uint32_t A, B;
- };
+ uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector = 0x0000) const;
- uint64_t pair;
- };
+ Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const;
+ Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const;
+
+ FontData();
+ FontData(const String &p_filename, int p_base_size);
+ FontData(const PackedByteArray &p_data, const String &p_type, int p_base_size);
+
+ ~FontData();
+};
+
+/*************************************************************************/
- _FORCE_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; }
+class TextLine;
+class TextParagraph;
+
+class Font : public Resource {
+ GDCLASS(Font, Resource);
+
+public:
+ enum SpacingType {
+ SPACING_TOP,
+ SPACING_BOTTOM
};
private:
- HashMap<char32_t, Character> char_map;
- Map<KerningPairKey, int> kerning_map;
-
- float height;
- float ascent;
- bool distance_field_hint;
+ int spacing_top = 0;
+ int spacing_bottom = 0;
- void _set_chars(const Vector<int> &p_chars);
- Vector<int> _get_chars() const;
- void _set_kernings(const Vector<int> &p_kernings);
- Vector<int> _get_kernings() const;
- void _set_textures(const Vector<Variant> &p_textures);
- Vector<Variant> _get_textures() const;
+ mutable LRUCache<uint64_t, Ref<TextLine>> cache;
+ mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap;
- Ref<BitmapFont> fallback;
+ Vector<Ref<FontData>> data;
protected:
static void _bind_methods();
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ void _data_changed();
+
public:
- Error create_from_fnt(const String &p_file);
+ Dictionary get_feature_list() const;
- void set_height(float p_height);
- float get_height() const override;
+ // Font data control.
+ void add_data(const Ref<FontData> &p_data);
+ void set_data(int p_idx, const Ref<FontData> &p_data);
+ int get_data_count() const;
+ Ref<FontData> get_data(int p_idx) const;
+ void remove_data(int p_idx);
- void set_ascent(float p_ascent);
- float get_ascent() const override;
- float get_descent() const override;
- float get_underline_position() const override;
- float get_underline_thickness() const override;
+ float get_height(int p_size = -1) const;
+ float get_ascent(int p_size = -1) const;
+ float get_descent(int p_size = -1) const;
- void add_texture(const Ref<Texture2D> &p_texture);
- void add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance = -1);
+ float get_underline_position(int p_size = -1) const;
+ float get_underline_thickness(int p_size = -1) const;
- int get_character_count() const;
- Vector<char32_t> get_char_keys() const;
- Character get_character(char32_t p_char) const;
+ int get_spacing(int p_type) const;
+ void set_spacing(int p_type, int p_value);
- int get_texture_count() const;
- Ref<Texture2D> get_texture(int p_idx) const;
+ // Drawing string.
+ Size2 get_string_size(const String &p_text, int p_size = -1) const;
+ Size2 get_multiline_string_size(const String &p_text, float p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
- void add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning);
- int get_kerning_pair(char32_t p_A, char32_t p_B) const;
- Vector<KerningPairKey> get_kerning_pair_keys() const;
+ void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const override;
+ // Helper functions.
+ bool has_char(char32_t p_char) const;
+ String get_supported_chars() const;
- void set_fallback(const Ref<BitmapFont> &p_fallback);
- Ref<BitmapFont> get_fallback() const;
+ Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const;
+ float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
- void clear();
+ Vector<RID> get_rids() const;
- void set_distance_field_hint(bool p_distance_field);
- bool is_distance_field_hint() const override;
+ void update_changes();
+
+ Font();
+ ~Font();
+};
+
+VARIANT_ENUM_CAST(Font::SpacingType);
- float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const override;
+/*************************************************************************/
- BitmapFont();
- ~BitmapFont();
+class ResourceFormatLoaderFont : public ResourceFormatLoader {
+public:
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+ virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
};
-class ResourceFormatLoaderBMFont : public ResourceFormatLoader {
+#ifndef DISABLE_DEPRECATED
+
+class ResourceFormatLoaderCompatFont : public ResourceFormatLoader {
public:
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+ virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
};
-#endif
+#endif /* DISABLE_DEPRECATED */
+
+#endif /* FONT_H */
diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp
index 4bcc52776b..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);
}
diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h
index 6518b13ee8..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);
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..f1c05b8014 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,31 @@ void Mesh::_bind_methods() {
BIND_ENUM_CONSTANT(ARRAY_FORMAT_COLOR);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV2);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM3);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_BONES);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_WEIGHTS);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_INDEX);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_NORMAL);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_TANGENT);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_COLOR);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_TEX_UV);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_TEX_UV2);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_INDEX);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_BLEND_SHAPE_MASK);
- BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_2D_VERTICES);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_BASE);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0_SHIFT);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1_SHIFT);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2_SHIFT);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM3_SHIFT);
- BIND_ENUM_CONSTANT(ARRAY_COMPRESS_DEFAULT);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_MASK);
+ BIND_ENUM_CONSTANT(ARRAY_COMPRESS_FLAGS_BASE);
- BIND_ENUM_CONSTANT(ARRAY_VERTEX);
- BIND_ENUM_CONSTANT(ARRAY_NORMAL);
- BIND_ENUM_CONSTANT(ARRAY_TANGENT);
- BIND_ENUM_CONSTANT(ARRAY_COLOR);
- BIND_ENUM_CONSTANT(ARRAY_TEX_UV);
- BIND_ENUM_CONSTANT(ARRAY_TEX_UV2);
- BIND_ENUM_CONSTANT(ARRAY_BONES);
- BIND_ENUM_CONSTANT(ARRAY_WEIGHTS);
- BIND_ENUM_CONSTANT(ARRAY_INDEX);
- BIND_ENUM_CONSTANT(ARRAY_MAX);
+ BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_2D_VERTICES);
+ BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_DYNAMIC_UPDATE);
+ BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_8_BONE_WEIGHTS);
+
+ BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED);
+ BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE);
}
void Mesh::clear_cache() const {
@@ -565,11 +582,50 @@ Vector<Ref<Shape3D>> Mesh::convex_decompose() const {
Mesh::Mesh() {
}
+#if 0
static Vector<uint8_t> _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_format, uint32_t p_elements) {
- bool vertex_16bit = p_format & ((1 << (Mesh::ARRAY_VERTEX + Mesh::ARRAY_COMPRESS_BASE)));
- bool has_bones = (p_format & Mesh::ARRAY_FORMAT_BONES);
- bool bone_8 = has_bones && !(p_format & (Mesh::ARRAY_COMPRESS_INDEX << 2));
- bool weight_32 = has_bones && !(p_format & (Mesh::ARRAY_COMPRESS_TEX_UV2 << 2));
+ enum ArrayType {
+ OLD_ARRAY_VERTEX = 0,
+ OLD_ARRAY_NORMAL = 1,
+ OLD_ARRAY_TANGENT = 2,
+ OLD_ARRAY_COLOR = 3,
+ OLD_ARRAY_TEX_UV = 4,
+ OLD_ARRAY_TEX_UV2 = 5,
+ OLD_ARRAY_BONES = 6,
+ OLD_ARRAY_WEIGHTS = 7,
+ OLD_ARRAY_INDEX = 8,
+ OLD_ARRAY_MAX = 9
+ };
+
+ enum ArrayFormat {
+ /* OLD_ARRAY FORMAT FLAGS */
+ OLD_ARRAY_FORMAT_VERTEX = 1 << OLD_ARRAY_VERTEX, // mandatory
+ OLD_ARRAY_FORMAT_NORMAL = 1 << OLD_ARRAY_NORMAL,
+ OLD_ARRAY_FORMAT_TANGENT = 1 << OLD_ARRAY_TANGENT,
+ OLD_ARRAY_FORMAT_COLOR = 1 << OLD_ARRAY_COLOR,
+ OLD_ARRAY_FORMAT_TEX_UV = 1 << OLD_ARRAY_TEX_UV,
+ OLD_ARRAY_FORMAT_TEX_UV2 = 1 << OLD_ARRAY_TEX_UV2,
+ OLD_ARRAY_FORMAT_BONES = 1 << OLD_ARRAY_BONES,
+ OLD_ARRAY_FORMAT_WEIGHTS = 1 << OLD_ARRAY_WEIGHTS,
+ OLD_ARRAY_FORMAT_INDEX = 1 << OLD_ARRAY_INDEX,
+
+ OLD_ARRAY_COMPRESS_BASE = (OLD_ARRAY_INDEX + 1),
+ OLD_ARRAY_COMPRESS_NORMAL = 1 << (OLD_ARRAY_NORMAL + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_TANGENT = 1 << (OLD_ARRAY_TANGENT + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_COLOR = 1 << (OLD_ARRAY_COLOR + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_TEX_UV = 1 << (OLD_ARRAY_TEX_UV + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_TEX_UV2 = 1 << (OLD_ARRAY_TEX_UV2 + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_INDEX = 1 << (OLD_ARRAY_INDEX + OLD_ARRAY_COMPRESS_BASE),
+ OLD_ARRAY_COMPRESS_DEFAULT = OLD_ARRAY_COMPRESS_NORMAL | OLD_ARRAY_COMPRESS_TANGENT | OLD_ARRAY_COMPRESS_COLOR | OLD_ARRAY_COMPRESS_TEX_UV | OLD_ARRAY_COMPRESS_TEX_UV2,
+
+ OLD_ARRAY_FLAG_USE_2D_VERTICES = OLD_ARRAY_COMPRESS_INDEX << 1,
+ OLD_ARRAY_FLAG_USE_DYNAMIC_UPDATE = OLD_ARRAY_COMPRESS_INDEX << 3,
+ };
+
+ bool vertex_16bit = p_format & ((1 << (OLD_ARRAY_VERTEX + OLD_ARRAY_COMPRESS_BASE)));
+ bool has_bones = (p_format & OLD_ARRAY_FORMAT_BONES);
+ bool bone_8 = has_bones && !(p_format & (OLD_ARRAY_COMPRESS_INDEX << 2));
+ bool weight_32 = has_bones && !(p_format & (OLD_ARRAY_COMPRESS_TEX_UV2 << 2));
print_line("convert vertex16: " + itos(vertex_16bit) + " convert bone 8 " + itos(bone_8) + " convert weight 32 " + itos(weight_32));
@@ -577,7 +633,7 @@ static Vector<uint8_t> _fix_array_compatibility(const Vector<uint8_t> &p_src, ui
return p_src;
}
- bool vertex_2d = (p_format & (Mesh::ARRAY_COMPRESS_INDEX << 1));
+ bool vertex_2d = (p_format & (OLD_ARRAY_COMPRESS_INDEX << 1));
uint32_t src_stride = p_src.size() / p_elements;
uint32_t dst_stride = src_stride + (vertex_16bit ? 4 : 0) + (bone_8 ? 4 : 0) - (weight_32 ? 8 : 0);
@@ -676,25 +732,11 @@ static Vector<uint8_t> _fix_array_compatibility(const Vector<uint8_t> &p_src, ui
return ret;
}
+#endif
bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
String sname = p_name;
- if (p_name == "blend_shape/names") {
- Vector<String> sk = p_value;
- int sz = sk.size();
- const String *r = sk.ptr();
- for (int i = 0; i < sz; i++) {
- add_blend_shape(r[i]);
- }
- return true;
- }
-
- if (p_name == "blend_shape/mode") {
- set_blend_shape_mode(BlendShapeMode(int(p_value)));
- return true;
- }
-
if (sname.begins_with("surface_")) {
int sl = sname.find("/");
if (sl == -1) {
@@ -732,6 +774,7 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
add_surface_from_arrays(PrimitiveType(int(d["primitive"])), d["arrays"], d["morph_arrays"]);
} else if (d.has("array_data")) {
+#if 0
//print_line("array data (old style");
//older format (3.x)
Vector<uint8_t> array_data = d["array_data"];
@@ -798,6 +841,7 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
}
add_surface(format, PrimitiveType(primitive), array_data, vertex_count, array_index_data, index_count, aabb, blend_shapes, bone_aabb);
+#endif
} else {
ERR_FAIL_V(false);
}
@@ -816,6 +860,28 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
return false;
}
+void ArrayMesh::_set_blend_shape_names(const PackedStringArray &p_names) {
+ ERR_FAIL_COND(surfaces.size() > 0);
+
+ blend_shapes.resize(p_names.size());
+ for (int i = 0; i < p_names.size(); i++) {
+ blend_shapes.write[i] = p_names[i];
+ }
+
+ if (mesh.is_valid()) {
+ RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size());
+ }
+}
+
+PackedStringArray ArrayMesh::_get_blend_shape_names() const {
+ PackedStringArray sarr;
+ sarr.resize(blend_shapes.size());
+ for (int i = 0; i < blend_shapes.size(); i++) {
+ sarr.write[i] = blend_shapes[i];
+ }
+ return sarr;
+}
+
Array ArrayMesh::_get_surfaces() const {
if (mesh.is_null()) {
return Array();
@@ -829,6 +895,12 @@ Array ArrayMesh::_get_surfaces() const {
data["primitive"] = surface.primitive;
data["vertex_data"] = surface.vertex_data;
data["vertex_count"] = surface.vertex_count;
+ if (surface.skin_data.size()) {
+ data["skin_data"] = surface.skin_data;
+ }
+ if (surface.attribute_data.size()) {
+ data["attribute_data"] = surface.attribute_data;
+ }
data["aabb"] = surface.aabb;
if (surface.index_count) {
data["index_data"] = surface.index_data;
@@ -853,9 +925,8 @@ Array ArrayMesh::_get_surfaces() const {
data["bone_aabbs"] = bone_aabbs;
}
- Array blend_shapes;
- for (int j = 0; j < surface.blend_shapes.size(); j++) {
- blend_shapes.push_back(surface.blend_shapes[j]);
+ if (surface.blend_shape_data.size()) {
+ data["blend_shapes"] = surface.blend_shape_data;
}
if (surfaces[i].material.is_valid()) {
@@ -872,7 +943,6 @@ Array ArrayMesh::_get_surfaces() const {
ret.push_back(data);
}
- print_line("Saving surfaces: " + itos(ret.size()));
return ret;
}
@@ -881,6 +951,7 @@ void ArrayMesh::_create_if_empty() const {
if (!mesh.is_valid()) {
mesh = RS::get_singleton()->mesh_create();
RS::get_singleton()->mesh_set_blend_shape_mode(mesh, (RS::BlendShapeMode)blend_shape_mode);
+ RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size());
}
}
@@ -902,6 +973,12 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
surface.primitive = RS::PrimitiveType(int(d["primitive"]));
surface.vertex_data = d["vertex_data"];
surface.vertex_count = d["vertex_count"];
+ if (d.has("attribute_data")) {
+ surface.attribute_data = d["attribute_data"];
+ }
+ if (d.has("skin_data")) {
+ surface.skin_data = d["skin_data"];
+ }
surface.aabb = d["aabb"];
if (d.has("index_data")) {
@@ -929,10 +1006,7 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
}
if (d.has("blend_shapes")) {
- Array blend_shapes;
- for (int j = 0; j < blend_shapes.size(); j++) {
- surface.blend_shapes.push_back(blend_shapes[j]);
- }
+ surface.blend_shape_data = d["blend_shapes"];
}
Ref<Material> material;
@@ -952,15 +1026,7 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
if (d.has("2d")) {
_2d = d["2d"];
}
- /*
- print_line("format: " + itos(surface.format));
- print_line("aabb: " + surface.aabb);
- print_line("array size: " + itos(surface.vertex_data.size()));
- print_line("vertex count: " + itos(surface.vertex_count));
- print_line("index size: " + itos(surface.index_data.size()));
- print_line("index count: " + itos(surface.index_count));
- print_line("primitive: " + itos(surface.primitive));
-*/
+
surface_data.push_back(surface);
surface_materials.push_back(material);
surface_names.push_back(name);
@@ -976,7 +1042,7 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
} else {
// if mesh does not exist (first time this is loaded, most likely),
// we can create it with a single call, which is a lot more efficient and thread friendly
- mesh = RS::get_singleton()->mesh_create_from_surfaces(surface_data);
+ mesh = RS::get_singleton()->mesh_create_from_surfaces(surface_data, blend_shapes.size());
RS::get_singleton()->mesh_set_blend_shape_mode(mesh, (RS::BlendShapeMode)blend_shape_mode);
}
@@ -988,7 +1054,6 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
s.aabb = surface_data[i].aabb;
if (i == 0) {
aabb = s.aabb;
- blend_shapes.resize(surface_data[i].blend_shapes.size());
} else {
aabb.merge_with(s.aabb);
}
@@ -1012,18 +1077,7 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const {
}
String sname = p_name;
-
- if (p_name == "blend_shape/names") {
- Vector<String> sk;
- for (int i = 0; i < blend_shapes.size(); i++) {
- sk.push_back(blend_shapes[i]);
- }
- r_ret = sk;
- return true;
- } else if (p_name == "blend_shape/mode") {
- r_ret = get_blend_shape_mode();
- return true;
- } else if (sname.begins_with("surface_")) {
+ if (sname.begins_with("surface_")) {
int sl = sname.find("/");
if (sl == -1) {
return false;
@@ -1046,11 +1100,6 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const {
return;
}
- if (blend_shapes.size()) {
- p_list->push_back(PropertyInfo(Variant::PACKED_STRING_ARRAY, "blend_shape/names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::INT, "blend_shape/mode", PROPERTY_HINT_ENUM, "Normalized,Relative"));
- }
-
for (int i = 0; i < surfaces.size(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i + 1) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
if (surfaces[i].is_2d) {
@@ -1076,7 +1125,7 @@ void ArrayMesh::_recompute_aabb() {
#ifndef _MSC_VER
#warning need to add binding to add_surface using future MeshSurfaceData object
#endif
-void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<Vector<uint8_t>> &p_blend_shapes, const Vector<AABB> &p_bone_aabb, const Vector<RS::SurfaceData::LOD> &p_lods) {
+void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods) {
_create_if_empty();
Surface s;
@@ -1096,10 +1145,12 @@ void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const
sd.aabb = p_aabb;
sd.vertex_count = p_vertex_count;
sd.vertex_data = p_array;
+ sd.attribute_data = p_attribute_array;
+ sd.skin_data = p_skin_array;
sd.index_count = p_index_count;
sd.index_data = p_index_array;
- sd.blend_shapes = p_blend_shapes;
- sd.bone_aabbs = p_bone_aabb;
+ sd.blend_shape_data = p_blend_shape_data;
+ sd.bone_aabbs = p_bone_aabbs;
sd.lods = p_lods;
RenderingServer::get_singleton()->mesh_add_surface(mesh, sd);
@@ -1117,15 +1168,17 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &
Error err = RS::get_singleton()->mesh_create_surface_data_from_arrays(&surface, (RenderingServer::PrimitiveType)p_primitive, p_arrays, p_blend_shapes, p_lods, p_flags);
ERR_FAIL_COND(err != OK);
- /* print_line("format: " + itos(surface.format));
+ /* Debug code.
+ print_line("format: " + itos(surface.format));
print_line("aabb: " + surface.aabb);
print_line("array size: " + itos(surface.vertex_data.size()));
print_line("vertex count: " + itos(surface.vertex_count));
print_line("index size: " + itos(surface.index_data.size()));
print_line("index count: " + itos(surface.index_count));
print_line("primitive: " + itos(surface.primitive));
-*/
- add_surface(surface.format, PrimitiveType(surface.primitive), surface.vertex_data, surface.vertex_count, surface.index_data, surface.index_count, surface.aabb, surface.blend_shapes, surface.bone_aabbs, surface.lods);
+ */
+
+ add_surface(surface.format, PrimitiveType(surface.primitive), surface.vertex_data, surface.attribute_data, surface.skin_data, surface.vertex_count, surface.index_data, surface.index_count, surface.aabb, surface.blend_shape_data, surface.bone_aabbs, surface.lods);
}
Array ArrayMesh::surface_get_arrays(int p_surface) const {
@@ -1161,7 +1214,10 @@ void ArrayMesh::add_blend_shape(const StringName &p_name) {
}
blend_shapes.push_back(name);
- //RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size());
+
+ if (mesh.is_valid()) {
+ RS::get_singleton()->mesh_set_blend_shape_count(mesh, blend_shapes.size());
+ }
}
int ArrayMesh::get_blend_shape_count() const {
@@ -1177,6 +1233,10 @@ void ArrayMesh::clear_blend_shapes() {
ERR_FAIL_COND_MSG(surfaces.size(), "Can't set shape key count if surfaces are already created.");
blend_shapes.clear();
+
+ if (mesh.is_valid()) {
+ RS::get_singleton()->mesh_set_blend_shape_count(mesh, 0);
+ }
}
void ArrayMesh::set_blend_shape_mode(BlendShapeMode p_mode) {
@@ -1314,7 +1374,7 @@ bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_v
struct ArrayMeshLightmapSurface {
Ref<Material> material;
- Vector<SurfaceTool::Vertex> vertices;
+ LocalVector<SurfaceTool::Vertex> vertices;
Mesh::PrimitiveType primitive;
uint32_t format;
};
@@ -1355,7 +1415,7 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
Array arrays = surface_get_arrays(i);
s.material = surface_get_material(i);
- s.vertices = SurfaceTool::create_vertex_array_from_triangle_arrays(arrays);
+ SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices);
Vector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX];
int vc = rvertices.size();
@@ -1458,29 +1518,29 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach
SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second];
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_COLOR) {
- surfaces_tools.write[surface]->add_color(v.color);
+ surfaces_tools.write[surface]->set_color(v.color);
}
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TEX_UV) {
- surfaces_tools.write[surface]->add_uv(v.uv);
+ surfaces_tools.write[surface]->set_uv(v.uv);
}
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_NORMAL) {
- surfaces_tools.write[surface]->add_normal(v.normal);
+ surfaces_tools.write[surface]->set_normal(v.normal);
}
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TANGENT) {
Plane t;
t.normal = v.tangent;
t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
- surfaces_tools.write[surface]->add_tangent(t);
+ surfaces_tools.write[surface]->set_tangent(t);
}
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_BONES) {
- surfaces_tools.write[surface]->add_bones(v.bones);
+ surfaces_tools.write[surface]->set_bones(v.bones);
}
if (lightmap_surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) {
- surfaces_tools.write[surface]->add_weights(v.weights);
+ surfaces_tools.write[surface]->set_weights(v.weights);
}
Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
- surfaces_tools.write[surface]->add_uv2(uv2);
+ surfaces_tools.write[surface]->set_uv2(uv2);
surfaces_tools.write[surface]->add_vertex(v.vertex);
}
@@ -1513,7 +1573,7 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ArrayMesh::set_blend_shape_mode);
ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ArrayMesh::get_blend_shape_mode);
- ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(ARRAY_COMPRESS_DEFAULT));
+ ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0));
ClassDB::bind_method(D_METHOD("clear_surfaces"), &ArrayMesh::clear_surfaces);
ClassDB::bind_method(D_METHOD("surface_update_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_region);
ClassDB::bind_method(D_METHOD("surface_get_array_len", "surf_idx"), &ArrayMesh::surface_get_array_len);
@@ -1536,36 +1596,16 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &ArrayMesh::set_custom_aabb);
ClassDB::bind_method(D_METHOD("get_custom_aabb"), &ArrayMesh::get_custom_aabb);
+ ClassDB::bind_method(D_METHOD("_set_blend_shape_names", "blend_shape_names"), &ArrayMesh::_set_blend_shape_names);
+ ClassDB::bind_method(D_METHOD("_get_blend_shape_names"), &ArrayMesh::_get_blend_shape_names);
+
ClassDB::bind_method(D_METHOD("_set_surfaces", "surfaces"), &ArrayMesh::_set_surfaces);
ClassDB::bind_method(D_METHOD("_get_surfaces"), &ArrayMesh::_get_surfaces);
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_shape_mode", PROPERTY_HINT_ENUM, "Normalized,Relative"), "set_blend_shape_mode", "get_blend_shape_mode");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb");
-
- BIND_CONSTANT(NO_INDEX_ARRAY);
- BIND_CONSTANT(ARRAY_WEIGHTS_SIZE);
-
- BIND_ENUM_CONSTANT(ARRAY_VERTEX);
- BIND_ENUM_CONSTANT(ARRAY_NORMAL);
- BIND_ENUM_CONSTANT(ARRAY_TANGENT);
- BIND_ENUM_CONSTANT(ARRAY_COLOR);
- BIND_ENUM_CONSTANT(ARRAY_TEX_UV);
- BIND_ENUM_CONSTANT(ARRAY_TEX_UV2);
- BIND_ENUM_CONSTANT(ARRAY_BONES);
- BIND_ENUM_CONSTANT(ARRAY_WEIGHTS);
- BIND_ENUM_CONSTANT(ARRAY_INDEX);
- BIND_ENUM_CONSTANT(ARRAY_MAX);
-
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_VERTEX);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_NORMAL);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TANGENT);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_COLOR);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_TEX_UV2);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_BONES);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_WEIGHTS);
- BIND_ENUM_CONSTANT(ARRAY_FORMAT_INDEX);
}
void ArrayMesh::reload_from_file() {
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index b0a30a5627..b7f60bf814 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,24 @@ protected:
public:
enum {
-
NO_INDEX_ARRAY = RenderingServer::NO_INDEX_ARRAY,
ARRAY_WEIGHTS_SIZE = RenderingServer::ARRAY_WEIGHTS_SIZE
};
-
+ enum BlendShapeMode {
+ BLEND_SHAPE_MODE_NORMALIZED = RS::BLEND_SHAPE_MODE_NORMALIZED,
+ BLEND_SHAPE_MODE_RELATIVE = RS::BLEND_SHAPE_MODE_RELATIVE,
+ };
enum ArrayType {
-
ARRAY_VERTEX = RenderingServer::ARRAY_VERTEX,
ARRAY_NORMAL = RenderingServer::ARRAY_NORMAL,
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 +75,47 @@ public:
};
+ enum ArrayCustomFormat {
+ ARRAY_CUSTOM_RGBA8_UNORM,
+ ARRAY_CUSTOM_RGBA8_SNORM,
+ ARRAY_CUSTOM_RG_HALF,
+ ARRAY_CUSTOM_RGBA_HALF,
+ ARRAY_CUSTOM_R_FLOAT,
+ ARRAY_CUSTOM_RG_FLOAT,
+ ARRAY_CUSTOM_RGB_FLOAT,
+ ARRAY_CUSTOM_RGBA_FLOAT,
+ ARRAY_CUSTOM_MAX
+ };
+
enum ArrayFormat {
- /* ARRAY FORMAT FLAGS */
- ARRAY_FORMAT_VERTEX = 1 << ARRAY_VERTEX, // mandatory
- ARRAY_FORMAT_NORMAL = 1 << ARRAY_NORMAL,
- ARRAY_FORMAT_TANGENT = 1 << ARRAY_TANGENT,
- ARRAY_FORMAT_COLOR = 1 << ARRAY_COLOR,
- ARRAY_FORMAT_TEX_UV = 1 << ARRAY_TEX_UV,
- ARRAY_FORMAT_TEX_UV2 = 1 << ARRAY_TEX_UV2,
- ARRAY_FORMAT_BONES = 1 << ARRAY_BONES,
- ARRAY_FORMAT_WEIGHTS = 1 << ARRAY_WEIGHTS,
- ARRAY_FORMAT_INDEX = 1 << ARRAY_INDEX,
-
- ARRAY_COMPRESS_BASE = (ARRAY_INDEX + 1),
- ARRAY_COMPRESS_NORMAL = 1 << (ARRAY_NORMAL + ARRAY_COMPRESS_BASE),
- ARRAY_COMPRESS_TANGENT = 1 << (ARRAY_TANGENT + ARRAY_COMPRESS_BASE),
- ARRAY_COMPRESS_COLOR = 1 << (ARRAY_COLOR + ARRAY_COMPRESS_BASE),
- ARRAY_COMPRESS_TEX_UV = 1 << (ARRAY_TEX_UV + ARRAY_COMPRESS_BASE),
- ARRAY_COMPRESS_TEX_UV2 = 1 << (ARRAY_TEX_UV2 + ARRAY_COMPRESS_BASE),
- ARRAY_COMPRESS_INDEX = 1 << (ARRAY_INDEX + ARRAY_COMPRESS_BASE),
-
- ARRAY_FLAG_USE_2D_VERTICES = ARRAY_COMPRESS_INDEX << 1,
- ARRAY_FLAG_USE_DYNAMIC_UPDATE = ARRAY_COMPRESS_INDEX << 3,
-
- ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2
+ ARRAY_FORMAT_VERTEX = RS::ARRAY_FORMAT_VERTEX,
+ ARRAY_FORMAT_NORMAL = RS::ARRAY_FORMAT_NORMAL,
+ ARRAY_FORMAT_TANGENT = RS::ARRAY_FORMAT_TANGENT,
+ ARRAY_FORMAT_COLOR = RS::ARRAY_FORMAT_COLOR,
+ ARRAY_FORMAT_TEX_UV = RS::ARRAY_FORMAT_TEX_UV,
+ ARRAY_FORMAT_TEX_UV2 = RS::ARRAY_FORMAT_TEX_UV2,
+ ARRAY_FORMAT_CUSTOM0 = RS::ARRAY_FORMAT_CUSTOM0,
+ ARRAY_FORMAT_CUSTOM1 = RS::ARRAY_FORMAT_CUSTOM1,
+ ARRAY_FORMAT_CUSTOM2 = RS::ARRAY_FORMAT_CUSTOM2,
+ ARRAY_FORMAT_CUSTOM3 = RS::ARRAY_FORMAT_CUSTOM3,
+ ARRAY_FORMAT_BONES = RS::ARRAY_FORMAT_BONES,
+ ARRAY_FORMAT_WEIGHTS = RS::ARRAY_FORMAT_WEIGHTS,
+ ARRAY_FORMAT_INDEX = RS::ARRAY_FORMAT_INDEX,
+
+ ARRAY_FORMAT_BLEND_SHAPE_MASK = RS::ARRAY_FORMAT_BLEND_SHAPE_MASK,
+
+ ARRAY_FORMAT_CUSTOM_BASE = RS::ARRAY_FORMAT_CUSTOM_BASE,
+ ARRAY_FORMAT_CUSTOM0_SHIFT = RS::ARRAY_FORMAT_CUSTOM0_SHIFT,
+ ARRAY_FORMAT_CUSTOM1_SHIFT = RS::ARRAY_FORMAT_CUSTOM1_SHIFT,
+ ARRAY_FORMAT_CUSTOM2_SHIFT = RS::ARRAY_FORMAT_CUSTOM2_SHIFT,
+ ARRAY_FORMAT_CUSTOM3_SHIFT = RS::ARRAY_FORMAT_CUSTOM3_SHIFT,
+
+ ARRAY_FORMAT_CUSTOM_MASK = RS::ARRAY_FORMAT_CUSTOM_MASK,
+ ARRAY_COMPRESS_FLAGS_BASE = RS::ARRAY_COMPRESS_FLAGS_BASE,
+
+ ARRAY_FLAG_USE_2D_VERTICES = RS::ARRAY_FLAG_USE_2D_VERTICES,
+ ARRAY_FLAG_USE_DYNAMIC_UPDATE = RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE,
+ ARRAY_FLAG_USE_8_BONE_WEIGHTS = RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS,
};
@@ -106,12 +128,6 @@ public:
PRIMITIVE_MAX = RenderingServer::PRIMITIVE_MAX,
};
- enum BlendShapeMode {
-
- BLEND_SHAPE_MODE_NORMALIZED = RS::BLEND_SHAPE_MODE_NORMALIZED,
- BLEND_SHAPE_MODE_RELATIVE = RS::BLEND_SHAPE_MODE_RELATIVE,
- };
-
virtual int get_surface_count() const = 0;
virtual int surface_get_array_len(int p_idx) const = 0;
virtual int surface_get_array_index_len(int p_idx) const = 0;
@@ -155,6 +171,9 @@ class ArrayMesh : public Mesh {
GDCLASS(ArrayMesh, Mesh);
RES_BASE_EXTENSION("mesh");
+ PackedStringArray _get_blend_shape_names() const;
+ void _set_blend_shape_names(const PackedStringArray &p_names);
+
Array _get_surfaces() const;
void _set_surfaces(const Array &p_data);
@@ -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>(), 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;
@@ -247,6 +266,7 @@ public:
VARIANT_ENUM_CAST(Mesh::ArrayType);
VARIANT_ENUM_CAST(Mesh::ArrayFormat);
+VARIANT_ENUM_CAST(Mesh::ArrayCustomFormat);
VARIANT_ENUM_CAST(Mesh::PrimitiveType);
VARIANT_ENUM_CAST(Mesh::BlendShapeMode);
diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h
index 450e2c16e3..0d5fb3005b 100644
--- a/scene/resources/mesh_library.h
+++ b/scene/resources/mesh_library.h
@@ -31,8 +31,8 @@
#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"
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 4bbfa8965a..e2f96c54cb 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -98,6 +98,9 @@ void ParticlesMaterial::init_shaders() {
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";
@@ -247,6 +254,11 @@ void ParticlesMaterial::_update_shader() {
code += "uniform sampler2D anim_offset_texture;\n";
}
+ if (collision_enabled) {
+ code += "uniform float collision_friction;\n";
+ code += "uniform float collision_bounce;\n";
+ }
+
//need a random function
code += "\n\n";
code += "float rand_from_seed(inout uint seed) {\n";
@@ -317,7 +329,7 @@ void ParticlesMaterial::_update_shader() {
code += " float tex_linear_velocity = 0.0;\n";
}
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n";
code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n";
@@ -365,7 +377,7 @@ void ParticlesMaterial::_update_shader() {
code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n";
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
code += " mat2 rotm;";
code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n";
code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n";
@@ -386,7 +398,7 @@ void ParticlesMaterial::_update_shader() {
code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n";
code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
code += " VELOCITY.z = 0.0;\n";
code += " TRANSFORM[3].z = 0.0;\n";
}
@@ -401,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 {
@@ -459,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";
@@ -469,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";
@@ -546,8 +562,8 @@ void ParticlesMaterial::_update_shader() {
}
code += "\n";
- if (flags[FLAG_DISABLE_Z]) {
- if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) {
code += " if (length(VELOCITY) > 0.0) {\n";
code += " TRANSFORM[1].xyz = normalize(VELOCITY);\n";
code += " } else {\n";
@@ -563,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";
@@ -582,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";
}
}
@@ -595,10 +611,17 @@ void ParticlesMaterial::_update_shader() {
code += " TRANSFORM[0].xyz *= base_scale;\n";
code += " TRANSFORM[1].xyz *= base_scale;\n";
code += " TRANSFORM[2].xyz *= base_scale;\n";
- if (flags[FLAG_DISABLE_Z]) {
+ if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
code += " VELOCITY.z = 0.0;\n";
code += " TRANSFORM[3].z = 0.0;\n";
}
+ 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) {
@@ -609,6 +632,7 @@ void ParticlesMaterial::_update_shader() {
} 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
@@ -892,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) {
@@ -1032,7 +1056,7 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}
- if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
+ if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
property.usage = 0;
}
}
@@ -1072,6 +1096,51 @@ 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 {
return Shader::MODE_PARTICLES;
}
@@ -1101,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);
@@ -1143,6 +1212,21 @@ void ParticlesMaterial::_bind_methods() {
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");
@@ -1154,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");
@@ -1221,6 +1305,14 @@ void ParticlesMaterial::_bind_methods() {
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);
@@ -1235,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);
@@ -1283,12 +1375,18 @@ ParticlesMaterial::ParticlesMaterial() :
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 fa8858f67f..7e8f05b706 100644
--- a/scene/resources/particles_material.h
+++ b/scene/resources/particles_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 PARTICLES_MATERIAL_H
@@ -37,11 +37,7 @@
/*
TODO:
-Path following
-*Manual emission
--Sub Emitters
--Attractors
-Emitter positions deformable by bones
--Collision
-Proper trails
*/
@@ -65,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 {
@@ -94,11 +90,14 @@ private:
struct {
uint32_t texture_mask : 16;
uint32_t texture_color : 1;
- uint32_t flags : 4;
+ uint32_t particle_flags : 4;
uint32_t emission_shape : 2;
uint32_t invalid_key : 1;
uint32_t has_emission_color : 1;
uint32_t sub_emitter : 2;
+ uint32_t attractor_enabled : 1;
+ uint32_t collision_enabled : 1;
+ uint32_t collision_scale : 1;
};
uint32_t key;
@@ -125,9 +124,9 @@ private:
mk.texture_mask |= (1 << i);
}
}
- for (int i = 0; i < FLAG_MAX; i++) {
- if (flags[i]) {
- mk.flags |= (1 << i);
+ for (int i = 0; i < PARTICLE_FLAG_MAX; i++) {
+ if (particle_flags[i]) {
+ mk.particle_flags |= (1 << i);
}
}
@@ -135,6 +134,9 @@ private:
mk.emission_shape = emission_shape;
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;
}
@@ -201,6 +203,9 @@ private:
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;
@@ -222,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;
@@ -244,6 +249,12 @@ private:
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;
@@ -273,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);
@@ -298,6 +309,21 @@ public:
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();
@@ -323,7 +349,7 @@ public:
};
VARIANT_ENUM_CAST(ParticlesMaterial::Parameter)
-VARIANT_ENUM_CAST(ParticlesMaterial::Flags)
+VARIANT_ENUM_CAST(ParticlesMaterial::ParticleFlags)
VARIANT_ENUM_CAST(ParticlesMaterial::EmissionShape)
VARIANT_ENUM_CAST(ParticlesMaterial::SubEmitterMode)
diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp
index 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 39df4c22f9..1705fb0f55 100644
--- a/scene/resources/ray_shape_3d.cpp
+++ b/scene/resources/ray_shape_3d.cpp
@@ -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/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index d4d8018d43..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.
@@ -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 92f0353abf..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);
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 eb9607e3a6..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;
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 fd33387df6..64e0a701b7 100644
--- a/scene/resources/sphere_shape_3d.cpp
+++ b/scene/resources/sphere_shape_3d.cpp
@@ -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/style_box.cpp b/scene/resources/style_box.cpp
index 7328fbdb10..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) {
@@ -288,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);
@@ -317,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);
@@ -425,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;
@@ -911,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..50308d641a 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -30,11 +30,12 @@
#include "surface_tool.h"
-#include "core/method_bind_ext.gen.inc"
-
#define _VERTEX_SNAP 0.0001
#define EQ_VERTEX_DIST 0.00001
+SurfaceTool::OptimizeVertexCacheFunc SurfaceTool::optimize_vertex_cache_func = nullptr;
+SurfaceTool::SimplifyFunc SurfaceTool::simplify_func = nullptr;
+
bool SurfaceTool::Vertex::operator==(const Vertex &p_vertex) const {
if (vertex != p_vertex.vertex) {
return false;
@@ -76,6 +77,16 @@ bool SurfaceTool::Vertex::operator==(const Vertex &p_vertex) const {
}
}
+ for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) {
+ if (custom[i] != p_vertex.custom[i]) {
+ return false;
+ }
+ }
+
+ if (smooth_group != p_vertex.smooth_group) {
+ return false;
+ }
+
return true;
}
@@ -89,6 +100,8 @@ uint32_t SurfaceTool::VertexHasher::hash(const Vertex &p_vtx) {
h = hash_djb2_buffer((const uint8_t *)&p_vtx.color, sizeof(real_t) * 4, h);
h = hash_djb2_buffer((const uint8_t *)p_vtx.bones.ptr(), p_vtx.bones.size() * sizeof(int), h);
h = hash_djb2_buffer((const uint8_t *)p_vtx.weights.ptr(), p_vtx.weights.size() * sizeof(float), h);
+ h = hash_djb2_buffer((const uint8_t *)&p_vtx.custom[0], sizeof(Color) * RS::ARRAY_CUSTOM_COUNT, h);
+ h = hash_djb2_one_32(p_vtx.smooth_group, h);
return h;
}
@@ -113,8 +126,13 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) {
vtx.bones = last_bones;
vtx.tangent = last_tangent.normal;
vtx.binormal = last_normal.cross(last_tangent.normal).normalized() * last_tangent.d;
+ vtx.smooth_group = last_smooth_group;
+
+ for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) {
+ vtx.custom[i] = last_custom[i];
+ }
- const int expected_vertices = 4;
+ const int expected_vertices = skin_weights == SKIN_8_WEIGHTS ? 8 : 4;
if ((format & Mesh::ARRAY_FORMAT_WEIGHTS || format & Mesh::ARRAY_FORMAT_BONES) && (vtx.weights.size() != expected_vertices || vtx.bones.size() != expected_vertices)) {
//ensure vertices are the expected amount
@@ -165,7 +183,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 +192,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 +201,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 +209,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 +217,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,29 +225,45 @@ void SurfaceTool::add_uv2(const Vector2 &p_uv2) {
last_uv2 = p_uv2;
}
-void SurfaceTool::add_bones(const Vector<int> &p_bones) {
+void SurfaceTool::set_custom(int p_index, const Color &p_custom) {
+ ERR_FAIL_INDEX(p_index, RS::ARRAY_CUSTOM_COUNT);
+ ERR_FAIL_COND(!begun);
+ ERR_FAIL_COND(last_custom_format[p_index] == CUSTOM_MAX);
+ static const uint32_t mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 };
+ static const uint32_t shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT };
+ ERR_FAIL_COND(!first && !(format & mask[p_index]));
+
+ if (first) {
+ format |= mask[p_index];
+ format |= last_custom_format[p_index] << shift[p_index];
+ }
+ last_custom[p_index] = p_custom;
+}
+
+void SurfaceTool::set_bones(const Vector<int> &p_bones) {
ERR_FAIL_COND(!begun);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_BONES));
format |= Mesh::ARRAY_FORMAT_BONES;
+ if (skin_weights == SKIN_8_WEIGHTS) {
+ format |= Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS;
+ }
last_bones = p_bones;
}
-void SurfaceTool::add_weights(const Vector<float> &p_weights) {
+void SurfaceTool::set_weights(const Vector<float> &p_weights) {
ERR_FAIL_COND(!begun);
ERR_FAIL_COND(!first && !(format & Mesh::ARRAY_FORMAT_WEIGHTS));
format |= Mesh::ARRAY_FORMAT_WEIGHTS;
+ if (skin_weights == SKIN_8_WEIGHTS) {
+ format |= Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS;
+ }
last_weights = p_weights;
}
-void SurfaceTool::add_smooth_group(bool p_smooth) {
- ERR_FAIL_COND(!begun);
- if (index_array.size()) {
- smooth_groups[index_array.size()] = p_smooth;
- } else {
- smooth_groups[vertex_array.size()] = p_smooth;
- }
+void SurfaceTool::set_smooth_group(uint32_t p_group) {
+ last_smooth_group = p_group;
}
void SurfaceTool::add_triangle_fan(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<Color> &p_colors, const Vector<Vector2> &p_uv2s, const Vector<Vector3> &p_normals, const Vector<Plane> &p_tangents) {
@@ -240,15 +274,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]); \
}
@@ -286,9 +320,8 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len);
Vector3 *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
switch (i) {
case Mesh::ARRAY_VERTEX: {
@@ -310,9 +343,8 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len);
Vector2 *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
switch (i) {
case Mesh::ARRAY_TEX_UV: {
@@ -331,9 +363,8 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len * 4);
float *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx += 4) {
- const Vertex &v = E->get();
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
w[idx + 0] = v.tangent.x;
w[idx + 1] = v.tangent.y;
@@ -352,27 +383,165 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len);
Color *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
w[idx] = v.color;
}
a[i] = array;
} break;
+ case Mesh::ARRAY_CUSTOM0:
+ case Mesh::ARRAY_CUSTOM1:
+ case Mesh::ARRAY_CUSTOM2:
+ case Mesh::ARRAY_CUSTOM3: {
+ int fmt = i - Mesh::ARRAY_CUSTOM0;
+ switch (last_custom_format[fmt]) {
+ case CUSTOM_RGBA8_UNORM: {
+ Vector<uint8_t> array;
+ array.resize(varr_len * 4);
+ uint8_t *w = array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 4 + 0] = CLAMP(int32_t(c.r * 255.0), 0, 255);
+ w[idx * 4 + 1] = CLAMP(int32_t(c.g * 255.0), 0, 255);
+ w[idx * 4 + 2] = CLAMP(int32_t(c.b * 255.0), 0, 255);
+ w[idx * 4 + 3] = CLAMP(int32_t(c.a * 255.0), 0, 255);
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RGBA8_SNORM: {
+ Vector<uint8_t> array;
+ array.resize(varr_len * 4);
+ uint8_t *w = array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 4 + 0] = uint8_t(int8_t(CLAMP(int32_t(c.r * 127.0), -128, 127)));
+ w[idx * 4 + 1] = uint8_t(int8_t(CLAMP(int32_t(c.g * 127.0), -128, 127)));
+ w[idx * 4 + 2] = uint8_t(int8_t(CLAMP(int32_t(c.b * 127.0), -128, 127)));
+ w[idx * 4 + 3] = uint8_t(int8_t(CLAMP(int32_t(c.a * 127.0), -128, 127)));
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RG_HALF: {
+ Vector<uint8_t> array;
+ array.resize(varr_len * 4);
+ uint16_t *w = (uint16_t *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 2 + 0] = Math::make_half_float(c.r);
+ w[idx * 2 + 1] = Math::make_half_float(c.g);
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RGBA_HALF: {
+ Vector<uint8_t> array;
+ array.resize(varr_len * 8);
+ uint16_t *w = (uint16_t *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 4 + 0] = Math::make_half_float(c.r);
+ w[idx * 4 + 1] = Math::make_half_float(c.g);
+ w[idx * 4 + 2] = Math::make_half_float(c.b);
+ w[idx * 4 + 3] = Math::make_half_float(c.a);
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_R_FLOAT: {
+ Vector<float> array;
+ array.resize(varr_len);
+ float *w = (float *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx] = c.r;
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RG_FLOAT: {
+ Vector<float> array;
+ array.resize(varr_len * 2);
+ float *w = (float *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 2 + 0] = c.r;
+ w[idx * 2 + 1] = c.g;
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RGB_FLOAT: {
+ Vector<float> array;
+ array.resize(varr_len * 3);
+ float *w = (float *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 3 + 0] = c.r;
+ w[idx * 3 + 1] = c.g;
+ w[idx * 3 + 2] = c.b;
+ }
+
+ a[i] = array;
+ } break;
+ case CUSTOM_RGBA_FLOAT: {
+ Vector<float> array;
+ array.resize(varr_len * 4);
+ float *w = (float *)array.ptrw();
+
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ const Color &c = v.custom[idx];
+ w[idx * 4 + 0] = c.r;
+ w[idx * 4 + 1] = c.g;
+ w[idx * 4 + 2] = c.b;
+ w[idx * 4 + 3] = c.a;
+ }
+
+ a[i] = array;
+ } break;
+ default: {
+ } //unreachable but compiler warning anyway
+ }
+ } break;
case Mesh::ARRAY_BONES: {
+ int count = skin_weights == SKIN_8_WEIGHTS ? 8 : 4;
Vector<int> array;
- array.resize(varr_len * 4);
+ array.resize(varr_len * count);
int *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx += 4) {
- const Vertex &v = E->get();
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
- ERR_CONTINUE(v.bones.size() != 4);
+ ERR_CONTINUE(v.bones.size() != count);
- for (int j = 0; j < 4; j++) {
- w[idx + j] = v.bones[j];
+ for (int j = 0; j < count; j++) {
+ w[idx * count + j] = v.bones[j];
}
}
@@ -381,16 +550,18 @@ Array SurfaceTool::commit_to_arrays() {
} break;
case Mesh::ARRAY_WEIGHTS: {
Vector<float> array;
- array.resize(varr_len * 4);
+ int count = skin_weights == SKIN_8_WEIGHTS ? 8 : 4;
+
+ array.resize(varr_len * count);
float *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx += 4) {
- const Vertex &v = E->get();
- ERR_CONTINUE(v.weights.size() != 4);
+ for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
+ const Vertex &v = vertex_array[idx];
+
+ ERR_CONTINUE(v.weights.size() != count);
- for (int j = 0; j < 4; j++) {
- w[idx + j] = v.weights[j];
+ for (int j = 0; j < count; j++) {
+ w[idx * count + j] = v.weights[j];
}
}
@@ -404,9 +575,8 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(index_array.size());
int *w = array.ptrw();
- int idx = 0;
- for (List<int>::Element *E = index_array.front(); E; E = E->next(), idx++) {
- w[idx] = E->get();
+ for (uint32_t idx = 0; idx < index_array.size(); idx++) {
+ w[idx] = index_array[idx];
}
a[i] = array;
@@ -453,15 +623,16 @@ void SurfaceTool::index() {
}
HashMap<Vertex, int, VertexHasher> indices;
- List<Vertex> new_vertices;
+ LocalVector<Vertex> old_vertex_array = vertex_array;
+ vertex_array.clear();
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) {
- int *idxptr = indices.getptr(E->get());
+ for (uint32_t i = 0; i < old_vertex_array.size(); i++) {
+ int *idxptr = indices.getptr(old_vertex_array[i]);
int idx;
if (!idxptr) {
idx = indices.size();
- new_vertices.push_back(E->get());
- indices[E->get()] = idx;
+ vertex_array.push_back(old_vertex_array[i]);
+ indices[old_vertex_array[i]] = idx;
} else {
idx = *idxptr;
}
@@ -469,9 +640,6 @@ void SurfaceTool::index() {
index_array.push_back(idx);
}
- vertex_array.clear();
- vertex_array = new_vertices;
-
format |= Mesh::ARRAY_FORMAT_INDEX;
}
@@ -479,29 +647,26 @@ void SurfaceTool::deindex() {
if (index_array.size() == 0) {
return; //nothing to deindex
}
- Vector<Vertex> varr;
- varr.resize(vertex_array.size());
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) {
- varr.write[idx++] = E->get();
- }
+
+ LocalVector<Vertex> old_vertex_array = vertex_array;
vertex_array.clear();
- for (List<int>::Element *E = index_array.front(); E; E = E->next()) {
- ERR_FAIL_INDEX(E->get(), varr.size());
- vertex_array.push_back(varr[E->get()]);
+ for (uint32_t i = 0; i < index_array.size(); i++) {
+ uint32_t index = index_array[i];
+ ERR_FAIL_COND(index >= old_vertex_array.size());
+ vertex_array.push_back(old_vertex_array[index]);
}
format &= ~Mesh::ARRAY_FORMAT_INDEX;
index_array.clear();
}
-void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, List<Vertex> *r_vertex, List<int> *r_index, int &lformat) {
+void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat) {
Array arr = p_existing->surface_get_arrays(p_surface);
ERR_FAIL_COND(arr.size() != RS::ARRAY_MAX);
_create_list_from_arrays(arr, r_vertex, r_index, lformat);
}
-Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays) {
- Vector<SurfaceTool::Vertex> ret;
+void SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<SurfaceTool::Vertex> &ret, uint32_t *r_format) {
+ ret.clear();
Vector<Vector3> varr = p_arrays[RS::ARRAY_VERTEX];
Vector<Vector3> narr = p_arrays[RS::ARRAY_NORMAL];
@@ -511,10 +676,14 @@ Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_array
Vector<Vector2> uv2arr = p_arrays[RS::ARRAY_TEX_UV2];
Vector<int> barr = p_arrays[RS::ARRAY_BONES];
Vector<float> warr = p_arrays[RS::ARRAY_WEIGHTS];
+ Vector<float> custom_float[RS::ARRAY_CUSTOM_COUNT];
int vc = varr.size();
if (vc == 0) {
- return ret;
+ if (r_format) {
+ *r_format = 0;
+ }
+ return;
}
int lformat = 0;
@@ -536,100 +705,40 @@ Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_array
if (uv2arr.size()) {
lformat |= RS::ARRAY_FORMAT_TEX_UV2;
}
- if (barr.size()) {
+ int wcount = 0;
+ if (barr.size() && warr.size()) {
lformat |= RS::ARRAY_FORMAT_BONES;
- }
- if (warr.size()) {
lformat |= RS::ARRAY_FORMAT_WEIGHTS;
- }
- for (int i = 0; i < vc; i++) {
- Vertex v;
- if (lformat & RS::ARRAY_FORMAT_VERTEX) {
- v.vertex = varr[i];
- }
- if (lformat & RS::ARRAY_FORMAT_NORMAL) {
- v.normal = narr[i];
- }
- if (lformat & RS::ARRAY_FORMAT_TANGENT) {
- Plane p(tarr[i * 4 + 0], tarr[i * 4 + 1], tarr[i * 4 + 2], tarr[i * 4 + 3]);
- v.tangent = p.normal;
- v.binormal = p.normal.cross(v.tangent).normalized() * p.d;
- }
- if (lformat & RS::ARRAY_FORMAT_COLOR) {
- v.color = carr[i];
- }
- if (lformat & RS::ARRAY_FORMAT_TEX_UV) {
- v.uv = uvarr[i];
- }
- if (lformat & RS::ARRAY_FORMAT_TEX_UV2) {
- v.uv2 = uv2arr[i];
- }
- 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;
+ wcount = barr.size() / varr.size();
+ if (wcount == 8) {
+ lformat |= RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS;
}
- if (lformat & RS::ARRAY_FORMAT_WEIGHTS) {
- Vector<float> w;
- w.resize(4);
- w.write[0] = warr[i * 4 + 0];
- w.write[1] = warr[i * 4 + 1];
- w.write[2] = warr[i * 4 + 2];
- w.write[3] = warr[i * 4 + 3];
- v.weights = w;
- }
-
- ret.push_back(v);
- }
-
- return ret;
-}
-
-void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, int &lformat) {
- Vector<Vector3> varr = arr[RS::ARRAY_VERTEX];
- Vector<Vector3> narr = arr[RS::ARRAY_NORMAL];
- Vector<float> tarr = arr[RS::ARRAY_TANGENT];
- Vector<Color> carr = arr[RS::ARRAY_COLOR];
- Vector<Vector2> uvarr = arr[RS::ARRAY_TEX_UV];
- Vector<Vector2> uv2arr = arr[RS::ARRAY_TEX_UV2];
- Vector<int> barr = arr[RS::ARRAY_BONES];
- Vector<float> warr = arr[RS::ARRAY_WEIGHTS];
-
- int vc = varr.size();
- if (vc == 0) {
- return;
}
- lformat = 0;
- if (varr.size()) {
- lformat |= RS::ARRAY_FORMAT_VERTEX;
- }
- if (narr.size()) {
- lformat |= RS::ARRAY_FORMAT_NORMAL;
- }
- if (tarr.size()) {
- lformat |= RS::ARRAY_FORMAT_TANGENT;
- }
- if (carr.size()) {
- lformat |= RS::ARRAY_FORMAT_COLOR;
- }
- if (uvarr.size()) {
- lformat |= RS::ARRAY_FORMAT_TEX_UV;
- }
- if (uv2arr.size()) {
- lformat |= RS::ARRAY_FORMAT_TEX_UV2;
- }
- if (barr.size()) {
- lformat |= RS::ARRAY_FORMAT_BONES;
- }
if (warr.size()) {
lformat |= RS::ARRAY_FORMAT_WEIGHTS;
}
+ static const uint32_t custom_mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 };
+ static const uint32_t custom_shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT };
+
+ for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) {
+ ERR_CONTINUE_MSG(p_arrays[RS::ARRAY_CUSTOM0 + i].get_type() == Variant::PACKED_BYTE_ARRAY, "Extracting Byte/Half formats is not supported");
+ if (p_arrays[RS::ARRAY_CUSTOM0 + i].get_type() == Variant::PACKED_FLOAT32_ARRAY) {
+ lformat |= custom_mask[i];
+ custom_float[i] = p_arrays[RS::ARRAY_CUSTOM0 + i];
+ int fmt = custom_float[i].size() / varr.size();
+ if (fmt == 1) {
+ lformat |= CUSTOM_R_FLOAT << custom_shift[i];
+ } else if (fmt == 2) {
+ lformat |= CUSTOM_RG_FLOAT << custom_shift[i];
+ } else if (fmt == 3) {
+ lformat |= CUSTOM_RGB_FLOAT << custom_shift[i];
+ } else if (fmt == 4) {
+ lformat |= CUSTOM_RGBA_FLOAT << custom_shift[i];
+ }
+ }
+ }
for (int i = 0; i < vc; i++) {
Vertex v;
@@ -655,27 +764,44 @@ void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, Li
}
if (lformat & RS::ARRAY_FORMAT_BONES) {
Vector<int> b;
- b.resize(4);
- b.write[0] = barr[i * 4 + 0];
- b.write[1] = barr[i * 4 + 1];
- b.write[2] = barr[i * 4 + 2];
- b.write[3] = barr[i * 4 + 3];
+ b.resize(wcount);
+ for (int j = 0; j < wcount; j++) {
+ b.write[j] = barr[i * wcount + j];
+ }
v.bones = b;
}
if (lformat & RS::ARRAY_FORMAT_WEIGHTS) {
Vector<float> w;
- w.resize(4);
- w.write[0] = warr[i * 4 + 0];
- w.write[1] = warr[i * 4 + 1];
- w.write[2] = warr[i * 4 + 2];
- w.write[3] = warr[i * 4 + 3];
+ w.resize(wcount);
+ for (int j = 0; j < wcount; j++) {
+ w.write[j] = warr[i * wcount + j];
+ }
v.weights = w;
}
- r_vertex->push_back(v);
+ for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) {
+ if (lformat & custom_mask[j]) {
+ int cc = custom_float[j].size() / varr.size();
+ for (int k = 0; k < cc; k++) {
+ v.custom[j][k] = custom_float[j][i * cc + k];
+ }
+ }
+ }
+
+ ret.push_back(v);
}
+ if (r_format) {
+ *r_format = lformat;
+ }
+}
+
+void SurfaceTool::_create_list_from_arrays(Array arr, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat) {
+ create_vertex_array_from_triangle_arrays(arr, *r_vertex, &lformat);
+ ERR_FAIL_COND(r_vertex->size() == 0);
+
//indices
+ r_index->clear();
Vector<int> idx = arr[RS::ARRAY_INDEX];
int is = idx.size();
@@ -727,15 +853,15 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const
format = 0;
}
- int nformat;
- List<Vertex> nvertices;
- List<int> nindices;
+ uint32_t nformat;
+ LocalVector<Vertex> nvertices;
+ LocalVector<int> nindices;
_create_list(p_existing, p_surface, &nvertices, &nindices, nformat);
format |= nformat;
int vfrom = vertex_array.size();
- for (List<Vertex>::Element *E = nvertices.front(); E; E = E->next()) {
- Vertex v = E->get();
+ for (uint32_t vi = 0; vi < nvertices.size(); vi++) {
+ Vertex v = nvertices[vi];
v.vertex = p_xform.xform(v.vertex);
if (nformat & RS::ARRAY_FORMAT_NORMAL) {
v.normal = p_xform.basis.xform(v.normal);
@@ -748,8 +874,8 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const
vertex_array.push_back(v);
}
- for (List<int>::Element *E = nindices.front(); E; E = E->next()) {
- int dst_index = E->get() + vfrom;
+ for (uint32_t i = 0; i < nindices.size(); i++) {
+ int dst_index = nindices[i] + vfrom;
index_array.push_back(dst_index);
}
if (index_array.size() % 3) {
@@ -760,18 +886,18 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const
//mikktspace callbacks
namespace {
struct TangentGenerationContextUserData {
- Vector<List<SurfaceTool::Vertex>::Element *> vertices;
- Vector<List<int>::Element *> indices;
+ LocalVector<SurfaceTool::Vertex> *vertices;
+ LocalVector<int> *indices;
};
} // namespace
int SurfaceTool::mikktGetNumFaces(const SMikkTSpaceContext *pContext) {
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
- if (triangle_data.indices.size() > 0) {
- return triangle_data.indices.size() / 3;
+ if (triangle_data.indices->size() > 0) {
+ return triangle_data.indices->size() / 3;
} else {
- return triangle_data.vertices.size() / 3;
+ return triangle_data.vertices->size() / 3;
}
}
@@ -782,13 +908,13 @@ int SurfaceTool::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, c
void SurfaceTool::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert) {
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
Vector3 v;
- if (triangle_data.indices.size() > 0) {
- int index = triangle_data.indices[iFace * 3 + iVert]->get();
- if (index < triangle_data.vertices.size()) {
- v = triangle_data.vertices[index]->get().vertex;
+ if (triangle_data.indices->size() > 0) {
+ uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
+ if (index < triangle_data.vertices->size()) {
+ v = triangle_data.vertices->operator[](index).vertex;
}
} else {
- v = triangle_data.vertices[iFace * 3 + iVert]->get().vertex;
+ v = triangle_data.vertices->operator[](iFace * 3 + iVert).vertex;
}
fvPosOut[0] = v.x;
@@ -799,13 +925,13 @@ void SurfaceTool::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvP
void SurfaceTool::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert) {
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
Vector3 v;
- if (triangle_data.indices.size() > 0) {
- int index = triangle_data.indices[iFace * 3 + iVert]->get();
- if (index < triangle_data.vertices.size()) {
- v = triangle_data.vertices[index]->get().normal;
+ if (triangle_data.indices->size() > 0) {
+ uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
+ if (index < triangle_data.vertices->size()) {
+ v = triangle_data.vertices->operator[](index).normal;
}
} else {
- v = triangle_data.vertices[iFace * 3 + iVert]->get().normal;
+ v = triangle_data.vertices->operator[](iFace * 3 + iVert).normal;
}
fvNormOut[0] = v.x;
@@ -816,13 +942,13 @@ void SurfaceTool::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNor
void SurfaceTool::mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert) {
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
Vector2 v;
- if (triangle_data.indices.size() > 0) {
- int index = triangle_data.indices[iFace * 3 + iVert]->get();
- if (index < triangle_data.vertices.size()) {
- v = triangle_data.vertices[index]->get().uv;
+ if (triangle_data.indices->size() > 0) {
+ uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
+ if (index < triangle_data.vertices->size()) {
+ v = triangle_data.vertices->operator[](index).uv;
}
} else {
- v = triangle_data.vertices[iFace * 3 + iVert]->get().uv;
+ v = triangle_data.vertices->operator[](iFace * 3 + iVert).uv;
}
fvTexcOut[0] = v.x;
@@ -833,13 +959,13 @@ void SurfaceTool::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, cons
const tbool bIsOrientationPreserving, const int iFace, const int iVert) {
TangentGenerationContextUserData &triangle_data = *reinterpret_cast<TangentGenerationContextUserData *>(pContext->m_pUserData);
Vertex *vtx = nullptr;
- if (triangle_data.indices.size() > 0) {
- int index = triangle_data.indices[iFace * 3 + iVert]->get();
- if (index < triangle_data.vertices.size()) {
- vtx = &triangle_data.vertices[index]->get();
+ if (triangle_data.indices->size() > 0) {
+ uint32_t index = triangle_data.indices->operator[](iFace * 3 + iVert);
+ if (index < triangle_data.vertices->size()) {
+ vtx = &triangle_data.vertices->operator[](index);
}
} else {
- vtx = &triangle_data.vertices[iFace * 3 + iVert]->get();
+ vtx = &triangle_data.vertices->operator[](iFace * 3 + iVert);
}
if (vtx != nullptr) {
@@ -865,18 +991,12 @@ void SurfaceTool::generate_tangents() {
msc.m_pInterface = &mkif;
TangentGenerationContextUserData triangle_data;
- triangle_data.vertices.resize(vertex_array.size());
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) {
- triangle_data.vertices.write[idx++] = E;
- E->get().binormal = Vector3();
- E->get().tangent = Vector3();
- }
- triangle_data.indices.resize(index_array.size());
- idx = 0;
- for (List<int>::Element *E = index_array.front(); E; E = E->next()) {
- triangle_data.indices.write[idx++] = E;
+ triangle_data.vertices = &vertex_array;
+ for (uint32_t i = 0; i < vertex_array.size(); i++) {
+ vertex_array[i].binormal = Vector3();
+ vertex_array[i].tangent = Vector3();
}
+ triangle_data.indices = &index_array;
msc.m_pUserData = &triangle_data;
bool res = genTangSpaceDefault(&msc);
@@ -892,66 +1012,36 @@ void SurfaceTool::generate_normals(bool p_flip) {
deindex();
- HashMap<Vertex, Vector3, VertexHasher> vertex_hash;
+ ERR_FAIL_COND((vertex_array.size() % 3) != 0);
- int count = 0;
- bool smooth = false;
- if (smooth_groups.has(0)) {
- smooth = smooth_groups[0];
- }
+ HashMap<Vertex, Vector3, VertexHasher> vertex_hash;
- List<Vertex>::Element *B = vertex_array.front();
- for (List<Vertex>::Element *E = B; E;) {
- List<Vertex>::Element *v[3];
- v[0] = E;
- v[1] = v[0]->next();
- ERR_FAIL_COND(!v[1]);
- v[2] = v[1]->next();
- ERR_FAIL_COND(!v[2]);
- E = v[2]->next();
+ for (uint32_t vi = 0; vi < vertex_array.size(); vi += 3) {
+ Vertex *v = &vertex_array[vi];
Vector3 normal;
if (!p_flip) {
- normal = Plane(v[0]->get().vertex, v[1]->get().vertex, v[2]->get().vertex).normal;
+ normal = Plane(v[0].vertex, v[1].vertex, v[2].vertex).normal;
} else {
- normal = Plane(v[2]->get().vertex, v[1]->get().vertex, v[0]->get().vertex).normal;
+ normal = Plane(v[2].vertex, v[1].vertex, v[0].vertex).normal;
}
- if (smooth) {
- for (int i = 0; i < 3; i++) {
- Vector3 *lv = vertex_hash.getptr(v[i]->get());
- if (!lv) {
- vertex_hash.set(v[i]->get(), normal);
- } else {
- (*lv) += normal;
- }
- }
- } else {
- for (int i = 0; i < 3; i++) {
- v[i]->get().normal = normal;
- }
- }
- count += 3;
-
- if (smooth_groups.has(count) || !E) {
- if (vertex_hash.size()) {
- while (B != E) {
- Vector3 *lv = vertex_hash.getptr(B->get());
- if (lv) {
- B->get().normal = lv->normalized();
- }
-
- B = B->next();
- }
-
+ for (int i = 0; i < 3; i++) {
+ Vector3 *lv = vertex_hash.getptr(v[i]);
+ if (!lv) {
+ vertex_hash.set(v[i], normal);
} else {
- B = E;
+ (*lv) += normal;
}
+ }
+ }
- vertex_hash.clear();
- if (E) {
- smooth = smooth_groups[count];
- }
+ for (uint32_t vi = 0; vi < vertex_array.size(); vi++) {
+ Vector3 *lv = vertex_hash.getptr(vertex_array[vi]);
+ if (!lv) {
+ vertex_array[vi].normal = Vector3();
+ } else {
+ vertex_array[vi].normal = lv->normalized();
}
}
@@ -959,7 +1049,6 @@ void SurfaceTool::generate_normals(bool p_flip) {
if (was_indexed) {
index();
- smooth_groups.clear();
}
}
@@ -975,22 +1064,97 @@ void SurfaceTool::clear() {
last_weights.clear();
index_array.clear();
vertex_array.clear();
- smooth_groups.clear();
material.unref();
+ last_smooth_group = 0;
+ for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) {
+ last_custom_format[i] = CUSTOM_MAX;
+ }
+ skin_weights = SKIN_4_WEIGHTS;
+}
+
+void SurfaceTool::set_skin_weight_count(SkinWeightCount p_weights) {
+ ERR_FAIL_COND(begun);
+ skin_weights = p_weights;
+}
+SurfaceTool::SkinWeightCount SurfaceTool::get_skin_weight_count() const {
+ return skin_weights;
+}
+
+void SurfaceTool::set_custom_format(int p_index, CustomFormat p_format) {
+ ERR_FAIL_INDEX(p_index, RS::ARRAY_CUSTOM_COUNT);
+ ERR_FAIL_COND(begun);
+ last_custom_format[p_index] = p_format;
+}
+SurfaceTool::CustomFormat SurfaceTool::get_custom_format(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, RS::ARRAY_CUSTOM_COUNT, CUSTOM_MAX);
+ return last_custom_format[p_index];
+}
+void SurfaceTool::optimize_indices_for_cache() {
+ ERR_FAIL_COND(optimize_vertex_cache_func == nullptr);
+ ERR_FAIL_COND(index_array.size() == 0);
+
+ LocalVector old_index_array = index_array;
+ zeromem(index_array.ptr(), index_array.size() * sizeof(int));
+ optimize_vertex_cache_func((unsigned int *)index_array.ptr(), (unsigned int *)old_index_array.ptr(), old_index_array.size(), vertex_array.size());
+}
+
+float SurfaceTool::get_max_axis_length() const {
+ ERR_FAIL_COND_V(vertex_array.size() == 0, 0);
+
+ AABB aabb;
+ for (uint32_t i = 0; i < vertex_array.size(); i++) {
+ if (i == 0) {
+ aabb.position = vertex_array[i].vertex;
+ } else {
+ aabb.expand_to(vertex_array[i].vertex);
+ }
+ }
+
+ return aabb.get_longest_axis_size();
+}
+Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_count) {
+ Vector<int> lod;
+
+ ERR_FAIL_COND_V(simplify_func == nullptr, lod);
+ ERR_FAIL_COND_V(vertex_array.size() == 0, lod);
+ ERR_FAIL_COND_V(index_array.size() == 0, lod);
+
+ lod.resize(index_array.size());
+ LocalVector<float> vertices; //uses floats
+ vertices.resize(vertex_array.size() * 3);
+ for (uint32_t i = 0; i < vertex_array.size(); i++) {
+ vertices[i * 3 + 0] = vertex_array[i].vertex.x;
+ vertices[i * 3 + 1] = vertex_array[i].vertex.y;
+ vertices[i * 3 + 2] = vertex_array[i].vertex.z;
+ }
+
+ float error;
+ uint32_t index_count = simplify_func((unsigned int *)lod.ptrw(), (unsigned int *)index_array.ptr(), index_array.size(), vertices.ptr(), vertex_array.size(), sizeof(float) * 3, p_target_index_count, p_threshold, &error);
+ ERR_FAIL_COND_V(index_count == 0, lod);
+ lod.resize(index_count);
+
+ return lod;
}
void SurfaceTool::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_skin_weight_count", "count"), &SurfaceTool::set_skin_weight_count);
+ ClassDB::bind_method(D_METHOD("get_skin_weight_count"), &SurfaceTool::get_skin_weight_count);
+
+ ClassDB::bind_method(D_METHOD("set_custom_format", "index", "format"), &SurfaceTool::set_custom_format);
+ ClassDB::bind_method(D_METHOD("get_custom_format", "index"), &SurfaceTool::get_custom_format);
+
ClassDB::bind_method(D_METHOD("begin", "primitive"), &SurfaceTool::begin);
ClassDB::bind_method(D_METHOD("add_vertex", "vertex"), &SurfaceTool::add_vertex);
- ClassDB::bind_method(D_METHOD("add_color", "color"), &SurfaceTool::add_color);
- ClassDB::bind_method(D_METHOD("add_normal", "normal"), &SurfaceTool::add_normal);
- ClassDB::bind_method(D_METHOD("add_tangent", "tangent"), &SurfaceTool::add_tangent);
- ClassDB::bind_method(D_METHOD("add_uv", "uv"), &SurfaceTool::add_uv);
- ClassDB::bind_method(D_METHOD("add_uv2", "uv2"), &SurfaceTool::add_uv2);
- ClassDB::bind_method(D_METHOD("add_bones", "bones"), &SurfaceTool::add_bones);
- ClassDB::bind_method(D_METHOD("add_weights", "weights"), &SurfaceTool::add_weights);
- ClassDB::bind_method(D_METHOD("add_smooth_group", "smooth"), &SurfaceTool::add_smooth_group);
+ ClassDB::bind_method(D_METHOD("set_color", "color"), &SurfaceTool::set_color);
+ ClassDB::bind_method(D_METHOD("set_normal", "normal"), &SurfaceTool::set_normal);
+ ClassDB::bind_method(D_METHOD("set_tangent", "tangent"), &SurfaceTool::set_tangent);
+ ClassDB::bind_method(D_METHOD("set_uv", "uv"), &SurfaceTool::set_uv);
+ ClassDB::bind_method(D_METHOD("set_uv2", "uv2"), &SurfaceTool::set_uv2);
+ ClassDB::bind_method(D_METHOD("set_bones", "bones"), &SurfaceTool::set_bones);
+ ClassDB::bind_method(D_METHOD("set_weights", "weights"), &SurfaceTool::set_weights);
+ ClassDB::bind_method(D_METHOD("set_custom", "index", "custom"), &SurfaceTool::set_custom);
+ ClassDB::bind_method(D_METHOD("set_smooth_group", "index"), &SurfaceTool::set_smooth_group);
ClassDB::bind_method(D_METHOD("add_triangle_fan", "vertices", "uvs", "colors", "uv2s", "normals", "tangents"), &SurfaceTool::add_triangle_fan, DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Color>()), DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Vector3>()), DEFVAL(Vector<Plane>()));
@@ -1001,6 +1165,11 @@ void SurfaceTool::_bind_methods() {
ClassDB::bind_method(D_METHOD("generate_normals", "flip"), &SurfaceTool::generate_normals, DEFVAL(false));
ClassDB::bind_method(D_METHOD("generate_tangents"), &SurfaceTool::generate_tangents);
+ ClassDB::bind_method(D_METHOD("optimize_indices_for_cache"), &SurfaceTool::optimize_indices_for_cache);
+
+ ClassDB::bind_method(D_METHOD("get_max_axis_length"), &SurfaceTool::get_max_axis_length);
+ ClassDB::bind_method(D_METHOD("generate_lod", "nd_threshold", "target_index_count"), &SurfaceTool::generate_lod, DEFVAL(3));
+
ClassDB::bind_method(D_METHOD("set_material", "material"), &SurfaceTool::set_material);
ClassDB::bind_method(D_METHOD("clear"), &SurfaceTool::clear);
@@ -1008,13 +1177,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..0e60bfe389 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -31,8 +31,8 @@
#ifndef SURFACE_TOOL_H
#define SURFACE_TOOL_H
+#include "core/templates/local_vector.h"
#include "scene/resources/mesh.h"
-
#include "thirdparty/misc/mikktspace.h"
class SurfaceTool : public Reference {
@@ -49,12 +49,36 @@ public:
Vector2 uv2;
Vector<int> bones;
Vector<float> weights;
+ Color custom[RS::ARRAY_CUSTOM_COUNT];
+ uint32_t smooth_group = 0;
bool operator==(const Vertex &p_vertex) const;
Vertex() {}
};
+ enum CustomFormat {
+ CUSTOM_RGBA8_UNORM = RS::ARRAY_CUSTOM_RGBA8_UNORM,
+ CUSTOM_RGBA8_SNORM = RS::ARRAY_CUSTOM_RGBA8_SNORM,
+ CUSTOM_RG_HALF = RS::ARRAY_CUSTOM_RG_HALF,
+ CUSTOM_RGBA_HALF = RS::ARRAY_CUSTOM_RGBA_HALF,
+ CUSTOM_R_FLOAT = RS::ARRAY_CUSTOM_R_FLOAT,
+ CUSTOM_RG_FLOAT = RS::ARRAY_CUSTOM_RG_FLOAT,
+ CUSTOM_RGB_FLOAT = RS::ARRAY_CUSTOM_RGB_FLOAT,
+ CUSTOM_RGBA_FLOAT = RS::ARRAY_CUSTOM_RGBA_FLOAT,
+ CUSTOM_MAX = RS::ARRAY_CUSTOM_MAX
+ };
+
+ enum SkinWeightCount {
+ SKIN_4_WEIGHTS,
+ SKIN_8_WEIGHTS
+ };
+
+ typedef void (*OptimizeVertexCacheFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count);
+ static OptimizeVertexCacheFunc optimize_vertex_cache_func;
+ typedef size_t (*SimplifyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float *r_error);
+ static SimplifyFunc simplify_func;
+
private:
struct VertexHasher {
static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx);
@@ -71,12 +95,11 @@ private:
bool begun;
bool first;
Mesh::PrimitiveType primitive;
- int format;
+ uint32_t format;
Ref<Material> material;
//arrays
- List<Vertex> vertex_array;
- List<int> index_array;
- Map<int, bool> smooth_groups;
+ LocalVector<Vertex> vertex_array;
+ LocalVector<int> index_array;
//memory
Color last_color;
@@ -86,9 +109,16 @@ private:
Vector<int> last_bones;
Vector<float> last_weights;
Plane last_tangent;
+ uint32_t last_smooth_group = 0;
+
+ SkinWeightCount skin_weights;
- void _create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, int &lformat);
- void _create_list(const Ref<Mesh> &p_existing, int p_surface, List<Vertex> *r_vertex, List<int> *r_index, int &lformat);
+ Color last_custom[RS::ARRAY_CUSTOM_COUNT];
+
+ CustomFormat last_custom_format[RS::ARRAY_CUSTOM_COUNT];
+
+ void _create_list_from_arrays(Array arr, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat);
+ void _create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat);
//mikktspace callbacks
static int mikktGetNumFaces(const SMikkTSpaceContext *pContext);
@@ -103,17 +133,25 @@ protected:
static void _bind_methods();
public:
+ void set_skin_weight_count(SkinWeightCount p_weights);
+ SkinWeightCount get_skin_weight_count() const;
+
+ void set_custom_format(int p_index, CustomFormat p_format);
+ CustomFormat get_custom_format(int p_index) const;
+
void begin(Mesh::PrimitiveType p_primitive);
+ void set_color(Color p_color);
+ void set_normal(const Vector3 &p_normal);
+ void set_tangent(const Plane &p_tangent);
+ void set_uv(const Vector2 &p_uv);
+ void set_uv2(const Vector2 &p_uv2);
+ void set_custom(int p_index, const Color &p_custom);
+ void set_bones(const Vector<int> &p_bones);
+ void set_weights(const Vector<float> &p_weights);
+ void set_smooth_group(uint32_t p_group);
+
void add_vertex(const Vector3 &p_vertex);
- void add_color(Color p_color);
- void add_normal(const Vector3 &p_normal);
- void add_tangent(const Plane &p_tangent);
- void add_uv(const Vector2 &p_uv);
- void add_uv2(const Vector2 &p_uv2);
- void add_bones(const Vector<int> &p_bones);
- void add_weights(const Vector<float> &p_weights);
- void add_smooth_group(bool p_smooth);
void add_triangle_fan(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs = Vector<Vector2>(), const Vector<Color> &p_colors = Vector<Color>(), const Vector<Vector2> &p_uv2s = Vector<Vector2>(), const Vector<Vector3> &p_normals = Vector<Vector3>(), const Vector<Plane> &p_tangents = Vector<Plane>());
@@ -124,21 +162,28 @@ public:
void generate_normals(bool p_flip = false);
void generate_tangents();
+ void optimize_indices_for_cache();
+ float get_max_axis_length() const;
+ Vector<int> generate_lod(float p_threshold, int p_target_index_count = 3);
+
void set_material(const Ref<Material> &p_material);
void clear();
- List<Vertex> &get_vertex_array() { return vertex_array; }
+ LocalVector<Vertex> &get_vertex_array() { return vertex_array; }
void create_from_triangle_arrays(const Array &p_arrays);
- static Vector<Vertex> create_vertex_array_from_triangle_arrays(const Array &p_arrays);
+ static void create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<Vertex> &ret, uint32_t *r_format = nullptr);
Array commit_to_arrays();
void create_from(const Ref<Mesh> &p_existing, int p_surface);
void create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String &p_blend_shape_name);
void append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform);
- Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_flags = Mesh::ARRAY_COMPRESS_DEFAULT);
+ Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_flags = 0);
SurfaceTool();
};
+VARIANT_ENUM_CAST(SurfaceTool::CustomFormat)
+VARIANT_ENUM_CAST(SurfaceTool::SkinWeightCount)
+
#endif
diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp
index 5d58e71fc5..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) {
@@ -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);
}
diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h
index 720227a256..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;
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 39237e1a33..706b18d2b5 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 {
@@ -913,7 +896,7 @@ Image::Format StreamTexture3D::get_format() const {
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(!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);
@@ -1204,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;
}
@@ -1219,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;
}
@@ -1242,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;
@@ -1257,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 {
@@ -1276,7 +1253,7 @@ bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect,
Vector2 scale = p_rect.size / src.size;
src.position += (rc.position - margin.position);
- Rect2 src_c = rc.clip(src);
+ Rect2 src_c = rc.intersection(src);
if (src_c.size == Size2()) {
return false;
}
@@ -1364,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;
}
@@ -1374,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;
}
@@ -1398,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;
}
@@ -1422,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 {
@@ -1569,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;
@@ -1586,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;
@@ -1604,12 +1575,12 @@ void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons
if (!p_src_rect.intersects(rect)) {
continue;
}
- Rect2 local = p_src_rect.clip(rect);
+ Rect2 local = p_src_rect.intersection(rect);
Rect2 target = local;
target.size *= scale;
target.position = p_rect.position + (p_src_rect.position + rect.position) * scale;
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);
}
}
@@ -1739,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;
@@ -2363,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 eebbf4f233..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;
@@ -664,7 +664,6 @@ public:
};
/*
enum CubeMapSide {
-
CUBEMAP_LEFT,
CUBEMAP_RIGHT,
CUBEMAP_BOTTOM,
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index 6a85d357ff..c8bfcdfab4 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_icon_type_list() const {
Vector<String> ilret;
List<StringName> il;
- get_stylebox_list(p_type, &il);
+ get_icon_type_list(&il);
ilret.resize(il.size());
int i = 0;
@@ -66,11 +66,11 @@ Vector<String> Theme::_get_stylebox_list(const String &p_type) const {
return ilret;
}
-Vector<String> Theme::_get_stylebox_types() const {
+Vector<String> Theme::_get_stylebox_list(const String &p_node_type) const {
Vector<String> ilret;
List<StringName> il;
- get_stylebox_types(&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_stylebox_type_list() const {
Vector<String> ilret;
List<StringName> il;
- get_font_list(p_type, &il);
+ get_stylebox_type_list(&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_list(const String &p_node_type) const {
Vector<String> ilret;
List<StringName> il;
- get_color_list(p_type, &il);
+ get_font_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_font_type_list() const {
Vector<String> ilret;
List<StringName> il;
- get_constant_list(p_type, &il);
+ get_font_type_list(&il);
ilret.resize(il.size());
int i = 0;
@@ -126,7 +126,82 @@ 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_font_size_list(const String &p_node_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_font_size_list(p_node_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_color_list(const String &p_node_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_color_list(p_node_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_color_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_color_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_constant_list(const String &p_node_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ 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_constant_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_constant_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_type_list() const {
Vector<String> ilret;
List<StringName> il;
@@ -291,11 +366,27 @@ Ref<Font> Theme::get_default_theme_font() const {
return default_theme_font;
}
+void Theme::set_default_theme_font_size(int p_font_size) {
+ if (default_theme_font_size == p_font_size) {
+ return;
+ }
+
+ default_theme_font_size = p_font_size;
+
+ _change_notify();
+ emit_changed();
+}
+
+int Theme::get_default_theme_font_size() const {
+ return default_theme_font_size;
+}
+
Ref<Theme> Theme::project_default_theme;
Ref<Theme> Theme::default_theme;
Ref<Texture2D> Theme::default_icon;
Ref<StyleBox> Theme::default_style;
Ref<Font> Theme::default_font;
+int Theme::default_font_size = 16;
Ref<Theme> Theme::get_default() {
return default_theme;
@@ -325,19 +416,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 +441,200 @@ 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::get_icon_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
- shader_map[p_type][p_name] = p_shader;
+ const StringName *key = nullptr;
+ while ((key = icon_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+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));
+ }
+
+ 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_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = style_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
- bool new_value = !style_map.has(p_type) || !style_map[p_type].has(p_name);
+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());
- 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));
+ 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 {
+void Theme::get_font_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
const StringName *key = nullptr;
- while ((key = style_map.next(key))) {
+ while ((key = font_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());
+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);
- 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 +642,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 +690,53 @@ 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::get_color_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = color_map.next(key))) {
p_list->push_back(*key);
}
}
-void Theme::set_constant(const StringName &p_name, const StringName &p_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 +744,46 @@ 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);
+ }
+}
+
+void Theme::get_constant_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = constant_map.next(key))) {
p_list->push_back(*key);
}
}
@@ -699,7 +832,6 @@ void Theme::clear() {
icon_map.clear();
style_map.clear();
font_map.clear();
- shader_map.clear();
color_map.clear();
constant_map.clear();
@@ -754,7 +886,6 @@ void Theme::copy_theme(const Ref<Theme> &p_other) {
color_map = p_other->color_map;
constant_map = p_other->constant_map;
- shader_map = p_other->shader_map;
_change_notify();
emit_changed();
@@ -800,48 +931,62 @@ 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("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_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("get_icon_type_list"), &Theme::_get_icon_type_list);
+
+ ClassDB::bind_method(D_METHOD("set_stylebox", "name", "node_type", "texture"), &Theme::set_stylebox);
+ ClassDB::bind_method(D_METHOD("get_stylebox", "name", "node_type"), &Theme::get_stylebox);
+ ClassDB::bind_method(D_METHOD("has_stylebox", "name", "node_type"), &Theme::has_stylebox);
+ ClassDB::bind_method(D_METHOD("clear_stylebox", "name", "node_type"), &Theme::clear_stylebox);
+ ClassDB::bind_method(D_METHOD("get_stylebox_list", "node_type"), &Theme::_get_stylebox_list);
+ ClassDB::bind_method(D_METHOD("get_stylebox_type_list"), &Theme::_get_stylebox_type_list);
+
+ ClassDB::bind_method(D_METHOD("set_font", "name", "node_type", "font"), &Theme::set_font);
+ ClassDB::bind_method(D_METHOD("get_font", "name", "node_type"), &Theme::get_font);
+ ClassDB::bind_method(D_METHOD("has_font", "name", "node_type"), &Theme::has_font);
+ ClassDB::bind_method(D_METHOD("clear_font", "name", "node_type"), &Theme::clear_font);
+ ClassDB::bind_method(D_METHOD("get_font_list", "node_type"), &Theme::_get_font_list);
+ ClassDB::bind_method(D_METHOD("get_font_type_list"), &Theme::_get_font_type_list);
+
+ ClassDB::bind_method(D_METHOD("set_font_size", "name", "node_type", "font_size"), &Theme::set_font_size);
+ ClassDB::bind_method(D_METHOD("get_font_size", "name", "node_type"), &Theme::get_font_size);
+ ClassDB::bind_method(D_METHOD("has_font_size", "name", "node_type"), &Theme::has_font_size);
+ ClassDB::bind_method(D_METHOD("clear_font_size", "name", "node_type"), &Theme::clear_font_size);
+ ClassDB::bind_method(D_METHOD("get_font_size_list", "node_type"), &Theme::_get_font_size_list);
+
+ ClassDB::bind_method(D_METHOD("set_color", "name", "node_type", "color"), &Theme::set_color);
+ ClassDB::bind_method(D_METHOD("get_color", "name", "node_type"), &Theme::get_color);
+ ClassDB::bind_method(D_METHOD("has_color", "name", "node_type"), &Theme::has_color);
+ ClassDB::bind_method(D_METHOD("clear_color", "name", "node_type"), &Theme::clear_color);
+ ClassDB::bind_method(D_METHOD("get_color_list", "node_type"), &Theme::_get_color_list);
+ ClassDB::bind_method(D_METHOD("get_color_type_list"), &Theme::_get_color_type_list);
+
+ ClassDB::bind_method(D_METHOD("set_constant", "name", "node_type", "constant"), &Theme::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant", "name", "node_type"), &Theme::get_constant);
+ ClassDB::bind_method(D_METHOD("has_constant", "name", "node_type"), &Theme::has_constant);
+ ClassDB::bind_method(D_METHOD("clear_constant", "name", "node_type"), &Theme::clear_constant);
+ ClassDB::bind_method(D_METHOD("get_constant_list", "node_type"), &Theme::_get_constant_list);
+ ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list);
ClassDB::bind_method(D_METHOD("clear"), &Theme::clear);
ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_theme_font);
ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_theme_font);
- ClassDB::bind_method(D_METHOD("get_type_list", "type"), &Theme::_get_type_list);
+ ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_theme_font_size);
+ ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_theme_font_size);
+
+ ClassDB::bind_method(D_METHOD("get_type_list"), &Theme::_get_type_list);
ClassDB::bind_method("copy_default_theme", &Theme::copy_default_theme);
ClassDB::bind_method(D_METHOD("copy_theme", "other"), &Theme::copy_theme);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size");
}
Theme::Theme() {
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index 3c72ddd8a2..ad05e0e2f5 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,22 @@ class Theme : public Resource {
HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map;
HashMap<StringName, HashMap<StringName, Ref<StyleBox>>> style_map;
HashMap<StringName, HashMap<StringName, Ref<Font>>> font_map;
- HashMap<StringName, HashMap<StringName, Ref<Shader>>> shader_map;
+ HashMap<StringName, HashMap<StringName, int>> font_size_map;
HashMap<StringName, HashMap<StringName, Color>> color_map;
HashMap<StringName, HashMap<StringName, int>> constant_map;
- Vector<String> _get_icon_list(const String &p_type) const;
- Vector<String> _get_stylebox_list(const String &p_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_icon_list(const String &p_node_type) const;
+ Vector<String> _get_icon_type_list() const;
+ Vector<String> _get_stylebox_list(const String &p_node_type) const;
+ Vector<String> _get_stylebox_type_list() const;
+ Vector<String> _get_font_list(const String &p_node_type) const;
+ Vector<String> _get_font_type_list() const;
+ Vector<String> _get_font_size_list(const String &p_node_type) const;
+ Vector<String> _get_color_list(const String &p_node_type) const;
+ Vector<String> _get_color_type_list() const;
+ Vector<String> _get_constant_list(const String &p_node_type) const;
+ Vector<String> _get_constant_type_list() const;
+ Vector<String> _get_type_list() const;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -69,8 +73,10 @@ protected:
static Ref<Texture2D> default_icon;
static Ref<StyleBox> default_style;
static Ref<Font> default_font;
+ static int default_font_size;
Ref<Font> default_theme_font;
+ int default_theme_font_size = -1;
static void _bind_methods();
@@ -84,46 +90,54 @@ public:
static void set_default_icon(const Ref<Texture2D> &p_icon);
static void set_default_style(const Ref<StyleBox> &p_style);
static void set_default_font(const Ref<Font> &p_font);
+ static void set_default_font_size(int p_font_size);
void set_default_theme_font(const Ref<Font> &p_default_font);
Ref<Font> get_default_theme_font() const;
- void set_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 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_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 get_icon_type_list(List<StringName> *p_list) const;
+
+ void set_stylebox(const StringName &p_name, const StringName &p_node_type, const Ref<StyleBox> &p_style);
+ Ref<StyleBox> get_stylebox(const StringName &p_name, const StringName &p_node_type) const;
+ bool has_stylebox(const StringName &p_name, const StringName &p_node_type) const;
+ void clear_stylebox(const StringName &p_name, const StringName &p_node_type);
+ void get_stylebox_list(StringName p_node_type, List<StringName> *p_list) const;
+ void get_stylebox_type_list(List<StringName> *p_list) const;
+
+ void set_font(const StringName &p_name, const StringName &p_node_type, const Ref<Font> &p_font);
+ Ref<Font> get_font(const StringName &p_name, const StringName &p_node_type) const;
+ bool has_font(const StringName &p_name, const StringName &p_node_type) const;
+ void clear_font(const StringName &p_name, const StringName &p_node_type);
+ void get_font_list(StringName p_node_type, List<StringName> *p_list) const;
+ void get_font_type_list(List<StringName> *p_list) const;
+
+ void set_font_size(const StringName &p_name, const StringName &p_node_type, int p_font_size);
+ int get_font_size(const StringName &p_name, const StringName &p_node_type) const;
+ bool has_font_size(const StringName &p_name, const StringName &p_node_type) const;
+ void clear_font_size(const StringName &p_name, const StringName &p_node_type);
+ void get_font_size_list(StringName p_node_type, List<StringName> *p_list) const;
+
+ void set_color(const StringName &p_name, const StringName &p_node_type, const Color &p_color);
+ Color get_color(const StringName &p_name, const StringName &p_node_type) const;
+ bool has_color(const StringName &p_name, const StringName &p_node_type) const;
+ void clear_color(const StringName &p_name, const StringName &p_node_type);
+ void get_color_list(StringName p_node_type, List<StringName> *p_list) const;
+ void get_color_type_list(List<StringName> *p_list) const;
+
+ void set_constant(const StringName &p_name, const StringName &p_node_type, int p_constant);
+ int get_constant(const StringName &p_name, const StringName &p_node_type) const;
+ bool has_constant(const StringName &p_name, const StringName &p_node_type) const;
+ void clear_constant(const StringName &p_name, const StringName &p_node_type);
+ void get_constant_list(StringName p_node_type, List<StringName> *p_list) const;
+ void get_constant_type_list(List<StringName> *p_list) const;
void get_type_list(List<StringName> *p_list) const;
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 84b067d1e2..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;
@@ -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") {
@@ -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 803ab265d4..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>();
}
@@ -807,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);
@@ -826,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;
@@ -988,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);
@@ -1055,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();
@@ -1239,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) {
@@ -1702,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" },
@@ -1719,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)" },
@@ -1740,29 +1748,34 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_pixel_size", "vec3(SCREEN_PIXEL_SIZE, 1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_texture", "NORMAL_TEXTURE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" },
// Canvas Item, Light
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vec", "vec3(LIGHT_VEC, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_height", "LIGHT_HEIGHT" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT_COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_uv", "vec3(LIGHT_UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_color", "SHADOW_COLOR.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_vec", "vec3(SHADOW_VEC, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_color_alpha", "LIGHT_COLOR.a" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_position", "LIGHT_POSITION" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_color", "SHADOW_MODULATE.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_MODULATE.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" },
// Particles, Emit
{ Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
@@ -1844,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)" },
@@ -1858,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)" },
@@ -1950,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";
@@ -2123,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";
}
@@ -2140,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;
@@ -2162,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 {
@@ -2222,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;
}
@@ -2264,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";
@@ -2286,16 +2306,29 @@ 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;
}
@@ -2336,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
@@ -2456,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;
}
@@ -2571,20 +2604,37 @@ Vector<StringName> VisualShaderNodeUniform::get_editable_properties() const {
VisualShaderNodeUniform::VisualShaderNodeUniform() {
}
-////////////// 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;
@@ -2700,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) {
@@ -2719,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 {
@@ -2768,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) {
@@ -2787,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 {
@@ -2838,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 {
@@ -2873,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 {
@@ -2908,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 {
@@ -2943,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 {
@@ -3003,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);
@@ -3033,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 {
@@ -3053,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 {
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 513a17b024..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"
@@ -175,7 +175,7 @@ public:
String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const;
- String validate_port_name(const String &p_name, const List<String> &p_input_ports, const List<String> &p_output_ports) const;
+ String validate_port_name(const String &p_port_name, VisualShaderNode *p_node, int p_port_id, bool p_output) const;
String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const;
VisualShader();
@@ -193,8 +193,7 @@ class VisualShaderNode : public Resource {
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 = true;
@@ -242,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;
@@ -454,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;
@@ -469,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;
@@ -481,14 +486,33 @@ 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 = Size2(0, 0);
String inputs = "";
String outputs = "";
bool editable = false;
@@ -506,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;
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 0e10682daf..a3358ea8c7 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -452,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 "";
}
@@ -491,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()) {
@@ -525,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()) {
@@ -549,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()) {
@@ -572,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()) {
@@ -595,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()) {
@@ -628,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()) {
@@ -717,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
}
@@ -775,6 +786,90 @@ void VisualShaderNodeTexture::_bind_methods() {
VisualShaderNodeTexture::VisualShaderNodeTexture() {
}
+////////////// 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
int VisualShaderNodeSample3D::get_input_port_count() const {
@@ -819,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;
@@ -837,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
@@ -882,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
}
@@ -1085,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) {
@@ -1108,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()) {
@@ -1128,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 "";
}
@@ -1171,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);
@@ -3490,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();
@@ -3649,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();
@@ -3823,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);
@@ -3913,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);
@@ -4012,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
}
@@ -4104,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
}
@@ -4202,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
@@ -4269,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 "";
}
@@ -5169,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;
@@ -5191,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;
@@ -5206,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);
@@ -5219,29 +5390,29 @@ 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() {
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 06ad42adf5..4b39c76388 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -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);
@@ -415,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();
};
@@ -1529,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;
@@ -1591,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;
@@ -1644,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;
@@ -1683,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;
@@ -1722,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;
@@ -1761,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;
@@ -2121,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 = TYPE_SCALAR;
+ OpType op_type = OP_TYPE_SCALAR;
protected:
static void _bind_methods();
@@ -2146,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/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;