summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/tile_map.cpp3
-rw-r--r--scene/3d/decal.cpp9
-rw-r--r--scene/3d/decal.h4
-rw-r--r--scene/3d/joint_3d.cpp116
-rw-r--r--scene/3d/visual_instance_3d.cpp9
-rw-r--r--scene/gui/control.cpp2897
-rw-r--r--scene/gui/control.h323
-rw-r--r--scene/gui/text_edit.cpp6
-rw-r--r--scene/main/viewport.cpp14
-rw-r--r--scene/register_scene_types.cpp1
-rw-r--r--scene/resources/font.cpp377
-rw-r--r--scene/resources/font.h72
-rw-r--r--scene/resources/material.cpp93
-rw-r--r--scene/resources/material.h11
-rw-r--r--scene/resources/shader.cpp14
-rw-r--r--scene/resources/shader.h2
-rw-r--r--scene/resources/tile_set.cpp55
-rw-r--r--scene/resources/tile_set.h3
-rw-r--r--scene/resources/visual_shader.cpp6
19 files changed, 2354 insertions, 1661 deletions
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index cf8b6b8f94..5ba8c95a06 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -2652,7 +2652,7 @@ void TileMap::clear_layer(int p_layer) {
// Remove all tiles.
_clear_layer_internals(p_layer);
layers[p_layer].tile_map.clear();
-
+ _recreate_layer_internals(p_layer);
used_rect_cache_dirty = true;
}
@@ -2662,6 +2662,7 @@ void TileMap::clear() {
for (unsigned int i = 0; i < layers.size(); i++) {
layers[i].tile_map.clear();
}
+ _recreate_internals();
used_rect_cache_dirty = true;
}
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
index 01cab493ec..0112f24e0c 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -215,11 +215,13 @@ void Decal::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_cull_mask"), &Decal::get_cull_mask);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,suffix:m"), "set_extents", "get_extents");
+
ADD_GROUP("Textures", "texture_");
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_albedo", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ALBEDO);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_NORMAL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_orm", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ORM);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_emission", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_EMISSION);
+
ADD_GROUP("Parameters", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_emission_energy", "get_emission_energy");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
@@ -227,13 +229,16 @@ void Decal::_bind_methods() {
// A Normal Fade of 1.0 causes the decal to be invisible even if fully perpendicular to a surface.
// Due to this, limit Normal Fade to 0.999.
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_fade", PROPERTY_HINT_RANGE, "0,0.999,0.001"), "set_normal_fade", "get_normal_fade");
+
ADD_GROUP("Vertical Fade", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "upper_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_upper_fade", "get_upper_fade");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lower_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_lower_fade", "get_lower_fade");
+
ADD_GROUP("Distance Fade", "distance_fade_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_NONE, "suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_NONE, "suffix:m"), "set_distance_fade_length", "get_distance_fade_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_begin", "get_distance_fade_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_distance_fade_length", "get_distance_fade_length");
+
ADD_GROUP("Cull Mask", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
diff --git a/scene/3d/decal.h b/scene/3d/decal.h
index d5990272c6..38da4c14e3 100644
--- a/scene/3d/decal.h
+++ b/scene/3d/decal.h
@@ -57,8 +57,8 @@ private:
real_t upper_fade = 0.3;
real_t lower_fade = 0.3;
bool distance_fade_enabled = false;
- real_t distance_fade_begin = 10.0;
- real_t distance_fade_length = 1.0;
+ real_t distance_fade_begin = 40.0;
+ real_t distance_fade_length = 10.0;
protected:
static void _bind_methods();
diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp
index 0b824ef28b..b0509475a7 100644
--- a/scene/3d/joint_3d.cpp
+++ b/scene/3d/joint_3d.cpp
@@ -737,7 +737,8 @@ 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);
- // X
+ ADD_GROUP("Linear Limit", "linear_limit_");
+
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", PROPERTY_HINT_NONE, "suffix:m"), "set_param_x", "get_param_x", PARAM_LINEAR_UPPER_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_x", "get_param_x", PARAM_LINEAR_LOWER_LIMIT);
@@ -745,15 +746,53 @@ void Generic6DOFJoint3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_RESTITUTION);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_UPPER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_LOWER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_RESTITUTION);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_DAMPING);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_UPPER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_LOWER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_RESTITUTION);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_DAMPING);
+
+ ADD_GROUP("Linear Motor", "linear_motor_");
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_MOTOR);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_x/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
+
+ ADD_GROUP("Linear Spring", "linear_spring_");
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_SPRING);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_STIFFNESS);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/damping"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/damping"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/damping"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
+
+ ADD_GROUP("Angular Limit", "angular_limit_");
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_x/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_x", "_get_angular_hi_limit_x");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_x/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_x", "_get_angular_lo_limit_x");
@@ -763,68 +802,15 @@ void Generic6DOFJoint3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_FORCE_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_x/erp"), "set_param_x", "get_param_x", PARAM_ANGULAR_ERP);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_MOTOR);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_SPRING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_STIFFNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/damping"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_DAMPING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
-
- // Y
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_UPPER_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_y", "get_param_y", PARAM_LINEAR_LOWER_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_LIMIT_SOFTNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_RESTITUTION);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_DAMPING);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_MOTOR);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_SPRING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_STIFFNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/damping"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_DAMPING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
-
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_y/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_y", "_get_angular_hi_limit_y");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_y/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_y", "_get_angular_lo_limit_y");
-
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_LIMIT_SOFTNESS);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_RESTITUTION);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_ANGULAR_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_FORCE_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_y/erp"), "set_param_y", "get_param_y", PARAM_ANGULAR_ERP);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_MOTOR);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_SPRING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_STIFFNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/damping"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_DAMPING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
-
- // Z
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/upper_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_UPPER_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/lower_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_LOWER_LIMIT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_LIMIT_SOFTNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_RESTITUTION);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_DAMPING);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_MOTOR);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/target_velocity", PROPERTY_HINT_NONE, "suffix:m/s"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
-
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_SPRING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_STIFFNESS);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/damping"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_DAMPING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT);
-
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_z/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_z", "_get_angular_hi_limit_z");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_limit_z/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_z", "_get_angular_lo_limit_z");
@@ -834,10 +820,32 @@ void Generic6DOFJoint3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/force_limit"), "set_param_z", "get_param_z", PARAM_ANGULAR_FORCE_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit_z/erp"), "set_param_z", "get_param_z", PARAM_ANGULAR_ERP);
+ ADD_GROUP("Angular Motor", "angular_motor_");
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_MOTOR);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_z/target_velocity"), "set_param_z", "get_param_z", PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+ ADD_GROUP("Angular Spring", "angular_spring_");
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/stiffness"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/damping"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_x/equilibrium_point"), "set_param_x", "get_param_x", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_SPRING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/stiffness"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_STIFFNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/damping"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_y/equilibrium_point"), "set_param_y", "get_param_y", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT);
+
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_spring_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_SPRING);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/stiffness"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_STIFFNESS);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/damping"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_DAMPING);
diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index 69917f6992..e76e85cfef 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -461,15 +461,16 @@ void GeometryInstance3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01,suffix:m"), "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_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling");
+
ADD_GROUP("Global Illumination", "gi_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)"), "set_gi_mode", "get_gi_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×")), "set_lightmap_scale", "get_lightmap_scale");
ADD_GROUP("Visibility Range", "visibility_range_");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_begin", "get_visibility_range_begin");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_end", "get_visibility_range_end");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,suffix:m"), "set_visibility_range_end_margin", "get_visibility_range_end_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_begin", "get_visibility_range_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_end", "get_visibility_range_end");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater,suffix:m"), "set_visibility_range_end_margin", "get_visibility_range_end_margin");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_range_fade_mode", PROPERTY_HINT_ENUM, "Disabled,Self,Dependencies"), "set_visibility_range_fade_mode", "get_visibility_range_fade_mode");
BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_OFF);
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 686045901c..242684b2a8 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -50,6 +50,9 @@
#include "editor/plugins/control_editor_plugin.h"
#endif
+// Editor plugin interoperability.
+
+// TODO: Decouple controls from their editor plugin and get rid of this.
#ifdef TOOLS_ENABLED
Dictionary Control::_edit_get_state() const {
Dictionary s;
@@ -181,6 +184,49 @@ Size2 Control::_edit_get_minimum_size() const {
}
#endif
+// Editor integration.
+
+void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+ Node::get_argument_options(p_function, p_idx, r_options);
+
+ if (p_idx == 0) {
+ List<StringName> sn;
+ String pf = p_function;
+ if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") {
+ Theme::get_default()->get_color_list(get_class(), &sn);
+ } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") {
+ Theme::get_default()->get_stylebox_list(get_class(), &sn);
+ } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") {
+ Theme::get_default()->get_font_list(get_class(), &sn);
+ } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") {
+ Theme::get_default()->get_font_size_list(get_class(), &sn);
+ } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") {
+ Theme::get_default()->get_constant_list(get_class(), &sn);
+ }
+
+ sn.sort_custom<StringName::AlphCompare>();
+ for (const StringName &name : sn) {
+ r_options->push_back(String(name).quote());
+ }
+ }
+}
+
+TypedArray<String> Control::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node::get_configuration_warnings();
+
+ if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) {
+ warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
+ }
+
+ return warnings;
+}
+
+bool Control::is_text_field() const {
+ return false;
+}
+
+// Dynamic properties.
+
String Control::properties_managed_by_container[] = {
"offset_left",
"offset_top",
@@ -196,58 +242,6 @@ String Control::properties_managed_by_container[] = {
"size"
};
-void Control::accept_event() {
- if (is_inside_tree()) {
- get_viewport()->_gui_accept_event();
- }
-}
-
-void Control::set_custom_minimum_size(const Size2 &p_custom) {
- if (p_custom == data.custom_minimum_size) {
- return;
- }
- data.custom_minimum_size = p_custom;
- update_minimum_size();
-}
-
-Size2 Control::get_custom_minimum_size() const {
- return data.custom_minimum_size;
-}
-
-void Control::_update_minimum_size_cache() {
- Size2 minsize = get_minimum_size();
- minsize.x = MAX(minsize.x, data.custom_minimum_size.x);
- minsize.y = MAX(minsize.y, data.custom_minimum_size.y);
-
- bool size_changed = false;
- if (data.minimum_size_cache != minsize) {
- size_changed = true;
- }
-
- data.minimum_size_cache = minsize;
- data.minimum_size_valid = true;
-
- if (size_changed) {
- update_minimum_size();
- }
-}
-
-Size2 Control::get_combined_minimum_size() const {
- if (!data.minimum_size_valid) {
- const_cast<Control *>(this)->_update_minimum_size_cache();
- }
- return data.minimum_size_cache;
-}
-
-Transform2D Control::_get_internal_transform() const {
- Transform2D rot_scale;
- rot_scale.set_rotation_and_scale(data.rotation, data.scale);
- Transform2D offset;
- offset.set_origin(-data.pivot_offset);
-
- return offset.affine_inverse() * (rot_scale * offset);
-}
-
bool Control::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
if (!name.begins_with("theme_override")) {
@@ -258,21 +252,21 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) {
if (name.begins_with("theme_override_icons/")) {
String dname = name.get_slicec('/', 1);
if (data.icon_override.has(dname)) {
- data.icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+ data.icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
}
data.icon_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
} else if (name.begins_with("theme_override_styles/")) {
String dname = name.get_slicec('/', 1);
if (data.style_override.has(dname)) {
- data.style_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+ data.style_override[dname]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
}
data.style_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
} else if (name.begins_with("theme_override_fonts/")) {
String dname = name.get_slicec('/', 1);
if (data.font_override.has(dname)) {
- data.font_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+ data.font_override[dname]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
}
data.font_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
@@ -318,21 +312,6 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
-void Control::_update_minimum_size() {
- if (!is_inside_tree()) {
- return;
- }
-
- Size2 minsize = get_combined_minimum_size();
- data.updating_last_minimum_size = false;
-
- if (minsize != data.last_minimum_size) {
- data.last_minimum_size = minsize;
- _size_changed();
- emit_signal(SceneStringNames::get_singleton()->minimum_size_changed);
- }
-}
-
bool Control::_get(const StringName &p_name, Variant &r_ret) const {
String sname = p_name;
if (!sname.begins_with("theme_override")) {
@@ -586,6 +565,12 @@ void Control::_validate_property(PropertyInfo &property) const {
}
}
+// Global relations.
+
+bool Control::is_top_level_control() const {
+ return is_inside_tree() && (!data.parent_canvas_item && !data.RI && is_set_as_top_level());
+}
+
Control *Control::get_parent_control() const {
return data.parent;
}
@@ -594,97 +579,64 @@ Window *Control::get_parent_window() const {
return data.parent_window;
}
-void Control::set_layout_direction(Control::LayoutDirection p_direction) {
- ERR_FAIL_INDEX((int)p_direction, 4);
-
- data.layout_dir = p_direction;
- data.is_rtl_dirty = true;
-
- propagate_notification(NOTIFICATION_LAYOUT_DIRECTION_CHANGED);
-}
+Control *Control::get_root_parent_control() const {
+ const CanvasItem *ci = this;
+ const Control *root = this;
-Control::LayoutDirection Control::get_layout_direction() const {
- return data.layout_dir;
-}
+ while (ci) {
+ const Control *c = Object::cast_to<Control>(ci);
+ if (c) {
+ root = c;
-bool Control::is_layout_rtl() const {
- if (data.is_rtl_dirty) {
- const_cast<Control *>(this)->data.is_rtl_dirty = false;
- if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) {
- Window *parent_window = get_parent_window();
- Control *parent_control = get_parent_control();
- if (parent_control) {
- const_cast<Control *>(this)->data.is_rtl = parent_control->is_layout_rtl();
- } else if (parent_window) {
- const_cast<Control *>(this)->data.is_rtl = parent_window->is_layout_rtl();
- } else {
- if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
- const_cast<Control *>(this)->data.is_rtl = true;
- } else {
- String locale = TranslationServer::get_singleton()->get_tool_locale();
- const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
- }
- }
- } else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) {
- if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
- const_cast<Control *>(this)->data.is_rtl = true;
- } else {
- String locale = TranslationServer::get_singleton()->get_tool_locale();
- const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
+ if (c->data.RI || c->is_top_level_control()) {
+ break;
}
- } else {
- const_cast<Control *>(this)->data.is_rtl = (data.layout_dir == LAYOUT_DIRECTION_RTL);
}
- }
- return data.is_rtl;
-}
-void Control::set_auto_translate(bool p_enable) {
- if (p_enable == data.auto_translate) {
- return;
+ ci = ci->get_parent_item();
}
- data.auto_translate = p_enable;
-
- notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
-}
-
-bool Control::is_auto_translating() const {
- return data.auto_translate;
-}
-
-void Control::_clear_size_warning() {
- data.size_warning = false;
+ return const_cast<Control *>(root);
}
-//moved theme configuration here, so controls can set up even if still not inside active scene
-
-void Control::add_child_notify(Node *p_child) {
- Control *child_c = Object::cast_to<Control>(p_child);
-
- if (child_c && child_c->data.theme.is_null() && (data.theme_owner || data.theme_owner_window)) {
- _propagate_theme_changed(child_c, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff
+Rect2 Control::get_parent_anchorable_rect() const {
+ if (!is_inside_tree()) {
+ return Rect2();
}
- Window *child_w = Object::cast_to<Window>(p_child);
+ Rect2 parent_rect;
+ if (data.parent_canvas_item) {
+ parent_rect = data.parent_canvas_item->get_anchorable_rect();
+ } else {
+#ifdef TOOLS_ENABLED
+ Node *edited_root = get_tree()->get_edited_scene_root();
+ if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) {
+ parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
+ } else {
+ parent_rect = get_viewport()->get_visible_rect();
+ }
- if (child_w && child_w->theme.is_null() && (data.theme_owner || data.theme_owner_window)) {
- _propagate_theme_changed(child_w, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff
+#else
+ parent_rect = get_viewport()->get_visible_rect();
+#endif
}
+
+ return parent_rect;
}
-void Control::remove_child_notify(Node *p_child) {
- Control *child_c = Object::cast_to<Control>(p_child);
+Size2 Control::get_parent_area_size() const {
+ return get_parent_anchorable_rect().size;
+}
- if (child_c && (child_c->data.theme_owner || child_c->data.theme_owner_window) && child_c->data.theme.is_null()) {
- _propagate_theme_changed(child_c, nullptr, nullptr);
- }
+// Positioning and sizing.
- Window *child_w = Object::cast_to<Window>(p_child);
+Transform2D Control::_get_internal_transform() const {
+ Transform2D rot_scale;
+ rot_scale.set_rotation_and_scale(data.rotation, data.scale);
+ Transform2D offset;
+ offset.set_origin(-data.pivot_offset);
- if (child_w && (child_w->theme_owner || child_w->theme_owner_window) && child_w->theme.is_null()) {
- _propagate_theme_changed(child_w, nullptr, nullptr);
- }
+ return offset.affine_inverse() * (rot_scale * offset);
}
void Control::_update_canvas_item_transform() {
@@ -699,815 +651,146 @@ void Control::_update_canvas_item_transform() {
RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), xform);
}
-void Control::_notification(int p_notification) {
- switch (p_notification) {
- case NOTIFICATION_POST_ENTER_TREE: {
- data.minimum_size_valid = false;
- data.is_rtl_dirty = true;
- _size_changed();
- } break;
-
- case NOTIFICATION_EXIT_TREE: {
- release_focus();
- get_viewport()->_gui_remove_control(this);
- } break;
-
- case NOTIFICATION_READY: {
-#ifdef DEBUG_ENABLED
- connect("ready", callable_mp(this, &Control::_clear_size_warning), varray(), CONNECT_DEFERRED | CONNECT_ONESHOT);
-#endif
- } break;
-
- case NOTIFICATION_ENTER_CANVAS: {
- data.parent = Object::cast_to<Control>(get_parent());
- data.parent_window = Object::cast_to<Window>(get_parent());
- data.is_rtl_dirty = true;
-
- if (data.theme.is_null()) {
- if (data.parent && (data.parent->data.theme_owner || data.parent->data.theme_owner_window)) {
- data.theme_owner = data.parent->data.theme_owner;
- data.theme_owner_window = data.parent->data.theme_owner_window;
- notification(NOTIFICATION_THEME_CHANGED);
- } else if (data.parent_window && (data.parent_window->theme_owner || data.parent_window->theme_owner_window)) {
- data.theme_owner = data.parent_window->theme_owner;
- data.theme_owner_window = data.parent_window->theme_owner_window;
- notification(NOTIFICATION_THEME_CHANGED);
- }
- }
-
- CanvasItem *node = this;
- bool has_parent_control = false;
-
- while (!node->is_set_as_top_level()) {
- CanvasItem *parent = Object::cast_to<CanvasItem>(node->get_parent());
- if (!parent) {
- break;
- }
-
- Control *parent_control = Object::cast_to<Control>(parent);
- if (parent_control) {
- has_parent_control = true;
- break;
- }
-
- node = parent;
- }
-
- if (has_parent_control) {
- // Do nothing, has a parent control.
- } else {
- // Is a regular root control or top_level.
- Viewport *viewport = get_viewport();
- ERR_FAIL_COND(!viewport);
- data.RI = viewport->_gui_add_root_control(this);
- }
-
- data.parent_canvas_item = get_parent_item();
-
- if (data.parent_canvas_item) {
- data.parent_canvas_item->connect("item_rect_changed", callable_mp(this, &Control::_size_changed));
- } else {
- // Connect viewport.
- Viewport *viewport = get_viewport();
- ERR_FAIL_COND(!viewport);
- viewport->connect("size_changed", callable_mp(this, &Control::_size_changed));
- }
- } break;
-
- case NOTIFICATION_EXIT_CANVAS: {
- if (data.parent_canvas_item) {
- data.parent_canvas_item->disconnect("item_rect_changed", callable_mp(this, &Control::_size_changed));
- data.parent_canvas_item = nullptr;
- } else if (!is_set_as_top_level()) {
- //disconnect viewport
- Viewport *viewport = get_viewport();
- ERR_FAIL_COND(!viewport);
- viewport->disconnect("size_changed", callable_mp(this, &Control::_size_changed));
- }
-
- if (data.RI) {
- get_viewport()->_gui_remove_root_control(data.RI);
- data.RI = nullptr;
- }
-
- data.parent = nullptr;
- data.parent_canvas_item = nullptr;
- data.parent_window = nullptr;
- data.is_rtl_dirty = true;
- } break;
-
- case NOTIFICATION_MOVED_IN_PARENT: {
- // some parents need to know the order of the children to draw (like TabContainer)
- // update if necessary
- if (data.parent) {
- data.parent->update();
- }
- update();
-
- if (data.RI) {
- get_viewport()->_gui_set_root_order_dirty();
- }
- } break;
-
- case NOTIFICATION_RESIZED: {
- emit_signal(SceneStringNames::get_singleton()->resized);
- } break;
-
- case NOTIFICATION_DRAW: {
- _update_canvas_item_transform();
- RenderingServer::get_singleton()->canvas_item_set_custom_rect(get_canvas_item(), !data.disable_visibility_clip, Rect2(Point2(), get_size()));
- RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), data.clip_contents);
- } break;
-
- case NOTIFICATION_MOUSE_ENTER: {
- emit_signal(SceneStringNames::get_singleton()->mouse_entered);
- } break;
-
- case NOTIFICATION_MOUSE_EXIT: {
- emit_signal(SceneStringNames::get_singleton()->mouse_exited);
- } break;
-
- case NOTIFICATION_FOCUS_ENTER: {
- emit_signal(SceneStringNames::get_singleton()->focus_entered);
- update();
- } break;
-
- case NOTIFICATION_FOCUS_EXIT: {
- emit_signal(SceneStringNames::get_singleton()->focus_exited);
- update();
- } break;
-
- case NOTIFICATION_THEME_CHANGED: {
- update_minimum_size();
- update();
- } break;
-
- case NOTIFICATION_VISIBILITY_CHANGED: {
- if (!is_visible_in_tree()) {
- if (get_viewport() != nullptr) {
- get_viewport()->_gui_hide_control(this);
- }
- } else {
- data.minimum_size_valid = false;
- _update_minimum_size();
- _size_changed();
- }
- } break;
-
- case NOTIFICATION_TRANSLATION_CHANGED:
- case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
- if (is_inside_tree()) {
- data.is_rtl_dirty = true;
- _size_changed();
- }
- } break;
- }
-}
-
-bool Control::has_point(const Point2 &p_point) const {
- bool ret;
- if (GDVIRTUAL_CALL(_has_point, p_point, ret)) {
- return ret;
- }
- return Rect2(Point2(), get_size()).has_point(p_point);
-}
-
-void Control::set_drag_forwarding(Object *p_target) {
- if (p_target) {
- data.drag_owner = p_target->get_instance_id();
- } else {
- data.drag_owner = ObjectID();
- }
-}
-
-Variant Control::get_drag_data(const Point2 &p_point) {
- if (data.drag_owner.is_valid()) {
- Object *obj = ObjectDB::get_instance(data.drag_owner);
- if (obj) {
- return obj->call("_get_drag_data_fw", p_point, this);
- }
- }
-
- Variant dd;
- if (GDVIRTUAL_CALL(_get_drag_data, p_point, dd)) {
- return dd;
- }
-
- return Variant();
-}
-
-bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
- if (data.drag_owner.is_valid()) {
- Object *obj = ObjectDB::get_instance(data.drag_owner);
- if (obj) {
- return obj->call("_can_drop_data_fw", p_point, p_data, this);
- }
- }
-
- bool ret;
- if (GDVIRTUAL_CALL(_can_drop_data, p_point, p_data, ret)) {
- return ret;
- }
- return false;
-}
-
-void Control::drop_data(const Point2 &p_point, const Variant &p_data) {
- if (data.drag_owner.is_valid()) {
- Object *obj = ObjectDB::get_instance(data.drag_owner);
- if (obj) {
- obj->call("_drop_data_fw", p_point, p_data, this);
- return;
- }
- }
-
- GDVIRTUAL_CALL(_drop_data, p_point, p_data);
-}
-
-void Control::force_drag(const Variant &p_data, Control *p_control) {
- ERR_FAIL_COND(!is_inside_tree());
- ERR_FAIL_COND(p_data.get_type() == Variant::NIL);
-
- get_viewport()->_gui_force_drag(this, p_data, p_control);
-}
-
-void Control::set_drag_preview(Control *p_control) {
- ERR_FAIL_COND(!is_inside_tree());
- ERR_FAIL_COND(!get_viewport()->gui_is_dragging());
- get_viewport()->_gui_set_drag_preview(this, p_control);
-}
-
-bool Control::is_drag_successful() const {
- return is_inside_tree() && get_viewport()->gui_is_drag_successful();
-}
-
-void Control::_call_gui_input(const Ref<InputEvent> &p_event) {
- emit_signal(SceneStringNames::get_singleton()->gui_input, p_event); //signal should be first, so it's possible to override an event (and then accept it)
- if (!is_inside_tree() || get_viewport()->is_input_handled()) {
- return; //input was handled, abort
- }
- GDVIRTUAL_CALL(_gui_input, p_event);
- if (!is_inside_tree() || get_viewport()->is_input_handled()) {
- return; //input was handled, abort
- }
- gui_input(p_event);
-}
-void Control::gui_input(const Ref<InputEvent> &p_event) {
-}
-
-Size2 Control::get_minimum_size() const {
- Vector2 ms;
- if (GDVIRTUAL_CALL(_get_minimum_size, ms)) {
- return ms;
- }
- return Vector2();
+Transform2D Control::get_transform() const {
+ Transform2D xform = _get_internal_transform();
+ xform[2] += get_position();
+ return xform;
}
-template <class T>
-T Control::get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) {
- ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, T(), "At least one theme type must be specified.");
-
- // First, look through each control or window node in the branch, until no valid parent can be found.
- // Only nodes with a theme resource attached are considered.
- Control *theme_owner = p_theme_owner;
- Window *theme_owner_window = p_theme_owner_window;
-
- while (theme_owner || theme_owner_window) {
- // For each theme resource check the theme types provided and see if p_name exists with any of them.
- for (const StringName &E : p_theme_types) {
- if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
- return theme_owner->data.theme->get_theme_item(p_data_type, p_name, E);
- }
-
- if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E)) {
- return theme_owner_window->theme->get_theme_item(p_data_type, p_name, E);
- }
- }
-
- Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
- Control *parent_c = Object::cast_to<Control>(parent);
- if (parent_c) {
- theme_owner = parent_c->data.theme_owner;
- theme_owner_window = parent_c->data.theme_owner_window;
- } else {
- Window *parent_w = Object::cast_to<Window>(parent);
- if (parent_w) {
- theme_owner = parent_w->theme_owner;
- theme_owner_window = parent_w->theme_owner_window;
- } else {
- theme_owner = nullptr;
- theme_owner_window = nullptr;
- }
- }
- }
-
- // Secondly, check the project-defined Theme resource.
- if (Theme::get_project_default().is_valid()) {
- for (const StringName &E : p_theme_types) {
- if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) {
- return Theme::get_project_default()->get_theme_item(p_data_type, p_name, E);
- }
- }
- }
+/// Anchors and offsets.
- // Lastly, fall back on the items defined in the default Theme, if they exist.
- for (const StringName &E : p_theme_types) {
- if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) {
- return Theme::get_default()->get_theme_item(p_data_type, p_name, E);
- }
- }
- // If they don't exist, use any type to return the default/empty value.
- return Theme::get_default()->get_theme_item(p_data_type, p_name, p_theme_types[0]);
+void Control::_set_anchor(Side p_side, real_t p_anchor) {
+ set_anchor(p_side, p_anchor);
}
-bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) {
- ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified.");
-
- // First, look through each control or window node in the branch, until no valid parent can be found.
- // Only nodes with a theme resource attached are considered.
- Control *theme_owner = p_theme_owner;
- Window *theme_owner_window = p_theme_owner_window;
-
- while (theme_owner || theme_owner_window) {
- // For each theme resource check the theme types provided and see if p_name exists with any of them.
- for (const StringName &E : p_theme_types) {
- if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
- return true;
- }
-
- if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E)) {
- return true;
- }
- }
-
- Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
- Control *parent_c = Object::cast_to<Control>(parent);
- if (parent_c) {
- theme_owner = parent_c->data.theme_owner;
- theme_owner_window = parent_c->data.theme_owner_window;
- } else {
- Window *parent_w = Object::cast_to<Window>(parent);
- if (parent_w) {
- theme_owner = parent_w->theme_owner;
- theme_owner_window = parent_w->theme_owner_window;
- } else {
- theme_owner = nullptr;
- theme_owner_window = nullptr;
- }
- }
- }
+void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) {
+ ERR_FAIL_INDEX((int)p_side, 4);
- // Secondly, check the project-defined Theme resource.
- if (Theme::get_project_default().is_valid()) {
- for (const StringName &E : p_theme_types) {
- if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) {
- return true;
- }
- }
- }
+ Rect2 parent_rect = get_parent_anchorable_rect();
+ real_t parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y;
+ real_t previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range;
+ real_t previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range;
- // Lastly, fall back on the items defined in the default Theme, if they exist.
- for (const StringName &E : p_theme_types) {
- if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) {
- return true;
- }
- }
- return false;
-}
+ data.anchor[p_side] = p_anchor;
-void Control::_get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (Theme::get_project_default().is_valid() && Theme::get_project_default()->get_type_variation_base(data.theme_type_variation) != StringName()) {
- Theme::get_project_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list);
+ if (((p_side == SIDE_LEFT || p_side == SIDE_TOP) && data.anchor[p_side] > data.anchor[(p_side + 2) % 4]) ||
+ ((p_side == SIDE_RIGHT || p_side == SIDE_BOTTOM) && data.anchor[p_side] < data.anchor[(p_side + 2) % 4])) {
+ if (p_push_opposite_anchor) {
+ data.anchor[(p_side + 2) % 4] = data.anchor[p_side];
} else {
- Theme::get_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list);
- }
- } else {
- Theme::get_default()->get_type_dependencies(p_theme_type, StringName(), p_list);
- }
-}
-
-Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const Ref<Texture2D> *tex = data.icon_override.getptr(p_name);
- if (tex) {
- return *tex;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<Ref<Texture2D>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types);
-}
-
-Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const Ref<StyleBox> *style = data.style_override.getptr(p_name);
- if (style) {
- return *style;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<Ref<StyleBox>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
-}
-
-Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const Ref<Font> *font = data.font_override.getptr(p_name);
- if (font) {
- return *font;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<Ref<Font>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types);
-}
-
-int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const int *font_size = data.font_size_override.getptr(p_name);
- if (font_size && (*font_size) > 0) {
- return *font_size;
+ data.anchor[p_side] = data.anchor[(p_side + 2) % 4];
}
}
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
-}
-
-Color Control::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const Color *color = data.color_override.getptr(p_name);
- if (color) {
- return *color;
+ if (!p_keep_offset) {
+ data.offset[p_side] = previous_pos - data.anchor[p_side] * parent_range;
+ if (p_push_opposite_anchor) {
+ data.offset[(p_side + 2) % 4] = previous_opposite_pos - data.anchor[(p_side + 2) % 4] * parent_range;
}
}
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<Color>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types);
-}
-
-int Control::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- const int *constant = data.constant_override.getptr(p_name);
- if (constant) {
- return *constant;
- }
+ if (is_inside_tree()) {
+ _size_changed();
}
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
-}
-
-bool Control::has_theme_icon_override(const StringName &p_name) const {
- const Ref<Texture2D> *tex = data.icon_override.getptr(p_name);
- return tex != nullptr;
-}
-
-bool Control::has_theme_stylebox_override(const StringName &p_name) const {
- const Ref<StyleBox> *style = data.style_override.getptr(p_name);
- return style != nullptr;
-}
-
-bool Control::has_theme_font_override(const StringName &p_name) const {
- const Ref<Font> *font = data.font_override.getptr(p_name);
- return font != nullptr;
-}
-
-bool Control::has_theme_font_size_override(const StringName &p_name) const {
- const int *font_size = data.font_size_override.getptr(p_name);
- return font_size != nullptr;
-}
-
-bool Control::has_theme_color_override(const StringName &p_name) const {
- const Color *color = data.color_override.getptr(p_name);
- return color != nullptr;
-}
-
-bool Control::has_theme_constant_override(const StringName &p_name) const {
- const int *constant = data.constant_override.getptr(p_name);
- return constant != nullptr;
+ update();
}
-bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_icon_override(p_name)) {
- return true;
- }
- }
+real_t Control::get_anchor(Side p_side) const {
+ ERR_FAIL_INDEX_V(int(p_side), 4, 0.0);
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types);
+ return data.anchor[p_side];
}
-bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_stylebox_override(p_name)) {
- return true;
- }
- }
+void Control::set_offset(Side p_side, real_t p_value) {
+ ERR_FAIL_INDEX((int)p_side, 4);
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
+ data.offset[p_side] = p_value;
+ _size_changed();
}
-bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_font_override(p_name)) {
- return true;
- }
- }
+real_t Control::get_offset(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0);
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types);
+ return data.offset[p_side];
}
-bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_font_size_override(p_name)) {
- return true;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
+void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor) {
+ set_anchor(p_side, p_anchor, false, p_push_opposite_anchor);
+ set_offset(p_side, p_pos);
}
-bool Control::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_color_override(p_name)) {
- return true;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types);
+void Control::set_begin(const Size2 &p_point) {
+ data.offset[0] = p_point.x;
+ data.offset[1] = p_point.y;
+ _size_changed();
}
-bool Control::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const {
- if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
- if (has_theme_constant_override(p_name)) {
- return true;
- }
- }
-
- List<StringName> theme_types;
- _get_theme_type_dependencies(p_theme_type, &theme_types);
- return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
+Size2 Control::get_begin() const {
+ return Size2(data.offset[0], data.offset[1]);
}
-float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window) {
- // First, look through each control or window node in the branch, until no valid parent can be found.
- // Only nodes with a theme resource attached are considered.
- // For each theme resource see if their assigned theme has the default value defined and valid.
- Control *theme_owner = p_theme_owner;
- Window *theme_owner_window = p_theme_owner_window;
-
- while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_base_scale()) {
- return theme_owner->data.theme->get_default_base_scale();
- }
-
- if (theme_owner_window && theme_owner_window->theme->has_default_base_scale()) {
- return theme_owner_window->theme->get_default_base_scale();
- }
-
- Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
- Control *parent_c = Object::cast_to<Control>(parent);
- if (parent_c) {
- theme_owner = parent_c->data.theme_owner;
- theme_owner_window = parent_c->data.theme_owner_window;
- } else {
- Window *parent_w = Object::cast_to<Window>(parent);
- if (parent_w) {
- theme_owner = parent_w->theme_owner;
- theme_owner_window = parent_w->theme_owner_window;
- } else {
- theme_owner = nullptr;
- theme_owner_window = nullptr;
- }
- }
- }
-
- // Secondly, check the project-defined Theme resource.
- if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_base_scale()) {
- return Theme::get_project_default()->get_default_base_scale();
- }
- }
-
- // Lastly, fall back on the default Theme.
- if (Theme::get_default()->has_default_base_scale()) {
- return Theme::get_default()->get_default_base_scale();
- }
- return Theme::get_fallback_base_scale();
+void Control::set_end(const Size2 &p_point) {
+ data.offset[2] = p_point.x;
+ data.offset[3] = p_point.y;
+ _size_changed();
}
-float Control::get_theme_default_base_scale() const {
- return fetch_theme_default_base_scale(data.theme_owner, data.theme_owner_window);
+Size2 Control::get_end() const {
+ return Size2(data.offset[2], data.offset[3]);
}
-Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window) {
- // First, look through each control or window node in the branch, until no valid parent can be found.
- // Only nodes with a theme resource attached are considered.
- // For each theme resource see if their assigned theme has the default value defined and valid.
- Control *theme_owner = p_theme_owner;
- Window *theme_owner_window = p_theme_owner_window;
-
- while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_font()) {
- return theme_owner->data.theme->get_default_font();
- }
-
- if (theme_owner_window && theme_owner_window->theme->has_default_font()) {
- return theme_owner_window->theme->get_default_font();
- }
-
- Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
- Control *parent_c = Object::cast_to<Control>(parent);
- if (parent_c) {
- theme_owner = parent_c->data.theme_owner;
- theme_owner_window = parent_c->data.theme_owner_window;
- } else {
- Window *parent_w = Object::cast_to<Window>(parent);
- if (parent_w) {
- theme_owner = parent_w->theme_owner;
- theme_owner_window = parent_w->theme_owner_window;
- } else {
- theme_owner = nullptr;
- theme_owner_window = nullptr;
- }
- }
- }
-
- // Secondly, check the project-defined Theme resource.
- if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_font()) {
- return Theme::get_project_default()->get_default_font();
- }
- }
+void Control::set_h_grow_direction(GrowDirection p_direction) {
+ ERR_FAIL_INDEX((int)p_direction, 3);
- // Lastly, fall back on the default Theme.
- if (Theme::get_default()->has_default_font()) {
- return Theme::get_default()->get_default_font();
- }
- return Theme::get_fallback_font();
+ data.h_grow = p_direction;
+ _size_changed();
}
-Ref<Font> Control::get_theme_default_font() const {
- return fetch_theme_default_font(data.theme_owner, data.theme_owner_window);
+Control::GrowDirection Control::get_h_grow_direction() const {
+ return data.h_grow;
}
-int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window) {
- // First, look through each control or window node in the branch, until no valid parent can be found.
- // Only nodes with a theme resource attached are considered.
- // For each theme resource see if their assigned theme has the default value defined and valid.
- Control *theme_owner = p_theme_owner;
- Window *theme_owner_window = p_theme_owner_window;
-
- while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_font_size()) {
- return theme_owner->data.theme->get_default_font_size();
- }
-
- if (theme_owner_window && theme_owner_window->theme->has_default_font_size()) {
- return theme_owner_window->theme->get_default_font_size();
- }
-
- Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
- Control *parent_c = Object::cast_to<Control>(parent);
- if (parent_c) {
- theme_owner = parent_c->data.theme_owner;
- theme_owner_window = parent_c->data.theme_owner_window;
- } else {
- Window *parent_w = Object::cast_to<Window>(parent);
- if (parent_w) {
- theme_owner = parent_w->theme_owner;
- theme_owner_window = parent_w->theme_owner_window;
- } else {
- theme_owner = nullptr;
- theme_owner_window = nullptr;
- }
- }
- }
-
- // Secondly, check the project-defined Theme resource.
- if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_font_size()) {
- return Theme::get_project_default()->get_default_font_size();
- }
- }
+void Control::set_v_grow_direction(GrowDirection p_direction) {
+ ERR_FAIL_INDEX((int)p_direction, 3);
- // Lastly, fall back on the default Theme.
- if (Theme::get_default()->has_default_font_size()) {
- return Theme::get_default()->get_default_font_size();
- }
- return Theme::get_fallback_font_size();
+ data.v_grow = p_direction;
+ _size_changed();
}
-int Control::get_theme_default_font_size() const {
- return fetch_theme_default_font_size(data.theme_owner, data.theme_owner_window);
+Control::GrowDirection Control::get_v_grow_direction() const {
+ return data.v_grow;
}
-Rect2 Control::get_parent_anchorable_rect() const {
- if (!is_inside_tree()) {
- return Rect2();
- }
-
- Rect2 parent_rect;
- if (data.parent_canvas_item) {
- parent_rect = data.parent_canvas_item->get_anchorable_rect();
- } else {
-#ifdef TOOLS_ENABLED
- Node *edited_root = get_tree()->get_edited_scene_root();
- if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) {
- parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
- } else {
- parent_rect = get_viewport()->get_visible_rect();
- }
+void Control::_compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]) {
+ Size2 parent_rect_size = get_parent_anchorable_rect().size;
+ ERR_FAIL_COND(parent_rect_size.x == 0.0);
+ ERR_FAIL_COND(parent_rect_size.y == 0.0);
-#else
- parent_rect = get_viewport()->get_visible_rect();
-#endif
+ real_t x = p_rect.position.x;
+ if (is_layout_rtl()) {
+ x = parent_rect_size.x - x - p_rect.size.x;
}
-
- return parent_rect;
-}
-
-Size2 Control::get_parent_area_size() const {
- return get_parent_anchorable_rect().size;
+ r_anchors[0] = (x - p_offsets[0]) / parent_rect_size.x;
+ r_anchors[1] = (p_rect.position.y - p_offsets[1]) / parent_rect_size.y;
+ r_anchors[2] = (x + p_rect.size.x - p_offsets[2]) / parent_rect_size.x;
+ r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_offsets[3]) / parent_rect_size.y;
}
-void Control::_size_changed() {
- Rect2 parent_rect = get_parent_anchorable_rect();
-
- real_t edge_pos[4];
-
- for (int i = 0; i < 4; i++) {
- real_t area = parent_rect.size[i & 1];
- edge_pos[i] = data.offset[i] + (data.anchor[i] * area);
- }
-
- Point2 new_pos_cache = Point2(edge_pos[0], edge_pos[1]);
- Size2 new_size_cache = Point2(edge_pos[2], edge_pos[3]) - new_pos_cache;
-
- Size2 minimum_size = get_combined_minimum_size();
-
- if (minimum_size.width > new_size_cache.width) {
- if (data.h_grow == GROW_DIRECTION_BEGIN) {
- new_pos_cache.x += new_size_cache.width - minimum_size.width;
- } else if (data.h_grow == GROW_DIRECTION_BOTH) {
- new_pos_cache.x += 0.5 * (new_size_cache.width - minimum_size.width);
- }
-
- new_size_cache.width = minimum_size.width;
- }
+void Control::_compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]) {
+ Size2 parent_rect_size = get_parent_anchorable_rect().size;
+ real_t x = p_rect.position.x;
if (is_layout_rtl()) {
- new_pos_cache.x = parent_rect.size.x - new_pos_cache.x - new_size_cache.x;
- }
-
- if (minimum_size.height > new_size_cache.height) {
- if (data.v_grow == GROW_DIRECTION_BEGIN) {
- new_pos_cache.y += new_size_cache.height - minimum_size.height;
- } else if (data.v_grow == GROW_DIRECTION_BOTH) {
- new_pos_cache.y += 0.5 * (new_size_cache.height - minimum_size.height);
- }
-
- new_size_cache.height = minimum_size.height;
- }
-
- bool pos_changed = new_pos_cache != data.pos_cache;
- bool size_changed = new_size_cache != data.size_cache;
-
- data.pos_cache = new_pos_cache;
- data.size_cache = new_size_cache;
-
- if (is_inside_tree()) {
- if (size_changed) {
- notification(NOTIFICATION_RESIZED);
- }
- if (pos_changed || size_changed) {
- item_rect_changed(size_changed);
- _notify_transform();
- }
-
- if (pos_changed && !size_changed) {
- _update_canvas_item_transform(); //move because it won't be updated
- }
+ x = parent_rect_size.x - x - p_rect.size.x;
}
+ r_offsets[0] = x - (p_anchors[0] * parent_rect_size.x);
+ r_offsets[1] = p_rect.position.y - (p_anchors[1] * parent_rect_size.y);
+ r_offsets[2] = x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x);
+ r_offsets[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y);
}
+/// Presets and layout modes.
+
void Control::_set_layout_mode(LayoutMode p_mode) {
bool list_changed = false;
@@ -1557,47 +840,6 @@ Control::LayoutMode Control::_get_layout_mode() const {
return LayoutMode::LAYOUT_MODE_POSITION;
}
-void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) {
- ERR_FAIL_INDEX((int)p_side, 4);
-
- Rect2 parent_rect = get_parent_anchorable_rect();
- real_t parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y;
- real_t previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range;
- real_t previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range;
-
- data.anchor[p_side] = p_anchor;
-
- if (((p_side == SIDE_LEFT || p_side == SIDE_TOP) && data.anchor[p_side] > data.anchor[(p_side + 2) % 4]) ||
- ((p_side == SIDE_RIGHT || p_side == SIDE_BOTTOM) && data.anchor[p_side] < data.anchor[(p_side + 2) % 4])) {
- if (p_push_opposite_anchor) {
- data.anchor[(p_side + 2) % 4] = data.anchor[p_side];
- } else {
- data.anchor[p_side] = data.anchor[(p_side + 2) % 4];
- }
- }
-
- if (!p_keep_offset) {
- data.offset[p_side] = previous_pos - data.anchor[p_side] * parent_range;
- if (p_push_opposite_anchor) {
- data.offset[(p_side + 2) % 4] = previous_opposite_pos - data.anchor[(p_side + 2) % 4] * parent_range;
- }
- }
- if (is_inside_tree()) {
- _size_changed();
- }
-
- update();
-}
-
-void Control::_set_anchor(Side p_side, real_t p_anchor) {
- set_anchor(p_side, p_anchor);
-}
-
-void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor) {
- set_anchor(p_side, p_anchor, false, p_push_opposite_anchor);
- set_offset(p_side, p_pos);
-}
-
void Control::_set_anchors_layout_preset(int p_preset) {
bool list_changed = false;
@@ -2037,43 +1279,37 @@ void Control::set_grow_direction_preset(LayoutPreset p_preset) {
}
}
-real_t Control::get_anchor(Side p_side) const {
- ERR_FAIL_INDEX_V(int(p_side), 4, 0.0);
+/// Manual positioning.
- return data.anchor[p_side];
+void Control::_set_position(const Size2 &p_point) {
+ set_position(p_point);
}
-void Control::set_offset(Side p_side, real_t p_value) {
- ERR_FAIL_INDEX((int)p_side, 4);
-
- data.offset[p_side] = p_value;
+void Control::set_position(const Size2 &p_point, bool p_keep_offsets) {
+ if (p_keep_offsets) {
+ _compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor);
+ } else {
+ _compute_offsets(Rect2(p_point, data.size_cache), data.anchor, data.offset);
+ }
_size_changed();
}
-void Control::set_begin(const Size2 &p_point) {
- data.offset[0] = p_point.x;
- data.offset[1] = p_point.y;
- _size_changed();
+Size2 Control::get_position() const {
+ return data.pos_cache;
}
-void Control::set_end(const Size2 &p_point) {
- data.offset[2] = p_point.x;
- data.offset[3] = p_point.y;
- _size_changed();
+void Control::_set_global_position(const Point2 &p_point) {
+ set_global_position(p_point);
}
-real_t Control::get_offset(Side p_side) const {
- ERR_FAIL_INDEX_V((int)p_side, 4, 0);
-
- return data.offset[p_side];
-}
+void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) {
+ Transform2D inv;
-Size2 Control::get_begin() const {
- return Size2(data.offset[0], data.offset[1]);
-}
+ if (data.parent_canvas_item) {
+ inv = data.parent_canvas_item->get_global_transform().affine_inverse();
+ }
-Size2 Control::get_end() const {
- return Size2(data.offset[2], data.offset[3]);
+ set_position(inv.xform(p_point), p_keep_offsets);
}
Point2 Control::get_global_position() const {
@@ -2091,72 +1327,6 @@ Point2 Control::get_screen_position() const {
return global_pos;
}
-void Control::_set_global_position(const Point2 &p_point) {
- set_global_position(p_point);
-}
-
-void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) {
- Transform2D inv;
-
- if (data.parent_canvas_item) {
- inv = data.parent_canvas_item->get_global_transform().affine_inverse();
- }
-
- set_position(inv.xform(p_point), p_keep_offsets);
-}
-
-void Control::_compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]) {
- Size2 parent_rect_size = get_parent_anchorable_rect().size;
- ERR_FAIL_COND(parent_rect_size.x == 0.0);
- ERR_FAIL_COND(parent_rect_size.y == 0.0);
-
- real_t x = p_rect.position.x;
- if (is_layout_rtl()) {
- x = parent_rect_size.x - x - p_rect.size.x;
- }
- r_anchors[0] = (x - p_offsets[0]) / parent_rect_size.x;
- r_anchors[1] = (p_rect.position.y - p_offsets[1]) / parent_rect_size.y;
- r_anchors[2] = (x + p_rect.size.x - p_offsets[2]) / parent_rect_size.x;
- r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_offsets[3]) / parent_rect_size.y;
-}
-
-void Control::_compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]) {
- Size2 parent_rect_size = get_parent_anchorable_rect().size;
-
- real_t x = p_rect.position.x;
- if (is_layout_rtl()) {
- x = parent_rect_size.x - x - p_rect.size.x;
- }
- r_offsets[0] = x - (p_anchors[0] * parent_rect_size.x);
- r_offsets[1] = p_rect.position.y - (p_anchors[1] * parent_rect_size.y);
- r_offsets[2] = x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x);
- r_offsets[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y);
-}
-
-void Control::_set_position(const Size2 &p_point) {
- set_position(p_point);
-}
-
-void Control::set_position(const Size2 &p_point, bool p_keep_offsets) {
- if (p_keep_offsets) {
- _compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor);
- } else {
- _compute_offsets(Rect2(p_point, data.size_cache), data.anchor, data.offset);
- }
- _size_changed();
-}
-
-void Control::set_rect(const Rect2 &p_rect) {
- for (int i = 0; i < 4; i++) {
- data.anchor[i] = ANCHOR_BEGIN;
- }
-
- _compute_offsets(p_rect, data.anchor, data.offset);
- if (is_inside_tree()) {
- _size_changed();
- }
-}
-
void Control::_set_size(const Size2 &p_size) {
#ifdef DEBUG_ENABLED
if (data.size_warning && (data.anchor[SIDE_LEFT] != data.anchor[SIDE_RIGHT] || data.anchor[SIDE_TOP] != data.anchor[SIDE_BOTTOM])) {
@@ -2184,10 +1354,6 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) {
_size_changed();
}
-Size2 Control::get_position() const {
- return data.pos_cache;
-}
-
Size2 Control::get_size() const {
return data.size_cache;
}
@@ -2196,6 +1362,21 @@ void Control::reset_size() {
set_size(Size2());
}
+void Control::set_rect(const Rect2 &p_rect) {
+ for (int i = 0; i < 4; i++) {
+ data.anchor[i] = ANCHOR_BEGIN;
+ }
+
+ _compute_offsets(p_rect, data.anchor, data.offset);
+ if (is_inside_tree()) {
+ _size_changed();
+ }
+}
+
+Rect2 Control::get_rect() const {
+ return Rect2(get_position(), get_size());
+}
+
Rect2 Control::get_global_rect() const {
return Rect2(get_global_position(), get_size());
}
@@ -2220,118 +1401,382 @@ Rect2 Control::get_window_rect() const {
return gr;
}
-Rect2 Control::get_rect() const {
- return Rect2(get_position(), get_size());
-}
-
Rect2 Control::get_anchorable_rect() const {
return Rect2(Point2(), get_size());
}
-void Control::begin_bulk_theme_override() {
- data.bulk_theme_override = true;
+void Control::set_scale(const Vector2 &p_scale) {
+ data.scale = p_scale;
+ // Avoid having 0 scale values, can lead to errors in physics and rendering.
+ if (data.scale.x == 0) {
+ data.scale.x = CMP_EPSILON;
+ }
+ if (data.scale.y == 0) {
+ data.scale.y = CMP_EPSILON;
+ }
+ update();
+ _notify_transform();
}
-void Control::end_bulk_theme_override() {
- ERR_FAIL_COND(!data.bulk_theme_override);
+Vector2 Control::get_scale() const {
+ return data.scale;
+}
- data.bulk_theme_override = false;
- _notify_theme_changed();
+void Control::set_rotation(real_t p_radians) {
+ data.rotation = p_radians;
+ update();
+ _notify_transform();
}
-void Control::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) {
- ERR_FAIL_COND(!p_icon.is_valid());
+real_t Control::get_rotation() const {
+ return data.rotation;
+}
- if (data.icon_override.has(p_name)) {
- data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+void Control::set_pivot_offset(const Vector2 &p_pivot) {
+ data.pivot_offset = p_pivot;
+ update();
+ _notify_transform();
+}
+
+Vector2 Control::get_pivot_offset() const {
+ return data.pivot_offset;
+}
+
+/// Sizes.
+
+void Control::_update_minimum_size() {
+ if (!is_inside_tree()) {
+ return;
}
- data.icon_override[p_name] = p_icon;
- data.icon_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- _notify_theme_changed();
+ Size2 minsize = get_combined_minimum_size();
+ data.updating_last_minimum_size = false;
+
+ if (minsize != data.last_minimum_size) {
+ data.last_minimum_size = minsize;
+ _size_changed();
+ emit_signal(SceneStringNames::get_singleton()->minimum_size_changed);
+ }
}
-void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) {
- ERR_FAIL_COND(!p_style.is_valid());
+void Control::update_minimum_size() {
+ if (!is_inside_tree() || data.block_minimum_size_adjust) {
+ return;
+ }
- if (data.style_override.has(p_name)) {
- data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+ Control *invalidate = this;
+
+ //invalidate cache upwards
+ while (invalidate && invalidate->data.minimum_size_valid) {
+ invalidate->data.minimum_size_valid = false;
+ if (invalidate->is_set_as_top_level()) {
+ break; // do not go further up
+ }
+ if (!invalidate->data.parent && get_parent()) {
+ Window *parent_window = Object::cast_to<Window>(get_parent());
+ if (parent_window && parent_window->is_wrapping_controls()) {
+ parent_window->child_controls_changed();
+ }
+ }
+ invalidate = invalidate->data.parent;
}
- data.style_override[p_name] = p_style;
- data.style_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- _notify_theme_changed();
+ if (!is_visible_in_tree()) {
+ return;
+ }
+
+ if (data.updating_last_minimum_size) {
+ return;
+ }
+
+ data.updating_last_minimum_size = true;
+
+ MessageQueue::get_singleton()->push_call(this, "_update_minimum_size");
}
-void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) {
- ERR_FAIL_COND(!p_font.is_valid());
+void Control::set_block_minimum_size_adjust(bool p_block) {
+ data.block_minimum_size_adjust = p_block;
+}
- if (data.font_override.has(p_name)) {
- data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+bool Control::is_minimum_size_adjust_blocked() const {
+ return data.block_minimum_size_adjust;
+}
+
+Size2 Control::get_minimum_size() const {
+ Vector2 ms;
+ if (GDVIRTUAL_CALL(_get_minimum_size, ms)) {
+ return ms;
}
+ return Vector2();
+}
- data.font_override[p_name] = p_font;
- data.font_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- _notify_theme_changed();
+void Control::set_custom_minimum_size(const Size2 &p_custom) {
+ if (p_custom == data.custom_minimum_size) {
+ return;
+ }
+ data.custom_minimum_size = p_custom;
+ update_minimum_size();
}
-void Control::add_theme_font_size_override(const StringName &p_name, int p_font_size) {
- data.font_size_override[p_name] = p_font_size;
- _notify_theme_changed();
+Size2 Control::get_custom_minimum_size() const {
+ return data.custom_minimum_size;
}
-void Control::add_theme_color_override(const StringName &p_name, const Color &p_color) {
- data.color_override[p_name] = p_color;
- _notify_theme_changed();
+void Control::_update_minimum_size_cache() {
+ Size2 minsize = get_minimum_size();
+ minsize.x = MAX(minsize.x, data.custom_minimum_size.x);
+ minsize.y = MAX(minsize.y, data.custom_minimum_size.y);
+
+ bool size_changed = false;
+ if (data.minimum_size_cache != minsize) {
+ size_changed = true;
+ }
+
+ data.minimum_size_cache = minsize;
+ data.minimum_size_valid = true;
+
+ if (size_changed) {
+ update_minimum_size();
+ }
}
-void Control::add_theme_constant_override(const StringName &p_name, int p_constant) {
- data.constant_override[p_name] = p_constant;
- _notify_theme_changed();
+Size2 Control::get_combined_minimum_size() const {
+ if (!data.minimum_size_valid) {
+ const_cast<Control *>(this)->_update_minimum_size_cache();
+ }
+ return data.minimum_size_cache;
}
-void Control::remove_theme_icon_override(const StringName &p_name) {
- if (data.icon_override.has(p_name)) {
- data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+void Control::_size_changed() {
+ Rect2 parent_rect = get_parent_anchorable_rect();
+
+ real_t edge_pos[4];
+
+ for (int i = 0; i < 4; i++) {
+ real_t area = parent_rect.size[i & 1];
+ edge_pos[i] = data.offset[i] + (data.anchor[i] * area);
}
- data.icon_override.erase(p_name);
- _notify_theme_changed();
+ Point2 new_pos_cache = Point2(edge_pos[0], edge_pos[1]);
+ Size2 new_size_cache = Point2(edge_pos[2], edge_pos[3]) - new_pos_cache;
+
+ Size2 minimum_size = get_combined_minimum_size();
+
+ if (minimum_size.width > new_size_cache.width) {
+ if (data.h_grow == GROW_DIRECTION_BEGIN) {
+ new_pos_cache.x += new_size_cache.width - minimum_size.width;
+ } else if (data.h_grow == GROW_DIRECTION_BOTH) {
+ new_pos_cache.x += 0.5 * (new_size_cache.width - minimum_size.width);
+ }
+
+ new_size_cache.width = minimum_size.width;
+ }
+
+ if (is_layout_rtl()) {
+ new_pos_cache.x = parent_rect.size.x - new_pos_cache.x - new_size_cache.x;
+ }
+
+ if (minimum_size.height > new_size_cache.height) {
+ if (data.v_grow == GROW_DIRECTION_BEGIN) {
+ new_pos_cache.y += new_size_cache.height - minimum_size.height;
+ } else if (data.v_grow == GROW_DIRECTION_BOTH) {
+ new_pos_cache.y += 0.5 * (new_size_cache.height - minimum_size.height);
+ }
+
+ new_size_cache.height = minimum_size.height;
+ }
+
+ bool pos_changed = new_pos_cache != data.pos_cache;
+ bool size_changed = new_size_cache != data.size_cache;
+
+ data.pos_cache = new_pos_cache;
+ data.size_cache = new_size_cache;
+
+ if (is_inside_tree()) {
+ if (size_changed) {
+ notification(NOTIFICATION_RESIZED);
+ }
+ if (pos_changed || size_changed) {
+ item_rect_changed(size_changed);
+ _notify_transform();
+ }
+
+ if (pos_changed && !size_changed) {
+ _update_canvas_item_transform(); //move because it won't be updated
+ }
+ }
}
-void Control::remove_theme_style_override(const StringName &p_name) {
- if (data.style_override.has(p_name)) {
- data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+void Control::_clear_size_warning() {
+ data.size_warning = false;
+}
+
+// Container sizing.
+
+void Control::set_h_size_flags(int p_flags) {
+ if (data.h_size_flags == p_flags) {
+ return;
}
+ data.h_size_flags = p_flags;
+ emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
+}
- data.style_override.erase(p_name);
- _notify_theme_changed();
+int Control::get_h_size_flags() const {
+ return data.h_size_flags;
}
-void Control::remove_theme_font_override(const StringName &p_name) {
- if (data.font_override.has(p_name)) {
- data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+void Control::set_v_size_flags(int p_flags) {
+ if (data.v_size_flags == p_flags) {
+ return;
}
+ data.v_size_flags = p_flags;
+ emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
+}
- data.font_override.erase(p_name);
- _notify_theme_changed();
+int Control::get_v_size_flags() const {
+ return data.v_size_flags;
}
-void Control::remove_theme_font_size_override(const StringName &p_name) {
- data.font_size_override.erase(p_name);
- _notify_theme_changed();
+void Control::set_stretch_ratio(real_t p_ratio) {
+ if (data.expand == p_ratio) {
+ return;
+ }
+
+ data.expand = p_ratio;
+ emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
}
-void Control::remove_theme_color_override(const StringName &p_name) {
- data.color_override.erase(p_name);
- _notify_theme_changed();
+real_t Control::get_stretch_ratio() const {
+ return data.expand;
}
-void Control::remove_theme_constant_override(const StringName &p_name) {
- data.constant_override.erase(p_name);
- _notify_theme_changed();
+// Input events.
+
+void Control::_call_gui_input(const Ref<InputEvent> &p_event) {
+ emit_signal(SceneStringNames::get_singleton()->gui_input, p_event); //signal should be first, so it's possible to override an event (and then accept it)
+ if (!is_inside_tree() || get_viewport()->is_input_handled()) {
+ return; //input was handled, abort
+ }
+ GDVIRTUAL_CALL(_gui_input, p_event);
+ if (!is_inside_tree() || get_viewport()->is_input_handled()) {
+ return; //input was handled, abort
+ }
+ gui_input(p_event);
+}
+
+void Control::gui_input(const Ref<InputEvent> &p_event) {
+}
+
+void Control::accept_event() {
+ if (is_inside_tree()) {
+ get_viewport()->_gui_accept_event();
+ }
+}
+
+bool Control::has_point(const Point2 &p_point) const {
+ bool ret;
+ if (GDVIRTUAL_CALL(_has_point, p_point, ret)) {
+ return ret;
+ }
+ return Rect2(Point2(), get_size()).has_point(p_point);
+}
+
+void Control::set_mouse_filter(MouseFilter p_filter) {
+ ERR_FAIL_INDEX(p_filter, 3);
+ data.mouse_filter = p_filter;
+ notify_property_list_changed();
+ update_configuration_warnings();
+}
+
+Control::MouseFilter Control::get_mouse_filter() const {
+ return data.mouse_filter;
+}
+
+void Control::set_force_pass_scroll_events(bool p_force_pass_scroll_events) {
+ data.force_pass_scroll_events = p_force_pass_scroll_events;
+}
+
+bool Control::is_force_pass_scroll_events() const {
+ return data.force_pass_scroll_events;
+}
+
+void Control::warp_mouse(const Point2 &p_position) {
+ ERR_FAIL_COND(!is_inside_tree());
+ get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position));
+}
+
+// Drag and drop handling.
+
+void Control::set_drag_forwarding(Object *p_target) {
+ if (p_target) {
+ data.drag_owner = p_target->get_instance_id();
+ } else {
+ data.drag_owner = ObjectID();
+ }
+}
+
+Variant Control::get_drag_data(const Point2 &p_point) {
+ if (data.drag_owner.is_valid()) {
+ Object *obj = ObjectDB::get_instance(data.drag_owner);
+ if (obj) {
+ return obj->call("_get_drag_data_fw", p_point, this);
+ }
+ }
+
+ Variant dd;
+ if (GDVIRTUAL_CALL(_get_drag_data, p_point, dd)) {
+ return dd;
+ }
+
+ return Variant();
+}
+
+bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+ if (data.drag_owner.is_valid()) {
+ Object *obj = ObjectDB::get_instance(data.drag_owner);
+ if (obj) {
+ return obj->call("_can_drop_data_fw", p_point, p_data, this);
+ }
+ }
+
+ bool ret;
+ if (GDVIRTUAL_CALL(_can_drop_data, p_point, p_data, ret)) {
+ return ret;
+ }
+ return false;
+}
+
+void Control::drop_data(const Point2 &p_point, const Variant &p_data) {
+ if (data.drag_owner.is_valid()) {
+ Object *obj = ObjectDB::get_instance(data.drag_owner);
+ if (obj) {
+ obj->call("_drop_data_fw", p_point, p_data, this);
+ return;
+ }
+ }
+
+ GDVIRTUAL_CALL(_drop_data, p_point, p_data);
+}
+
+void Control::force_drag(const Variant &p_data, Control *p_control) {
+ ERR_FAIL_COND(!is_inside_tree());
+ ERR_FAIL_COND(p_data.get_type() == Variant::NIL);
+
+ get_viewport()->_gui_force_drag(this, p_data, p_control);
+}
+
+void Control::set_drag_preview(Control *p_control) {
+ ERR_FAIL_COND(!is_inside_tree());
+ ERR_FAIL_COND(!get_viewport()->gui_is_dragging());
+ get_viewport()->_gui_set_drag_preview(this, p_control);
+}
+
+bool Control::is_drag_successful() const {
+ return is_inside_tree() && get_viewport()->gui_is_drag_successful();
}
+// Focus.
+
void Control::set_focus_mode(FocusMode p_focus_mode) {
ERR_FAIL_INDEX((int)p_focus_mode, 3);
@@ -2342,6 +1787,41 @@ void Control::set_focus_mode(FocusMode p_focus_mode) {
data.focus_mode = p_focus_mode;
}
+Control::FocusMode Control::get_focus_mode() const {
+ return data.focus_mode;
+}
+
+bool Control::has_focus() const {
+ return is_inside_tree() && get_viewport()->_gui_control_has_focus(this);
+}
+
+void Control::grab_focus() {
+ ERR_FAIL_COND(!is_inside_tree());
+
+ if (data.focus_mode == FOCUS_NONE) {
+ WARN_PRINT("This control can't grab focus. Use set_focus_mode() to allow a control to get focus.");
+ return;
+ }
+
+ get_viewport()->_gui_control_grab_focus(this);
+}
+
+void Control::grab_click_focus() {
+ ERR_FAIL_COND(!is_inside_tree());
+
+ get_viewport()->_gui_grab_click_focus(this);
+}
+
+void Control::release_focus() {
+ ERR_FAIL_COND(!is_inside_tree());
+
+ if (!has_focus()) {
+ return;
+ }
+
+ get_viewport()->gui_release_focus();
+}
+
static Control *_next_control(Control *p_from) {
if (p_from->is_set_as_top_level()) {
return nullptr; // Can't go above.
@@ -2520,181 +2000,6 @@ Control *Control::find_prev_valid_focus() const {
return nullptr;
}
-Control::FocusMode Control::get_focus_mode() const {
- return data.focus_mode;
-}
-
-bool Control::has_focus() const {
- return is_inside_tree() && get_viewport()->_gui_control_has_focus(this);
-}
-
-void Control::grab_focus() {
- ERR_FAIL_COND(!is_inside_tree());
-
- if (data.focus_mode == FOCUS_NONE) {
- WARN_PRINT("This control can't grab focus. Use set_focus_mode() to allow a control to get focus.");
- return;
- }
-
- get_viewport()->_gui_control_grab_focus(this);
-}
-
-void Control::release_focus() {
- ERR_FAIL_COND(!is_inside_tree());
-
- if (!has_focus()) {
- return;
- }
-
- get_viewport()->gui_release_focus();
-}
-
-bool Control::is_top_level_control() const {
- return is_inside_tree() && (!data.parent_canvas_item && !data.RI && is_set_as_top_level());
-}
-
-void Control::_propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign) {
- Control *c = Object::cast_to<Control>(p_at);
-
- if (c && c != p_owner && c->data.theme.is_valid()) { // has a theme, this can't be propagated
- return;
- }
-
- Window *w = c == nullptr ? Object::cast_to<Window>(p_at) : nullptr;
-
- if (w && w != p_owner_window && w->theme.is_valid()) { // has a theme, this can't be propagated
- return;
- }
-
- for (int i = 0; i < p_at->get_child_count(); i++) {
- CanvasItem *child = Object::cast_to<CanvasItem>(p_at->get_child(i));
- if (child) {
- _propagate_theme_changed(child, p_owner, p_owner_window, p_assign);
- } else {
- Window *window = Object::cast_to<Window>(p_at->get_child(i));
- if (window) {
- _propagate_theme_changed(window, p_owner, p_owner_window, p_assign);
- }
- }
- }
-
- if (c) {
- if (p_assign) {
- c->data.theme_owner = p_owner;
- c->data.theme_owner_window = p_owner_window;
- }
- c->notification(Control::NOTIFICATION_THEME_CHANGED);
- c->emit_signal(SceneStringNames::get_singleton()->theme_changed);
- }
-
- if (w) {
- if (p_assign) {
- w->theme_owner = p_owner;
- w->theme_owner_window = p_owner_window;
- }
- w->notification(Window::NOTIFICATION_THEME_CHANGED);
- w->emit_signal(SceneStringNames::get_singleton()->theme_changed);
- }
-}
-
-void Control::_theme_changed() {
- _propagate_theme_changed(this, this, nullptr, false);
-}
-
-void Control::_notify_theme_changed() {
- if (!data.bulk_theme_override) {
- notification(NOTIFICATION_THEME_CHANGED);
- }
-}
-
-void Control::set_theme(const Ref<Theme> &p_theme) {
- if (data.theme == p_theme) {
- return;
- }
-
- if (data.theme.is_valid()) {
- data.theme->disconnect("changed", callable_mp(this, &Control::_theme_changed));
- }
-
- data.theme = p_theme;
- if (!p_theme.is_null()) {
- data.theme_owner = this;
- data.theme_owner_window = nullptr;
- _propagate_theme_changed(this, this, nullptr);
- } else {
- Control *parent_c = Object::cast_to<Control>(get_parent());
-
- if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) {
- Control::_propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window);
- } else {
- Window *parent_w = cast_to<Window>(get_parent());
- if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) {
- Control::_propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window);
- } else {
- Control::_propagate_theme_changed(this, nullptr, nullptr);
- }
- }
- }
-
- if (data.theme.is_valid()) {
- data.theme->connect("changed", callable_mp(this, &Control::_theme_changed), varray(), CONNECT_DEFERRED);
- }
-}
-
-Ref<Theme> Control::get_theme() const {
- return data.theme;
-}
-
-void Control::set_theme_type_variation(const StringName &p_theme_type) {
- data.theme_type_variation = p_theme_type;
- _propagate_theme_changed(this, data.theme_owner, data.theme_owner_window);
-}
-
-StringName Control::get_theme_type_variation() const {
- return data.theme_type_variation;
-}
-
-void Control::set_tooltip(const String &p_tooltip) {
- data.tooltip = p_tooltip;
- update_configuration_warnings();
-}
-
-String Control::get_tooltip(const Point2 &p_pos) const {
- return data.tooltip;
-}
-
-Control *Control::make_custom_tooltip(const String &p_text) const {
- Object *ret = nullptr;
- if (GDVIRTUAL_CALL(_make_custom_tooltip, p_text, ret)) {
- return Object::cast_to<Control>(ret);
- }
- return nullptr;
-}
-
-void Control::set_default_cursor_shape(CursorShape p_shape) {
- ERR_FAIL_INDEX(int(p_shape), CURSOR_MAX);
-
- data.default_cursor = p_shape;
-}
-
-Control::CursorShape Control::get_default_cursor_shape() const {
- return data.default_cursor;
-}
-
-Control::CursorShape Control::get_cursor_shape(const Point2 &p_pos) const {
- return data.default_cursor;
-}
-
-Transform2D Control::get_transform() const {
- Transform2D xform = _get_internal_transform();
- xform[2] += get_position();
- return xform;
-}
-
-String Control::_get_tooltip() const {
- return data.tooltip;
-}
-
void Control::set_focus_neighbor(Side p_side, const NodePath &p_neighbor) {
ERR_FAIL_INDEX((int)p_side, 4);
data.focus_neighbor[p_side] = p_neighbor;
@@ -2861,273 +2166,1011 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons
}
}
-void Control::set_h_size_flags(int p_flags) {
- if (data.h_size_flags == p_flags) {
+// Rendering.
+
+void Control::set_default_cursor_shape(CursorShape p_shape) {
+ ERR_FAIL_INDEX(int(p_shape), CURSOR_MAX);
+
+ data.default_cursor = p_shape;
+}
+
+Control::CursorShape Control::get_default_cursor_shape() const {
+ return data.default_cursor;
+}
+
+Control::CursorShape Control::get_cursor_shape(const Point2 &p_pos) const {
+ return data.default_cursor;
+}
+
+void Control::set_disable_visibility_clip(bool p_ignore) {
+ data.disable_visibility_clip = p_ignore;
+ update();
+}
+
+bool Control::is_visibility_clip_disabled() const {
+ return data.disable_visibility_clip;
+}
+
+void Control::set_clip_contents(bool p_clip) {
+ data.clip_contents = p_clip;
+ update();
+}
+
+bool Control::is_clipping_contents() {
+ return data.clip_contents;
+}
+
+// Theming.
+
+void Control::_propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign) {
+ Control *c = Object::cast_to<Control>(p_at);
+
+ if (c && c != p_owner && c->data.theme.is_valid()) { // has a theme, this can't be propagated
return;
}
- data.h_size_flags = p_flags;
- emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
+
+ Window *w = c == nullptr ? Object::cast_to<Window>(p_at) : nullptr;
+
+ if (w && w != p_owner_window && w->theme.is_valid()) { // has a theme, this can't be propagated
+ return;
+ }
+
+ for (int i = 0; i < p_at->get_child_count(); i++) {
+ CanvasItem *child = Object::cast_to<CanvasItem>(p_at->get_child(i));
+ if (child) {
+ _propagate_theme_changed(child, p_owner, p_owner_window, p_assign);
+ } else {
+ Window *window = Object::cast_to<Window>(p_at->get_child(i));
+ if (window) {
+ _propagate_theme_changed(window, p_owner, p_owner_window, p_assign);
+ }
+ }
+ }
+
+ if (c) {
+ if (p_assign) {
+ c->data.theme_owner = p_owner;
+ c->data.theme_owner_window = p_owner_window;
+ }
+ c->notification(Control::NOTIFICATION_THEME_CHANGED);
+ c->emit_signal(SceneStringNames::get_singleton()->theme_changed);
+ }
+
+ if (w) {
+ if (p_assign) {
+ w->theme_owner = p_owner;
+ w->theme_owner_window = p_owner_window;
+ }
+ w->notification(Window::NOTIFICATION_THEME_CHANGED);
+ w->emit_signal(SceneStringNames::get_singleton()->theme_changed);
+ }
}
-int Control::get_h_size_flags() const {
- return data.h_size_flags;
+void Control::_theme_changed() {
+ _propagate_theme_changed(this, this, nullptr, false);
}
-void Control::set_v_size_flags(int p_flags) {
- if (data.v_size_flags == p_flags) {
- return;
+void Control::_theme_property_override_changed() {
+ notification(NOTIFICATION_THEME_CHANGED);
+ emit_signal(SceneStringNames::get_singleton()->theme_changed);
+ update_minimum_size(); // Overrides are likely to affect minimum size.
+}
+
+void Control::_notify_theme_changed() {
+ if (!data.bulk_theme_override) {
+ notification(NOTIFICATION_THEME_CHANGED);
}
- data.v_size_flags = p_flags;
- emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
}
-void Control::set_stretch_ratio(real_t p_ratio) {
- if (data.expand == p_ratio) {
+void Control::set_theme(const Ref<Theme> &p_theme) {
+ if (data.theme == p_theme) {
return;
}
- data.expand = p_ratio;
- emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
+ if (data.theme.is_valid()) {
+ data.theme->disconnect("changed", callable_mp(this, &Control::_theme_changed));
+ }
+
+ data.theme = p_theme;
+ if (!p_theme.is_null()) {
+ data.theme_owner = this;
+ data.theme_owner_window = nullptr;
+ _propagate_theme_changed(this, this, nullptr);
+ } else {
+ Control *parent_c = Object::cast_to<Control>(get_parent());
+
+ if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) {
+ Control::_propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window);
+ } else {
+ Window *parent_w = cast_to<Window>(get_parent());
+ if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) {
+ Control::_propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window);
+ } else {
+ Control::_propagate_theme_changed(this, nullptr, nullptr);
+ }
+ }
+ }
+
+ if (data.theme.is_valid()) {
+ data.theme->connect("changed", callable_mp(this, &Control::_theme_changed), varray(), CONNECT_DEFERRED);
+ }
}
-real_t Control::get_stretch_ratio() const {
- return data.expand;
+Ref<Theme> Control::get_theme() const {
+ return data.theme;
}
-void Control::grab_click_focus() {
- ERR_FAIL_COND(!is_inside_tree());
+void Control::set_theme_type_variation(const StringName &p_theme_type) {
+ data.theme_type_variation = p_theme_type;
+ _propagate_theme_changed(this, data.theme_owner, data.theme_owner_window);
+}
- get_viewport()->_gui_grab_click_focus(this);
+StringName Control::get_theme_type_variation() const {
+ return data.theme_type_variation;
}
-void Control::update_minimum_size() {
- if (!is_inside_tree() || data.block_minimum_size_adjust) {
- return;
+/// Theme property lookup.
+
+template <class T>
+T Control::get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) {
+ ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, T(), "At least one theme type must be specified.");
+
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ // For each theme resource check the theme types provided and see if p_name exists with any of them.
+ for (const StringName &E : p_theme_types) {
+ if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
+ return theme_owner->data.theme->get_theme_item(p_data_type, p_name, E);
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E)) {
+ return theme_owner_window->theme->get_theme_item(p_data_type, p_name, E);
+ }
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
+ }
}
- Control *invalidate = this;
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ for (const StringName &E : p_theme_types) {
+ if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) {
+ return Theme::get_project_default()->get_theme_item(p_data_type, p_name, E);
+ }
+ }
+ }
- //invalidate cache upwards
- while (invalidate && invalidate->data.minimum_size_valid) {
- invalidate->data.minimum_size_valid = false;
- if (invalidate->is_set_as_top_level()) {
- break; // do not go further up
+ // Lastly, fall back on the items defined in the default Theme, if they exist.
+ for (const StringName &E : p_theme_types) {
+ if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) {
+ return Theme::get_default()->get_theme_item(p_data_type, p_name, E);
}
- if (!invalidate->data.parent && get_parent()) {
- Window *parent_window = Object::cast_to<Window>(get_parent());
- if (parent_window && parent_window->is_wrapping_controls()) {
- parent_window->child_controls_changed();
+ }
+ // If they don't exist, use any type to return the default/empty value.
+ return Theme::get_default()->get_theme_item(p_data_type, p_name, p_theme_types[0]);
+}
+
+bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) {
+ ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified.");
+
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ // For each theme resource check the theme types provided and see if p_name exists with any of them.
+ for (const StringName &E : p_theme_types) {
+ if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
+ return true;
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E)) {
+ return true;
+ }
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
}
}
- invalidate = invalidate->data.parent;
}
- if (!is_visible_in_tree()) {
- return;
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ for (const StringName &E : p_theme_types) {
+ if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) {
+ return true;
+ }
+ }
}
- if (data.updating_last_minimum_size) {
- return;
+ // Lastly, fall back on the items defined in the default Theme, if they exist.
+ for (const StringName &E : p_theme_types) {
+ if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) {
+ return true;
+ }
}
+ return false;
+}
- data.updating_last_minimum_size = true;
+void Control::_get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (Theme::get_project_default().is_valid() && Theme::get_project_default()->get_type_variation_base(data.theme_type_variation) != StringName()) {
+ Theme::get_project_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list);
+ } else {
+ Theme::get_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list);
+ }
+ } else {
+ Theme::get_default()->get_type_dependencies(p_theme_type, StringName(), p_list);
+ }
+}
- MessageQueue::get_singleton()->push_call(this, "_update_minimum_size");
+Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const Ref<Texture2D> *tex = data.icon_override.getptr(p_name);
+ if (tex) {
+ return *tex;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<Ref<Texture2D>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types);
}
-int Control::get_v_size_flags() const {
- return data.v_size_flags;
+Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const Ref<StyleBox> *style = data.style_override.getptr(p_name);
+ if (style) {
+ return *style;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<Ref<StyleBox>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
}
-void Control::set_mouse_filter(MouseFilter p_filter) {
- ERR_FAIL_INDEX(p_filter, 3);
- data.mouse_filter = p_filter;
- notify_property_list_changed();
- update_configuration_warnings();
+Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const Ref<Font> *font = data.font_override.getptr(p_name);
+ if (font) {
+ return *font;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<Ref<Font>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types);
}
-Control::MouseFilter Control::get_mouse_filter() const {
- return data.mouse_filter;
+int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const int *font_size = data.font_size_override.getptr(p_name);
+ if (font_size && (*font_size) > 0) {
+ return *font_size;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
}
-void Control::set_force_pass_scroll_events(bool p_force_pass_scroll_events) {
- data.force_pass_scroll_events = p_force_pass_scroll_events;
+Color Control::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const Color *color = data.color_override.getptr(p_name);
+ if (color) {
+ return *color;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<Color>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types);
}
-bool Control::is_force_pass_scroll_events() const {
- return data.force_pass_scroll_events;
+int Control::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ const int *constant = data.constant_override.getptr(p_name);
+ if (constant) {
+ return *constant;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
}
-void Control::warp_mouse(const Point2 &p_position) {
- ERR_FAIL_COND(!is_inside_tree());
- get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position));
+bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_icon_override(p_name)) {
+ return true;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types);
}
-bool Control::is_text_field() const {
- return false;
+bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_stylebox_override(p_name)) {
+ return true;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
}
-Array Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
- if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) {
- Array ret;
- if (GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret)) {
- return ret;
- } else {
- return Array();
+bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_font_override(p_name)) {
+ return true;
}
- } else {
- return TS->parse_structured_text(p_parser_type, p_args, p_text);
}
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types);
}
-void Control::set_rotation(real_t p_radians) {
- data.rotation = p_radians;
- update();
- _notify_transform();
+bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_font_size_override(p_name)) {
+ return true;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
}
-real_t Control::get_rotation() const {
- return data.rotation;
+bool Control::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_color_override(p_name)) {
+ return true;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types);
}
-void Control::_override_changed() {
- notification(NOTIFICATION_THEME_CHANGED);
- emit_signal(SceneStringNames::get_singleton()->theme_changed);
- update_minimum_size(); // Overrides are likely to affect minimum size.
+bool Control::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const {
+ if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
+ if (has_theme_constant_override(p_name)) {
+ return true;
+ }
+ }
+
+ List<StringName> theme_types;
+ _get_theme_type_dependencies(p_theme_type, &theme_types);
+ return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
}
-void Control::set_pivot_offset(const Vector2 &p_pivot) {
- data.pivot_offset = p_pivot;
- update();
- _notify_transform();
+/// Local property overrides.
+
+void Control::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) {
+ ERR_FAIL_COND(!p_icon.is_valid());
+
+ if (data.icon_override.has(p_name)) {
+ data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
+ }
+
+ data.icon_override[p_name] = p_icon;
+ data.icon_override[p_name]->connect("changed", callable_mp(this, &Control::_theme_property_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ _notify_theme_changed();
}
-Vector2 Control::get_pivot_offset() const {
- return data.pivot_offset;
+void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) {
+ ERR_FAIL_COND(!p_style.is_valid());
+
+ if (data.style_override.has(p_name)) {
+ data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
+ }
+
+ data.style_override[p_name] = p_style;
+ data.style_override[p_name]->connect("changed", callable_mp(this, &Control::_theme_property_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ _notify_theme_changed();
}
-void Control::set_scale(const Vector2 &p_scale) {
- data.scale = p_scale;
- // Avoid having 0 scale values, can lead to errors in physics and rendering.
- if (data.scale.x == 0) {
- data.scale.x = CMP_EPSILON;
+void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) {
+ ERR_FAIL_COND(!p_font.is_valid());
+
+ if (data.font_override.has(p_name)) {
+ data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
}
- if (data.scale.y == 0) {
- data.scale.y = CMP_EPSILON;
+
+ data.font_override[p_name] = p_font;
+ data.font_override[p_name]->connect("changed", callable_mp(this, &Control::_theme_property_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ _notify_theme_changed();
+}
+
+void Control::add_theme_font_size_override(const StringName &p_name, int p_font_size) {
+ data.font_size_override[p_name] = p_font_size;
+ _notify_theme_changed();
+}
+
+void Control::add_theme_color_override(const StringName &p_name, const Color &p_color) {
+ data.color_override[p_name] = p_color;
+ _notify_theme_changed();
+}
+
+void Control::add_theme_constant_override(const StringName &p_name, int p_constant) {
+ data.constant_override[p_name] = p_constant;
+ _notify_theme_changed();
+}
+
+void Control::remove_theme_icon_override(const StringName &p_name) {
+ if (data.icon_override.has(p_name)) {
+ data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
}
- update();
- _notify_transform();
+
+ data.icon_override.erase(p_name);
+ _notify_theme_changed();
}
-Vector2 Control::get_scale() const {
- return data.scale;
+void Control::remove_theme_style_override(const StringName &p_name) {
+ if (data.style_override.has(p_name)) {
+ data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
+ }
+
+ data.style_override.erase(p_name);
+ _notify_theme_changed();
}
-Control *Control::get_root_parent_control() const {
- const CanvasItem *ci = this;
- const Control *root = this;
+void Control::remove_theme_font_override(const StringName &p_name) {
+ if (data.font_override.has(p_name)) {
+ data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed));
+ }
- while (ci) {
- const Control *c = Object::cast_to<Control>(ci);
- if (c) {
- root = c;
+ data.font_override.erase(p_name);
+ _notify_theme_changed();
+}
- if (c->data.RI || c->is_top_level_control()) {
- break;
+void Control::remove_theme_font_size_override(const StringName &p_name) {
+ data.font_size_override.erase(p_name);
+ _notify_theme_changed();
+}
+
+void Control::remove_theme_color_override(const StringName &p_name) {
+ data.color_override.erase(p_name);
+ _notify_theme_changed();
+}
+
+void Control::remove_theme_constant_override(const StringName &p_name) {
+ data.constant_override.erase(p_name);
+ _notify_theme_changed();
+}
+
+bool Control::has_theme_icon_override(const StringName &p_name) const {
+ const Ref<Texture2D> *tex = data.icon_override.getptr(p_name);
+ return tex != nullptr;
+}
+
+bool Control::has_theme_stylebox_override(const StringName &p_name) const {
+ const Ref<StyleBox> *style = data.style_override.getptr(p_name);
+ return style != nullptr;
+}
+
+bool Control::has_theme_font_override(const StringName &p_name) const {
+ const Ref<Font> *font = data.font_override.getptr(p_name);
+ return font != nullptr;
+}
+
+bool Control::has_theme_font_size_override(const StringName &p_name) const {
+ const int *font_size = data.font_size_override.getptr(p_name);
+ return font_size != nullptr;
+}
+
+bool Control::has_theme_color_override(const StringName &p_name) const {
+ const Color *color = data.color_override.getptr(p_name);
+ return color != nullptr;
+}
+
+bool Control::has_theme_constant_override(const StringName &p_name) const {
+ const int *constant = data.constant_override.getptr(p_name);
+ return constant != nullptr;
+}
+
+/// Default theme properties.
+
+float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_base_scale()) {
+ return theme_owner->data.theme->get_default_base_scale();
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_default_base_scale()) {
+ return theme_owner_window->theme->get_default_base_scale();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
}
}
+ }
- ci = ci->get_parent_item();
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_base_scale()) {
+ return Theme::get_project_default()->get_default_base_scale();
+ }
}
- return const_cast<Control *>(root);
+ // Lastly, fall back on the default Theme.
+ if (Theme::get_default()->has_default_base_scale()) {
+ return Theme::get_default()->get_default_base_scale();
+ }
+ return Theme::get_fallback_base_scale();
}
-void Control::set_block_minimum_size_adjust(bool p_block) {
- data.block_minimum_size_adjust = p_block;
+float Control::get_theme_default_base_scale() const {
+ return fetch_theme_default_base_scale(data.theme_owner, data.theme_owner_window);
}
-bool Control::is_minimum_size_adjust_blocked() const {
- return data.block_minimum_size_adjust;
-}
+Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
-void Control::set_disable_visibility_clip(bool p_ignore) {
- data.disable_visibility_clip = p_ignore;
- update();
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_font()) {
+ return theme_owner->data.theme->get_default_font();
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_default_font()) {
+ return theme_owner_window->theme->get_default_font();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
+ }
+ }
+
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_font()) {
+ return Theme::get_project_default()->get_default_font();
+ }
+ }
+
+ // Lastly, fall back on the default Theme.
+ if (Theme::get_default()->has_default_font()) {
+ return Theme::get_default()->get_default_font();
+ }
+ return Theme::get_fallback_font();
}
-bool Control::is_visibility_clip_disabled() const {
- return data.disable_visibility_clip;
+Ref<Font> Control::get_theme_default_font() const {
+ return fetch_theme_default_font(data.theme_owner, data.theme_owner_window);
}
-void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
- Node::get_argument_options(p_function, p_idx, r_options);
+int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
- if (p_idx == 0) {
- List<StringName> sn;
- String pf = p_function;
- if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") {
- Theme::get_default()->get_color_list(get_class(), &sn);
- } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") {
- Theme::get_default()->get_stylebox_list(get_class(), &sn);
- } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") {
- Theme::get_default()->get_font_list(get_class(), &sn);
- } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") {
- Theme::get_default()->get_font_size_list(get_class(), &sn);
- } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") {
- Theme::get_default()->get_constant_list(get_class(), &sn);
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_font_size()) {
+ return theme_owner->data.theme->get_default_font_size();
}
- sn.sort_custom<StringName::AlphCompare>();
- for (const StringName &name : sn) {
- r_options->push_back(String(name).quote());
+ if (theme_owner_window && theme_owner_window->theme->has_default_font_size()) {
+ return theme_owner_window->theme->get_default_font_size();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
}
}
+
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_font_size()) {
+ return Theme::get_project_default()->get_default_font_size();
+ }
+ }
+
+ // Lastly, fall back on the default Theme.
+ if (Theme::get_default()->has_default_font_size()) {
+ return Theme::get_default()->get_default_font_size();
+ }
+ return Theme::get_fallback_font_size();
}
-TypedArray<String> Control::get_configuration_warnings() const {
- TypedArray<String> warnings = Node::get_configuration_warnings();
+int Control::get_theme_default_font_size() const {
+ return fetch_theme_default_font_size(data.theme_owner, data.theme_owner_window);
+}
- if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) {
- warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
+/// Bulk actions.
+
+void Control::begin_bulk_theme_override() {
+ data.bulk_theme_override = true;
+}
+
+void Control::end_bulk_theme_override() {
+ ERR_FAIL_COND(!data.bulk_theme_override);
+
+ data.bulk_theme_override = false;
+ _notify_theme_changed();
+}
+
+// Internationalization.
+
+Array Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
+ if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) {
+ Array ret;
+ if (GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret)) {
+ return ret;
+ } else {
+ return Array();
+ }
+ } else {
+ return TS->parse_structured_text(p_parser_type, p_args, p_text);
}
+}
- return warnings;
+void Control::set_layout_direction(Control::LayoutDirection p_direction) {
+ ERR_FAIL_INDEX((int)p_direction, 4);
+
+ data.layout_dir = p_direction;
+ data.is_rtl_dirty = true;
+
+ propagate_notification(NOTIFICATION_LAYOUT_DIRECTION_CHANGED);
}
-void Control::set_clip_contents(bool p_clip) {
- data.clip_contents = p_clip;
- update();
+Control::LayoutDirection Control::get_layout_direction() const {
+ return data.layout_dir;
}
-bool Control::is_clipping_contents() {
- return data.clip_contents;
+bool Control::is_layout_rtl() const {
+ if (data.is_rtl_dirty) {
+ const_cast<Control *>(this)->data.is_rtl_dirty = false;
+ if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) {
+ Window *parent_window = get_parent_window();
+ Control *parent_control = get_parent_control();
+ if (parent_control) {
+ const_cast<Control *>(this)->data.is_rtl = parent_control->is_layout_rtl();
+ } else if (parent_window) {
+ const_cast<Control *>(this)->data.is_rtl = parent_window->is_layout_rtl();
+ } else {
+ if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
+ const_cast<Control *>(this)->data.is_rtl = true;
+ } else {
+ String locale = TranslationServer::get_singleton()->get_tool_locale();
+ const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
+ }
+ }
+ } else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) {
+ if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
+ const_cast<Control *>(this)->data.is_rtl = true;
+ } else {
+ String locale = TranslationServer::get_singleton()->get_tool_locale();
+ const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
+ }
+ } else {
+ const_cast<Control *>(this)->data.is_rtl = (data.layout_dir == LAYOUT_DIRECTION_RTL);
+ }
+ }
+ return data.is_rtl;
}
-void Control::set_h_grow_direction(GrowDirection p_direction) {
- ERR_FAIL_INDEX((int)p_direction, 3);
+void Control::set_auto_translate(bool p_enable) {
+ if (p_enable == data.auto_translate) {
+ return;
+ }
- data.h_grow = p_direction;
- _size_changed();
+ data.auto_translate = p_enable;
+
+ notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
}
-Control::GrowDirection Control::get_h_grow_direction() const {
- return data.h_grow;
+bool Control::is_auto_translating() const {
+ return data.auto_translate;
}
-void Control::set_v_grow_direction(GrowDirection p_direction) {
- ERR_FAIL_INDEX((int)p_direction, 3);
+// Extra properties.
- data.v_grow = p_direction;
- _size_changed();
+void Control::set_tooltip(const String &p_tooltip) {
+ data.tooltip = p_tooltip;
+ update_configuration_warnings();
}
-Control::GrowDirection Control::get_v_grow_direction() const {
- return data.v_grow;
+String Control::_get_tooltip() const {
+ return data.tooltip;
+}
+
+String Control::get_tooltip(const Point2 &p_pos) const {
+ return data.tooltip;
+}
+
+Control *Control::make_custom_tooltip(const String &p_text) const {
+ Object *ret = nullptr;
+ if (GDVIRTUAL_CALL(_make_custom_tooltip, p_text, ret)) {
+ return Object::cast_to<Control>(ret);
+ }
+ return nullptr;
+}
+
+// Base object overrides.
+
+void Control::add_child_notify(Node *p_child) {
+ Control *child_c = Object::cast_to<Control>(p_child);
+
+ if (child_c && child_c->data.theme.is_null() && (data.theme_owner || data.theme_owner_window)) {
+ _propagate_theme_changed(child_c, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff
+ }
+
+ Window *child_w = Object::cast_to<Window>(p_child);
+
+ if (child_w && child_w->theme.is_null() && (data.theme_owner || data.theme_owner_window)) {
+ _propagate_theme_changed(child_w, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff
+ }
+}
+
+void Control::remove_child_notify(Node *p_child) {
+ Control *child_c = Object::cast_to<Control>(p_child);
+
+ if (child_c && (child_c->data.theme_owner || child_c->data.theme_owner_window) && child_c->data.theme.is_null()) {
+ _propagate_theme_changed(child_c, nullptr, nullptr);
+ }
+
+ Window *child_w = Object::cast_to<Window>(p_child);
+
+ if (child_w && (child_w->theme_owner || child_w->theme_owner_window) && child_w->theme.is_null()) {
+ _propagate_theme_changed(child_w, nullptr, nullptr);
+ }
+}
+
+void Control::_notification(int p_notification) {
+ switch (p_notification) {
+ case NOTIFICATION_POST_ENTER_TREE: {
+ data.minimum_size_valid = false;
+ data.is_rtl_dirty = true;
+ _size_changed();
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ release_focus();
+ get_viewport()->_gui_remove_control(this);
+ } break;
+
+ case NOTIFICATION_READY: {
+#ifdef DEBUG_ENABLED
+ connect("ready", callable_mp(this, &Control::_clear_size_warning), varray(), CONNECT_DEFERRED | CONNECT_ONESHOT);
+#endif
+ } break;
+
+ case NOTIFICATION_ENTER_CANVAS: {
+ data.parent = Object::cast_to<Control>(get_parent());
+ data.parent_window = Object::cast_to<Window>(get_parent());
+ data.is_rtl_dirty = true;
+
+ if (data.theme.is_null()) {
+ if (data.parent && (data.parent->data.theme_owner || data.parent->data.theme_owner_window)) {
+ data.theme_owner = data.parent->data.theme_owner;
+ data.theme_owner_window = data.parent->data.theme_owner_window;
+ notification(NOTIFICATION_THEME_CHANGED);
+ } else if (data.parent_window && (data.parent_window->theme_owner || data.parent_window->theme_owner_window)) {
+ data.theme_owner = data.parent_window->theme_owner;
+ data.theme_owner_window = data.parent_window->theme_owner_window;
+ notification(NOTIFICATION_THEME_CHANGED);
+ }
+ }
+
+ CanvasItem *node = this;
+ bool has_parent_control = false;
+
+ while (!node->is_set_as_top_level()) {
+ CanvasItem *parent = Object::cast_to<CanvasItem>(node->get_parent());
+ if (!parent) {
+ break;
+ }
+
+ Control *parent_control = Object::cast_to<Control>(parent);
+ if (parent_control) {
+ has_parent_control = true;
+ break;
+ }
+
+ node = parent;
+ }
+
+ if (has_parent_control) {
+ // Do nothing, has a parent control.
+ } else {
+ // Is a regular root control or top_level.
+ Viewport *viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+ data.RI = viewport->_gui_add_root_control(this);
+ }
+
+ data.parent_canvas_item = get_parent_item();
+
+ if (data.parent_canvas_item) {
+ data.parent_canvas_item->connect("item_rect_changed", callable_mp(this, &Control::_size_changed));
+ } else {
+ // Connect viewport.
+ Viewport *viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+ viewport->connect("size_changed", callable_mp(this, &Control::_size_changed));
+ }
+ } break;
+
+ case NOTIFICATION_EXIT_CANVAS: {
+ if (data.parent_canvas_item) {
+ data.parent_canvas_item->disconnect("item_rect_changed", callable_mp(this, &Control::_size_changed));
+ data.parent_canvas_item = nullptr;
+ } else if (!is_set_as_top_level()) {
+ //disconnect viewport
+ Viewport *viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+ viewport->disconnect("size_changed", callable_mp(this, &Control::_size_changed));
+ }
+
+ if (data.RI) {
+ get_viewport()->_gui_remove_root_control(data.RI);
+ data.RI = nullptr;
+ }
+
+ data.parent = nullptr;
+ data.parent_canvas_item = nullptr;
+ data.parent_window = nullptr;
+ data.is_rtl_dirty = true;
+ } break;
+
+ case NOTIFICATION_MOVED_IN_PARENT: {
+ // some parents need to know the order of the children to draw (like TabContainer)
+ // update if necessary
+ if (data.parent) {
+ data.parent->update();
+ }
+ update();
+
+ if (data.RI) {
+ get_viewport()->_gui_set_root_order_dirty();
+ }
+ } break;
+
+ case NOTIFICATION_RESIZED: {
+ emit_signal(SceneStringNames::get_singleton()->resized);
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ _update_canvas_item_transform();
+ RenderingServer::get_singleton()->canvas_item_set_custom_rect(get_canvas_item(), !data.disable_visibility_clip, Rect2(Point2(), get_size()));
+ RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), data.clip_contents);
+ } break;
+
+ case NOTIFICATION_MOUSE_ENTER: {
+ emit_signal(SceneStringNames::get_singleton()->mouse_entered);
+ } break;
+
+ case NOTIFICATION_MOUSE_EXIT: {
+ emit_signal(SceneStringNames::get_singleton()->mouse_exited);
+ } break;
+
+ case NOTIFICATION_FOCUS_ENTER: {
+ emit_signal(SceneStringNames::get_singleton()->focus_entered);
+ update();
+ } break;
+
+ case NOTIFICATION_FOCUS_EXIT: {
+ emit_signal(SceneStringNames::get_singleton()->focus_exited);
+ update();
+ } break;
+
+ case NOTIFICATION_THEME_CHANGED: {
+ update_minimum_size();
+ update();
+ } break;
+
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (!is_visible_in_tree()) {
+ if (get_viewport() != nullptr) {
+ get_viewport()->_gui_hide_control(this);
+ }
+ } else {
+ data.minimum_size_valid = false;
+ _update_minimum_size();
+ _size_changed();
+ }
+ } break;
+
+ case NOTIFICATION_TRANSLATION_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ if (is_inside_tree()) {
+ data.is_rtl_dirty = true;
+ _size_changed();
+ }
+ } break;
+ }
}
void Control::_bind_methods() {
- //ClassDB::bind_method(D_METHOD("_window_resize_event"),&Control::_window_resize_event);
ClassDB::bind_method(D_METHOD("_update_minimum_size"), &Control::_update_minimum_size);
ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index d9c29a9c87..9f17eccc3b 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -159,13 +159,16 @@ private:
};
struct Data {
- Point2 pos_cache;
- Size2 size_cache;
- Size2 minimum_size_cache;
- bool minimum_size_valid = false;
+ // Global relations.
- Size2 last_minimum_size;
- bool updating_last_minimum_size = false;
+ List<Control *>::Element *RI = nullptr;
+
+ Control *parent = nullptr;
+ Window *parent_window = nullptr;
+ CanvasItem *parent_canvas_item = nullptr;
+ ObjectID drag_owner;
+
+ // Positioning and sizing.
real_t offset[4] = { 0.0, 0.0, 0.0, 0.0 };
real_t anchor[4] = { ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN };
@@ -173,49 +176,51 @@ private:
GrowDirection h_grow = GROW_DIRECTION_END;
GrowDirection v_grow = GROW_DIRECTION_END;
- LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;
- bool is_rtl_dirty = true;
- bool is_rtl = false;
-
- bool auto_translate = true;
-
real_t rotation = 0.0;
Vector2 scale = Vector2(1, 1);
Vector2 pivot_offset;
+
+ Point2 pos_cache;
+ Size2 size_cache;
+ Size2 minimum_size_cache;
+ bool minimum_size_valid = false;
+
+ Size2 last_minimum_size;
+ bool updating_last_minimum_size = false;
+ bool block_minimum_size_adjust = false;
+
bool size_warning = true;
+ // Container sizing.
+
int h_size_flags = SIZE_FILL;
int v_size_flags = SIZE_FILL;
real_t expand = 1.0;
Point2 custom_minimum_size;
+ // Input events and rendering.
+
MouseFilter mouse_filter = MOUSE_FILTER_STOP;
bool force_pass_scroll_events = true;
bool clip_contents = false;
-
- bool block_minimum_size_adjust = false;
bool disable_visibility_clip = false;
- Control *parent = nullptr;
- ObjectID drag_owner;
- Ref<Theme> theme;
- Control *theme_owner = nullptr;
- Window *theme_owner_window = nullptr;
- Window *parent_window = nullptr;
- StringName theme_type_variation;
-
- String tooltip;
CursorShape default_cursor = CURSOR_ARROW;
- List<Control *>::Element *RI = nullptr;
-
- CanvasItem *parent_canvas_item = nullptr;
+ // Focus.
NodePath focus_neighbor[4];
NodePath focus_next;
NodePath focus_prev;
+ // Theming.
+
+ Ref<Theme> theme;
+ Control *theme_owner = nullptr;
+ Window *theme_owner_window = nullptr;
+ StringName theme_type_variation;
+
bool bulk_theme_override = false;
Theme::ThemeIconMap icon_override;
Theme::ThemeStyleMap style_override;
@@ -224,50 +229,69 @@ private:
Theme::ThemeColorMap color_override;
Theme::ThemeConstantMap constant_override;
+ // Internationalization.
+
+ LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;
+ bool is_rtl_dirty = true;
+ bool is_rtl = false;
+
+ bool auto_translate = true;
+
+ // Extra properties.
+
+ String tooltip;
+
} data;
+ // Dynamic properties.
+
static constexpr unsigned properties_managed_by_container_count = 12;
static String properties_managed_by_container[properties_managed_by_container_count];
- void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest);
- Control *_get_focus_neighbor(Side p_side, int p_count = 0);
+ // Global relations.
+
+ friend class Viewport;
+ friend class Window;
+
+ // Positioning and sizing.
+
+ void _update_canvas_item_transform();
+ Transform2D _get_internal_transform() const;
void _set_anchor(Side p_side, real_t p_anchor);
void _set_position(const Point2 &p_point);
void _set_global_position(const Point2 &p_point);
void _set_size(const Size2 &p_size);
+ void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]);
+ void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]);
+
void _set_layout_mode(LayoutMode p_mode);
LayoutMode _get_layout_mode() const;
-
void _set_anchors_layout_preset(int p_preset);
int _get_anchors_layout_preset() const;
- void _theme_changed();
- void _notify_theme_changed();
-
+ void _update_minimum_size_cache();
void _update_minimum_size();
+ void _size_changed();
void _clear_size_warning();
- void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]);
- void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]);
-
- void _size_changed();
- String _get_tooltip() const;
+ // Input events.
- void _override_changed();
+ void _call_gui_input(const Ref<InputEvent> &p_event);
- void _update_canvas_item_transform();
+ // Focus.
- Transform2D _get_internal_transform() const;
+ void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest);
+ Control *_get_focus_neighbor(Side p_side, int p_count = 0);
- friend class Viewport;
+ // Theming.
- void _call_gui_input(const Ref<InputEvent> &p_event);
+ void _theme_changed();
+ void _theme_property_override_changed();
+ void _notify_theme_changed();
- void _update_minimum_size_cache();
- friend class Window;
static void _propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign = true);
template <class T>
@@ -275,24 +299,31 @@ private:
static bool has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types);
_FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const;
-protected:
- virtual void add_child_notify(Node *p_child) override;
- virtual void remove_child_notify(Node *p_child) override;
+ // Extra properties.
- //virtual void _window_gui_input(InputEvent p_event);
+ String _get_tooltip() const;
- virtual Array structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
+protected:
+ // Dynamic properties.
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
-
virtual void _validate_property(PropertyInfo &property) const override;
+ // Internationalization.
+
+ virtual Array structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
+
+ // Base object overrides.
+
+ virtual void add_child_notify(Node *p_child) override;
+ virtual void remove_child_notify(Node *p_child) override;
+
void _notification(int p_notification);
static void _bind_methods();
- //bind helpers
+ // Exposed virtual methods.
GDVIRTUAL1RC(bool, _has_point, Vector2)
GDVIRTUAL2RC(Array, _structured_text_parser, Array, String)
@@ -307,8 +338,6 @@ protected:
public:
enum {
- /* NOTIFICATION_DRAW=30,
- NOTIFICATION_VISIBILITY_CHANGED=38*/
NOTIFICATION_RESIZED = 40,
NOTIFICATION_MOUSE_ENTER = 41,
NOTIFICATION_MOUSE_EXIT = 42,
@@ -318,10 +347,11 @@ public:
NOTIFICATION_SCROLL_BEGIN = 47,
NOTIFICATION_SCROLL_END = 48,
NOTIFICATION_LAYOUT_DIRECTION_CHANGED = 49,
-
};
- /* EDITOR */
+ // Editor plugin interoperability.
+
+ // TODO: Decouple controls from their editor plugin and get rid of this.
#ifdef TOOLS_ENABLED
virtual Dictionary _edit_get_state() const override;
virtual void _edit_set_state(const Dictionary &p_state) override;
@@ -347,56 +377,50 @@ public:
virtual Size2 _edit_get_minimum_size() const override;
#endif
- virtual void gui_input(const Ref<InputEvent> &p_event);
+ // Editor integration.
- void accept_event();
+ virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
+ TypedArray<String> get_configuration_warnings() const override;
- virtual Size2 get_minimum_size() const;
- virtual Size2 get_combined_minimum_size() const;
- virtual bool has_point(const Point2 &p_point) const;
- virtual void set_drag_forwarding(Object *p_target);
- virtual Variant get_drag_data(const Point2 &p_point);
- virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
- virtual void drop_data(const Point2 &p_point, const Variant &p_data);
- void set_drag_preview(Control *p_control);
- void force_drag(const Variant &p_data, Control *p_control);
- bool is_drag_successful() const;
+ virtual bool is_text_field() const;
- void set_custom_minimum_size(const Size2 &p_custom);
- Size2 get_custom_minimum_size() const;
+ // Global relations.
+
+ bool is_top_level_control() const;
Control *get_parent_control() const;
Window *get_parent_window() const;
+ Control *get_root_parent_control() const;
- void set_layout_direction(LayoutDirection p_direction);
- LayoutDirection get_layout_direction() const;
- virtual bool is_layout_rtl() const;
-
- void set_auto_translate(bool p_enable);
- bool is_auto_translating() const;
- _FORCE_INLINE_ String atr(const String p_string) const { return is_auto_translating() ? tr(p_string) : p_string; };
+ Size2 get_parent_area_size() const;
+ Rect2 get_parent_anchorable_rect() const;
- /* POSITIONING */
+ // Positioning and sizing.
- void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true);
- void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
- void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
- void set_grow_direction_preset(LayoutPreset p_preset);
+ virtual Transform2D get_transform() const override;
void set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset = true, bool p_push_opposite_anchor = true);
real_t get_anchor(Side p_side) const;
-
void set_offset(Side p_side, real_t p_value);
real_t get_offset(Side p_side) const;
-
void set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor = true);
- void set_begin(const Point2 &p_point); // helper
- void set_end(const Point2 &p_point); // helper
-
+ // TODO: Rename to set_begin/end_offsets ?
+ void set_begin(const Point2 &p_point);
Point2 get_begin() const;
+ void set_end(const Point2 &p_point);
Point2 get_end() const;
+ void set_h_grow_direction(GrowDirection p_direction);
+ GrowDirection get_h_grow_direction() const;
+ void set_v_grow_direction(GrowDirection p_direction);
+ GrowDirection get_v_grow_direction() const;
+
+ void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true);
+ void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
+ void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
+ void set_grow_direction_preset(LayoutPreset p_preset);
+
void set_position(const Point2 &p_point, bool p_keep_offsets = false);
void set_global_position(const Point2 &p_point, bool p_keep_offsets = false);
Point2 get_position() const;
@@ -407,52 +431,72 @@ public:
Size2 get_size() const;
void reset_size();
+ void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting.
Rect2 get_rect() const;
Rect2 get_global_rect() const;
Rect2 get_screen_rect() const;
Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the rendering server
Rect2 get_anchorable_rect() const override;
- void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting.
-
+ void set_scale(const Vector2 &p_scale);
+ Vector2 get_scale() const;
void set_rotation(real_t p_radians);
real_t get_rotation() const;
-
- void set_h_grow_direction(GrowDirection p_direction);
- GrowDirection get_h_grow_direction() const;
-
- void set_v_grow_direction(GrowDirection p_direction);
- GrowDirection get_v_grow_direction() const;
-
void set_pivot_offset(const Vector2 &p_pivot);
Vector2 get_pivot_offset() const;
- void set_scale(const Vector2 &p_scale);
- Vector2 get_scale() const;
+ void update_minimum_size();
- void set_theme(const Ref<Theme> &p_theme);
- Ref<Theme> get_theme() const;
+ void set_block_minimum_size_adjust(bool p_block);
+ bool is_minimum_size_adjust_blocked() const;
- void set_theme_type_variation(const StringName &p_theme_type);
- StringName get_theme_type_variation() const;
+ virtual Size2 get_minimum_size() const;
+ virtual Size2 get_combined_minimum_size() const;
+
+ void set_custom_minimum_size(const Size2 &p_custom);
+ Size2 get_custom_minimum_size() const;
+
+ // Container sizing.
void set_h_size_flags(int p_flags);
int get_h_size_flags() const;
-
void set_v_size_flags(int p_flags);
int get_v_size_flags() const;
-
void set_stretch_ratio(real_t p_ratio);
real_t get_stretch_ratio() const;
- void update_minimum_size();
+ // Input events.
- /* FOCUS */
+ virtual void gui_input(const Ref<InputEvent> &p_event);
+ void accept_event();
+
+ virtual bool has_point(const Point2 &p_point) const;
+
+ void set_mouse_filter(MouseFilter p_filter);
+ MouseFilter get_mouse_filter() const;
+
+ void set_force_pass_scroll_events(bool p_force_pass_scroll_events);
+ bool is_force_pass_scroll_events() const;
+
+ void warp_mouse(const Point2 &p_position);
+
+ // Drag and drop handling.
+
+ virtual void set_drag_forwarding(Object *p_target);
+ virtual Variant get_drag_data(const Point2 &p_point);
+ virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
+ virtual void drop_data(const Point2 &p_point, const Variant &p_data);
+ void set_drag_preview(Control *p_control);
+ void force_drag(const Variant &p_data, Control *p_control);
+ bool is_drag_successful() const;
+
+ // Focus.
void set_focus_mode(FocusMode p_focus_mode);
FocusMode get_focus_mode() const;
bool has_focus() const;
void grab_focus();
+ void grab_click_focus();
void release_focus();
Control *find_next_valid_focus() const;
@@ -466,13 +510,25 @@ public:
void set_focus_previous(const NodePath &p_prev);
NodePath get_focus_previous() const;
- void set_mouse_filter(MouseFilter p_filter);
- MouseFilter get_mouse_filter() const;
+ // Rendering.
- void set_force_pass_scroll_events(bool p_force_pass_scroll_events);
- bool is_force_pass_scroll_events() const;
+ void set_default_cursor_shape(CursorShape p_shape);
+ CursorShape get_default_cursor_shape() const;
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const;
+
+ void set_clip_contents(bool p_clip);
+ bool is_clipping_contents();
- /* SKINNING */
+ void set_disable_visibility_clip(bool p_ignore);
+ bool is_visibility_clip_disabled() const;
+
+ // Theming.
+
+ void set_theme(const Ref<Theme> &p_theme);
+ Ref<Theme> get_theme() const;
+
+ void set_theme_type_variation(const StringName &p_theme_type);
+ StringName get_theme_type_variation() const;
void begin_bulk_theme_override();
void end_bulk_theme_override();
@@ -520,44 +576,23 @@ public:
Ref<Font> get_theme_default_font() const;
int get_theme_default_font_size() const;
- /* TOOLTIP */
+ // Internationalization.
- void set_tooltip(const String &p_tooltip);
- virtual String get_tooltip(const Point2 &p_pos) const;
- virtual Control *make_custom_tooltip(const String &p_text) const;
-
- /* CURSOR */
-
- void set_default_cursor_shape(CursorShape p_shape);
- CursorShape get_default_cursor_shape() const;
- virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const;
-
- virtual Transform2D get_transform() const override;
-
- bool is_top_level_control() const;
-
- Size2 get_parent_area_size() const;
- Rect2 get_parent_anchorable_rect() const;
-
- void grab_click_focus();
-
- void warp_mouse(const Point2 &p_position);
-
- virtual bool is_text_field() const;
-
- Control *get_root_parent_control() const;
-
- void set_clip_contents(bool p_clip);
- bool is_clipping_contents();
+ void set_layout_direction(LayoutDirection p_direction);
+ LayoutDirection get_layout_direction() const;
+ virtual bool is_layout_rtl() const;
- void set_block_minimum_size_adjust(bool p_block);
- bool is_minimum_size_adjust_blocked() const;
+ void set_auto_translate(bool p_enable);
+ bool is_auto_translating() const;
+ _FORCE_INLINE_ String atr(const String p_string) const {
+ return is_auto_translating() ? tr(p_string) : p_string;
+ };
- void set_disable_visibility_clip(bool p_ignore);
- bool is_visibility_clip_disabled() const;
+ // Extra properties.
- virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
- TypedArray<String> get_configuration_warnings() const override;
+ void set_tooltip(const String &p_tooltip);
+ virtual String get_tooltip(const Point2 &p_pos) const;
+ virtual Control *make_custom_tooltip(const String &p_text) const;
Control() {}
};
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 06553cd0f6..630a3316d6 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -443,8 +443,10 @@ void TextEdit::_notification(int p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
- _update_caches();
- _update_wrap_at_column(true);
+ if (is_inside_tree()) {
+ _update_caches();
+ _update_wrap_at_column(true);
+ }
} break;
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index c2fa1ace8d..7876b00202 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1234,13 +1234,23 @@ void Viewport::_gui_show_tooltip() {
Rect2i vr = window->get_usable_parent_rect();
if (r.size.x + r.position.x > vr.size.x + vr.position.x) {
- r.position.x = vr.position.x + vr.size.x - r.size.x;
+ // Place it in the opposite direction. If it fails, just hug the border.
+ r.position.x = gui.tooltip_pos.x - r.size.x - tooltip_offset.x;
+
+ if (r.position.x < vr.position.x) {
+ r.position.x = vr.position.x + vr.size.x - r.size.x;
+ }
} else if (r.position.x < vr.position.x) {
r.position.x = vr.position.x;
}
if (r.size.y + r.position.y > vr.size.y + vr.position.y) {
- r.position.y = vr.position.y + vr.size.y - r.size.y;
+ // Same as above.
+ r.position.y = gui.tooltip_pos.y - r.size.y - tooltip_offset.y;
+
+ if (r.position.y < vr.position.y) {
+ r.position.y = vr.position.y + vr.size.y - r.size.y;
+ }
} else if (r.position.y < vr.position.y) {
r.position.y = vr.position.y;
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 09a283ea53..ef5ac36114 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -870,6 +870,7 @@ void register_scene_types() {
GDREGISTER_ABSTRACT_CLASS(Font);
GDREGISTER_CLASS(FontFile);
GDREGISTER_CLASS(FontVariation);
+ GDREGISTER_CLASS(SystemFont);
GDREGISTER_CLASS(Curve);
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index f61ac7fcaa..c469946b45 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -79,9 +79,9 @@ void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &Font::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL));
// Drawing char.
- ClassDB::bind_method(D_METHOD("get_char_size", "char"), &Font::get_char_size);
- ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "modulate"), &Font::draw_char, DEFVAL(Color(1.0, 1.0, 1.0)));
- ClassDB::bind_method(D_METHOD("draw_char_outline", "canvas_item", "pos", "char", "size", "modulate"), &Font::draw_char_outline, DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)));
+ ClassDB::bind_method(D_METHOD("get_char_size", "char", "font_size"), &Font::get_char_size);
+ ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "font_size", "modulate"), &Font::draw_char, DEFVAL(Color(1.0, 1.0, 1.0)));
+ ClassDB::bind_method(D_METHOD("draw_char_outline", "canvas_item", "pos", "char", "font_size", "size", "modulate"), &Font::draw_char_outline, DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)));
// Helper functions.
ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char);
@@ -2693,3 +2693,374 @@ FontVariation::FontVariation() {
FontVariation::~FontVariation() {
reset_state();
}
+
+/*************************************************************************/
+/* SystemFont */
+/*************************************************************************/
+
+void SystemFont::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &SystemFont::set_antialiased);
+ ClassDB::bind_method(D_METHOD("is_antialiased"), &SystemFont::is_antialiased);
+
+ ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &SystemFont::set_generate_mipmaps);
+ ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &SystemFont::get_generate_mipmaps);
+
+ ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &SystemFont::set_force_autohinter);
+ ClassDB::bind_method(D_METHOD("is_force_autohinter"), &SystemFont::is_force_autohinter);
+
+ ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &SystemFont::set_hinting);
+ ClassDB::bind_method(D_METHOD("get_hinting"), &SystemFont::get_hinting);
+
+ ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &SystemFont::set_subpixel_positioning);
+ ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &SystemFont::get_subpixel_positioning);
+
+ ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &SystemFont::set_oversampling);
+ ClassDB::bind_method(D_METHOD("get_oversampling"), &SystemFont::get_oversampling);
+
+ ClassDB::bind_method(D_METHOD("get_font_names"), &SystemFont::get_font_names);
+ ClassDB::bind_method(D_METHOD("set_font_names", "names"), &SystemFont::set_font_names);
+
+ ClassDB::bind_method(D_METHOD("set_font_style", "style"), &SystemFont::set_font_style);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "font_names"), "set_font_names", "get_font_names");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic"), "set_font_style", "get_font_style");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "is_antialiased");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "get_generate_mipmaps");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "is_force_autohinter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel"), "set_subpixel_positioning", "get_subpixel_positioning");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks");
+}
+
+void SystemFont::_update_rids() const {
+ Ref<Font> f = _get_base_font_or_default();
+
+ rids.clear();
+ if (fallbacks.is_empty() && f.is_valid()) {
+ RID rid = _get_rid();
+ if (rid.is_valid()) {
+ rids.push_back(rid);
+ }
+
+ const TypedArray<Font> &base_fallbacks = f->get_fallbacks();
+ for (int i = 0; i < base_fallbacks.size(); i++) {
+ _update_rids_fb(base_fallbacks[i], 0);
+ }
+ } else {
+ _update_rids_fb(const_cast<SystemFont *>(this), 0);
+ }
+ dirty_rids = false;
+}
+
+void SystemFont::_update_base_font() {
+ if (base_font.is_valid()) {
+ base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ base_font.unref();
+ }
+
+ face_indeces.clear();
+ ftr_weight = 0;
+ ftr_italic = 0;
+ for (const String &E : names) {
+ if (E.is_empty()) {
+ continue;
+ }
+
+ String path = OS::get_singleton()->get_system_font_path(E, style & TextServer::FONT_BOLD, style & TextServer::FONT_ITALIC);
+ if (path.is_empty()) {
+ continue;
+ }
+ Ref<FontFile> file;
+ file.instantiate();
+ Error err = file->load_dynamic_font(path);
+ if (err != OK) {
+ continue;
+ }
+
+ // If it's a font collection check all faces to match requested style.
+ for (int i = 0; i < file->get_face_count(); i++) {
+ file->set_face_index(0, i);
+ if (((file->get_font_style() & TextServer::FONT_BOLD) == (style & TextServer::FONT_BOLD)) && ((file->get_font_style() & TextServer::FONT_ITALIC) == (style & TextServer::FONT_ITALIC))) {
+ face_indeces.push_back(i);
+ }
+ }
+ if (face_indeces.is_empty()) {
+ face_indeces.push_back(0);
+ }
+ file->set_face_index(0, face_indeces[0]);
+
+ // If it's a variable font, apply weight and italic coordinates to match requested style.
+ Dictionary ftr = file->get_supported_variation_list();
+ if ((style & TextServer::FONT_BOLD) && ftr.has(TS->name_to_tag("weight"))) {
+ ftr_weight = 700;
+ }
+ if ((style & TextServer::FONT_ITALIC) && ftr.has(TS->name_to_tag("italic"))) {
+ ftr_italic = 1;
+ }
+
+ // Apply font rendering settings.
+ file->set_antialiased(antialiased);
+ file->set_generate_mipmaps(mipmaps);
+ file->set_force_autohinter(force_autohinter);
+ file->set_hinting(hinting);
+ file->set_subpixel_positioning(subpixel_positioning);
+ file->set_oversampling(oversampling);
+
+ base_font = file;
+ }
+
+ if (base_font.is_valid()) {
+ base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+
+ _invalidate_rids();
+ notify_property_list_changed();
+}
+
+void SystemFont::reset_state() {
+ if (base_font.is_valid()) {
+ base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ base_font.unref();
+ }
+
+ if (theme_font.is_valid()) {
+ theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ theme_font.unref();
+ }
+
+ names.clear();
+ face_indeces.clear();
+ ftr_weight = 0;
+ ftr_italic = 0;
+ style = 0;
+ antialiased = true;
+ mipmaps = false;
+ force_autohinter = false;
+ hinting = TextServer::HINTING_LIGHT;
+ subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
+ oversampling = 0.f;
+
+ Font::reset_state();
+}
+
+Ref<Font> SystemFont::_get_base_font_or_default() const {
+ if (theme_font.is_valid()) {
+ theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids));
+ theme_font.unref();
+ }
+
+ if (base_font.is_valid()) {
+ return base_font;
+ }
+
+ // Check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ List<StringName> theme_types;
+ Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types);
+
+ for (const StringName &E : theme_types) {
+ if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) {
+ Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
+ if (f.is_valid()) {
+ theme_font = f;
+ theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+ return f;
+ }
+ }
+ }
+
+ // Lastly, fall back on the items defined in the default Theme, if they exist.
+ if (Theme::get_default().is_valid()) {
+ List<StringName> theme_types;
+ Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types);
+
+ for (const StringName &E : theme_types) {
+ if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) {
+ Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
+ if (f.is_valid()) {
+ theme_font = f;
+ theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+ return f;
+ }
+ }
+
+ // If they don't exist, use any type to return the default/empty value.
+ Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName());
+ if (f.is_valid()) {
+ theme_font = f;
+ theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+ return f;
+ }
+
+ return Ref<Font>();
+}
+
+void SystemFont::set_antialiased(bool p_antialiased) {
+ if (antialiased != p_antialiased) {
+ antialiased = p_antialiased;
+ if (base_font.is_valid()) {
+ base_font->set_antialiased(antialiased);
+ }
+ emit_changed();
+ }
+}
+
+bool SystemFont::is_antialiased() const {
+ return antialiased;
+}
+
+void SystemFont::set_generate_mipmaps(bool p_generate_mipmaps) {
+ if (mipmaps != p_generate_mipmaps) {
+ mipmaps = p_generate_mipmaps;
+ if (base_font.is_valid()) {
+ base_font->set_generate_mipmaps(mipmaps);
+ }
+ emit_changed();
+ }
+}
+
+bool SystemFont::get_generate_mipmaps() const {
+ return mipmaps;
+}
+
+void SystemFont::set_force_autohinter(bool p_force_autohinter) {
+ if (force_autohinter != p_force_autohinter) {
+ force_autohinter = p_force_autohinter;
+ if (base_font.is_valid()) {
+ base_font->set_force_autohinter(force_autohinter);
+ }
+ emit_changed();
+ }
+}
+
+bool SystemFont::is_force_autohinter() const {
+ return force_autohinter;
+}
+
+void SystemFont::set_hinting(TextServer::Hinting p_hinting) {
+ if (hinting != p_hinting) {
+ hinting = p_hinting;
+ if (base_font.is_valid()) {
+ base_font->set_hinting(hinting);
+ }
+ emit_changed();
+ }
+}
+
+TextServer::Hinting SystemFont::get_hinting() const {
+ return hinting;
+}
+
+void SystemFont::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) {
+ if (subpixel_positioning != p_subpixel) {
+ subpixel_positioning = p_subpixel;
+ if (base_font.is_valid()) {
+ base_font->set_subpixel_positioning(subpixel_positioning);
+ }
+ emit_changed();
+ }
+}
+
+TextServer::SubpixelPositioning SystemFont::get_subpixel_positioning() const {
+ return subpixel_positioning;
+}
+
+void SystemFont::set_oversampling(real_t p_oversampling) {
+ if (oversampling != p_oversampling) {
+ oversampling = p_oversampling;
+ if (base_font.is_valid()) {
+ base_font->set_oversampling(oversampling);
+ }
+ emit_changed();
+ }
+}
+
+real_t SystemFont::get_oversampling() const {
+ return oversampling;
+}
+
+void SystemFont::set_font_names(const PackedStringArray &p_names) {
+ if (names != p_names) {
+ names = p_names;
+ _update_base_font();
+ }
+}
+
+PackedStringArray SystemFont::get_font_names() const {
+ return names;
+}
+
+void SystemFont::set_font_style(BitField<TextServer::FontStyle> p_style) {
+ if (style != p_style) {
+ style = p_style;
+ _update_base_font();
+ }
+}
+
+BitField<TextServer::FontStyle> SystemFont::get_font_style() const {
+ return style;
+}
+
+int SystemFont::get_spacing(TextServer::SpacingType p_spacing) const {
+ if (base_font.is_valid()) {
+ return base_font->get_spacing(p_spacing);
+ } else {
+ return 0;
+ }
+}
+
+RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const {
+ Ref<Font> f = _get_base_font_or_default();
+ if (f.is_valid()) {
+ Dictionary var = p_variation_coordinates;
+ if (ftr_weight > 0 && !var.has(TS->name_to_tag("weight"))) {
+ var[TS->name_to_tag("weight")] = ftr_weight;
+ }
+ if (ftr_italic > 0 && !var.has(TS->name_to_tag("italic"))) {
+ var[TS->name_to_tag("italic")] = ftr_italic;
+ }
+
+ if (!face_indeces.is_empty()) {
+ int face_index = CLAMP(p_face_index, 0, face_indeces.size() - 1);
+ return f->find_variation(var, face_indeces[face_index], p_strength, p_transform);
+ } else {
+ return f->find_variation(var, 0, p_strength, p_transform);
+ }
+ }
+ return RID();
+}
+
+RID SystemFont::_get_rid() const {
+ Ref<Font> f = _get_base_font_or_default();
+ if (f.is_valid()) {
+ if (!face_indeces.is_empty()) {
+ Dictionary var;
+ if (ftr_weight > 0) {
+ var[TS->name_to_tag("weight")] = ftr_weight;
+ }
+ if (ftr_italic > 0) {
+ var[TS->name_to_tag("italic")] = ftr_italic;
+ }
+ return f->find_variation(var, face_indeces[0]);
+ } else {
+ return f->_get_rid();
+ }
+ }
+ return RID();
+}
+
+int64_t SystemFont::get_face_count() const {
+ return face_indeces.size();
+}
+
+SystemFont::SystemFont() {
+ /* NOP */
+}
+
+SystemFont::~SystemFont() {
+ reset_state();
+}
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 8a2f83c414..260b4e521f 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -41,7 +41,7 @@ class TextLine;
class TextParagraph;
/*************************************************************************/
-/* Font */
+/* Font */
/*************************************************************************/
class Font : public Resource {
@@ -381,4 +381,74 @@ public:
~FontVariation();
};
+/*************************************************************************/
+/* SystemFont */
+/*************************************************************************/
+
+class SystemFont : public Font {
+ GDCLASS(SystemFont, Font);
+
+ PackedStringArray names;
+ BitField<TextServer::FontStyle> style = 0;
+
+ mutable Ref<Font> theme_font;
+
+ Ref<FontFile> base_font;
+ Vector<int> face_indeces;
+ int ftr_weight = 0;
+ int ftr_italic = 0;
+
+ bool antialiased = true;
+ bool mipmaps = false;
+ bool force_autohinter = false;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
+ real_t oversampling = 0.f;
+
+protected:
+ static void _bind_methods();
+
+ virtual void _update_base_font();
+ virtual void _update_rids() const override;
+
+ virtual void reset_state() override;
+
+public:
+ virtual Ref<Font> _get_base_font_or_default() const;
+
+ virtual void set_antialiased(bool p_antialiased);
+ virtual bool is_antialiased() const;
+
+ virtual void set_generate_mipmaps(bool p_generate_mipmaps);
+ virtual bool get_generate_mipmaps() const;
+
+ virtual void set_force_autohinter(bool p_force_autohinter);
+ virtual bool is_force_autohinter() const;
+
+ virtual void set_hinting(TextServer::Hinting p_hinting);
+ virtual TextServer::Hinting get_hinting() const;
+
+ virtual void set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel);
+ virtual TextServer::SubpixelPositioning get_subpixel_positioning() const;
+
+ virtual void set_oversampling(real_t p_oversampling);
+ virtual real_t get_oversampling() const;
+
+ virtual void set_font_names(const PackedStringArray &p_names);
+ virtual PackedStringArray get_font_names() const;
+
+ virtual void set_font_style(BitField<TextServer::FontStyle> p_style);
+ virtual BitField<TextServer::FontStyle> get_font_style() const override;
+
+ virtual int get_spacing(TextServer::SpacingType p_spacing) const override;
+
+ virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override;
+ virtual RID _get_rid() const override;
+
+ int64_t get_face_count() const override;
+
+ SystemFont();
+ ~SystemFont();
+};
+
#endif // FONT_H
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index b7a3b677f5..f07232a3ad 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -202,7 +202,98 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const {
void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
if (!shader.is_null()) {
- shader->get_param_list(p_list);
+ List<PropertyInfo> list;
+ shader->get_param_list(&list, true);
+
+ HashMap<String, HashMap<String, List<PropertyInfo>>> groups;
+ {
+ HashMap<String, List<PropertyInfo>> none_subgroup;
+ none_subgroup.insert("<None>", List<PropertyInfo>());
+ groups.insert("<None>", none_subgroup);
+ }
+
+ String last_group = "<None>";
+ String last_subgroup = "<None>";
+
+ bool is_none_group_undefined = true;
+ bool is_none_group = true;
+
+ for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) {
+ if (E->get().usage == PROPERTY_USAGE_GROUP) {
+ if (!E->get().name.is_empty()) {
+ Vector<String> vgroup = E->get().name.split("::");
+ last_group = vgroup[0];
+ if (vgroup.size() > 1) {
+ last_subgroup = vgroup[1];
+ } else {
+ last_subgroup = "<None>";
+ }
+ is_none_group = false;
+
+ if (!groups.has(last_group)) {
+ PropertyInfo info;
+ info.usage = PROPERTY_USAGE_GROUP;
+ info.name = last_group;
+
+ List<PropertyInfo> none_subgroup;
+ none_subgroup.push_back(info);
+
+ HashMap<String, List<PropertyInfo>> subgroup_map;
+ subgroup_map.insert("<None>", none_subgroup);
+
+ groups.insert(last_group, subgroup_map);
+ }
+
+ if (!groups[last_group].has(last_subgroup)) {
+ PropertyInfo info;
+ info.usage = PROPERTY_USAGE_SUBGROUP;
+ info.name = last_subgroup;
+
+ List<PropertyInfo> subgroup;
+ subgroup.push_back(info);
+
+ groups[last_group].insert(last_subgroup, subgroup);
+ }
+ } else {
+ last_group = "<None>";
+ last_subgroup = "<None>";
+ is_none_group = true;
+ }
+ continue; // Pass group.
+ }
+
+ if (is_none_group_undefined && is_none_group) {
+ is_none_group_undefined = false;
+
+ PropertyInfo info;
+ info.usage = PROPERTY_USAGE_GROUP;
+ info.name = "Shader Param";
+ groups["<None>"]["<None>"].push_back(info);
+ }
+
+ PropertyInfo info = E->get();
+ info.name = info.name;
+ groups[last_group][last_subgroup].push_back(info);
+ }
+
+ // Sort groups alphabetically.
+ List<UniformProp> props;
+ for (HashMap<String, HashMap<String, List<PropertyInfo>>>::Iterator group = groups.begin(); group; ++group) {
+ for (HashMap<String, List<PropertyInfo>>::Iterator subgroup = group->value.begin(); subgroup; ++subgroup) {
+ for (List<PropertyInfo>::Element *item = subgroup->value.front(); item; item = item->next()) {
+ if (subgroup->key == "<None>") {
+ props.push_back({ group->key, item->get() });
+ } else {
+ props.push_back({ group->key + "::" + subgroup->key, item->get() });
+ }
+ }
+ }
+ }
+ props.sort_custom<UniformPropComparator>();
+
+ for (List<UniformProp>::Element *E = props.front(); E; E = E->next()) {
+ p_list->push_back(E->get().info);
+ }
}
}
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 905e604e95..8c04817c6b 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -84,6 +84,17 @@ class ShaderMaterial : public Material {
HashMap<StringName, Variant> param_cache;
+ struct UniformProp {
+ String str;
+ PropertyInfo info;
+ };
+
+ struct UniformPropComparator {
+ bool operator()(const UniformProp &p_a, const UniformProp &p_b) const {
+ return p_a.str.naturalnocasecmp_to(p_b.str) < 0;
+ }
+ };
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 16117986fe..74031e02d7 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -103,7 +103,7 @@ String Shader::get_code() const {
return code;
}
-void Shader::get_param_list(List<PropertyInfo> *p_params) const {
+void Shader::get_param_list(List<PropertyInfo> *p_params, bool p_get_groups) const {
_update_shader();
List<PropertyInfo> local;
@@ -112,12 +112,16 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const {
params_cache_dirty = false;
for (PropertyInfo &pi : local) {
- if (default_textures.has(pi.name)) { //do not show default textures
+ bool is_group = pi.usage == PROPERTY_USAGE_GROUP || pi.usage == PROPERTY_USAGE_SUBGROUP;
+ if (!p_get_groups && is_group) {
continue;
}
- String original_name = pi.name;
- pi.name = "shader_param/" + pi.name;
- params_cache[pi.name] = original_name;
+ if (!is_group) {
+ if (default_textures.has(pi.name)) { //do not show default textures
+ continue;
+ }
+ params_cache[pi.name] = pi.name;
+ }
if (p_params) {
//small little hack
if (pi.type == Variant::RID) {
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 5de8ad5518..7aa14651a5 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -78,7 +78,7 @@ public:
void set_code(const String &p_code);
String get_code() const;
- void get_param_list(List<PropertyInfo> *p_params) const;
+ void get_param_list(List<PropertyInfo> *p_params, bool p_get_groups = false) const;
bool has_param(const StringName &p_param) const;
void set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index = 0);
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 22b5ef0108..a59870f4a9 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -2467,9 +2467,42 @@ Vector<Point2> TileSet::_get_half_offset_side_terrain_peering_bit_polygon(Vector
}
void TileSet::reset_state() {
+ // Rendering
occlusion_layers.clear();
+ tile_lines_mesh.instantiate();
+ tile_filled_mesh.instantiate();
+ tile_meshes_dirty = true;
+
+ // Physics
physics_layers.clear();
+
+ // Terrains
+ terrain_sets.clear();
+ terrain_meshes.clear();
+ terrain_peering_bits_meshes.clear();
+ per_terrain_pattern_tiles.clear();
+ terrains_cache_dirty = true;
+
+ // Navigation
+ navigation_layers.clear();
+
custom_data_layers.clear();
+ custom_data_layers_by_name.clear();
+
+ // Proxies
+ source_level_proxies.clear();
+ coords_level_proxies.clear();
+ alternative_level_proxies.clear();
+
+#ifndef DISABLE_DEPRECATED
+ for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) {
+ memdelete(E.value);
+ }
+ compatibility_data.clear();
+#endif // DISABLE_DEPRECATED
+ while (!source_ids.is_empty()) {
+ remove_source(source_ids[0]);
+ }
}
const Vector2i TileSetSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1);
@@ -3457,6 +3490,10 @@ void TileSetSource::set_tile_set(const TileSet *p_tile_set) {
tile_set = p_tile_set;
}
+void TileSetSource::reset_state() {
+ tile_set = nullptr;
+};
+
void TileSetSource::_bind_methods() {
// Base tiles
ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetSource::get_tiles_count);
@@ -3640,12 +3677,17 @@ void TileSetAtlasSource::remove_custom_data_layer(int p_index) {
}
void TileSetAtlasSource::reset_state() {
- // Reset all TileData.
+ tile_set = nullptr;
+
for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
- for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
- E_alternative.value->reset_state();
+ for (const KeyValue<int, TileData *> &E_tile_data : E_tile.value.alternatives) {
+ memdelete(E_tile_data.value);
}
}
+ _coords_mapping_cache.clear();
+ tiles.clear();
+ tiles_ids.clear();
+ _queue_update_padded_texture();
}
void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
@@ -4975,13 +5017,6 @@ void TileData::remove_custom_data_layer(int p_index) {
custom_data.remove_at(p_index);
}
-void TileData::reset_state() {
- occluders.clear();
- physics.clear();
- navigation.clear();
- custom_data.clear();
-}
-
void TileData::set_allow_transform(bool p_allow_transform) {
allow_transform = p_allow_transform;
}
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 7368d2bd87..bfd21190d8 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -569,7 +569,7 @@ public:
virtual void add_custom_data_layer(int p_index){};
virtual void move_custom_data_layer(int p_from_index, int p_to_pos){};
virtual void remove_custom_data_layer(int p_index){};
- virtual void reset_state() override{};
+ virtual void reset_state() override;
// Tiles.
virtual int get_tiles_count() const = 0;
@@ -847,7 +847,6 @@ public:
void add_custom_data_layer(int p_index);
void move_custom_data_layer(int p_from_index, int p_to_pos);
void remove_custom_data_layer(int p_index);
- void reset_state();
void set_allow_transform(bool p_allow_transform);
bool is_allowing_transform() const;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index b68cce9dda..3cf643221b 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -967,6 +967,12 @@ void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from
ERR_FAIL_COND(!g->nodes.has(p_to_node));
ERR_FAIL_INDEX(p_to_port, g->nodes[p_to_node].node->get_input_port_count());
+ for (const Connection &E : g->connections) {
+ if (E.from_node == p_from_node && E.from_port == p_from_port && E.to_node == p_to_node && E.to_port == p_to_port) {
+ return;
+ }
+ }
+
Connection c;
c.from_node = p_from_node;
c.from_port = p_from_port;