summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/camera_2d.cpp3
-rw-r--r--scene/2d/node_2d.cpp9
-rw-r--r--scene/2d/node_2d.h14
-rw-r--r--scene/2d/path_2d.cpp11
-rw-r--r--scene/2d/path_2d.h18
-rw-r--r--scene/3d/area_3d.cpp10
-rw-r--r--scene/3d/area_3d.h4
-rw-r--r--scene/3d/audio_stream_player_3d.cpp2
-rw-r--r--scene/3d/camera_3d.cpp6
-rw-r--r--scene/3d/node_3d.cpp26
-rw-r--r--scene/3d/node_3d.h33
-rw-r--r--scene/3d/path_3d.cpp17
-rw-r--r--scene/3d/path_3d.h18
-rw-r--r--scene/3d/physics_joint_3d.cpp13
-rw-r--r--scene/3d/physics_joint_3d.h7
-rw-r--r--scene/3d/proximity_group_3d.cpp94
-rw-r--r--scene/3d/proximity_group_3d.h27
-rw-r--r--scene/3d/reflection_probe.cpp14
-rw-r--r--scene/3d/reflection_probe.h4
-rw-r--r--scene/3d/visual_instance_3d.cpp16
-rw-r--r--scene/3d/visual_instance_3d.h5
-rw-r--r--scene/SCsub18
-rw-r--r--scene/animation/SCsub21
-rw-r--r--scene/animation/animation_player.cpp84
-rw-r--r--scene/animation/animation_player.h19
-rw-r--r--scene/gui/control.cpp73
-rw-r--r--scene/gui/control.h59
-rw-r--r--scene/gui/dialogs.cpp14
-rw-r--r--scene/gui/dialogs.h6
-rw-r--r--scene/gui/file_dialog.cpp20
-rw-r--r--scene/gui/graph_edit.cpp389
-rw-r--r--scene/gui/graph_edit.h67
-rw-r--r--scene/gui/panel.cpp3
-rw-r--r--scene/gui/panel.h1
-rw-r--r--scene/gui/popup_menu.cpp39
-rw-r--r--scene/gui/rich_text_effect.cpp31
-rw-r--r--scene/gui/rich_text_effect.h29
-rw-r--r--scene/gui/rich_text_label.cpp2724
-rw-r--r--scene/gui/rich_text_label.h268
-rw-r--r--scene/gui/tab_container.cpp68
-rw-r--r--scene/gui/tab_container.h2
-rw-r--r--scene/gui/text_edit.cpp25
-rw-r--r--scene/gui/text_edit.h2
-rw-r--r--scene/gui/texture_progress_bar.cpp (renamed from scene/gui/texture_progress.cpp)114
-rw-r--r--scene/gui/texture_progress_bar.h (renamed from scene/gui/texture_progress.h)16
-rw-r--r--scene/main/canvas_item.cpp28
-rw-r--r--scene/main/canvas_item.h60
-rw-r--r--scene/main/node.cpp29
-rw-r--r--scene/main/node.h62
-rw-r--r--scene/main/scene_tree.cpp45
-rw-r--r--scene/main/scene_tree.h49
-rw-r--r--scene/main/viewport.cpp25
-rw-r--r--scene/main/viewport.h8
-rw-r--r--scene/register_scene_types.cpp5
-rw-r--r--scene/resources/SCsub21
-rw-r--r--scene/resources/animation.cpp8
-rw-r--r--scene/resources/animation.h4
-rw-r--r--scene/resources/bit_map.cpp6
-rw-r--r--scene/resources/default_theme/default_theme.cpp54
-rw-r--r--scene/resources/default_theme/icon_grid_minimap.pngbin0 -> 640 bytes
-rw-r--r--scene/resources/default_theme/theme_data.h4
-rw-r--r--scene/resources/font.cpp44
-rw-r--r--scene/resources/font.h4
-rw-r--r--scene/resources/mesh.cpp101
-rw-r--r--scene/resources/mesh.h18
-rw-r--r--scene/resources/surface_tool.cpp360
-rw-r--r--scene/resources/surface_tool.h28
-rw-r--r--scene/resources/texture.cpp4
-rw-r--r--scene/resources/theme.cpp112
-rw-r--r--scene/resources/theme.h14
70 files changed, 3611 insertions, 1925 deletions
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 79b0b64efb..0d09d21a71 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -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();
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 42c2585487..a2f687cd96 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -475,12 +475,3 @@ void Node2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_index", "get_z_index");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "z_as_relative"), "set_z_as_relative", "is_z_relative");
}
-
-Node2D::Node2D() {
- angle = 0;
- _scale = Vector2(1, 1);
- skew = 0;
- _xform_dirty = false;
- z_index = 0;
- z_relative = true;
-}
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
index e20f746447..a66e7f625d 100644
--- a/scene/2d/node_2d.h
+++ b/scene/2d/node_2d.h
@@ -37,15 +37,15 @@ class Node2D : public CanvasItem {
GDCLASS(Node2D, CanvasItem);
Point2 pos;
- float angle;
- Size2 _scale;
- float skew;
- int z_index;
- bool z_relative;
+ float angle = 0;
+ Size2 _scale = Vector2(1, 1);
+ float skew = 0;
+ int z_index = 0;
+ bool z_relative = true;
Transform2D _mat;
- bool _xform_dirty;
+ bool _xform_dirty = false;
void _update_transform();
@@ -121,7 +121,7 @@ public:
Transform2D get_transform() const override;
- Node2D();
+ Node2D() {}
};
#endif // NODE2D_H
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index f40a993423..6571474c9b 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -387,14 +387,3 @@ void PathFollow2D::set_loop(bool p_loop) {
bool PathFollow2D::has_loop() const {
return loop;
}
-
-PathFollow2D::PathFollow2D() {
- offset = 0;
- h_offset = 0;
- v_offset = 0;
- path = nullptr;
- rotates = true;
- cubic = true;
- loop = true;
- lookahead = 4;
-}
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index fcb8b40125..40042a04ef 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -63,14 +63,14 @@ class PathFollow2D : public Node2D {
public:
private:
- Path2D *path;
- real_t offset;
- real_t h_offset;
- real_t v_offset;
- real_t lookahead;
- bool cubic;
- bool loop;
- bool rotates;
+ Path2D *path = nullptr;
+ real_t offset = 0;
+ real_t h_offset = 0;
+ real_t v_offset = 0;
+ real_t lookahead = 4;
+ bool cubic = true;
+ bool loop = true;
+ bool rotates = true;
void _update_transform();
@@ -107,7 +107,7 @@ public:
String get_configuration_warning() const override;
- PathFollow2D();
+ PathFollow2D() {}
};
#endif // PATH_2D_H
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp
index b1ffe76662..b1adb0e88e 100644
--- a/scene/3d/area_3d.cpp
+++ b/scene/3d/area_3d.cpp
@@ -505,11 +505,11 @@ bool Area3D::is_overriding_audio_bus() const {
return audio_bus_override;
}
-void Area3D::set_audio_bus(const StringName &p_audio_bus) {
+void Area3D::set_audio_bus_name(const StringName &p_audio_bus) {
audio_bus = p_audio_bus;
}
-StringName Area3D::get_audio_bus() const {
+StringName Area3D::get_audio_bus_name() const {
for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
if (AudioServer::get_singleton()->get_bus_name(i) == audio_bus) {
return audio_bus;
@@ -625,8 +625,8 @@ void Area3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_audio_bus_override", "enable"), &Area3D::set_audio_bus_override);
ClassDB::bind_method(D_METHOD("is_overriding_audio_bus"), &Area3D::is_overriding_audio_bus);
- ClassDB::bind_method(D_METHOD("set_audio_bus", "name"), &Area3D::set_audio_bus);
- ClassDB::bind_method(D_METHOD("get_audio_bus"), &Area3D::get_audio_bus);
+ ClassDB::bind_method(D_METHOD("set_audio_bus_name", "name"), &Area3D::set_audio_bus_name);
+ ClassDB::bind_method(D_METHOD("get_audio_bus_name"), &Area3D::get_audio_bus_name);
ClassDB::bind_method(D_METHOD("set_use_reverb_bus", "enable"), &Area3D::set_use_reverb_bus);
ClassDB::bind_method(D_METHOD("is_using_reverb_bus"), &Area3D::is_using_reverb_bus);
@@ -665,7 +665,7 @@ void Area3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
ADD_GROUP("Audio Bus", "audio_bus_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_bus_override"), "set_audio_bus_override", "is_overriding_audio_bus");
- ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "audio_bus_name", PROPERTY_HINT_ENUM, ""), "set_audio_bus", "get_audio_bus");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "audio_bus_name", PROPERTY_HINT_ENUM, ""), "set_audio_bus_name", "get_audio_bus_name");
ADD_GROUP("Reverb Bus", "reverb_bus_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reverb_bus_enable"), "set_use_reverb_bus", "is_using_reverb_bus");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "reverb_bus_name", PROPERTY_HINT_ENUM, ""), "set_reverb_bus", "get_reverb_bus");
diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h
index 7d1f030baf..51f6317517 100644
--- a/scene/3d/area_3d.h
+++ b/scene/3d/area_3d.h
@@ -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 37bc032356..2907eb3c7e 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -484,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/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 191159448a..178c5c8ff8 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -519,8 +519,8 @@ void Camera3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_EXP_RANGE, "0.001,8192,0.001,or_greater"), "set_znear", "get_znear");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01,or_greater"), "set_zfar", "get_zfar");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_EXP_RANGE, "0.001,10,0.001,or_greater"), "set_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/node_3d.cpp b/scene/3d/node_3d.cpp
index e8005f38ed..deb04f0978 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -784,28 +784,4 @@ void Node3D::_bind_methods() {
}
Node3D::Node3D() :
- xform_change(this) {
- data.dirty = DIRTY_NONE;
- data.children_lock = 0;
-
- data.ignore_notification = false;
- data.top_level = false;
- data.top_level_active = false;
- data.scale = Vector3(1, 1, 1);
- data.viewport = nullptr;
- data.inside_world = false;
- data.visible = true;
- data.disable_scale = false;
-
-#ifdef TOOLS_ENABLED
- data.gizmo_disabled = false;
- data.gizmo_dirty = false;
-#endif
- data.notify_local_transform = false;
- data.notify_transform = false;
- data.parent = nullptr;
- data.C = nullptr;
-}
-
-Node3D::~Node3D() {
-}
+ xform_change(this) {}
diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h
index 5fb421c930..180c441a2a 100644
--- a/scene/3d/node_3d.h
+++ b/scene/3d/node_3d.h
@@ -65,32 +65,32 @@ class Node3D : public Node {
mutable Transform global_transform;
mutable Transform local_transform;
mutable Vector3 rotation;
- mutable Vector3 scale;
+ mutable Vector3 scale = Vector3(1, 1, 1);
- mutable int dirty;
+ mutable int dirty = DIRTY_NONE;
- Viewport *viewport;
+ Viewport *viewport = nullptr;
- bool top_level_active;
- bool top_level;
- bool inside_world;
+ bool top_level_active = false;
+ bool top_level = false;
+ bool inside_world = false;
- int children_lock;
- Node3D *parent;
+ int children_lock = 0;
+ Node3D *parent = nullptr;
List<Node3D *> children;
- List<Node3D *>::Element *C;
+ List<Node3D *>::Element *C = nullptr;
- bool ignore_notification;
- bool notify_local_transform;
- bool notify_transform;
+ bool ignore_notification = false;
+ bool notify_local_transform = false;
+ bool notify_transform = false;
- bool visible;
- bool disable_scale;
+ bool visible = true;
+ bool disable_scale = false;
#ifdef TOOLS_ENABLED
Ref<Node3DGizmo> gizmo;
- bool gizmo_disabled;
- bool gizmo_dirty;
+ bool gizmo_disabled = false;
+ bool gizmo_dirty = false;
#endif
} data;
@@ -197,7 +197,6 @@ public:
void force_update_transform();
Node3D();
- ~Node3D();
};
#endif // NODE_3D_H
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index f25a64c567..54e6330722 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -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();
@@ -389,14 +385,3 @@ void PathFollow3D::set_loop(bool p_loop) {
bool PathFollow3D::has_loop() const {
return loop;
}
-
-PathFollow3D::PathFollow3D() {
- offset = 0;
- delta_offset = 0;
- h_offset = 0;
- v_offset = 0;
- path = nullptr;
- rotation_mode = ROTATION_XYZ;
- cubic = true;
- loop = true;
-}
diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h
index 1b0f5fa4e0..39f04f1556 100644
--- a/scene/3d/path_3d.h
+++ b/scene/3d/path_3d.h
@@ -65,14 +65,14 @@ public:
};
private:
- Path3D *path;
- real_t delta_offset; // change in offset since last _update_transform
- real_t offset;
- real_t h_offset;
- real_t v_offset;
- bool cubic;
- bool loop;
- RotationMode rotation_mode;
+ Path3D *path = nullptr;
+ real_t delta_offset = 0; // Change in offset since last _update_transform.
+ real_t offset = 0;
+ real_t h_offset = 0;
+ real_t v_offset = 0;
+ bool cubic = true;
+ bool loop = true;
+ RotationMode rotation_mode = ROTATION_XYZ;
void _update_transform(bool p_update_xyz_rot = true);
@@ -106,7 +106,7 @@ public:
String get_configuration_warning() const override;
- PathFollow3D();
+ PathFollow3D() {}
};
VARIANT_ENUM_CAST(PathFollow3D::RotationMode);
diff --git a/scene/3d/physics_joint_3d.cpp b/scene/3d/physics_joint_3d.cpp
index ab9cdb9fd8..06de5ad0ae 100644
--- a/scene/3d/physics_joint_3d.cpp
+++ b/scene/3d/physics_joint_3d.cpp
@@ -710,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);
@@ -801,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);
@@ -921,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 a65f6db3bf..250ae8bf52 100644
--- a/scene/3d/physics_joint_3d.h
+++ b/scene/3d/physics_joint_3d.h
@@ -300,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();
@@ -324,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/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/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 51bcb411da..0810b7b4ce 100644
--- a/scene/3d/visual_instance_3d.h
+++ b/scene/3d/visual_instance_3d.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/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_player.cpp b/scene/animation/animation_player.cpp
index e9e17148d6..159ccae130 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -36,6 +36,7 @@
#include "servers/audio/audio_stream.h"
#ifdef TOOLS_ENABLED
+#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "scene/2d/skeleton_2d.h"
@@ -53,6 +54,21 @@ void AnimatedValuesBackup::update_skeletons() {
}
}
}
+
+void AnimatedValuesBackup::restore() const {
+ for (int i = 0; i < entries.size(); i++) {
+ const AnimatedValuesBackup::Entry *entry = &entries[i];
+ if (entry->bone_idx == -1) {
+ entry->object->set_indexed(entry->subpath, entry->value);
+ } else {
+ Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value);
+ }
+ }
+}
+
+void AnimatedValuesBackup::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("restore"), &AnimatedValuesBackup::restore);
+}
#endif
bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) {
@@ -1379,6 +1395,14 @@ String AnimationPlayer::get_autoplay() const {
return autoplay;
}
+void AnimationPlayer::set_reset_on_save_enabled(bool p_enabled) {
+ reset_on_save = p_enabled;
+}
+
+bool AnimationPlayer::is_reset_on_save_enabled() const {
+ return reset_on_save;
+}
+
void AnimationPlayer::set_animation_process_mode(AnimationProcessMode p_mode) {
if (animation_process_mode == p_mode) {
return;
@@ -1473,15 +1497,15 @@ void AnimationPlayer::get_argument_options(const StringName &p_function, int p_i
}
#ifdef TOOLS_ENABLED
-AnimatedValuesBackup AnimationPlayer::backup_animated_values() {
+Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values() {
+ Ref<AnimatedValuesBackup> backup;
if (!playback.current.from) {
- return AnimatedValuesBackup();
+ return backup;
}
_ensure_node_caches(playback.current.from);
- AnimatedValuesBackup backup;
-
+ backup.instance();
for (int i = 0; i < playback.current.from->node_cache.size(); i++) {
TrackNodeCache *nc = playback.current.from->node_cache[i];
if (!nc) {
@@ -1497,7 +1521,7 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() {
entry.object = nc->skeleton;
entry.bone_idx = nc->bone_idx;
entry.value = nc->skeleton->get_bone_pose(nc->bone_idx);
- backup.entries.push_back(entry);
+ backup->entries.push_back(entry);
} else {
if (nc->spatial) {
AnimatedValuesBackup::Entry entry;
@@ -1505,7 +1529,7 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() {
entry.subpath.push_back("transform");
entry.value = nc->spatial->get_transform();
entry.bone_idx = -1;
- backup.entries.push_back(entry);
+ backup->entries.push_back(entry);
} else {
for (Map<StringName, TrackNodeCache::PropertyAnim>::Element *E = nc->property_anim.front(); E; E = E->next()) {
AnimatedValuesBackup::Entry entry;
@@ -1515,7 +1539,7 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() {
entry.value = E->value().object->get_indexed(E->value().subpath, &valid);
entry.bone_idx = -1;
if (valid) {
- backup.entries.push_back(entry);
+ backup->entries.push_back(entry);
}
}
}
@@ -1525,15 +1549,40 @@ AnimatedValuesBackup AnimationPlayer::backup_animated_values() {
return backup;
}
-void AnimationPlayer::restore_animated_values(const AnimatedValuesBackup &p_backup) {
- for (int i = 0; i < p_backup.entries.size(); i++) {
- const AnimatedValuesBackup::Entry *entry = &p_backup.entries[i];
- if (entry->bone_idx == -1) {
- entry->object->set_indexed(entry->subpath, entry->value);
- } else {
- Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value);
- }
+Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
+ ERR_FAIL_COND_V(!can_apply_reset(), Ref<AnimatedValuesBackup>());
+
+ Ref<Animation> reset_anim = animation_set["RESET"].animation;
+ ERR_FAIL_COND_V(reset_anim.is_null(), Ref<AnimatedValuesBackup>());
+
+ Node *root_node = get_node_or_null(root);
+ ERR_FAIL_COND_V(!root_node, Ref<AnimatedValuesBackup>());
+
+ AnimationPlayer *aux_player = memnew(AnimationPlayer);
+ EditorNode::get_singleton()->add_child(aux_player);
+ aux_player->set_root(aux_player->get_path_to(root_node));
+ aux_player->add_animation("RESET", reset_anim);
+ aux_player->set_assigned_animation("RESET");
+ Ref<AnimatedValuesBackup> old_values = aux_player->backup_animated_values();
+ aux_player->seek(0.0f, true);
+ aux_player->queue_delete();
+
+ if (p_user_initiated) {
+ Ref<AnimatedValuesBackup> new_values = aux_player->backup_animated_values();
+ old_values->restore();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Anim Apply Reset"));
+ ur->add_do_method(new_values.ptr(), "restore");
+ ur->add_undo_method(old_values.ptr(), "restore");
+ ur->commit_action();
}
+
+ return old_values;
+}
+
+bool AnimationPlayer::can_apply_reset() const {
+ return has_animation("RESET") && playback.assigned != StringName("RESET");
}
#endif
@@ -1577,6 +1626,9 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_autoplay", "name"), &AnimationPlayer::set_autoplay);
ClassDB::bind_method(D_METHOD("get_autoplay"), &AnimationPlayer::get_autoplay);
+ ClassDB::bind_method(D_METHOD("set_reset_on_save_enabled", "enabled"), &AnimationPlayer::set_reset_on_save_enabled);
+ ClassDB::bind_method(D_METHOD("is_reset_on_save_enabled"), &AnimationPlayer::is_reset_on_save_enabled);
+
ClassDB::bind_method(D_METHOD("set_root", "path"), &AnimationPlayer::set_root);
ClassDB::bind_method(D_METHOD("get_root"), &AnimationPlayer::get_root);
@@ -1600,6 +1652,7 @@ void AnimationPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ANIMATE_AS_TRIGGER), "set_current_animation", "get_current_animation");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "assigned_animation", PROPERTY_HINT_NONE, "", 0), "set_assigned_animation", "get_assigned_animation");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_autoplay", "get_autoplay");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_length", PROPERTY_HINT_NONE, "", 0), "", "get_current_animation_length");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_position", PROPERTY_HINT_NONE, "", 0), "", "get_current_animation_position");
@@ -1631,6 +1684,7 @@ AnimationPlayer::AnimationPlayer() {
speed_scale = 1;
end_reached = false;
end_notify = false;
+ reset_on_save = true;
animation_process_mode = ANIMATION_PROCESS_IDLE;
method_call_mode = ANIMATION_METHOD_CALL_DEFERRED;
processing = false;
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index dbce5643c7..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
@@ -215,6 +220,7 @@ private:
bool end_notify;
String autoplay;
+ bool reset_on_save;
AnimationProcessMode animation_process_mode;
AnimationMethodCallMode method_call_mode;
bool processing;
@@ -304,6 +310,9 @@ public:
void set_autoplay(const String &p_name);
String get_autoplay() const;
+ void set_reset_on_save_enabled(bool p_enabled);
+ bool is_reset_on_save_enabled() const;
+
void set_animation_process_mode(AnimationProcessMode p_mode);
AnimationProcessMode get_animation_process_mode() const;
@@ -325,9 +334,9 @@ public:
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
#ifdef TOOLS_ENABLED
- // These may be interesting for games, but are too dangerous for general use
- AnimatedValuesBackup backup_animated_values();
- void restore_animated_values(const AnimatedValuesBackup &p_backup);
+ Ref<AnimatedValuesBackup> backup_animated_values();
+ Ref<AnimatedValuesBackup> apply_reset(bool p_user_initiated = false);
+ bool can_apply_reset() const;
#endif
AnimationPlayer();
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index b471bb9d4e..bc37045386 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -2206,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) {
@@ -2232,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() + ".");
@@ -2260,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;
}
@@ -2310,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
}
@@ -2368,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);
}
}
@@ -2843,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);
@@ -2922,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");
@@ -3043,38 +3043,3 @@ void Control::_bind_methods() {
BIND_VMETHOD(MethodInfo(Variant::BOOL, "has_point", PropertyInfo(Variant::VECTOR2, "point")));
}
-
-Control::Control() {
- data.parent = nullptr;
-
- data.mouse_filter = MOUSE_FILTER_STOP;
-
- data.RI = nullptr;
- data.theme_owner = nullptr;
- data.theme_owner_window = nullptr;
- data.default_cursor = CURSOR_ARROW;
- data.layout_dir = LAYOUT_DIRECTION_INHERITED;
- data.h_size_flags = SIZE_FILL;
- data.v_size_flags = SIZE_FILL;
- data.expand = 1;
- data.rotation = 0;
- data.parent_canvas_item = nullptr;
- data.scale = Vector2(1, 1);
-
- data.block_minimum_size_adjust = false;
- data.disable_visibility_clip = false;
- data.h_grow = GROW_DIRECTION_END;
- data.v_grow = GROW_DIRECTION_END;
- data.minimum_size_valid = false;
- data.updating_last_minimum_size = false;
-
- data.clip_contents = false;
- for (int i = 0; i < 4; i++) {
- data.anchor[i] = ANCHOR_BEGIN;
- data.margin[i] = 0;
- }
- data.focus_mode = FOCUS_NONE;
-}
-
-Control::~Control() {
-}
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 8496729f05..2241c242bb 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -166,48 +166,48 @@ private:
Point2 pos_cache;
Size2 size_cache;
Size2 minimum_size_cache;
- bool minimum_size_valid;
+ bool minimum_size_valid = false;
Size2 last_minimum_size;
- bool updating_last_minimum_size;
+ bool updating_last_minimum_size = false;
- float margin[4];
- float anchor[4];
- FocusMode focus_mode;
- GrowDirection h_grow;
- GrowDirection v_grow;
+ float margin[4] = { 0.0, 0.0, 0.0, 0.0 };
+ float anchor[4] = { ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN };
+ FocusMode focus_mode = FOCUS_NONE;
+ GrowDirection h_grow = GROW_DIRECTION_END;
+ GrowDirection v_grow = GROW_DIRECTION_END;
- LayoutDirection layout_dir;
+ LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;
- float rotation;
- Vector2 scale;
+ float rotation = 0;
+ Vector2 scale = Vector2(1, 1);
Vector2 pivot_offset;
- int h_size_flags;
- int v_size_flags;
- float expand;
+ int h_size_flags = SIZE_FILL;
+ int v_size_flags = SIZE_FILL;
+ float expand = 1;
Point2 custom_minimum_size;
- MouseFilter mouse_filter;
+ MouseFilter mouse_filter = MOUSE_FILTER_STOP;
- bool clip_contents;
+ bool clip_contents = false;
- bool block_minimum_size_adjust;
- bool disable_visibility_clip;
+ bool block_minimum_size_adjust = false;
+ bool disable_visibility_clip = false;
- Control *parent;
+ Control *parent = nullptr;
ObjectID drag_owner;
Ref<Theme> theme;
- Control *theme_owner;
- Window *theme_owner_window;
+ Control *theme_owner = nullptr;
+ Window *theme_owner_window = nullptr;
String tooltip;
- CursorShape default_cursor;
+ CursorShape default_cursor = CURSOR_ARROW;
- List<Control *>::Element *RI;
+ List<Control *>::Element *RI = nullptr;
- CanvasItem *parent_canvas_item;
+ CanvasItem *parent_canvas_item = nullptr;
- NodePath focus_neighbour[4];
+ NodePath focus_neighbor[4];
NodePath focus_next;
NodePath focus_prev;
@@ -223,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);
@@ -436,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;
@@ -518,8 +518,7 @@ public:
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
virtual String get_configuration_warning() const override;
- Control();
- ~Control();
+ Control() {}
};
VARIANT_ENUM_CAST(Control::FocusMode);
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 4f59f4a36a..4e80498108 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -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) {
@@ -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 eb3d5d5c6d..041b8ef174 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -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"));
}
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 96810e8707..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,6 +692,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting = false;
top_layer->update();
+ minimap->update();
update();
connections_layer->update();
}
@@ -632,12 +784,12 @@ void GraphEdit::_bake_segment2d(Vector<Vector2> &points, Vector<Color> &colors,
}
}
-void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color) {
+void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_bezier_ratio = 1.0) {
//cubic bezier code
float diff = p_to.x - p_from.x;
float cp_offset;
- int cp_len = get_theme_constant("bezier_len_pos");
- int cp_neg_len = get_theme_constant("bezier_len_neg");
+ int cp_len = get_theme_constant("bezier_len_pos") * p_bezier_ratio;
+ int cp_neg_len = get_theme_constant("bezier_len_neg") * p_bezier_ratio;
if (diff > 0) {
cp_offset = MIN(cp_len, diff * 0.5);
@@ -659,9 +811,9 @@ void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const
colors.push_back(p_to_color);
#ifdef TOOLS_ENABLED
- p_where->draw_polyline_colors(points, colors, Math::floor(2 * EDSCALE), true);
+ p_where->draw_polyline_colors(points, colors, Math::floor(p_width * EDSCALE), lines_antialiased);
#else
- p_where->draw_polyline_colors(points, colors, 2, true);
+ 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));
@@ -836,6 +1096,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
}
top_layer->update();
+ minimap->update();
}
Ref<InputEventMouseButton> b = p_ev;
@@ -858,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());
}
@@ -902,6 +1165,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
dragging = false;
top_layer->update();
+ minimap->update();
update();
connections_layer->update();
}
@@ -1012,6 +1276,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
box_selecting = false;
previus_selected.clear();
top_layer->update();
+ minimap->update();
}
if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) {
@@ -1079,6 +1344,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por
if (Math::is_equal_approx(E->get().activity, p_activity)) {
//update only if changed
top_layer->update();
+ minimap->update();
connections_layer->update();
}
E->get().activity = p_activity;
@@ -1089,6 +1355,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por
void GraphEdit::clear_connections() {
connections.clear();
+ minimap->update();
update();
connections_layer->update();
}
@@ -1112,6 +1379,7 @@ void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) {
top_layer->update();
_update_scroll();
+ minimap->update();
connections_layer->update();
if (is_visible_in_tree()) {
@@ -1229,6 +1497,63 @@ void GraphEdit::_snap_value_changed(double) {
update();
}
+void GraphEdit::set_minimap_size(Vector2 p_size) {
+ minimap->set_size(p_size);
+ Vector2 minimap_size = minimap->get_size(); // The size might've been adjusted by the minimum size.
+
+ minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT);
+ minimap->set_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;
}
@@ -1260,6 +1585,20 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_snap", "enable"), &GraphEdit::set_use_snap);
ClassDB::bind_method(D_METHOD("is_using_snap"), &GraphEdit::is_using_snap);
+ ClassDB::bind_method(D_METHOD("set_connection_lines_thickness", "pixels"), &GraphEdit::set_connection_lines_thickness);
+ ClassDB::bind_method(D_METHOD("get_connection_lines_thickness"), &GraphEdit::get_connection_lines_thickness);
+
+ ClassDB::bind_method(D_METHOD("set_connection_lines_antialiased", "pixels"), &GraphEdit::set_connection_lines_antialiased);
+ ClassDB::bind_method(D_METHOD("is_connection_lines_antialiased"), &GraphEdit::is_connection_lines_antialiased);
+
+ ClassDB::bind_method(D_METHOD("set_minimap_size", "p_size"), &GraphEdit::set_minimap_size);
+ ClassDB::bind_method(D_METHOD("get_minimap_size"), &GraphEdit::get_minimap_size);
+ ClassDB::bind_method(D_METHOD("set_minimap_opacity", "p_opacity"), &GraphEdit::set_minimap_opacity);
+ ClassDB::bind_method(D_METHOD("get_minimap_opacity"), &GraphEdit::get_minimap_opacity);
+
+ ClassDB::bind_method(D_METHOD("set_minimap_enabled", "enable"), &GraphEdit::set_minimap_enabled);
+ ClassDB::bind_method(D_METHOD("is_minimap_enabled"), &GraphEdit::is_minimap_enabled);
+
ClassDB::bind_method(D_METHOD("set_right_disconnects", "enable"), &GraphEdit::set_right_disconnects);
ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled);
@@ -1275,6 +1614,12 @@ void GraphEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_distance"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_snap"), "set_use_snap", "is_using_snap");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_thickness"), "set_connection_lines_thickness", "get_connection_lines_thickness");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "connection_lines_antialiased"), "set_connection_lines_antialiased", "is_connection_lines_antialiased");
+ ADD_GROUP("Minimap", "minimap");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_enabled"), "set_minimap_enabled", "is_minimap_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimap_size"), "set_minimap_size", "get_minimap_size");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "minimap_opacity"), "set_minimap_opacity", "get_minimap_opacity");
ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot")));
ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot")));
@@ -1380,6 +1725,32 @@ GraphEdit::GraphEdit() {
snap_amount->connect("value_changed", callable_mp(this, &GraphEdit::_snap_value_changed));
zoom_hb->add_child(snap_amount);
+ minimap_button = memnew(Button);
+ minimap_button->set_flat(true);
+ minimap_button->set_toggle_mode(true);
+ minimap_button->set_tooltip(RTR("Enable grid minimap."));
+ minimap_button->connect("pressed", callable_mp(this, &GraphEdit::_minimap_toggled));
+ minimap_button->set_pressed(true);
+ minimap_button->set_focus_mode(FOCUS_NONE);
+ zoom_hb->add_child(minimap_button);
+
+ Vector2 minimap_size = Vector2(240, 160);
+ float minimap_opacity = 0.65;
+
+ minimap = memnew(GraphEditMinimap(this));
+ top_layer->add_child(minimap);
+ minimap->set_name("_minimap");
+ minimap->set_modulate(Color(1, 1, 1, minimap_opacity));
+ minimap->set_mouse_filter(MOUSE_FILTER_STOP);
+ minimap->set_custom_minimum_size(Vector2(50, 50));
+ minimap->set_size(minimap_size);
+ minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT);
+ minimap->set_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 d87bd41f27..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();
@@ -116,9 +158,12 @@ private:
bool awaiting_scroll_offset_update;
List<Connection> connections;
+ float lines_thickness = 2.0f;
+ bool lines_antialiased = true;
+
void _bake_segment2d(Vector<Vector2> &points, Vector<Color> &colors, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_min_depth, int p_max_depth, float p_tol, const Color &p_color, const Color &p_to_color, int &lines) const;
- void _draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color);
+ void _draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_bezier_ratio);
void _graph_node_raised(Node *p_gn);
void _graph_node_moved(Node *p_gn);
@@ -129,12 +174,14 @@ private:
Control *connections_layer;
GraphEditFilter *top_layer;
+ GraphEditMinimap *minimap;
void _top_layer_input(const Ref<InputEvent> &p_ev);
bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos);
void _top_layer_draw();
void _connections_layer_draw();
+ void _minimap_draw();
void _update_scroll_offset();
Array _get_connection_list() const;
@@ -171,6 +218,9 @@ private:
void _snap_toggled();
void _snap_value_changed(double);
+ friend class GraphEditMinimap;
+ void _minimap_toggled();
+
bool _check_clickable_control(Control *p_control, const Vector2 &pos);
protected:
@@ -196,7 +246,16 @@ public:
void set_zoom_custom(float p_zoom, const Vector2 &p_center);
float get_zoom() const;
+ void set_minimap_size(Vector2 p_size);
+ Vector2 get_minimap_size() const;
+ void set_minimap_opacity(float p_opacity);
+ float get_minimap_opacity() const;
+
+ void set_minimap_enabled(bool p_enable);
+ bool is_minimap_enabled() const;
+
GraphEditFilter *get_top_layer() const { return top_layer; }
+ GraphEditMinimap *get_minimap() const { return minimap; }
void get_connection_list(List<Connection> *r_connections) const;
void set_right_disconnects(bool p_enable);
@@ -219,6 +278,12 @@ public:
int get_snap() const;
void set_snap(int p_snap);
+ void set_connection_lines_thickness(float p_thickness);
+ float get_connection_lines_thickness() const;
+
+ void set_connection_lines_antialiased(bool p_antialiased);
+ bool is_connection_lines_antialiased() const;
+
HBoxContainer *get_zoom_hbox();
GraphEdit();
diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp
index acbb6d7ab5..28cc056d6e 100644
--- a/scene/gui/panel.cpp
+++ b/scene/gui/panel.cpp
@@ -63,6 +63,3 @@ Panel::Panel() {
// Has visible stylebox, so stop by default.
set_mouse_filter(MOUSE_FILTER_STOP);
}
-
-Panel::~Panel() {
-}
diff --git a/scene/gui/panel.h b/scene/gui/panel.h
index a68c3d3f0c..e2c1ddc91d 100644
--- a/scene/gui/panel.h
+++ b/scene/gui/panel.h
@@ -54,7 +54,6 @@ public:
Mode get_mode() const;
Panel();
- ~Panel();
};
VARIANT_ENUM_CAST(Panel::Mode)
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 6dbf005f73..07f03ad40e 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -232,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;
@@ -245,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;
@@ -261,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)) {
@@ -446,6 +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");
+ 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;
@@ -548,7 +581,7 @@ void PopupMenu::_draw_items() {
if (items[i].separator) {
if (text != String()) {
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_disabled);
+ 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;
diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp
index 76ca8abcc7..5b201f45d3 100644
--- a/scene/gui/rich_text_effect.cpp
+++ b/scene/gui/rich_text_effect.cpp
@@ -64,11 +64,8 @@ RichTextEffect::RichTextEffect() {
}
void CharFXTransform::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_relative_index"), &CharFXTransform::get_relative_index);
- ClassDB::bind_method(D_METHOD("set_relative_index", "index"), &CharFXTransform::set_relative_index);
-
- ClassDB::bind_method(D_METHOD("get_absolute_index"), &CharFXTransform::get_absolute_index);
- ClassDB::bind_method(D_METHOD("set_absolute_index", "index"), &CharFXTransform::set_absolute_index);
+ ClassDB::bind_method(D_METHOD("get_range"), &CharFXTransform::get_range);
+ ClassDB::bind_method(D_METHOD("set_range", "range"), &CharFXTransform::set_range);
ClassDB::bind_method(D_METHOD("get_elapsed_time"), &CharFXTransform::get_elapsed_time);
ClassDB::bind_method(D_METHOD("set_elapsed_time", "time"), &CharFXTransform::set_elapsed_time);
@@ -76,6 +73,9 @@ void CharFXTransform::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_visible"), &CharFXTransform::is_visible);
ClassDB::bind_method(D_METHOD("set_visibility", "visibility"), &CharFXTransform::set_visibility);
+ ClassDB::bind_method(D_METHOD("is_outline"), &CharFXTransform::is_outline);
+ ClassDB::bind_method(D_METHOD("set_outline", "outline"), &CharFXTransform::set_outline);
+
ClassDB::bind_method(D_METHOD("get_offset"), &CharFXTransform::get_offset);
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &CharFXTransform::set_offset);
@@ -85,27 +85,24 @@ void CharFXTransform::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_environment"), &CharFXTransform::get_environment);
ClassDB::bind_method(D_METHOD("set_environment", "environment"), &CharFXTransform::set_environment);
- ClassDB::bind_method(D_METHOD("get_character"), &CharFXTransform::get_character);
- ClassDB::bind_method(D_METHOD("set_character", "character"), &CharFXTransform::set_character);
+ ClassDB::bind_method(D_METHOD("get_glyph_index"), &CharFXTransform::get_glyph_index);
+ ClassDB::bind_method(D_METHOD("set_glyph_index", "glyph_index"), &CharFXTransform::set_glyph_index);
+
+ ClassDB::bind_method(D_METHOD("get_font"), &CharFXTransform::get_font);
+ ClassDB::bind_method(D_METHOD("set_font", "font"), &CharFXTransform::set_font);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_index"), "set_relative_index", "get_relative_index");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "absolute_index"), "set_absolute_index", "get_absolute_index");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "range"), "set_range", "get_range");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "elapsed_time"), "set_elapsed_time", "get_elapsed_time");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visibility", "is_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "outline"), "set_outline", "is_outline");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "env"), "set_environment", "get_environment");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "character"), "set_character", "get_character");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "glyph_index"), "set_glyph_index", "get_glyph_index");
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "font"), "set_font", "get_font");
}
CharFXTransform::CharFXTransform() {
- relative_index = 0;
- absolute_index = 0;
- visibility = true;
- offset = Point2();
- color = Color();
- character = 0;
- elapsed_time = 0.0f;
}
CharFXTransform::~CharFXTransform() {
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
index e6b9f09e4d..1aa62c1c83 100644
--- a/scene/gui/rich_text_effect.h
+++ b/scene/gui/rich_text_effect.h
@@ -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 e3b645591c..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++;
+ }
+
+ 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;
+ }
+ }
- 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);
+ //Apply BiDi override.
+ l.text_buf->set_bidi_override(structured_text_parser(_find_stt(l.from), st_args, text));
- const bool previously_visible = visible;
+ *r_char_offset = l.char_offset + l.char_count;
- for (int j = 0; j < fx_stack.size(); j++) {
- ItemFX *item_fx = fx_stack[j];
+ 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;
+ }
+}
- if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
- ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
+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;
- Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
- Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
+ ERR_FAIL_COND_V(p_frame == nullptr, off.y);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), off.y);
- 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;
+ Line &l = p_frame->lines.write[p_line];
- bool effect_status = custom_effect->_process_effect_impl(charfx);
- custom_fx_ok = effect_status;
+ 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;
- 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);
- }
- }
+ if (it_from == nullptr) {
+ return off.y;
+ }
- if (visible) {
- line_is_blank = false;
- w += font->get_char_size(c[i], c[i + 1]).x;
- }
+ RID ci = get_canvas_item();
+ bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL);
+ bool lrtl = is_layout_rtl();
- if (c[i] == '\t') {
- visible = false;
- }
+ Vector<int> list_index;
+ Vector<ItemList *> list_items;
+ _find_list(l.from, list_index, list_items);
- 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);
- }
+ 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_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], -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 (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], -1, p_font_color_shadow);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], -1, p_font_color_shadow);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y) + fx_offset, fx_char, c[i + 1], -1, p_font_color_shadow);
- }
- }
+ float width = l.text_buf->get_width();
+ float length = TS->shaped_text_get_width(rid);
- if (selected) {
- font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], -1, override_selected_font_color ? selection_fg : fx_color);
- } else {
- cw = font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], -1, fx_color);
- }
- } else if (previously_visible && c[i] != '\t') {
- backtrack += font->get_char_size(fx_char, c[i + 1]).x;
- }
+ // Draw line.
- p_char_count++;
- if (c[i] == '\t') {
- cw = tab_size * font->get_char_size(' ').width;
- backtrack = MAX(0, backtrack - cw);
+ 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;
+ }
+ }
+
+ // 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++;
+ gloff.x += glyphs[i].advance;
+ }
+ }
- ADVANCE(img->size.width);
- CHECK_HEIGHT((img->size.height + font->get_descent()));
+ // Draw main text.
+ Color selection_fg = get_theme_color("font_color_selected");
+ Color selection_bg = get_theme_color("selection_color");
- } break;
- case ITEM_NEWLINE: {
- lh = 0;
+ int sel_start = -1;
+ int sel_end = -1;
- if (p_mode != PROCESS_CACHE) {
- lh = line < l.height_caches.size() ? l.height_caches[line] : 1;
- line_is_blank = true;
- }
+ 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);
- } 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;
-
- 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++;
+ }
+
+ if (from_line >= main->lines.size()) {
+ return;
+ }
- int ly = 0;
- int yofs = 0;
+ 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;
+
+ int idx = 0;
+ int col_count = table->columns.size();
+ int row_count = table->rows.size();
- Item *itp = it;
+ for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
+ ItemFrame *frame = static_cast<ItemFrame *>(E->get());
- it = _get_next_item(it);
+ int col = idx % col_count;
+ int row = idx / col_count;
- 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;
+ if (frame->lines.size() != 0 && row < row_count) {
+ Vector2 coff = frame->lines[0].offset;
+ if (rtl) {
+ coff.x = rect.size.width - table->columns[col].width - coff.x;
+ }
+ Rect2 crect = Rect2(p_ofs + off + rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size);
+ if (col == col_count - 1) {
+ if (rtl) {
+ crect.size.x = crect.position.x + crect.size.x;
+ crect.position.x = 0;
+ } else {
+ crect.size.x = get_size().x;
+ }
+ }
+ if (crect.has_point(p_click)) {
+ for (int j = 0; j < frame->lines.size(); j++) {
+ _find_click_in_line(frame, j, p_ofs + off + rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char);
+ if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) {
+ return off.y;
+ }
+ }
+ }
+ }
+ idx++;
+ }
+ } break;
+ default:
+ break;
+ }
}
- *r_click_item = itp;
- *r_click_char = rchar;
- RETURN;
}
+ }
+ Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)), Size2(get_size().x, TS->shaped_text_get_size(rid).y));
- break;
+ if (rect.has_point(p_click) && !table_hit) {
+ char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
}
+ off.y += TS->shaped_text_get_descent(rid);
}
- NEW_LINE;
- RETURN;
+ if (char_pos >= 0) {
+ // Find item.
+ if (r_click_item != nullptr) {
+ Item *it = p_frame->lines[p_line].from;
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ it = _get_item_at_pos(it, it_to, char_pos);
+ *r_click_item = it;
+ }
+
+ if (r_click_frame != nullptr) {
+ *r_click_frame = p_frame;
+ }
+
+ if (r_click_line != nullptr) {
+ *r_click_line = p_line;
+ }
+
+ if (r_click_char != nullptr) {
+ *r_click_char = char_pos;
+ }
+ }
-#undef RETURN
-#undef NEW_LINE
-#undef ENSURE_WIDTH
-#undef ADVANCE
-#undef CHECK_HEIGHT
+ 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;
- 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.to_frame = c_frame;
+ selection.to_line = c_line;
+ selection.to_item = c_item;
+ selection.to_char = words[i].y;
+
+ 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;
+
+ while (item) {
+ if (item->type == ITEM_LIST) {
+ return static_cast<ItemList *>(item);
+ }
+ item = item->parent;
+ }
+
+ return nullptr;
+}
+
+int RichTextLabel::_find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list) {
+ Item *item = p_item;
+ Item *prev_item = p_item;
+
+ int level = 0;
+
+ while (item) {
+ if (item->type == ITEM_LIST) {
+ ItemList *list = static_cast<ItemList *>(item);
+
+ ItemFrame *frame = nullptr;
+ int line = -1;
+ _find_frame(list, &frame, &line);
+
+ int index = 1;
+ if (frame != nullptr) {
+ for (int i = list->line + 1; i <= prev_item->line; i++) {
+ if (_find_list_item(frame->lines[i].from) == list) {
+ index++;
+ }
+ }
+ }
+
+ r_index.push_back(index);
+ r_list.push_back(list);
+
+ prev_item = item;
+ }
+ level++;
+ item = item->parent;
+ }
+
+ return level;
+}
+
+int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size) {
Item *item = p_item;
- int margin = 0;
+ float margin = 0;
while (item) {
if (item->type == ITEM_INDENT) {
@@ -1348,16 +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,6 +1907,21 @@ Color RichTextLabel::_find_color(Item *p_item, const Color &p_default_color) {
return p_default_color;
}
+Color RichTextLabel::_find_outline_color(Item *p_item, const Color &p_default_color) {
+ Item *item = p_item;
+
+ while (item) {
+ if (item->type == ITEM_OUTLINE_COLOR) {
+ ItemOutlineColor *color = static_cast<ItemOutlineColor *>(item);
+ return color->color;
+ }
+
+ item = item->parent;
+ }
+
+ return p_default_color;
+}
+
bool RichTextLabel::_find_underline(Item *p_item) {
Item *item = p_item;
@@ -1516,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);
@@ -1555,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) {
@@ -1627,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;
@@ -1672,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;
}
@@ -1682,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
@@ -1750,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;
}
@@ -1799,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);
@@ -1807,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);
@@ -1821,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);
}
@@ -1838,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);
}
@@ -1856,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) {
@@ -1920,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);
@@ -1928,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 {
@@ -1958,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;
}
@@ -1972,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();
}
@@ -2121,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--;
}
@@ -2163,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") {
@@ -2183,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") {
@@ -2195,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) {
@@ -2237,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();
@@ -2275,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;
@@ -2287,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());
@@ -2299,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;
@@ -2431,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 {
@@ -2458,95 +3330,160 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) {
}
}
-bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) {
- ERR_FAIL_COND_V(!selection.enabled, false);
- Item *it = main;
- int charidx = 0;
-
- if (p_from_selection && selection.active) {
- it = selection.to;
- charidx = selection.to_char + 1;
- }
-
- while (it) {
- if (it->type == ITEM_TEXT) {
- ItemText *t = static_cast<ItemText *>(it);
- int sp = t->text.findn(p_string, charidx);
- if (sp != -1) {
- selection.from = it;
- selection.from_char = sp;
- selection.to = it;
- selection.to_char = sp + p_string.length() - 1;
- selection.active = true;
- update();
-
- _validate_line_caches(main);
+bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p_string, Item *p_from, Item *p_to) {
+ ERR_FAIL_COND_V(p_frame == nullptr, false);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), false);
- int fh = _find_font(t).is_valid() ? _find_font(t)->get_height() : get_theme_font("normal_font")->get_height();
+ Line &l = p_frame->lines.write[p_line];
- float offset = 0;
+ String text;
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ switch (it->type) {
+ case ITEM_NEWLINE: {
+ text += "\n";
+ } break;
+ case ITEM_TEXT: {
+ ItemText *t = (ItemText *)it;
+ text += t->text;
+ } break;
+ case ITEM_IMAGE: {
+ text += " ";
+ } break;
+ case ITEM_TABLE: {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ int idx = 0;
+ for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
+ ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E->get());
- int line = t->line;
- Item *item = t;
- while (item) {
- if (item->type == ITEM_FRAME) {
- ItemFrame *frame = static_cast<ItemFrame *>(item);
- if (line >= 0 && line < frame->lines.size()) {
- offset += frame->lines[line].height_accum_cache - frame->lines[line].height_cache;
- line = frame->line;
+ for (int i = 0; i < frame->lines.size(); i++) {
+ if (_search_line(frame, i, p_string, p_from, p_to)) {
+ return true;
}
}
- item = item->parent;
+ idx++;
}
- vscroll->set_value(offset - fh);
+ } break;
+ default:
+ break;
+ }
+ }
+ int sp = text.findn(p_string, 0);
+ if (sp != -1) {
+ selection.from_frame = p_frame;
+ selection.from_line = p_line;
+ selection.from_item = _get_item_at_pos(l.from, it_to, sp);
+ selection.from_char = sp;
+ selection.to_frame = p_frame;
+ selection.to_line = p_line;
+ selection.to_item = _get_item_at_pos(l.from, it_to, sp + p_string.length() - 1);
+ selection.to_char = sp + p_string.length() - 1;
+ selection.active = true;
+ return true;
+ }
+
+ return false;
+}
+bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) {
+ ERR_FAIL_COND_V(!selection.enabled, false);
+
+ if (p_from_selection && selection.active) {
+ for (int i = 0; i < main->lines.size(); i++) {
+ if (_search_line(main, i, p_string, selection.from_item, selection.to_item)) {
+ update();
return true;
}
}
-
- if (p_search_previous) {
- it = _get_prev_item(it, true);
- } else {
- it = _get_next_item(it, true);
+ } else {
+ for (int i = 0; i < main->lines.size(); i++) {
+ if (_search_line(main, i, p_string, nullptr, nullptr)) {
+ update();
+ return true;
+ }
}
- charidx = 0;
}
return false;
}
-String RichTextLabel::get_selected_text() {
- if (!selection.active || !selection.enabled) {
- return "";
- }
-
+String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p_selection) {
String text;
+ ERR_FAIL_COND_V(p_frame == nullptr, text);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), text);
- RichTextLabel::Item *item = selection.from;
-
- while (item) {
- if (item->type == ITEM_TEXT) {
- String itext = static_cast<ItemText *>(item)->text;
- if (item == selection.from && item == selection.to) {
- text += itext.substr(selection.from_char, selection.to_char - selection.from_char + 1);
- } else if (item == selection.from) {
- text += itext.substr(selection.from_char, itext.size());
- } else if (item == selection.to) {
- text += itext.substr(0, selection.to_char + 1);
- } else {
- text += itext;
- }
+ Line &l = p_frame->lines.write[p_line];
- } else if (item->type == ITEM_NEWLINE) {
- text += "\n";
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ int end_idx = 0;
+ if (it_to != nullptr) {
+ end_idx = it_to->index;
+ } else {
+ for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ end_idx = it->index + 1;
+ }
+ }
+ for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ if ((p_selection.to_item != nullptr) && (p_selection.to_item->index < l.from->index)) {
+ break;
}
- if (item == selection.to) {
+ if ((p_selection.from_item != nullptr) && (p_selection.from_item->index >= end_idx)) {
break;
}
+ switch (it->type) {
+ case ITEM_NEWLINE: {
+ text += "\n";
+ } break;
+ case ITEM_TEXT: {
+ ItemText *t = (ItemText *)it;
+ text += t->text;
+ } break;
+ case ITEM_IMAGE: {
+ text += " ";
+ } break;
+ case ITEM_TABLE: {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ int idx = 0;
+ int col_count = table->columns.size();
+ for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
+ ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E->get());
+ int column = idx % col_count;
+
+ for (int i = 0; i < frame->lines.size(); i++) {
+ text += _get_line_text(frame, i, p_selection);
+ }
+ if (column == col_count - 1) {
+ text += "\n";
+ } else {
+ text += " ";
+ }
+ idx++;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ if ((l.from != nullptr) && (p_frame == p_selection.to_frame) && (p_selection.to_item != nullptr) && (p_selection.to_item->index >= l.from->index) && (p_selection.to_item->index < end_idx)) {
+ text = text.substr(0, p_selection.to_char);
+ }
+ if ((l.from != nullptr) && (p_frame == p_selection.from_frame) && (p_selection.from_item != nullptr) && (p_selection.from_item->index >= l.from->index) && (p_selection.from_item->index < end_idx)) {
+ text = text.substr(p_selection.from_char, -1);
+ }
+ return text;
+}
- item = _get_next_item(item, true);
+String RichTextLabel::get_selected_text() {
+ if (!selection.active || !selection.enabled) {
+ return "";
}
+ String text;
+ for (int i = 0; i < main->lines.size(); i++) {
+ text += _get_line_text(main, i, selection);
+ }
return text;
}
@@ -2597,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);
@@ -2610,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;
}
@@ -2657,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;
}
@@ -2667,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);
@@ -2764,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)));
@@ -2775,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);
@@ -2782,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);
@@ -2915,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;
@@ -2940,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 2c74eb741d..93e48dd449 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -33,6 +33,7 @@
#include "rich_text_effect.h"
#include "scene/gui/scroll_bar.h"
+#include "scene/resources/text_paragraph.h"
class RichTextLabel : public Control {
GDCLASS(RichTextLabel, Control);
@@ -48,6 +49,7 @@ public:
enum ListType {
LIST_NUMBERS,
LIST_LETTERS,
+ LIST_ROMAN,
LIST_DOTS
};
@@ -57,10 +59,14 @@ public:
ITEM_IMAGE,
ITEM_NEWLINE,
ITEM_FONT,
+ ITEM_FONT_SIZE,
+ ITEM_FONT_FEATURES,
ITEM_COLOR,
+ ITEM_OUTLINE_SIZE,
+ ITEM_OUTLINE_COLOR,
ITEM_UNDERLINE,
ITEM_STRIKETHROUGH,
- ITEM_ALIGN,
+ ITEM_PARAGRAPH,
ITEM_INDENT,
ITEM_LIST,
ITEM_TABLE,
@@ -80,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()) {
@@ -113,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 {
@@ -145,6 +142,7 @@ private:
struct ItemImage : public Item {
Ref<Texture2D> image;
+ VAlign inline_align = VALIGN_TOP;
Size2 size;
Color color;
ItemImage() { type = ITEM_IMAGE; }
@@ -155,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; }
};
@@ -173,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; }
};
@@ -202,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;
@@ -251,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 {
@@ -291,7 +296,6 @@ private:
ItemCustomFX() {
type = ITEM_CUSTOMFX;
-
char_fx_transform.instance();
}
@@ -316,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;
@@ -336,23 +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?
@@ -364,13 +371,34 @@ 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);
@@ -394,8 +422,6 @@ private:
bool use_bbcode;
String bbcode;
- void _update_all_lines();
-
int fixed_width;
bool fit_content_height;
@@ -406,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);
@@ -430,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();
@@ -484,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/tab_container.cpp b/scene/gui/tab_container.cpp
index 01c1a15b79..809b4ffd64 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -568,16 +568,12 @@ void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, in
text_buf[p_index]->draw(canvas, text_pos, p_font_color);
}
-void TabContainer::_on_theme_changed() {
- if (!_theme_changing) {
- return;
- }
-
+void TabContainer::_refresh_texts() {
text_buf.clear();
+ Vector<Control *> tabs = _get_tabs();
bool rtl = is_layout_rtl();
Ref<Font> font = get_theme_font("font");
int font_size = get_theme_font_size("font_size");
- 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()));
@@ -587,6 +583,14 @@ void TabContainer::_on_theme_changed() {
name->add_string(text, font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
text_buf.push_back(name);
}
+}
+
+void TabContainer::_on_theme_changed() {
+ if (!_theme_changing) {
+ return;
+ }
+
+ _refresh_texts();
minimum_size_changed();
if (get_tab_count() > 0) {
@@ -679,21 +683,7 @@ 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);
- }
-
+ _refresh_texts();
update();
}
@@ -708,20 +698,8 @@ void TabContainer::add_child_notify(Node *p_child) {
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);
- }
+ _refresh_texts();
bool first = false;
@@ -743,7 +721,6 @@ void TabContainer::add_child_notify(Node *p_child) {
c->set_margin(Margin(MARGIN_LEFT), c->get_margin(Margin(MARGIN_LEFT)) + sb->get_margin(Margin(MARGIN_LEFT)));
c->set_margin(Margin(MARGIN_RIGHT), c->get_margin(Margin(MARGIN_RIGHT)) - sb->get_margin(Margin(MARGIN_RIGHT)));
c->set_margin(Margin(MARGIN_BOTTOM), c->get_margin(Margin(MARGIN_BOTTOM)) - sb->get_margin(Margin(MARGIN_BOTTOM)));
-
update();
p_child->connect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback));
if (first && is_inside_tree()) {
@@ -751,6 +728,13 @@ void TabContainer::add_child_notify(Node *p_child) {
}
}
+void TabContainer::move_child_notify(Node *p_child) {
+ Container::move_child_notify(p_child);
+ call_deferred("_update_current_tab");
+ _refresh_texts();
+ update();
+}
+
int TabContainer::get_tab_count() const {
return _get_tabs().size();
}
@@ -813,20 +797,8 @@ void TabContainer::remove_child_notify(Node *p_child) {
}
void TabContainer::_update_current_tab() {
- 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);
- }
+ _refresh_texts();
int tc = tabs.size();
if (current >= tc) {
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 91153e5fc3..9ff56afe6e 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -73,12 +73,14 @@ private:
void _on_mouse_exited();
void _update_current_tab();
void _draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x);
+ void _refresh_texts();
protected:
void _child_renamed_callback();
void _gui_input(const Ref<InputEvent> &p_event);
void _notification(int p_what);
virtual void add_child_notify(Node *p_child) override;
+ virtual void move_child_notify(Node *p_child) override;
virtual void remove_child_notify(Node *p_child) override;
Variant get_drag_data(const Point2 &p_point) override;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 6b16806789..f3569f9ce3 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1650,20 +1650,23 @@ void TextEdit::_notification(int p_what) {
if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
- DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + _get_cursor_pixel_pos(), get_viewport()->get_window_id());
+ DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + _get_cursor_pixel_pos(false), get_viewport()->get_window_id());
}
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
- String text = _base_get_text(0, 0, selection.selecting_line, selection.selecting_column);
- int cursor_start = text.length();
+ int cursor_start = -1;
int cursor_end = -1;
- if (selection.active) {
- String selected_text = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
+ if (!selection.active) {
+ String full_text = _base_get_text(0, 0, cursor.line, cursor.column);
- if (selected_text.length() > 0) {
- cursor_end = cursor_start + selected_text.length();
- }
+ cursor_start = full_text.length();
+ } else {
+ String pre_text = _base_get_text(0, 0, selection.from_line, selection.from_column);
+ String post_text = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
+
+ cursor_start = pre_text.length();
+ cursor_end = cursor_start + post_text.length();
}
DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true, -1, cursor_start, cursor_end);
@@ -2072,8 +2075,10 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
r_col = col;
}
-Vector2i TextEdit::_get_cursor_pixel_pos() {
- adjust_viewport_to_cursor();
+Vector2i TextEdit::_get_cursor_pixel_pos(bool p_adjust_viewport) {
+ if (p_adjust_viewport) {
+ adjust_viewport_to_cursor();
+ }
int row = 1;
for (int i = get_first_visible_line(); i < cursor.line; i++) {
if (!is_line_hidden(i)) {
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index c2fe4afec5..3f16ed1366 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -700,7 +700,7 @@ public:
int cursor_get_column() const;
int cursor_get_line() const;
- Vector2i _get_cursor_pixel_pos();
+ Vector2i _get_cursor_pixel_pos(bool p_adjust_viewport = true);
bool cursor_get_blink_enabled() const;
void cursor_set_blink_enabled(const bool p_enabled);
diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress_bar.cpp
index e0d98d1c22..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/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/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 43350fae37..8babb1460f 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -292,15 +292,10 @@ void CanvasItemMaterial::_bind_methods() {
CanvasItemMaterial::CanvasItemMaterial() :
element(this) {
- blend_mode = BLEND_MODE_MIX;
- light_mode = LIGHT_MODE_NORMAL;
- particles_animation = false;
-
set_particles_anim_h_frames(1);
set_particles_anim_v_frames(1);
set_particles_anim_loop(false);
- current_key.key = 0;
current_key.invalid_key = 1;
_queue_shader_change();
}
@@ -1411,30 +1406,7 @@ CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const {
CanvasItem::CanvasItem() :
xform_change(this) {
- window = nullptr;
canvas_item = RenderingServer::get_singleton()->canvas_item_create();
- visible = true;
- pending_update = false;
- modulate = Color(1, 1, 1, 1);
- self_modulate = Color(1, 1, 1, 1);
- top_level = false;
- first_draw = false;
- drawing = false;
- behind = false;
- clip_children = false;
- block_transform_notify = false;
- canvas_layer = nullptr;
- use_parent_material = false;
- global_invalid = true;
- notify_local_transform = false;
- notify_transform = false;
- light_mask = 1;
- texture_repeat = TEXTURE_REPEAT_PARENT_NODE;
- texture_filter = TEXTURE_FILTER_PARENT_NODE;
- texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
- texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
-
- C = nullptr;
}
CanvasItem::~CanvasItem() {
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index 3cde6b69c1..34268c1a78 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -73,7 +73,7 @@ private:
uint32_t invalid_key : 1;
};
- uint32_t key;
+ uint32_t key = 0;
bool operator<(const MaterialKey &p_key) const {
return key < p_key.key;
@@ -114,10 +114,11 @@ private:
_FORCE_INLINE_ void _queue_shader_change();
_FORCE_INLINE_ bool _is_shader_dirty() const;
- BlendMode blend_mode;
- LightMode light_mode;
- bool particles_animation;
+ BlendMode blend_mode = BLEND_MODE_MIX;
+ LightMode light_mode = LIGHT_MODE_NORMAL;
+ bool particles_animation = false;
+ // Initialized in the constructor.
int particles_anim_h_frames;
int particles_anim_v_frames;
bool particles_anim_loop;
@@ -188,39 +189,38 @@ private:
RID canvas_item;
String group;
- CanvasLayer *canvas_layer;
+ CanvasLayer *canvas_layer = nullptr;
- Color modulate;
- Color self_modulate;
+ Color modulate = Color(1, 1, 1, 1);
+ Color self_modulate = Color(1, 1, 1, 1);
List<CanvasItem *> children_items;
- List<CanvasItem *>::Element *C;
-
- int light_mask;
-
- Window *window;
- bool first_draw;
- bool visible;
- bool clip_children;
- bool pending_update;
- bool top_level;
- bool drawing;
- bool block_transform_notify;
- bool behind;
- bool use_parent_material;
- bool notify_local_transform;
- bool notify_transform;
-
- RS::CanvasItemTextureFilter texture_filter_cache;
- RS::CanvasItemTextureRepeat texture_repeat_cache;
-
- TextureFilter texture_filter;
- TextureRepeat texture_repeat;
+ List<CanvasItem *>::Element *C = nullptr;
+
+ int light_mask = 1;
+
+ Window *window = nullptr;
+ bool first_draw = false;
+ bool visible = true;
+ bool clip_children = false;
+ bool pending_update = false;
+ bool top_level = false;
+ bool drawing = false;
+ bool block_transform_notify = false;
+ bool behind = false;
+ bool use_parent_material = false;
+ bool notify_local_transform = false;
+ bool notify_transform = false;
+
+ RS::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
+ RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
+ TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE;
+ TextureRepeat texture_repeat = TEXTURE_REPEAT_PARENT_NODE;
Ref<Material> material;
mutable Transform2D global_transform;
- mutable bool global_invalid;
+ mutable bool global_invalid = true;
void _top_level_raise_self();
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 47440f8c60..e2df2860ea 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -2925,35 +2925,6 @@ String Node::_get_name_num_separator() {
}
Node::Node() {
- data.pos = -1;
- data.depth = -1;
- data.blocked = 0;
- data.parent = nullptr;
- data.tree = nullptr;
- data.physics_process = false;
- data.idle_process = false;
- data.process_priority = 0;
- data.physics_process_internal = false;
- data.idle_process_internal = false;
- data.inside_tree = false;
- data.ready_notified = false;
-
- data.owner = nullptr;
- data.OW = nullptr;
- data.input = false;
- data.unhandled_input = false;
- data.unhandled_key_input = false;
- data.pause_mode = PAUSE_MODE_INHERIT;
- data.pause_owner = nullptr;
- data.network_master = 1; //server by default
- data.path_cache = nullptr;
- data.parent_owned = false;
- data.in_constructor = true;
- data.viewport = nullptr;
- data.use_placeholder = false;
- data.display_folded = false;
- data.ready_first = true;
-
orphan_node_count++;
}
diff --git a/scene/main/node.h b/scene/main/node.h
index 024d036fd3..5c178d401c 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -90,54 +90,54 @@ private:
HashMap<NodePath, int> editable_instances;
- Node *parent;
- Node *owner;
- Vector<Node *> children; // list of children
- int pos;
- int depth;
- int blocked; // safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed.
+ Node *parent = nullptr;
+ Node *owner = nullptr;
+ Vector<Node *> children;
+ int pos = -1;
+ int depth = -1;
+ int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed.
StringName name;
- SceneTree *tree;
- bool inside_tree;
- bool ready_notified; //this is a small hack, so if a node is added during _ready() to the tree, it correctly gets the _ready() notification
- bool ready_first;
+ SceneTree *tree = nullptr;
+ bool inside_tree = false;
+ bool ready_notified = false; // This is a small hack, so if a node is added during _ready() to the tree, it correctly gets the _ready() notification.
+ bool ready_first = true;
#ifdef TOOLS_ENABLED
- NodePath import_path; //path used when imported, used by scene editors to keep tracking
+ NodePath import_path; // Path used when imported, used by scene editors to keep tracking.
#endif
- Viewport *viewport;
+ Viewport *viewport = nullptr;
Map<StringName, GroupData> grouped;
- List<Node *>::Element *OW; // owned element
+ List<Node *>::Element *OW = nullptr; // Owned element.
List<Node *> owned;
- PauseMode pause_mode;
- Node *pause_owner;
+ PauseMode pause_mode = PAUSE_MODE_INHERIT;
+ Node *pause_owner = nullptr;
- int network_master;
+ int network_master = 1; // Server by default.
Vector<NetData> rpc_methods;
Vector<NetData> rpc_properties;
- // variables used to properly sort the node when processing, ignored otherwise
- //should move all the stuff below to bits
- bool physics_process;
- bool idle_process;
- int process_priority;
+ // Variables used to properly sort the node when processing, ignored otherwise.
+ // TODO: Should move all the stuff below to bits.
+ bool physics_process = false;
+ bool idle_process = false;
+ int process_priority = 0;
- bool physics_process_internal;
- bool idle_process_internal;
+ bool physics_process_internal = false;
+ bool idle_process_internal = false;
- bool input;
- bool unhandled_input;
- bool unhandled_key_input;
+ bool input = false;
+ bool unhandled_input = false;
+ bool unhandled_key_input = false;
- bool parent_owned;
- bool in_constructor;
- bool use_placeholder;
+ bool parent_owned = false;
+ bool in_constructor = true;
+ bool use_placeholder = false;
- bool display_folded;
+ bool display_folded = false;
- mutable NodePath *path_cache;
+ mutable NodePath *path_cache = nullptr;
} data;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 9e396d4030..5cf3cbd382 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1332,14 +1332,6 @@ SceneTree::SceneTree() {
if (singleton == nullptr) {
singleton = this;
}
- _quit = false;
- accept_quit = true;
- quit_on_go_back = true;
- initialized = false;
-#ifdef DEBUG_ENABLED
- debug_collisions_hint = false;
- debug_navigation_hint = false;
-#endif
debug_collisions_color = GLOBAL_DEF("debug/shapes/collision/shape_color", Color(0.0, 0.6, 0.7, 0.5));
debug_collision_contact_color = GLOBAL_DEF("debug/shapes/collision/contact_color", Color(1.0, 0.2, 0.1, 0.8));
debug_navigation_color = GLOBAL_DEF("debug/shapes/navigation/geometry_color", Color(0.1, 1.0, 0.7, 0.4));
@@ -1347,23 +1339,7 @@ SceneTree::SceneTree() {
collision_debug_contacts = GLOBAL_DEF("debug/shapes/collision/max_contacts_displayed", 10000);
ProjectSettings::get_singleton()->set_custom_property_info("debug/shapes/collision/max_contacts_displayed", PropertyInfo(Variant::INT, "debug/shapes/collision/max_contacts_displayed", PROPERTY_HINT_RANGE, "0,20000,1")); // No negative
- tree_version = 1;
- physics_process_time = 1;
- idle_process_time = 1;
-
- root = nullptr;
- pause = false;
- current_frame = 0;
- tree_changed_name = "tree_changed";
- node_added_name = "node_added";
- node_removed_name = "node_removed";
- node_renamed_name = "node_renamed";
- ugc_locked = false;
- call_lock = 0;
- root_lock = 0;
- node_count = 0;
-
- //create with mainloop
+ // Create with mainloop.
root = memnew(Window);
root->set_name("root");
@@ -1371,8 +1347,7 @@ SceneTree::SceneTree() {
root->set_world_3d(Ref<World3D>(memnew(World3D)));
}
- // Initialize network state
- multiplayer_poll = true;
+ // Initialize network state.
set_multiplayer(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
//root->set_world_2d( Ref<World2D>( memnew( World2D )));
@@ -1391,6 +1366,10 @@ SceneTree::SceneTree() {
const bool use_debanding = GLOBAL_DEF("rendering/quality/screen_filters/use_debanding", false);
root->set_use_debanding(use_debanding);
+ float lod_threshold = GLOBAL_DEF("rendering/quality/mesh_lod/threshold_pixels", 1.0);
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/mesh_lod/threshold_pixels", PropertyInfo(Variant::FLOAT, "rendering/quality/mesh_lod/threshold_pixels", PROPERTY_HINT_RANGE, "0,1024,0.1"));
+ root->set_lod_threshold(lod_threshold);
+
bool snap_2d_transforms = GLOBAL_DEF("rendering/quality/2d/snap_2d_transforms_to_pixel", false);
root->set_snap_2d_transforms_to_pixel(snap_2d_transforms);
@@ -1405,8 +1384,8 @@ SceneTree::SceneTree() {
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/2d_sdf/oversize", PropertyInfo(Variant::INT, "rendering/quality/2d_sdf/oversize", PROPERTY_HINT_ENUM, "100%,120%,150%,200%"));
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/2d_sdf/scale", PropertyInfo(Variant::INT, "rendering/quality/2d_sdf/scale", PROPERTY_HINT_ENUM, "100%,50%,25%"));
- { //load default fallback environment
- //get possible extensions
+ { // Load default fallback environment.
+ // Get possible extensions.
List<String> exts;
ResourceLoader::get_recognized_extensions_for_type("Environment", &exts);
String ext_hint;
@@ -1416,9 +1395,9 @@ SceneTree::SceneTree() {
}
ext_hint += "*." + E->get();
}
- //get path
+ // Get path.
String env_path = GLOBAL_DEF("rendering/environment/default_environment", "");
- //setup property
+ // Setup property.
ProjectSettings::get_singleton()->set_custom_property_info("rendering/environment/default_environment", PropertyInfo(Variant::STRING, "rendering/viewport/default_environment", PROPERTY_HINT_FILE, ext_hint));
env_path = env_path.strip_edges();
if (env_path != String()) {
@@ -1427,10 +1406,10 @@ SceneTree::SceneTree() {
root->get_world_3d()->set_fallback_environment(env);
} else {
if (Engine::get_singleton()->is_editor_hint()) {
- //file was erased, clear the field.
+ // File was erased, clear the field.
ProjectSettings::get_singleton()->set("rendering/environment/default_environment", "");
} else {
- //file was erased, notify user.
+ // File was erased, notify user.
ERR_PRINT(RTR("Default Environment as specified in Project Settings (Rendering -> Environment -> Default Environment) could not be loaded."));
}
}
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 3e5802ce2e..9cf129d959 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -80,37 +80,36 @@ public:
private:
struct Group {
Vector<Node *> nodes;
- //uint64_t last_tree_version;
bool changed;
Group() { changed = false; };
};
- Window *root;
+ Window *root = nullptr;
- uint64_t tree_version;
- float physics_process_time;
- float idle_process_time;
- bool accept_quit;
- bool quit_on_go_back;
+ uint64_t tree_version = 1;
+ float physics_process_time = 1.0;
+ float idle_process_time = 1.0;
+ bool accept_quit = true;
+ bool quit_on_go_back = true;
#ifdef DEBUG_ENABLED
- bool debug_collisions_hint;
- bool debug_navigation_hint;
+ bool debug_collisions_hint = false;
+ bool debug_navigation_hint = false;
#endif
- bool pause;
- int root_lock;
+ bool pause = false;
+ int root_lock = 0;
Map<StringName, Group> group_map;
- bool _quit;
- bool initialized;
+ bool _quit = false;
+ bool initialized = false;
- StringName tree_changed_name;
- StringName node_added_name;
- StringName node_removed_name;
- StringName node_renamed_name;
+ StringName tree_changed_name = "tree_changed";
+ StringName node_added_name = "node_added";
+ StringName node_removed_name = "node_removed";
+ StringName node_renamed_name = "node_renamed";
- int64_t current_frame;
- int node_count;
+ int64_t current_frame = 0;
+ int node_count = 0;
#ifdef TOOLS_ENABLED
Node *edited_scene_root;
@@ -122,14 +121,14 @@ private:
bool operator<(const UGCall &p_with) const { return group == p_with.group ? call < p_with.call : group < p_with.group; }
};
- //safety for when a node is deleted while a group is being called
- int call_lock;
- Set<Node *> call_skip; //skip erased nodes
+ // Safety for when a node is deleted while a group is being called.
+ int call_lock = 0;
+ Set<Node *> call_skip; // Skip erased nodes.
List<ObjectID> delete_queue;
Map<UGCall, Vector<Variant>> unique_group_calls;
- bool ugc_locked;
+ bool ugc_locked = false;
void _flush_ugc();
_FORCE_INLINE_ void _update_group_order(Group &g, bool p_use_priority = false);
@@ -157,7 +156,7 @@ private:
///network///
Ref<MultiplayerAPI> multiplayer;
- bool multiplayer_poll;
+ bool multiplayer_poll = true;
void _network_peer_connected(int p_id);
void _network_peer_disconnected(int p_id);
@@ -183,7 +182,7 @@ private:
Variant _call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
void _flush_delete_queue();
- //optimization
+ // Optimization.
friend class CanvasItem;
friend class Node3D;
friend class Viewport;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 6350777a3d..c96dd4ad35 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -2392,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) {
@@ -3192,6 +3192,14 @@ bool Viewport::is_using_debanding() const {
return use_debanding;
}
+void Viewport::set_lod_threshold(float p_pixels) {
+ lod_threshold = p_pixels;
+ RS::get_singleton()->viewport_set_lod_threshold(viewport, lod_threshold);
+}
+float Viewport::get_lod_threshold() const {
+ return lod_threshold;
+}
+
void Viewport::set_debug_draw(DebugDraw p_debug_draw) {
debug_draw = p_debug_draw;
RS::get_singleton()->viewport_set_debug_draw(viewport, RS::ViewportDebugDraw(p_debug_draw));
@@ -3505,6 +3513,9 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sdf_scale", "scale"), &Viewport::set_sdf_scale);
ClassDB::bind_method(D_METHOD("get_sdf_scale"), &Viewport::get_sdf_scale);
+ ClassDB::bind_method(D_METHOD("set_lod_threshold", "pixels"), &Viewport::set_lod_threshold);
+ ClassDB::bind_method(D_METHOD("get_lod_threshold"), &Viewport::get_lod_threshold);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", 0), "set_world_2d", "get_world_2d");
@@ -3516,6 +3527,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x,AndroidVR 2x,AndroidVR 4x"), "set_msaa", "get_msaa");
ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled,FXAA"), "set_screen_space_aa", "get_screen_space_aa");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
ADD_GROUP("Canvas Items", "canvas_item_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,MipmapLinear,MipmapNearest"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter");
@@ -3590,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);
@@ -3652,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;
@@ -3824,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 7ce202d27c..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 {
@@ -297,6 +298,8 @@ private:
MSAA msaa;
ScreenSpaceAA screen_space_aa;
bool use_debanding = false;
+ float lod_threshold = 1.0;
+
Ref<ViewportTexture> default_texture;
Set<ViewportTexture *> viewport_textures;
@@ -542,6 +545,9 @@ public:
void set_use_debanding(bool p_use_debanding);
bool is_using_debanding() const;
+ void set_lod_threshold(float p_pixels);
+ float get_lod_threshold() const;
+
Vector2 get_camera_coords(const Vector2 &p_viewport_coords) const;
Vector2 get_camera_rect_size() const;
@@ -624,7 +630,7 @@ public:
enum ClearMode {
CLEAR_MODE_ALWAYS,
CLEAR_MODE_NEVER,
- CLEAR_MODE_ONLY_NEXT_FRAME
+ CLEAR_MODE_ONCE
};
enum UpdateMode {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 73507d36fc..30077aa642 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -118,7 +118,7 @@
#include "scene/gui/tabs.h"
#include "scene/gui/text_edit.h"
#include "scene/gui/texture_button.h"
-#include "scene/gui/texture_progress.h"
+#include "scene/gui/texture_progress_bar.h"
#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
#include "scene/gui/video_player.h"
@@ -349,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>();
@@ -915,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");
diff --git a/scene/resources/SCsub b/scene/resources/SCsub
index 3a86b22835..f4dc7a46fb 100644
--- a/scene/resources/SCsub
+++ b/scene/resources/SCsub
@@ -2,6 +2,25 @@
Import("env")
-env.add_source_files(env.scene_sources, "*.cpp")
+# Thirdparty code
+
+thirdparty_obj = []
+
+thirdparty_sources = "#thirdparty/misc/mikktspace.c"
+
+env_thirdparty = env.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.scene_sources += thirdparty_obj
+
+# Godot source files
+
+scene_obj = []
+
+env.add_source_files(scene_obj, "*.cpp")
+env.scene_sources += scene_obj
+
+# Needed to force rebuilding the scene files when the thirdparty code is updated.
+env.Depends(scene_obj, thirdparty_obj)
SConscript("default_theme/SCsub")
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index b2aad97d3b..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;
@@ -810,8 +808,8 @@ int Animation::transform_track_insert_key(int p_track, float p_time, const Vecto
return ret;
}
-void Animation::track_remove_key_at_position(int p_track, float p_pos) {
- int idx = track_find_key(p_track, p_pos, true);
+void Animation::track_remove_key_at_time(int p_track, float p_time) {
+ int idx = track_find_key(p_track, p_time, true);
ERR_FAIL_COND(idx < 0);
track_remove_key(p_track, idx);
}
@@ -2608,7 +2606,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key);
ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key);
- ClassDB::bind_method(D_METHOD("track_remove_key_at_position", "track_idx", "position"), &Animation::track_remove_key_at_position);
+ ClassDB::bind_method(D_METHOD("track_remove_key_at_time", "track_idx", "time"), &Animation::track_remove_key_at_time);
ClassDB::bind_method(D_METHOD("track_set_key_value", "track_idx", "key", "value"), &Animation::track_set_key_value);
ClassDB::bind_method(D_METHOD("track_set_key_transition", "track_idx", "key_idx", "transition"), &Animation::track_set_key_transition);
ClassDB::bind_method(D_METHOD("track_set_key_time", "track_idx", "key_idx", "time"), &Animation::track_set_key_time);
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index c52431f5f6..650a54ebfc 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -33,6 +33,8 @@
#include "core/io/resource.h"
+#define ANIM_MIN_LENGTH 0.001
+
class Animation : public Resource {
GDCLASS(Animation, Resource);
RES_BASE_EXTENSION("anim");
@@ -293,7 +295,7 @@ public:
void track_set_key_time(int p_track, int p_key_idx, float p_time);
int track_find_key(int p_track, float p_time, bool p_exact = false) const;
void track_remove_key(int p_track, int p_idx);
- void track_remove_key_at_position(int p_track, float p_pos);
+ void track_remove_key_at_time(int p_track, float p_time);
int track_get_key_count(int p_track) const;
Variant track_get_key_value(int p_track, int p_key_idx) const;
float track_get_key_time(int p_track, int p_key_idx) const;
diff --git a/scene/resources/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/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 1d92ed4830..2de28b5e5c 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -77,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);
@@ -97,6 +108,25 @@ static Ref<Texture2D> make_icon(T p_src) {
return texture;
}
+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;
+ }
+
+ Ref<ImageTexture> texture(memnew(ImageTexture));
+ Ref<Image> img = p_texture->get_data();
+
+ if (p_flip_y) {
+ img->flip_y();
+ }
+ if (p_flip_x) {
+ img->flip_x();
+ }
+
+ texture->create_from_image(img);
+ return texture;
+}
+
static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_botton = -1) {
Ref<StyleBox> style(memnew(StyleBoxEmpty));
@@ -571,6 +601,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
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);
@@ -802,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));
@@ -816,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));
@@ -844,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));
@@ -857,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);
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/theme_data.h b/scene/resources/default_theme/theme_data.h
index 7765348f80..b905c9db69 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -166,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
};
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 7c17610df7..791f260c0e 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -53,6 +53,11 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
ClassDB::bind_method(D_METHOD("get_antialiased"), &FontData::get_antialiased);
+ ClassDB::bind_method(D_METHOD("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);
@@ -115,6 +120,11 @@ bool FontData::_set(const StringName &p_name, const Variant &p_value) {
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;
+ }
return false;
}
@@ -137,6 +147,12 @@ bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_script_support_override(scr);
return true;
}
+ if (str.begins_with("variation/")) {
+ String name = str.get_slicec('/', 1);
+
+ r_ret = get_variation(name);
+ return true;
+ }
return false;
}
@@ -153,6 +169,12 @@ void FontData::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE));
}
p_list->push_back(PropertyInfo(Variant::NIL, "script_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+
+ 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));
+ }
}
RID FontData::get_rid() const {
@@ -239,6 +261,26 @@ float FontData::get_underline_thickness(int p_size) const {
return TS->font_get_underline_thickness(rid, (p_size < 0) ? base_size : p_size);
}
+Dictionary FontData::get_variation_list() const {
+ if (rid == RID()) {
+ return Dictionary();
+ }
+ return TS->font_get_variation_list(rid);
+}
+
+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();
+}
+
+double FontData::get_variation(const String &p_name) const {
+ if (rid == RID()) {
+ return 0;
+ }
+ return TS->font_get_variation(rid, p_name);
+}
+
void FontData::set_antialiased(bool p_antialiased) {
ERR_FAIL_COND(rid == RID());
TS->font_set_antialiased(rid, p_antialiased);
@@ -927,7 +969,7 @@ RES ResourceFormatLoaderFont::load(const String &p_path, const String &p_origina
void ResourceFormatLoaderFont::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
#ifndef DISABLE_DEPRECATED
- if (p_type == "DynacmicFontData") {
+ if (p_type == "DynamicFontData") {
p_extensions->push_back("ttf");
p_extensions->push_back("otf");
p_extensions->push_back("woff");
diff --git a/scene/resources/font.h b/scene/resources/font.h
index bc82a6fabf..fe28e1aea5 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -68,6 +68,10 @@ public:
float get_descent(int p_size) const;
Dictionary get_feature_list() const;
+ Dictionary get_variation_list() const;
+
+ void set_variation(const String &p_name, double p_value);
+ double get_variation(const String &p_name) const;
float get_underline_position(int p_size) const;
float get_underline_thickness(int p_size) const;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index c6815c8ecc..f1c05b8014 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -533,6 +533,9 @@ void Mesh::_bind_methods() {
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 {
@@ -734,21 +737,6 @@ static Vector<uint8_t> _fix_array_compatibility(const Vector<uint8_t> &p_src, ui
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) {
@@ -872,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();
@@ -917,7 +927,6 @@ Array ArrayMesh::_get_surfaces() const {
if (surface.blend_shape_data.size()) {
data["blend_shapes"] = surface.blend_shape_data;
- data["blend_shapes_count"] = surface.blend_shape_count;
}
if (surfaces[i].material.is_valid()) {
@@ -942,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());
}
}
@@ -995,9 +1005,8 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
}
}
- if (d.has("blend_shapes") && d.has("blend_shape_count")) {
+ if (d.has("blend_shapes")) {
surface.blend_shape_data = d["blend_shapes"];
- surface.blend_shape_count = d["blend_shape_count"];
}
Ref<Material> material;
@@ -1017,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);
@@ -1041,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);
}
@@ -1053,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_shape_count);
} else {
aabb.merge_with(s.aabb);
}
@@ -1077,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;
@@ -1111,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) {
@@ -1141,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, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data, uint32_t p_blend_shape_count, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods) {
+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;
@@ -1166,7 +1150,6 @@ void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const
sd.index_count = p_index_count;
sd.index_data = p_index_array;
sd.blend_shape_data = p_blend_shape_data;
- sd.blend_shape_count = p_blend_shape_count;
sd.bone_aabbs = p_bone_aabbs;
sd.lods = p_lods;
@@ -1195,7 +1178,7 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &
print_line("primitive: " + itos(surface.primitive));
*/
- add_surface(surface.format, PrimitiveType(surface.primitive), surface.vertex_data, surface.attribute_data, surface.skin_data, surface.vertex_count, surface.index_data, surface.index_count, surface.aabb, surface.blend_shape_data, surface.blend_shape_count, surface.bone_aabbs, surface.lods);
+ 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 {
@@ -1231,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 {
@@ -1247,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) {
@@ -1384,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;
};
@@ -1425,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();
@@ -1606,15 +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_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED);
- BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE);
}
void ArrayMesh::reload_from_file() {
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index ae2139a0cf..b7f60bf814 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -53,7 +53,10 @@ public:
NO_INDEX_ARRAY = RenderingServer::NO_INDEX_ARRAY,
ARRAY_WEIGHTS_SIZE = RenderingServer::ARRAY_WEIGHTS_SIZE
};
-
+ enum BlendShapeMode {
+ BLEND_SHAPE_MODE_NORMALIZED = RS::BLEND_SHAPE_MODE_NORMALIZED,
+ BLEND_SHAPE_MODE_RELATIVE = RS::BLEND_SHAPE_MODE_RELATIVE,
+ };
enum ArrayType {
ARRAY_VERTEX = RenderingServer::ARRAY_VERTEX,
ARRAY_NORMAL = RenderingServer::ARRAY_NORMAL,
@@ -168,15 +171,12 @@ 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);
-public:
- enum BlendShapeMode {
- BLEND_SHAPE_MODE_NORMALIZED = RS::BLEND_SHAPE_MODE_NORMALIZED,
- BLEND_SHAPE_MODE_RELATIVE = RS::BLEND_SHAPE_MODE_RELATIVE,
- };
-
private:
struct Surface {
uint32_t format;
@@ -211,7 +211,7 @@ protected:
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 = 0);
- void add_surface(uint32_t p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data = Vector<uint8_t>(), uint32_t p_blend_shape_count = 0, const Vector<AABB> &p_bone_aabbs = Vector<AABB>(), const Vector<RS::SurfaceData::LOD> &p_lods = Vector<RS::SurfaceData::LOD>());
+ 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;
@@ -268,6 +268,6 @@ VARIANT_ENUM_CAST(Mesh::ArrayType);
VARIANT_ENUM_CAST(Mesh::ArrayFormat);
VARIANT_ENUM_CAST(Mesh::ArrayCustomFormat);
VARIANT_ENUM_CAST(Mesh::PrimitiveType);
-VARIANT_ENUM_CAST(ArrayMesh::BlendShapeMode);
+VARIANT_ENUM_CAST(Mesh::BlendShapeMode);
#endif
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 7899627048..50308d641a 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -33,6 +33,9 @@
#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;
@@ -80,6 +83,10 @@ bool SurfaceTool::Vertex::operator==(const Vertex &p_vertex) const {
}
}
+ if (smooth_group != p_vertex.smooth_group) {
+ return false;
+ }
+
return true;
}
@@ -94,6 +101,7 @@ uint32_t SurfaceTool::VertexHasher::hash(const Vertex &p_vtx) {
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;
}
@@ -118,6 +126,8 @@ 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];
}
@@ -252,13 +262,8 @@ void SurfaceTool::set_weights(const Vector<float> &p_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) {
@@ -315,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: {
@@ -339,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: {
@@ -360,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;
@@ -381,9 +383,9 @@ 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;
}
@@ -400,9 +402,9 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len * 4);
uint8_t *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ 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);
@@ -417,9 +419,9 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len * 4);
uint8_t *w = array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ 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)));
@@ -434,9 +436,9 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len * 4);
uint16_t *w = (uint16_t *)array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ 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);
@@ -449,9 +451,9 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len * 8);
uint16_t *w = (uint16_t *)array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ 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);
@@ -466,9 +468,9 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len);
float *w = (float *)array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ 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;
}
@@ -480,9 +482,9 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len * 2);
float *w = (float *)array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ 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;
@@ -495,9 +497,9 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len * 3);
float *w = (float *)array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ 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;
@@ -511,9 +513,9 @@ Array SurfaceTool::commit_to_arrays() {
array.resize(varr_len * 4);
float *w = (float *)array.ptrw();
- int idx = 0;
- for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next(), idx++) {
- const Vertex &v = E->get();
+ 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;
@@ -533,14 +535,13 @@ Array SurfaceTool::commit_to_arrays() {
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 += count) {
- 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() != count);
for (int j = 0; j < count; j++) {
- w[idx + j] = v.bones[j];
+ w[idx * count + j] = v.bones[j];
}
}
@@ -554,13 +555,13 @@ Array SurfaceTool::commit_to_arrays() {
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 += count) {
- const Vertex &v = E->get();
+ 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 < count; j++) {
- w[idx + j] = v.weights[j];
+ w[idx * count + j] = v.weights[j];
}
}
@@ -574,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;
@@ -623,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;
}
@@ -639,9 +640,6 @@ void SurfaceTool::index() {
index_array.push_back(idx);
}
- vertex_array.clear();
- vertex_array = new_vertices;
-
format |= Mesh::ARRAY_FORMAT_INDEX;
}
@@ -649,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, uint32_t &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, uint32_t *r_format) {
- 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];
@@ -688,7 +683,7 @@ Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_array
if (r_format) {
*r_format = 0;
}
- return ret;
+ return;
}
int lformat = 0;
@@ -799,19 +794,14 @@ Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_array
if (r_format) {
*r_format = lformat;
}
-
- return ret;
}
-void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, uint32_t &lformat) {
- Vector<Vertex> arrays = create_vertex_array_from_triangle_arrays(arr, &lformat);
- ERR_FAIL_COND(arrays.size() == 0);
-
- for (int i = 0; i < arrays.size(); i++) {
- r_vertex->push_back(arrays[i]);
- }
+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();
@@ -864,14 +854,14 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const
}
uint32_t nformat;
- List<Vertex> nvertices;
- List<int> nindices;
+ 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);
@@ -884,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) {
@@ -896,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;
}
}
@@ -918,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;
@@ -935,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;
@@ -952,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;
@@ -969,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) {
@@ -1001,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);
@@ -1028,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();
}
}
@@ -1095,7 +1049,6 @@ void SurfaceTool::generate_normals(bool p_flip) {
if (was_indexed) {
index();
- smooth_groups.clear();
}
}
@@ -1111,8 +1064,8 @@ 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;
}
@@ -1136,6 +1089,52 @@ 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);
@@ -1155,7 +1154,7 @@ void SurfaceTool::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bones", "bones"), &SurfaceTool::set_bones);
ClassDB::bind_method(D_METHOD("set_weights", "weights"), &SurfaceTool::set_weights);
ClassDB::bind_method(D_METHOD("set_custom", "index", "custom"), &SurfaceTool::set_custom);
- ClassDB::bind_method(D_METHOD("add_smooth_group", "smooth"), &SurfaceTool::add_smooth_group);
+ ClassDB::bind_method(D_METHOD("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>()));
@@ -1166,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);
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index 4a5c7d990c..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 {
@@ -50,6 +50,7 @@ public:
Vector<int> bones;
Vector<float> weights;
Color custom[RS::ARRAY_CUSTOM_COUNT];
+ uint32_t smooth_group = 0;
bool operator==(const Vertex &p_vertex) const;
@@ -73,6 +74,11 @@ public:
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);
@@ -92,9 +98,8 @@ private:
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;
@@ -104,6 +109,7 @@ private:
Vector<int> last_bones;
Vector<float> last_weights;
Plane last_tangent;
+ uint32_t last_smooth_group = 0;
SkinWeightCount skin_weights;
@@ -111,8 +117,8 @@ private:
CustomFormat last_custom_format[RS::ARRAY_CUSTOM_COUNT];
- void _create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, uint32_t &lformat);
- void _create_list(const Ref<Mesh> &p_existing, int p_surface, List<Vertex> *r_vertex, List<int> *r_index, uint32_t &lformat);
+ 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);
@@ -143,10 +149,10 @@ public:
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_smooth_group(bool p_smooth);
void add_triangle_fan(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs = Vector<Vector2>(), const Vector<Color> &p_colors = Vector<Color>(), const Vector<Vector2> &p_uv2s = Vector<Vector2>(), const Vector<Vector3> &p_normals = Vector<Vector3>(), const Vector<Plane> &p_tangents = Vector<Plane>());
void add_index(int p_index);
@@ -156,14 +162,18 @@ 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, uint32_t *r_format = nullptr);
+ 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);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 1507537cd0..706b18d2b5 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1253,7 +1253,7 @@ bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect,
Vector2 scale = p_rect.size / src.size;
src.position += (rc.position - margin.position);
- Rect2 src_c = rc.clip(src);
+ Rect2 src_c = rc.intersection(src);
if (src_c.size == Size2()) {
return false;
}
@@ -1575,7 +1575,7 @@ void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons
if (!p_src_rect.intersects(rect)) {
continue;
}
- Rect2 local = p_src_rect.clip(rect);
+ Rect2 local = p_src_rect.intersection(rect);
Rect2 target = local;
target.size *= scale;
target.position = p_rect.position + (p_src_rect.position + rect.position) * scale;
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index 6a752d32e7..c8bfcdfab4 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -51,6 +51,21 @@ Vector<String> Theme::_get_icon_list(const String &p_node_type) const {
return ilret;
}
+Vector<String> Theme::_get_icon_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_icon_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
Vector<String> Theme::_get_stylebox_list(const String &p_node_type) const {
Vector<String> ilret;
List<StringName> il;
@@ -66,11 +81,11 @@ Vector<String> Theme::_get_stylebox_list(const String &p_node_type) const {
return ilret;
}
-Vector<String> Theme::_get_stylebox_types() const {
+Vector<String> Theme::_get_stylebox_type_list() const {
Vector<String> ilret;
List<StringName> il;
- get_stylebox_types(&il);
+ get_stylebox_type_list(&il);
ilret.resize(il.size());
int i = 0;
@@ -96,6 +111,21 @@ Vector<String> Theme::_get_font_list(const String &p_node_type) const {
return ilret;
}
+Vector<String> Theme::_get_font_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_font_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
Vector<String> Theme::_get_font_size_list(const String &p_node_type) const {
Vector<String> ilret;
List<StringName> il;
@@ -126,6 +156,21 @@ Vector<String> Theme::_get_color_list(const String &p_node_type) const {
return ilret;
}
+Vector<String> Theme::_get_color_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_color_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
Vector<String> Theme::_get_constant_list(const String &p_node_type) const {
Vector<String> ilret;
List<StringName> il;
@@ -141,7 +186,22 @@ Vector<String> Theme::_get_constant_list(const String &p_node_type) const {
return ilret;
}
-Vector<String> Theme::_get_type_list(const String &p_node_type) const {
+Vector<String> Theme::_get_constant_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_constant_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_type_list() const {
Vector<String> ilret;
List<StringName> il;
@@ -421,6 +481,15 @@ void Theme::get_icon_list(StringName p_node_type, List<StringName> *p_list) cons
}
}
+void Theme::get_icon_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ 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());
@@ -482,7 +551,7 @@ void Theme::get_stylebox_list(StringName p_node_type, List<StringName> *p_list)
}
}
-void Theme::get_stylebox_types(List<StringName> *p_list) const {
+void Theme::get_stylebox_type_list(List<StringName> *p_list) const {
ERR_FAIL_NULL(p_list);
const StringName *key = nullptr;
@@ -553,6 +622,15 @@ void Theme::get_font_list(StringName p_node_type, List<StringName> *p_list) cons
}
}
+void Theme::get_font_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = font_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
void Theme::set_font_size(const StringName &p_name, const StringName &p_node_type, int p_font_size) {
bool new_value = !font_size_map.has(p_node_type) || !font_size_map[p_node_type].has(p_name);
@@ -647,6 +725,15 @@ void Theme::get_color_list(StringName p_node_type, List<StringName> *p_list) con
}
}
+void Theme::get_color_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = color_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
void Theme::set_constant(const StringName &p_name, const StringName &p_node_type, int p_constant) {
bool new_value = !constant_map.has(p_node_type) || !constant_map[p_node_type].has(p_name);
constant_map[p_node_type][p_name] = p_constant;
@@ -692,6 +779,15 @@ void Theme::get_constant_list(StringName p_node_type, List<StringName> *p_list)
}
}
+void Theme::get_constant_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = constant_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
void Theme::clear() {
//these need disconnecting
{
@@ -840,19 +936,21 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_icon", "name", "node_type"), &Theme::has_icon);
ClassDB::bind_method(D_METHOD("clear_icon", "name", "node_type"), &Theme::clear_icon);
ClassDB::bind_method(D_METHOD("get_icon_list", "node_type"), &Theme::_get_icon_list);
+ ClassDB::bind_method(D_METHOD("get_icon_type_list"), &Theme::_get_icon_type_list);
ClassDB::bind_method(D_METHOD("set_stylebox", "name", "node_type", "texture"), &Theme::set_stylebox);
ClassDB::bind_method(D_METHOD("get_stylebox", "name", "node_type"), &Theme::get_stylebox);
ClassDB::bind_method(D_METHOD("has_stylebox", "name", "node_type"), &Theme::has_stylebox);
ClassDB::bind_method(D_METHOD("clear_stylebox", "name", "node_type"), &Theme::clear_stylebox);
ClassDB::bind_method(D_METHOD("get_stylebox_list", "node_type"), &Theme::_get_stylebox_list);
- ClassDB::bind_method(D_METHOD("get_stylebox_types"), &Theme::_get_stylebox_types);
+ ClassDB::bind_method(D_METHOD("get_stylebox_type_list"), &Theme::_get_stylebox_type_list);
ClassDB::bind_method(D_METHOD("set_font", "name", "node_type", "font"), &Theme::set_font);
ClassDB::bind_method(D_METHOD("get_font", "name", "node_type"), &Theme::get_font);
ClassDB::bind_method(D_METHOD("has_font", "name", "node_type"), &Theme::has_font);
ClassDB::bind_method(D_METHOD("clear_font", "name", "node_type"), &Theme::clear_font);
ClassDB::bind_method(D_METHOD("get_font_list", "node_type"), &Theme::_get_font_list);
+ ClassDB::bind_method(D_METHOD("get_font_type_list"), &Theme::_get_font_type_list);
ClassDB::bind_method(D_METHOD("set_font_size", "name", "node_type", "font_size"), &Theme::set_font_size);
ClassDB::bind_method(D_METHOD("get_font_size", "name", "node_type"), &Theme::get_font_size);
@@ -865,12 +963,14 @@ void Theme::_bind_methods() {
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);
@@ -880,7 +980,7 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_theme_font_size);
ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_theme_font_size);
- ClassDB::bind_method(D_METHOD("get_type_list", "node_type"), &Theme::_get_type_list);
+ ClassDB::bind_method(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);
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index 6ac47e8931..ad05e0e2f5 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -51,13 +51,17 @@ class Theme : public Resource {
HashMap<StringName, HashMap<StringName, int>> constant_map;
Vector<String> _get_icon_list(const String &p_node_type) const;
+ Vector<String> _get_icon_type_list() const;
Vector<String> _get_stylebox_list(const String &p_node_type) const;
- Vector<String> _get_stylebox_types() const;
+ Vector<String> _get_stylebox_type_list() const;
Vector<String> _get_font_list(const String &p_node_type) const;
+ Vector<String> _get_font_type_list() const;
Vector<String> _get_font_size_list(const String &p_node_type) const;
Vector<String> _get_color_list(const String &p_node_type) const;
+ Vector<String> _get_color_type_list() const;
Vector<String> _get_constant_list(const String &p_node_type) const;
- Vector<String> _get_type_list(const String &p_node_type) const;
+ Vector<String> _get_constant_type_list() const;
+ Vector<String> _get_type_list() const;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -99,19 +103,21 @@ public:
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_types(List<StringName> *p_list) const;
+ void get_stylebox_type_list(List<StringName> *p_list) const;
void set_font(const StringName &p_name, const StringName &p_node_type, const Ref<Font> &p_font);
Ref<Font> get_font(const StringName &p_name, const StringName &p_node_type) const;
bool has_font(const StringName &p_name, const StringName &p_node_type) const;
void clear_font(const StringName &p_name, const StringName &p_node_type);
void get_font_list(StringName p_node_type, List<StringName> *p_list) const;
+ void get_font_type_list(List<StringName> *p_list) const;
void set_font_size(const StringName &p_name, const StringName &p_node_type, int p_font_size);
int get_font_size(const StringName &p_name, const StringName &p_node_type) const;
@@ -124,12 +130,14 @@ public:
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;