diff options
Diffstat (limited to 'scene')
76 files changed, 965 insertions, 1142 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index 2d05d46342..257e334873 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -158,14 +158,14 @@ void AnimatedSprite2D::_notification(int p_what) { return; } - double speed = frames->get_animation_speed(animation) * speed_scale; - if (speed == 0) { - return; //do nothing - } - double remaining = get_process_delta_time(); while (remaining) { + double speed = frames->get_animation_speed(animation) * speed_scale; + if (speed == 0) { + return; // Do nothing. + } + if (timeout <= 0) { timeout = _get_frame_duration(); diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp index 0467c39746..c2773191ea 100644 --- a/scene/2d/joint_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -128,7 +128,7 @@ void Joint2D::set_node_a(const NodePath &p_node_a) { return; } - if (joint.is_valid()) { + if (is_configured()) { _disconnect_signals(); } @@ -145,7 +145,7 @@ void Joint2D::set_node_b(const NodePath &p_node_b) { return; } - if (joint.is_valid()) { + if (is_configured()) { _disconnect_signals(); } @@ -159,15 +159,18 @@ NodePath Joint2D::get_node_b() const { void Joint2D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_READY: { + case NOTIFICATION_POST_ENTER_TREE: { + if (is_configured()) { + _disconnect_signals(); + } _update_joint(); } break; case NOTIFICATION_EXIT_TREE: { - if (joint.is_valid()) { + if (is_configured()) { _disconnect_signals(); - _update_joint(true); } + _update_joint(true); } break; } } @@ -187,7 +190,9 @@ void Joint2D::set_exclude_nodes_from_collision(bool p_enable) { if (exclude_from_collision == p_enable) { return; } - + if (is_configured()) { + _disconnect_signals(); + } _update_joint(true); exclude_from_collision = p_enable; _update_joint(); diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 312ba0272e..2716bb2e25 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -247,10 +247,7 @@ float Line2D::get_sharp_limit() const { } void Line2D::set_round_precision(int p_precision) { - if (p_precision < 1) { - p_precision = 1; - } - _round_precision = p_precision; + _round_precision = MAX(1, p_precision); update(); } @@ -409,7 +406,7 @@ void Line2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "end_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round"), "set_end_cap_mode", "get_end_cap_mode"); ADD_GROUP("Border", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sharp_limit"), "set_sharp_limit", "get_sharp_limit"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "round_precision"), "set_round_precision", "get_round_precision"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "round_precision", PROPERTY_HINT_RANGE, "1,32,1"), "set_round_precision", "get_round_precision"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "get_antialiased"); BIND_ENUM_CONSTANT(LINE_JOINT_SHARP); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index a4ad0a8d99..eb4d9d6445 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1168,7 +1168,7 @@ bool CharacterBody2D::move_and_slide() { if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) { current_platform_velocity = current_platform_velocity.slide(up_direction); } - motion_velocity += current_platform_velocity; + velocity += current_platform_velocity; } } @@ -1176,7 +1176,7 @@ bool CharacterBody2D::move_and_slide() { } void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) { - Vector2 motion = motion_velocity * p_delta; + Vector2 motion = velocity * p_delta; Vector2 motion_slide_up = motion.slide(up_direction); Vector2 prev_floor_normal = floor_normal; @@ -1194,7 +1194,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // If the platform's ceiling push down the body. bool apply_ceiling_velocity = false; bool first_slide = true; - bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0; + bool vel_dir_facing_up = velocity.dot(up_direction) > 0; Vector2 last_travel; for (int iteration = 0; iteration < max_slides; ++iteration) { @@ -1211,26 +1211,26 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo motion_results.push_back(result); _set_collision_direction(result); - // If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one. + // If we hit a ceiling platform, we set the vertical velocity to at least the platform one. if (on_ceiling && result.collider_velocity != Vector2() && result.collider_velocity.dot(up_direction) < 0) { // If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward. if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (result.collision_normal + up_direction).length() < 0.01) { apply_ceiling_velocity = true; Vector2 ceiling_vertical_velocity = up_direction * up_direction.dot(result.collider_velocity); - Vector2 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity); + Vector2 motion_vertical_velocity = up_direction * up_direction.dot(velocity); if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) { - motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction); + velocity = ceiling_vertical_velocity + velocity.slide(up_direction); } } } - if (on_floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) { + if (on_floor && floor_stop_on_slope && (velocity.normalized() + up_direction).length() < 0.01) { Transform2D gt = get_global_transform(); if (result.travel.length() <= margin + CMP_EPSILON) { gt.elements[2] -= result.travel; } set_global_transform(gt); - motion_velocity = Vector2(); + velocity = Vector2(); last_motion = Vector2(); motion = Vector2(); break; @@ -1254,7 +1254,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo } // Determines if you are on the ground. _snap_on_floor(true, false); - motion_velocity = Vector2(); + velocity = Vector2(); last_motion = Vector2(); motion = Vector2(); break; @@ -1276,7 +1276,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling. else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up) && !apply_ceiling_velocity) { Vector2 slide_motion = result.remainder.slide(result.collision_normal); - if (slide_motion.dot(motion_velocity) > 0.0) { + if (slide_motion.dot(velocity) > 0.0) { motion = slide_motion; } else { motion = Vector2(); @@ -1284,10 +1284,10 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (slide_on_ceiling && on_ceiling) { // Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall. if (vel_dir_facing_up) { - motion_velocity = motion_velocity.slide(result.collision_normal); + velocity = velocity.slide(result.collision_normal); } else { // Avoid acceleration in slope when falling. - motion_velocity = up_direction * up_direction.dot(motion_velocity); + velocity = up_direction * up_direction.dot(velocity); } } } @@ -1295,7 +1295,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo else { motion = result.remainder; if (on_ceiling && !slide_on_ceiling && vel_dir_facing_up) { - motion_velocity = motion_velocity.slide(up_direction); + velocity = velocity.slide(up_direction); motion = motion.slide(up_direction); } } @@ -1329,23 +1329,23 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Scales the horizontal velocity according to the wall slope. if (is_on_wall_only() && motion_slide_up.dot(motion_results.get(0).collision_normal) < 0) { - Vector2 slide_motion = motion_velocity.slide(motion_results.get(0).collision_normal); + Vector2 slide_motion = velocity.slide(motion_results.get(0).collision_normal); if (motion_slide_up.dot(slide_motion) < 0) { - motion_velocity = up_direction * up_direction.dot(motion_velocity); + velocity = up_direction * up_direction.dot(velocity); } else { - // Keeps the vertical motion from motion_velocity and add the horizontal motion of the projection. - motion_velocity = up_direction * up_direction.dot(motion_velocity) + slide_motion.slide(up_direction); + // Keeps the vertical motion from velocity and add the horizontal motion of the projection. + velocity = up_direction * up_direction.dot(velocity) + slide_motion.slide(up_direction); } } // Reset the gravity accumulation when touching the ground. if (on_floor && !vel_dir_facing_up) { - motion_velocity = motion_velocity.slide(up_direction); + velocity = velocity.slide(up_direction); } } void CharacterBody2D::_move_and_slide_floating(double p_delta) { - Vector2 motion = motion_velocity * p_delta; + Vector2 motion = velocity * p_delta; platform_rid = RID(); platform_object_id = ObjectID(); @@ -1370,7 +1370,7 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) { break; } - if (wall_min_slide_angle != 0 && result.get_angle(-motion_velocity.normalized()) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { + if (wall_min_slide_angle != 0 && result.get_angle(-velocity.normalized()) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { motion = Vector2(); } else if (first_slide) { Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized(); @@ -1379,7 +1379,7 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) { motion = result.remainder.slide(result.collision_normal); } - if (motion.dot(motion_velocity) <= 0.0) { + if (motion.dot(velocity) <= 0.0) { motion = Vector2(); } } @@ -1471,12 +1471,12 @@ void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_ platform_layer = PhysicsServer2D::get_singleton()->body_get_collision_layer(platform_rid); } -const Vector2 &CharacterBody2D::get_motion_velocity() const { - return motion_velocity; +const Vector2 &CharacterBody2D::get_velocity() const { + return velocity; } -void CharacterBody2D::set_motion_velocity(const Vector2 &p_velocity) { - motion_velocity = p_velocity; +void CharacterBody2D::set_velocity(const Vector2 &p_velocity) { + velocity = p_velocity; } bool CharacterBody2D::is_on_floor() const { @@ -1697,8 +1697,8 @@ void CharacterBody2D::_notification(int p_what) { void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody2D::move_and_slide); - ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody2D::set_motion_velocity); - ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody2D::get_motion_velocity); + ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &CharacterBody2D::set_velocity); + ClassDB::bind_method(D_METHOD("get_velocity"), &CharacterBody2D::get_velocity); ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody2D::set_safe_margin); ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody2D::get_safe_margin); @@ -1750,7 +1750,7 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Floating", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_motion_velocity", "get_motion_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle"); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index cfaa2570fb..8d9e31d4dd 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -337,8 +337,8 @@ public: }; bool move_and_slide(); - const Vector2 &get_motion_velocity() const; - void set_motion_velocity(const Vector2 &p_velocity); + const Vector2 &get_velocity() const; + void set_velocity(const Vector2 &p_velocity); bool is_on_floor() const; bool is_on_floor_only() const; @@ -378,7 +378,7 @@ private: Vector2 up_direction = Vector2(0.0, -1.0); uint32_t moving_platform_floor_layers = UINT32_MAX; uint32_t moving_platform_wall_layers = 0; - Vector2 motion_velocity; + Vector2 velocity; Vector2 floor_normal; Vector2 platform_velocity; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 0d50d7f8d6..db33e6561a 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1997,6 +1997,10 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c } } +void TileMap::erase_cell(int p_layer, const Vector2i &p_coords) { + set_cell(p_layer, p_coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); +} + int TileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSet::INVALID_SOURCE); @@ -3622,7 +3626,8 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "navigation_visibility_mode"), &TileMap::set_navigation_visibility_mode); ClassDB::bind_method(D_METHOD("get_navigation_visibility_mode"), &TileMap::get_navigation_visibility_mode); - ClassDB::bind_method(D_METHOD("set_cell", "layer", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); + ClassDB::bind_method(D_METHOD("set_cell", "layer", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("erase_cell", "layer", "coords"), &TileMap::erase_cell); ClassDB::bind_method(D_METHOD("get_cell_source_id", "layer", "coords", "use_proxies"), &TileMap::get_cell_source_id); ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords); ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 0da04bfeae..a0655dea2a 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -321,7 +321,8 @@ public: VisibilityMode get_navigation_visibility_mode(); // Cells accessors. - void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE); + void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0); + void erase_cell(int p_layer, const Vector2i &p_coords); int get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; Vector2i get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; int get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp index 36abd0a5c5..ce7c0d8292 100644 --- a/scene/3d/joint_3d.cpp +++ b/scene/3d/joint_3d.cpp @@ -124,7 +124,7 @@ void Joint3D::set_node_a(const NodePath &p_node_a) { return; } - if (joint.is_valid()) { + if (is_configured()) { _disconnect_signals(); } @@ -141,7 +141,7 @@ void Joint3D::set_node_b(const NodePath &p_node_b) { return; } - if (joint.is_valid()) { + if (is_configured()) { _disconnect_signals(); } @@ -166,15 +166,18 @@ int Joint3D::get_solver_priority() const { void Joint3D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_READY: { + case NOTIFICATION_POST_ENTER_TREE: { + if (is_configured()) { + _disconnect_signals(); + } _update_joint(); } break; case NOTIFICATION_EXIT_TREE: { - if (joint.is_valid()) { + if (is_configured()) { _disconnect_signals(); - _update_joint(true); } + _update_joint(true); } break; } } @@ -183,6 +186,10 @@ void Joint3D::set_exclude_nodes_from_collision(bool p_enable) { if (exclude_from_collision == p_enable) { return; } + if (is_configured()) { + _disconnect_signals(); + } + _update_joint(true); exclude_from_collision = p_enable; _update_joint(); } diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index b2e605a262..0dd3fc8a23 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -74,6 +74,43 @@ bool Light3D::is_negative() const { return negative; } +void Light3D::set_enable_distance_fade(bool p_enable) { + distance_fade_enabled = p_enable; + RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length); + notify_property_list_changed(); +} + +bool Light3D::is_distance_fade_enabled() const { + return distance_fade_enabled; +} + +void Light3D::set_distance_fade_begin(real_t p_distance) { + distance_fade_begin = p_distance; + RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length); +} + +real_t Light3D::get_distance_fade_begin() const { + return distance_fade_begin; +} + +void Light3D::set_distance_fade_shadow(real_t p_distance) { + distance_fade_shadow = p_distance; + RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length); +} + +real_t Light3D::get_distance_fade_shadow() const { + return distance_fade_shadow; +} + +void Light3D::set_distance_fade_length(real_t p_length) { + distance_fade_length = p_length; + RS::get_singleton()->light_set_distance_fade(light, distance_fade_enabled, distance_fade_begin, distance_fade_shadow, distance_fade_length); +} + +real_t Light3D::get_distance_fade_length() const { + return distance_fade_length; +} + void Light3D::set_cull_mask(uint32_t p_cull_mask) { cull_mask = p_cull_mask; RS::get_singleton()->light_set_cull_mask(light, p_cull_mask); @@ -195,7 +232,7 @@ bool Light3D::is_editor_only() const { } void Light3D::_validate_property(PropertyInfo &property) const { - if (!shadow && (property.name == "shadow_color" || property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_blur")) { + if (!shadow && (property.name == "shadow_color" || property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_blur" || property.name == "distance_fade_shadow")) { property.usage = PROPERTY_USAGE_NO_EDITOR; } @@ -203,6 +240,11 @@ void Light3D::_validate_property(PropertyInfo &property) const { // Angular distance is only used in DirectionalLight3D. property.usage = PROPERTY_USAGE_NONE; } + + if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_shadow" || property.name == "distance_fade_length")) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + } + VisualInstance3D::_validate_property(property); } @@ -222,6 +264,18 @@ void Light3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cull_mask", "cull_mask"), &Light3D::set_cull_mask); ClassDB::bind_method(D_METHOD("get_cull_mask"), &Light3D::get_cull_mask); + ClassDB::bind_method(D_METHOD("set_enable_distance_fade", "enable"), &Light3D::set_enable_distance_fade); + ClassDB::bind_method(D_METHOD("is_distance_fade_enabled"), &Light3D::is_distance_fade_enabled); + + ClassDB::bind_method(D_METHOD("set_distance_fade_begin", "distance"), &Light3D::set_distance_fade_begin); + ClassDB::bind_method(D_METHOD("get_distance_fade_begin"), &Light3D::get_distance_fade_begin); + + ClassDB::bind_method(D_METHOD("set_distance_fade_shadow", "distance"), &Light3D::set_distance_fade_shadow); + ClassDB::bind_method(D_METHOD("get_distance_fade_shadow"), &Light3D::get_distance_fade_shadow); + + ClassDB::bind_method(D_METHOD("set_distance_fade_length", "distance"), &Light3D::set_distance_fade_length); + ClassDB::bind_method(D_METHOD("get_distance_fade_length"), &Light3D::get_distance_fade_length); + ClassDB::bind_method(D_METHOD("set_color", "color"), &Light3D::set_color); ClassDB::bind_method(D_METHOD("get_color"), &Light3D::get_color); @@ -257,6 +311,11 @@ void Light3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_fog_fade", PROPERTY_HINT_RANGE, "0.01,10,0.01"), "set_param", "get_param", PARAM_SHADOW_VOLUMETRIC_FOG_FADE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0.1,8,0.01"), "set_param", "get_param", PARAM_SHADOW_BLUR); + ADD_GROUP("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_RANGE, "0.0,4096.0,0.01,or_greater"), "set_distance_fade_begin", "get_distance_fade_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_shadow", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater"), "set_distance_fade_shadow", "get_distance_fade_shadow"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_length", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01,or_greater"), "set_distance_fade_length", "get_distance_fade_length"); ADD_GROUP("Editor", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only"); ADD_GROUP("", ""); @@ -391,6 +450,12 @@ void DirectionalLight3D::_validate_property(PropertyInfo &property) const { property.usage = PROPERTY_USAGE_NONE; } + if (property.name == "distance_fade_enabled" || property.name == "distance_fade_begin" || property.name == "distance_fade_shadow" || property.name == "distance_fade_length") { + // Not relevant for DirectionalLight3D, as the light LOD system only pertains to point lights. + // For DirectionalLight3D, `directional_shadow_max_distance` can be used instead. + property.usage = PROPERTY_USAGE_NONE; + } + Light3D::_validate_property(property); } diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index d5d2aee43d..21d785e2f7 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -75,6 +75,10 @@ private: bool negative = false; bool reverse_cull = false; uint32_t cull_mask = 0; + bool distance_fade_enabled = false; + real_t distance_fade_begin = 40.0; + real_t distance_fade_shadow = 50.0; + real_t distance_fade_length = 10.0; RS::LightType type = RenderingServer::LIGHT_DIRECTIONAL; bool editor_only = false; void _update_visibility(); @@ -107,6 +111,18 @@ public: void set_negative(bool p_enable); bool is_negative() const; + void set_enable_distance_fade(bool p_enable); + bool is_distance_fade_enabled() const; + + void set_distance_fade_begin(real_t p_distance); + real_t get_distance_fade_begin() const; + + void set_distance_fade_shadow(real_t p_distance); + real_t get_distance_fade_shadow() const; + + void set_distance_fade_length(real_t p_length); + real_t get_distance_fade_length() const; + void set_cull_mask(uint32_t p_cull_mask); uint32_t get_cull_mask() const; diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index c6d7e1df86..25411e54c0 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -1169,7 +1169,7 @@ bool CharacterBody3D::move_and_slide() { for (int i = 0; i < 3; i++) { if (locked_axis & (1 << i)) { - motion_velocity[i] = 0.0; + velocity[i] = 0.0; } } @@ -1239,7 +1239,7 @@ bool CharacterBody3D::move_and_slide() { if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) { current_platform_velocity = current_platform_velocity.slide(up_direction); } - motion_velocity += current_platform_velocity; + velocity += current_platform_velocity; } } @@ -1247,7 +1247,7 @@ bool CharacterBody3D::move_and_slide() { } void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) { - Vector3 motion = motion_velocity * p_delta; + Vector3 motion = velocity * p_delta; Vector3 motion_slide_up = motion.slide(up_direction); Vector3 prev_floor_normal = floor_normal; @@ -1267,7 +1267,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // If the platform's ceiling push down the body. bool apply_ceiling_velocity = false; bool first_slide = true; - bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0; + bool vel_dir_facing_up = velocity.dot(up_direction) > 0; Vector3 total_travel; for (int iteration = 0; iteration < max_slides; ++iteration) { @@ -1287,26 +1287,26 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo CollisionState result_state; _set_collision_direction(result, result_state); - // If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one. + // If we hit a ceiling platform, we set the vertical velocity to at least the platform one. if (collision_state.ceiling && platform_ceiling_velocity != Vector3() && platform_ceiling_velocity.dot(up_direction) < 0) { // If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward. if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (ceiling_normal + up_direction).length() < 0.01) { apply_ceiling_velocity = true; Vector3 ceiling_vertical_velocity = up_direction * up_direction.dot(platform_ceiling_velocity); - Vector3 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity); + Vector3 motion_vertical_velocity = up_direction * up_direction.dot(velocity); if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) { - motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction); + velocity = ceiling_vertical_velocity + velocity.slide(up_direction); } } } - if (collision_state.floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) { + if (collision_state.floor && floor_stop_on_slope && (velocity.normalized() + up_direction).length() < 0.01) { Transform3D gt = get_global_transform(); if (result.travel.length() <= margin + CMP_EPSILON) { gt.origin -= result.travel; } set_global_transform(gt); - motion_velocity = Vector3(); + velocity = Vector3(); motion = Vector3(); last_motion = Vector3(); break; @@ -1367,11 +1367,11 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Scales the horizontal velocity according to the wall slope. if (vel_dir_facing_up) { - Vector3 slide_motion = motion_velocity.slide(result.collisions[0].normal); - // Keeps the vertical motion from motion_velocity and add the horizontal motion of the projection. - motion_velocity = up_direction * up_direction.dot(motion_velocity) + slide_motion.slide(up_direction); + Vector3 slide_motion = velocity.slide(result.collisions[0].normal); + // Keeps the vertical motion from velocity and add the horizontal motion of the projection. + velocity = up_direction * up_direction.dot(velocity) + slide_motion.slide(up_direction); } else { - motion_velocity = motion_velocity.slide(forward); + velocity = velocity.slide(forward); } // Allow only lateral motion along previous floor when already on floor. @@ -1401,7 +1401,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (stop_all_motion) { motion = Vector3(); - motion_velocity = Vector3(); + velocity = Vector3(); } } } @@ -1412,7 +1412,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo real_t motion_angle = Math::abs(Math::acos(-horizontal_normal.dot(motion_slide_up.normalized()))); if (motion_angle < wall_min_slide_angle) { motion = up_direction * motion.dot(up_direction); - motion_velocity = up_direction * motion_velocity.dot(up_direction); + velocity = up_direction * velocity.dot(up_direction); apply_default_sliding = false; } @@ -1437,7 +1437,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo slide_motion *= motion_length; } - if (slide_motion.dot(motion_velocity) > 0.0) { + if (slide_motion.dot(velocity) > 0.0) { motion = slide_motion; } else { motion = Vector3(); @@ -1446,10 +1446,10 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (slide_on_ceiling && result_state.ceiling) { // Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall. if (vel_dir_facing_up) { - motion_velocity = motion_velocity.slide(collision.normal); + velocity = velocity.slide(collision.normal); } else { // Avoid acceleration in slope when falling. - motion_velocity = up_direction * up_direction.dot(motion_velocity); + velocity = up_direction * up_direction.dot(velocity); } } } @@ -1457,7 +1457,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo else { motion = result.remainder; if (result_state.ceiling && !slide_on_ceiling && vel_dir_facing_up) { - motion_velocity = motion_velocity.slide(up_direction); + velocity = velocity.slide(up_direction); motion = motion.slide(up_direction); } } @@ -1502,12 +1502,12 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Reset the gravity accumulation when touching the ground. if (collision_state.floor && !vel_dir_facing_up) { - motion_velocity = motion_velocity.slide(up_direction); + velocity = velocity.slide(up_direction); } } void CharacterBody3D::_move_and_slide_floating(double p_delta) { - Vector3 motion = motion_velocity * p_delta; + Vector3 motion = velocity * p_delta; platform_rid = RID(); platform_object_id = ObjectID(); @@ -1534,7 +1534,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { break; } - if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-motion_velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { + if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { motion = Vector3(); if (result.travel.length() < margin + CMP_EPSILON) { Transform3D gt = get_global_transform(); @@ -1548,7 +1548,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { motion = result.remainder.slide(wall_normal); } - if (motion.dot(motion_velocity) <= 0.0) { + if (motion.dot(velocity) <= 0.0) { motion = Vector3(); } } @@ -1723,12 +1723,12 @@ real_t CharacterBody3D::get_safe_margin() const { return margin; } -const Vector3 &CharacterBody3D::get_motion_velocity() const { - return motion_velocity; +const Vector3 &CharacterBody3D::get_velocity() const { + return velocity; } -void CharacterBody3D::set_motion_velocity(const Vector3 &p_velocity) { - motion_velocity = p_velocity; +void CharacterBody3D::set_velocity(const Vector3 &p_velocity) { + velocity = p_velocity; } bool CharacterBody3D::is_on_floor() const { @@ -1943,8 +1943,8 @@ void CharacterBody3D::_notification(int p_what) { void CharacterBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody3D::move_and_slide); - ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody3D::set_motion_velocity); - ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody3D::get_motion_velocity); + ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &CharacterBody3D::set_velocity); + ClassDB::bind_method(D_METHOD("get_velocity"), &CharacterBody3D::get_velocity); ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody3D::set_safe_margin); ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin); @@ -1997,7 +1997,7 @@ void CharacterBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Floating", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_motion_velocity", "get_motion_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle"); ADD_GROUP("Floor", "floor_"); diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 67dc7382c3..0f753fef76 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -354,8 +354,8 @@ public: }; bool move_and_slide(); - const Vector3 &get_motion_velocity() const; - void set_motion_velocity(const Vector3 &p_velocity); + const Vector3 &get_velocity() const; + void set_velocity(const Vector3 &p_velocity); bool is_on_floor() const; bool is_on_floor_only() const; @@ -416,7 +416,7 @@ private: real_t floor_max_angle = Math::deg2rad((real_t)45.0); real_t wall_min_slide_angle = Math::deg2rad((real_t)15.0); Vector3 up_direction = Vector3(0.0, 1.0, 0.0); - Vector3 motion_velocity; + Vector3 velocity; Vector3 floor_normal; Vector3 wall_normal; Vector3 ceiling_normal; diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index b9fb3e9287..ce281c79bc 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -1019,14 +1019,14 @@ void AnimatedSprite3D::_notification(int p_what) { return; } - float speed = frames->get_animation_speed(animation); - if (speed == 0) { - return; //do nothing - } - double remaining = get_process_delta_time(); while (remaining) { + double speed = frames->get_animation_speed(animation); + if (speed == 0) { + return; // Do nothing. + } + if (timeout <= 0) { timeout = 1.0 / speed; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index b18819c920..efae81e048 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -448,11 +448,6 @@ void XRController3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand); - ClassDB::bind_method(D_METHOD("get_rumble"), &XRController3D::get_rumble); - ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &XRController3D::set_rumble); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rumble", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_rumble", "get_rumble"); - ADD_PROPERTY_DEFAULT("rumble", 0.0); - ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::STRING, "name"))); ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::STRING, "name"))); ADD_SIGNAL(MethodInfo("input_value_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value"))); @@ -558,20 +553,6 @@ Vector2 XRController3D::get_axis(const StringName &p_name) const { } } -real_t XRController3D::get_rumble() const { - if (!tracker.is_valid()) { - return 0.0; - } - - return tracker->get_rumble(); -} - -void XRController3D::set_rumble(real_t p_rumble) { - if (tracker.is_valid()) { - tracker->set_rumble(p_rumble); - } -} - XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const { // get our XRServer if (!tracker.is_valid()) { @@ -612,7 +593,7 @@ TypedArray<String> XROrigin3D::get_configuration_warnings() const { } } - bool xr_enabled = GLOBAL_GET("rendering/xr/enabled"); + bool xr_enabled = GLOBAL_GET("xr/shaders/enabled"); if (!xr_enabled) { warnings.push_back(TTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled.")); } diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index 5675cbd944..3079e20dc7 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -139,9 +139,6 @@ public: float get_value(const StringName &p_name) const; Vector2 get_axis(const StringName &p_name) const; - real_t get_rumble() const; - void set_rumble(real_t p_rumble); - XRPositionalTracker::TrackerHand get_tracker_hand() const; XRController3D() {} diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 27e8b102be..724714b93b 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -583,8 +583,8 @@ void Button::_bind_methods() { Button::Button(const String &p_text) { text_buf.instantiate(); text_buf->set_flags(TextServer::BREAK_MANDATORY); - set_mouse_filter(MOUSE_FILTER_STOP); + set_text(p_text); } diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index 5e3131f8a0..527b0061ac 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -111,9 +111,12 @@ void CheckButton::_notification(int p_what) { } } -CheckButton::CheckButton() { +CheckButton::CheckButton(const String &p_text) : + Button(p_text) { set_toggle_mode(true); + set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); + if (is_layout_rtl()) { _set_internal_margin(SIDE_LEFT, get_icon_size().width); } else { diff --git a/scene/gui/check_button.h b/scene/gui/check_button.h index 9a72d04db2..7d4bb8bdfc 100644 --- a/scene/gui/check_button.h +++ b/scene/gui/check_button.h @@ -42,7 +42,7 @@ protected: void _notification(int p_what); public: - CheckButton(); + CheckButton(const String &p_text = String()); ~CheckButton(); }; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 3ea2a9795d..aa4391e9f1 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -1429,7 +1429,8 @@ void ColorPickerButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha"); } -ColorPickerButton::ColorPickerButton() { +ColorPickerButton::ColorPickerButton(const String &p_text) : + Button(p_text) { set_toggle_mode(true); } diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index d6067b1cf4..6f3e16009c 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -231,7 +231,7 @@ public: ColorPicker *get_picker(); PopupPanel *get_popup(); - ColorPickerButton(); + ColorPickerButton(const String &p_text = String()); }; VARIANT_ENUM_CAST(ColorPicker::PickerShapeType); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 79aaf5c511..678229683f 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -262,7 +262,8 @@ void FileDialog::_action_pressed() { return; } - String f = dir_access->get_current_dir().plus_file(file->get_text()); + String file_text = file->get_text(); + String f = file_text.is_absolute_path() ? file_text : dir_access->get_current_dir().plus_file(file_text); if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) { emit_signal(SNAME("file_selected"), f); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 9585b4d51d..e83524b06c 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1241,7 +1241,7 @@ void ItemList::_notification(int p_what) { text_ofs.x = size.width - text_ofs.x - max_len; } - items.write[i].text_buf->set_width(max_len); + items.write[i].text_buf->set_width(width - text_ofs.x); if (rtl) { items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT); @@ -1253,7 +1253,9 @@ void ItemList::_notification(int p_what) { items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color); } - items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate); + if (width - text_ofs.x > 0) { + items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate); + } } } diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 77e910870f..96735678c1 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -99,7 +99,7 @@ private: SelectMode select_mode = SELECT_SINGLE; IconMode icon_mode = ICON_MODE_LEFT; VScrollBar *scroll_bar; - TextParagraph::OverrunBehavior text_overrun_behavior = TextParagraph::OVERRUN_NO_TRIMMING; + TextParagraph::OverrunBehavior text_overrun_behavior = TextParagraph::OVERRUN_TRIM_ELLIPSIS; uint64_t search_time_msec = 0; String search_string; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 883eb1a1ba..6ad296d7c7 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -847,7 +847,8 @@ void LineEdit::_notification(int p_what) { // Draw carets. ofs.x = x_ofs + scroll_offset; if (draw_caret || drag_caret_force_displayed) { - const int caret_width = get_theme_constant(SNAME("caret_width")) * get_theme_default_base_scale(); + // Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1). + const int caret_width = get_theme_constant(SNAME("caret_width")) * MAX(1, get_theme_default_base_scale()); if (ime_text.length() == 0) { // Normal caret. @@ -2436,7 +2437,7 @@ void LineEdit::_ensure_menu() { } } -LineEdit::LineEdit() { +LineEdit::LineEdit(const String &p_placeholder) { text_rid = TS->create_shaped_text(); _create_undo_state(); @@ -2451,6 +2452,8 @@ LineEdit::LineEdit() { caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret)); set_caret_blink_enabled(false); + set_placeholder(p_placeholder); + set_editable(true); // Initialise to opposite first, so we get past the early-out in set_editable. } diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 1519c09d73..444c9a1c50 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -332,7 +332,7 @@ public: void show_virtual_keyboard(); - LineEdit(); + LineEdit(const String &p_placeholder = String()); ~LineEdit(); }; diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index 8f40f717c2..dc4f09d22d 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -317,8 +317,10 @@ void LinkButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); } -LinkButton::LinkButton() { +LinkButton::LinkButton(const String &p_text) { text_buf.instantiate(); set_focus_mode(FOCUS_NONE); set_default_cursor_shape(CURSOR_POINTING_HAND); + + set_text(p_text); } diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h index a455e866b1..f996558f32 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -90,7 +90,7 @@ public: void set_underline_mode(UnderlineMode p_underline_mode); UnderlineMode get_underline_mode() const; - LinkButton(); + LinkButton(const String &p_text = String()); }; VARIANT_ENUM_CAST(LinkButton::UnderlineMode); diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 46d8a61ca1..c04690cdb3 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -227,7 +227,8 @@ void MenuButton::set_disable_shortcuts(bool p_disabled) { disable_shortcuts = p_disabled; } -MenuButton::MenuButton() { +MenuButton::MenuButton(const String &p_text) : + Button(p_text) { set_flat(true); set_toggle_mode(true); set_disable_shortcuts(false); diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index 3647a69d33..9cfb780255 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -67,7 +67,7 @@ public: void set_item_count(int p_count); int get_item_count() const; - MenuButton(); + MenuButton(const String &p_text = String()); ~MenuButton(); }; diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 698d74843c..b3804e73d9 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -412,7 +412,8 @@ void OptionButton::_bind_methods() { ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "index"))); } -OptionButton::OptionButton() { +OptionButton::OptionButton(const String &p_text) : + Button(p_text) { set_toggle_mode(true); set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); if (is_layout_rtl()) { diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index adf2bb90ef..5352fe18a6 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -94,7 +94,7 @@ public: virtual void get_translatable_strings(List<String> *p_strings) const override; - OptionButton(); + OptionButton(const String &p_text = String()); ~OptionButton(); }; diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 4a5dc57e36..24b91cd16a 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -42,26 +42,30 @@ void Popup::_input_from_window(const Ref<InputEvent> &p_event) { } void Popup::_initialize_visible_parents() { - visible_parents.clear(); - - Window *parent_window = this; - while (parent_window) { - parent_window = parent_window->get_parent_visible_window(); - if (parent_window) { - visible_parents.push_back(parent_window); - parent_window->connect("focus_entered", callable_mp(this, &Popup::_parent_focused)); - parent_window->connect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents)); + if (is_embedded()) { + visible_parents.clear(); + + Window *parent_window = this; + while (parent_window) { + parent_window = parent_window->get_parent_visible_window(); + if (parent_window) { + visible_parents.push_back(parent_window); + parent_window->connect("focus_entered", callable_mp(this, &Popup::_parent_focused)); + parent_window->connect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents)); + } } } } void Popup::_deinitialize_visible_parents() { - for (uint32_t i = 0; i < visible_parents.size(); ++i) { - visible_parents[i]->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused)); - visible_parents[i]->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents)); - } + if (is_embedded()) { + for (uint32_t i = 0; i < visible_parents.size(); ++i) { + visible_parents[i]->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused)); + visible_parents[i]->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents)); + } - visible_parents.clear(); + visible_parents.clear(); + } } void Popup::_notification(int p_what) { @@ -94,7 +98,7 @@ void Popup::_notification(int p_what) { } void Popup::_parent_focused() { - if (popped_up && close_on_parent_focus) { + if (popped_up && get_flag(FLAG_POPUP)) { _close_pressed(); } } @@ -111,19 +115,7 @@ void Popup::set_as_minsize() { set_size(get_contents_minimum_size()); } -void Popup::set_close_on_parent_focus(bool p_close) { - close_on_parent_focus = p_close; -} - -bool Popup::get_close_on_parent_focus() { - return close_on_parent_focus; -} - void Popup::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_close_on_parent_focus", "close"), &Popup::set_close_on_parent_focus); - ClassDB::bind_method(D_METHOD("get_close_on_parent_focus"), &Popup::get_close_on_parent_focus); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "close_on_parent_focus"), "set_close_on_parent_focus", "get_close_on_parent_focus"); - ADD_SIGNAL(MethodInfo("popup_hide")); } @@ -184,6 +176,7 @@ Popup::Popup() { set_transient(true); set_flag(FLAG_BORDERLESS, true); set_flag(FLAG_RESIZE_DISABLED, true); + set_flag(FLAG_POPUP, true); connect("window_input", callable_mp(this, &Popup::_input_from_window)); } diff --git a/scene/gui/popup.h b/scene/gui/popup.h index 5678043b23..a3c56c9ff1 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -42,7 +42,6 @@ class Popup : public Window { LocalVector<Window *> visible_parents; bool popped_up = false; - bool close_on_parent_focus = true; void _input_from_window(const Ref<InputEvent> &p_event); @@ -61,9 +60,6 @@ protected: public: void set_as_minsize(); - void set_close_on_parent_focus(bool p_close); - bool get_close_on_parent_focus(); - Popup(); ~Popup(); }; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index deca1451ee..af2edfa090 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -205,7 +205,6 @@ void PopupMenu::_activate_submenu(int p_over) { submenu_pos.x = this_pos.x - submenu_size.width; } - submenu_popup->set_close_on_parent_focus(false); submenu_popup->set_position(submenu_pos); PopupMenu *submenu_pum = Object::cast_to<PopupMenu>(submenu_popup); @@ -223,6 +222,11 @@ void PopupMenu::_activate_submenu(int p_over) { // Set autohide areas. + Rect2 safe_area = this_rect; + safe_area.position.y += items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2; + safe_area.size.y = items[p_over]._height_cache; + DisplayServer::get_singleton()->window_set_popup_safe_rect(submenu_popup->get_window_id(), safe_area); + // Make the position of the parent popup relative to submenu popup. this_rect.position = this_rect.position - submenu_pum->get_position(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index dd07831b83..1c9eb14a24 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -4712,7 +4712,7 @@ Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressi return d; } -RichTextLabel::RichTextLabel() { +RichTextLabel::RichTextLabel(const String &p_text) { main = memnew(ItemFrame); main->index = 0; current = main; @@ -4734,6 +4734,8 @@ RichTextLabel::RichTextLabel() { vscroll->set_step(1); vscroll->hide(); + set_text(p_text); + set_clip_contents(true); } diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 53c2046c8f..076b68a0da 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -620,7 +620,7 @@ public: void set_fixed_size_to_width(int p_width); virtual Size2 get_minimum_size() const override; - RichTextLabel(); + RichTextLabel(const String &p_text = String()); ~RichTextLabel(); }; diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index ce78c286f5..c66e145bc4 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -148,6 +148,24 @@ void SubViewportContainer::_notification(int p_what) { } } } break; + + case NOTIFICATION_MOUSE_ENTER: { + _notify_viewports(NOTIFICATION_VP_MOUSE_ENTER); + } break; + + case NOTIFICATION_MOUSE_EXIT: { + _notify_viewports(NOTIFICATION_VP_MOUSE_EXIT); + } break; + } +} + +void SubViewportContainer::_notify_viewports(int p_notification) { + for (int i = 0; i < get_child_count(); i++) { + SubViewport *c = Object::cast_to<SubViewport>(get_child(i)); + if (!c) { + continue; + } + c->notification(p_notification); } } diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index 3138a6144c..f52f01e4e2 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -38,6 +38,7 @@ class SubViewportContainer : public Container { bool stretch = false; int shrink = 1; + void _notify_viewports(int p_notification); protected: void _notification(int p_what); diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index ce60da762f..0e088a44e5 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -552,10 +552,6 @@ void TabBar::set_current_tab(int p_current) { emit_signal(SNAME("tab_selected"), current); return; } - // Triggered by dragging a tab from another TabBar to the selected index, to ensure that tab_changed is emitted. - if (previous == -1) { - previous = current; - } emit_signal(SNAME("tab_selected"), current); @@ -954,9 +950,17 @@ void TabBar::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) { } update(); update_minimum_size(); + + if (tabs.size() == 1 && is_inside_tree()) { + emit_signal(SNAME("tab_changed"), 0); + } } void TabBar::clear_tabs() { + if (tabs.is_empty()) { + return; + } + tabs.clear(); offset = 0; max_drawn_tab = 0; @@ -971,14 +975,16 @@ void TabBar::clear_tabs() { void TabBar::remove_tab(int p_idx) { ERR_FAIL_INDEX(p_idx, tabs.size()); tabs.remove_at(p_idx); - if (current >= p_idx) { + + bool is_tab_changing = current == p_idx && !tabs.is_empty(); + + if (current >= p_idx && current > 0) { current--; } - if (current < 0) { + if (tabs.is_empty()) { offset = 0; max_drawn_tab = 0; - current = 0; previous = 0; } else { offset = MIN(offset, tabs.size() - 1); @@ -986,7 +992,7 @@ void TabBar::remove_tab(int p_idx) { _update_cache(); _ensure_no_over_offset(); - if (scroll_to_selected && !tabs.is_empty()) { + if (scroll_to_selected) { ensure_tab_visible(current); } } @@ -994,15 +1000,18 @@ void TabBar::remove_tab(int p_idx) { update(); update_minimum_size(); notify_property_list_changed(); + + if (is_tab_changing && is_inside_tree()) { + emit_signal(SNAME("tab_changed"), current); + } } Variant TabBar::get_drag_data(const Point2 &p_point) { if (!drag_to_rearrange_enabled) { - return Variant(); + return Control::get_drag_data(p_point); // Allow stuff like TabContainer to override it. } int tab_over = get_tab_idx_at_point(p_point); - if (tab_over < 0) { return Variant(); } @@ -1025,12 +1034,13 @@ Variant TabBar::get_drag_data(const Point2 &p_point) { drag_data["type"] = "tab_element"; drag_data["tab_element"] = tab_over; drag_data["from_path"] = get_path(); + return drag_data; } bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const { if (!drag_to_rearrange_enabled) { - return false; + return Control::can_drop_data(p_point, p_data); // Allow stuff like TabContainer to override it. } Dictionary d = p_data; @@ -1052,16 +1062,16 @@ bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const { } } } + return false; } void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { if (!drag_to_rearrange_enabled) { + Control::drop_data(p_point, p_data); // Allow stuff like TabContainer to override it. return; } - int hover_now = get_tab_idx_at_point(p_point); - Dictionary d = p_data; if (!d.has("type")) { return; @@ -1069,6 +1079,7 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { if (String(d["type"]) == "tab_element") { int tab_from_id = d["tab_element"]; + int hover_now = get_tab_idx_at_point(p_point); NodePath from_path = d["from_path"]; NodePath to_path = get_path(); @@ -1096,15 +1107,25 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { hover_now = get_tab_count(); } - // Workaround to ensure that tab_changed is emitted. - if (current == hover_now) { - current = -1; + from_tabs->remove_tab(tab_from_id); + tabs.insert(hover_now, moving_tab); + + if (tabs.size() > 1) { + if (current >= hover_now) { + current++; + } + if (previous >= hover_now) { + previous++; + } } - tabs.insert(hover_now, moving_tab); - from_tabs->remove_tab(tab_from_id); set_current_tab(hover_now); update_minimum_size(); + + if (tabs.size() == 1) { + emit_signal(SNAME("tab_selected"), 0); + emit_signal(SNAME("tab_changed"), 0); + } } } } @@ -1157,17 +1178,33 @@ bool TabBar::get_clip_tabs() const { return clip_tabs; } -void TabBar::move_tab(int from, int to) { - if (from == to) { +void TabBar::move_tab(int p_from, int p_to) { + if (p_from == p_to) { return; } - ERR_FAIL_INDEX(from, tabs.size()); - ERR_FAIL_INDEX(to, tabs.size()); + ERR_FAIL_INDEX(p_from, tabs.size()); + ERR_FAIL_INDEX(p_to, tabs.size()); + + Tab tab_from = tabs[p_from]; + tabs.remove_at(p_from); + tabs.insert(p_to, tab_from); - Tab tab_from = tabs[from]; - tabs.remove_at(from); - tabs.insert(to, tab_from); + if (current == p_from) { + current = p_to; + } else if (current > p_from && current <= p_to) { + current--; + } else if (current < p_from && current >= p_to) { + current++; + } + + if (previous == p_from) { + previous = p_to; + } else if (previous > p_from && previous >= p_to) { + previous--; + } else if (previous < p_from && previous <= p_to) { + previous++; + } _update_cache(); _ensure_no_over_offset(); @@ -1466,6 +1503,7 @@ void TabBar::_bind_methods() { ClassDB::bind_method(D_METHOD("is_tab_hidden", "tab_idx"), &TabBar::is_tab_hidden); ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &TabBar::remove_tab); ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &TabBar::add_tab, DEFVAL(""), DEFVAL(Ref<Texture2D>())); + ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabBar::get_tab_idx_at_point); ClassDB::bind_method(D_METHOD("set_tab_alignment", "alignment"), &TabBar::set_tab_alignment); ClassDB::bind_method(D_METHOD("get_tab_alignment"), &TabBar::get_tab_alignment); ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &TabBar::set_clip_tabs); diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h index b428538570..82ae8bce3f 100644 --- a/scene/gui/tab_bar.h +++ b/scene/gui/tab_bar.h @@ -126,7 +126,6 @@ protected: Variant get_drag_data(const Point2 &p_point) override; bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override; void drop_data(const Point2 &p_point, const Variant &p_data) override; - int get_tab_idx_at_point(const Point2 &p_point) const; public: void add_tab(const String &p_str = "", const Ref<Texture2D> &p_icon = Ref<Texture2D>()); @@ -156,13 +155,15 @@ public: void set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon); Ref<Texture2D> get_tab_button_icon(int p_tab) const; + int get_tab_idx_at_point(const Point2 &p_point) const; + void set_tab_alignment(AlignmentMode p_alignment); AlignmentMode get_tab_alignment() const; void set_clip_tabs(bool p_clip_tabs); bool get_clip_tabs() const; - void move_tab(int from, int to); + void move_tab(int p_from, int p_to); void set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy); CloseButtonDisplayPolicy get_tab_close_display_policy() const; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 31a5e41086..9953637e4f 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -30,44 +30,17 @@ #include "tab_container.h" -#include "core/object/message_queue.h" -#include "core/string/translation.h" #include "scene/gui/box_container.h" #include "scene/gui/label.h" #include "scene/gui/texture_rect.h" int TabContainer::_get_top_margin() const { - if (!tabs_visible) { - return 0; + int height = 0; + if (tabs_visible && get_tab_count() > 0) { + height = tab_bar->get_minimum_size().height; } - // Respect the minimum tab height. - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - - int tab_height = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height); - - // Font height or higher icon wins. - int content_height = 0; - - Vector<Control *> tabs = _get_tabs(); - for (int i = 0; i < tabs.size(); i++) { - content_height = MAX(content_height, text_buf[i]->get_size().y); - - Control *c = tabs[i]; - if (!c->has_meta("_tab_icon")) { - continue; - } - - Ref<Texture2D> tex = c->get_meta("_tab_icon"); - if (!tex.is_valid()) { - continue; - } - content_height = MAX(content_height, tex->get_size().height); - } - - return tab_height + content_height; + return height; } void TabContainer::gui_input(const Ref<InputEvent> &p_event) { @@ -113,77 +86,6 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { return; } } - - // Do not activate tabs when tabs is empty. - if (get_tab_count() == 0) { - return; - } - - Vector<Control *> tabs = _get_tabs(); - - // Handle navigation buttons. - if (buttons_visible_cache) { - int popup_ofs = 0; - if (popup) { - popup_ofs = menu->get_width(); - } - - Ref<Texture2D> increment = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement")); - if (is_layout_rtl()) { - if (pos.x < popup_ofs + decrement->get_width()) { - if (last_tab_cache < tabs.size() - 1) { - first_tab_cache += 1; - update(); - } - return; - } else if (pos.x < popup_ofs + increment->get_width() + decrement->get_width()) { - if (first_tab_cache > 0) { - first_tab_cache -= 1; - update(); - } - return; - } - } else { - if (pos.x > size.width - increment->get_width() - popup_ofs && pos.x) { - if (last_tab_cache < tabs.size() - 1) { - first_tab_cache += 1; - update(); - } - return; - } else if (pos.x > size.width - increment->get_width() - decrement->get_width() - popup_ofs) { - if (first_tab_cache > 0) { - first_tab_cache -= 1; - update(); - } - return; - } - } - } - - // Activate the clicked tab. - if (is_layout_rtl()) { - pos.x = size.width - pos.x; - } - - if (pos.x < tabs_ofs_cache) { - return; - } - - pos.x -= tabs_ofs_cache; - for (int i = first_tab_cache; i <= last_tab_cache; i++) { - if (get_tab_hidden(i)) { - continue; - } - int tab_width = _get_tab_width(i); - if (pos.x < tab_width) { - if (!get_tab_disabled(i)) { - set_current_tab(i); - } - break; - } - pos.x -= tab_width; - } } Ref<InputEventMouseMotion> mm = p_event; @@ -194,9 +96,8 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { // Mouse must be on tabs in the tab header area. if (pos.y > _get_top_margin()) { - if (menu_hovered || highlight_arrow > -1) { + if (menu_hovered) { menu_hovered = false; - highlight_arrow = -1; update(); } return; @@ -208,7 +109,6 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { if (pos.x <= menu->get_width()) { if (!menu_hovered) { menu_hovered = true; - highlight_arrow = -1; update(); return; } @@ -220,7 +120,6 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { if (pos.x >= size.width - menu->get_width()) { if (!menu_hovered) { menu_hovered = true; - highlight_arrow = -1; update(); return; } @@ -234,102 +133,19 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { return; } } - - // Do not activate tabs when tabs is empty. - if ((get_tab_count() == 0 || !buttons_visible_cache) && menu_hovered) { - highlight_arrow = -1; - update(); - return; - } - - int popup_ofs = 0; - if (popup) { - popup_ofs = menu->get_width(); - } - - Ref<Texture2D> increment = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement")); - - if (is_layout_rtl()) { - if (pos.x <= popup_ofs + decrement->get_width()) { - if (highlight_arrow != 1) { - highlight_arrow = 1; - update(); - } - } else if (pos.x <= popup_ofs + increment->get_width() + decrement->get_width()) { - if (highlight_arrow != 0) { - highlight_arrow = 0; - update(); - } - } else if (highlight_arrow > -1) { - highlight_arrow = -1; - update(); - } - } else { - if (pos.x >= size.width - increment->get_width() - popup_ofs) { - if (highlight_arrow != 1) { - highlight_arrow = 1; - update(); - } - } else if (pos.x >= size.width - increment->get_width() - decrement->get_width() - popup_ofs) { - if (highlight_arrow != 0) { - highlight_arrow = 0; - update(); - } - } else if (highlight_arrow > -1) { - highlight_arrow = -1; - update(); - } - } } } void TabContainer::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_READY: case NOTIFICATION_RESIZED: { - Vector<Control *> tabs = _get_tabs(); - int side_margin = get_theme_constant(SNAME("side_margin")); - Ref<Texture2D> menu = get_theme_icon(SNAME("menu")); - Ref<Texture2D> increment = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement")); - int header_width = get_size().width - side_margin * 2; - - // Find the width of the header area. - Popup *popup = get_popup(); - if (popup) { - header_width -= menu->get_width(); - } - if (buttons_visible_cache) { - header_width -= increment->get_width() + decrement->get_width(); - } - if (popup || buttons_visible_cache) { - header_width += side_margin; - } - - // Find the width of all tabs after first_tab_cache. - int all_tabs_width = 0; - for (int i = first_tab_cache; i < tabs.size(); i++) { - int tab_width = _get_tab_width(i); - all_tabs_width += tab_width; - } - - // Check if tabs before first_tab_cache would fit into the header area. - for (int i = first_tab_cache - 1; i >= 0; i--) { - int tab_width = _get_tab_width(i); - - if (all_tabs_width + tab_width > header_width) { - break; - } - - all_tabs_width += tab_width; - first_tab_cache--; - } + _update_margins(); } break; case NOTIFICATION_DRAW: { RID canvas = get_canvas_item(); Size2 size = get_size(); - bool rtl = is_layout_rtl(); // Draw only the tab area if the header is hidden. Ref<StyleBox> panel = get_theme_stylebox(SNAME("panel")); @@ -338,481 +154,171 @@ void TabContainer::_notification(int p_what) { return; } - Vector<Control *> tabs = _get_tabs(); - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - Ref<Texture2D> increment = get_theme_icon(SNAME("increment")); - Ref<Texture2D> increment_hl = get_theme_icon(SNAME("increment_highlight")); - Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement")); - Ref<Texture2D> decrement_hl = get_theme_icon(SNAME("decrement_highlight")); - Ref<Texture2D> menu = get_theme_icon(SNAME("menu")); - Ref<Texture2D> menu_hl = get_theme_icon(SNAME("menu_highlight")); - Color font_selected_color = get_theme_color(SNAME("font_selected_color")); - Color font_unselected_color = get_theme_color(SNAME("font_unselected_color")); - Color font_disabled_color = get_theme_color(SNAME("font_disabled_color")); - int side_margin = get_theme_constant(SNAME("side_margin")); - - // Find out start and width of the header area. - int header_x = side_margin; - int header_width = size.width - side_margin * 2; int header_height = _get_top_margin(); - Popup *popup = get_popup(); - if (popup) { - header_width -= menu->get_width(); - } - - // Check if all tabs would fit into the header area. - int all_tabs_width = 0; - for (int i = 0; i < tabs.size(); i++) { - if (get_tab_hidden(i)) { - continue; - } - int tab_width = _get_tab_width(i); - all_tabs_width += tab_width; - - if (all_tabs_width > header_width) { - // Not all tabs are visible at the same time - reserve space for navigation buttons. - buttons_visible_cache = true; - header_width -= decrement->get_width() + increment->get_width(); - break; - } else { - buttons_visible_cache = false; - } - } - // With buttons, a right side margin does not need to be respected. - if (popup || buttons_visible_cache) { - header_width += side_margin; - } - - if (!buttons_visible_cache) { - first_tab_cache = 0; - } - - // Go through the visible tabs to find the width they occupy. - all_tabs_width = 0; - Vector<int> tab_widths; - for (int i = first_tab_cache; i < tabs.size(); i++) { - if (get_tab_hidden(i)) { - tab_widths.push_back(0); - continue; - } - int tab_width = _get_tab_width(i); - if (all_tabs_width + tab_width > header_width && tab_widths.size() > 0) { - break; - } - all_tabs_width += tab_width; - tab_widths.push_back(tab_width); - } - - // Find the offset at which to draw tabs, according to the alignment. - switch (alignment) { - case ALIGNMENT_LEFT: - tabs_ofs_cache = header_x; - break; - case ALIGNMENT_CENTER: - tabs_ofs_cache = header_x + (header_width / 2) - (all_tabs_width / 2); - break; - case ALIGNMENT_RIGHT: - tabs_ofs_cache = header_x + header_width - all_tabs_width; - break; - } - - if (all_tabs_in_front) { - // Draw the tab area. - panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); - } - - // Draw unselected tabs in back - int x = 0; - int x_current = 0; - int index = 0; - for (int i = 0; i < tab_widths.size(); i++) { - index = i + first_tab_cache; - if (get_tab_hidden(index)) { - continue; - } - - int tab_width = tab_widths[i]; - if (index == current) { - x_current = x; - } else if (get_tab_disabled(index)) { - if (rtl) { - _draw_tab(tab_disabled, font_disabled_color, index, size.width - (tabs_ofs_cache + x) - tab_width); - } else { - _draw_tab(tab_disabled, font_disabled_color, index, tabs_ofs_cache + x); - } - } else { - if (rtl) { - _draw_tab(tab_unselected, font_unselected_color, index, size.width - (tabs_ofs_cache + x) - tab_width); - } else { - _draw_tab(tab_unselected, font_unselected_color, index, tabs_ofs_cache + x); - } - } - x += tab_width; - last_tab_cache = index; - } + panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); - if (!all_tabs_in_front) { - // Draw the tab area. - panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); - } + // Draw the popup menu. + if (get_popup()) { + Ref<Texture2D> menu = get_theme_icon(SNAME("menu")); + Ref<Texture2D> menu_hl = get_theme_icon(SNAME("menu_highlight")); - // Draw selected tab in front. Only draw selected tab when it's in visible range. - if (tabs.size() > 0 && current - first_tab_cache < tab_widths.size() && current >= first_tab_cache) { - Ref<StyleBox> current_style_box = get_tab_disabled(current) ? tab_disabled : tab_selected; - if (rtl) { - _draw_tab(current_style_box, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]); - } else { - _draw_tab(current_style_box, font_selected_color, current, tabs_ofs_cache + x_current); - } - } + int x = is_layout_rtl() ? 0 : get_size().width - menu->get_width(); - // Draw the popup menu. - if (rtl) { - x = 0; - } else { - x = get_size().width; - } - if (popup) { - if (!rtl) { - x -= menu->get_width(); - } if (menu_hovered) { menu_hl->draw(get_canvas_item(), Size2(x, (header_height - menu_hl->get_height()) / 2)); } else { menu->draw(get_canvas_item(), Size2(x, (header_height - menu->get_height()) / 2)); } - if (rtl) { - x += menu->get_width(); - } - } - - // Draw the navigation buttons. - if (buttons_visible_cache) { - if (rtl) { - if (last_tab_cache < tabs.size() - 1) { - draw_texture(highlight_arrow == 1 ? decrement_hl : decrement, Point2(x, (header_height - increment->get_height()) / 2)); - } else { - draw_texture(decrement, Point2(x, (header_height - increment->get_height()) / 2), Color(1, 1, 1, 0.5)); - } - x += increment->get_width(); - - if (first_tab_cache > 0) { - draw_texture(highlight_arrow == 0 ? increment_hl : increment, Point2(x, (header_height - decrement->get_height()) / 2)); - } else { - draw_texture(increment, Point2(x, (header_height - decrement->get_height()) / 2), Color(1, 1, 1, 0.5)); - } - x += decrement->get_width(); - } else { - x -= increment->get_width(); - if (last_tab_cache < tabs.size() - 1) { - draw_texture(highlight_arrow == 1 ? increment_hl : increment, Point2(x, (header_height - increment->get_height()) / 2)); - } else { - draw_texture(increment, Point2(x, (header_height - increment->get_height()) / 2), Color(1, 1, 1, 0.5)); - } - - x -= decrement->get_width(); - if (first_tab_cache > 0) { - draw_texture(highlight_arrow == 0 ? decrement_hl : decrement, Point2(x, (header_height - decrement->get_height()) / 2)); - } else { - draw_texture(decrement, Point2(x, (header_height - decrement->get_height()) / 2), Color(1, 1, 1, 0.5)); - } - } } } break; case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { - Vector<Control *> tabs = _get_tabs(); - for (int i = 0; i < tabs.size(); i++) { - text_buf.write[i]->clear(); - } - _theme_changing = true; + theme_changing = true; call_deferred(SNAME("_on_theme_changed")); // Wait until all changed theme. } break; } } -void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) { - Control *control = get_tab_control(p_index); - RID canvas = get_canvas_item(); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - int icon_text_distance = get_theme_constant(SNAME("icon_separation")); - int tab_width = _get_tab_width(p_index); - int header_height = _get_top_margin(); - - // Draw the tab background. - Rect2 tab_rect(p_x, 0, tab_width, header_height); - p_tab_style->draw(canvas, tab_rect); - - // Draw the tab contents. - String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name())); - - int x_content = tab_rect.position.x + p_tab_style->get_margin(SIDE_LEFT); - int top_margin = p_tab_style->get_margin(SIDE_TOP); - int y_center = top_margin + (tab_rect.size.y - p_tab_style->get_minimum_size().y) / 2; - - // Draw the tab icon. - if (control->has_meta("_tab_icon")) { - Ref<Texture2D> icon = control->get_meta("_tab_icon"); - if (icon.is_valid()) { - int y = y_center - (icon->get_height() / 2); - icon->draw(canvas, Point2i(x_content, y)); - if (!text.is_empty()) { - x_content += icon->get_width() + icon_text_distance; - } - } - } - - // Draw the tab text. - Point2i text_pos(x_content, y_center - text_buf[p_index]->get_size().y / 2); - if (outline_size > 0 && font_outline_color.a > 0) { - text_buf[p_index]->draw_outline(canvas, text_pos, outline_size, font_outline_color); - } - text_buf[p_index]->draw(canvas, text_pos, p_font_color); -} - -void TabContainer::_refresh_texts() { - text_buf.clear(); - Vector<Control *> tabs = _get_tabs(); - bool rtl = is_layout_rtl(); - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - for (int i = 0; i < tabs.size(); i++) { - Control *control = Object::cast_to<Control>(tabs[i]); - String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name())); - - Ref<TextLine> name; - name.instantiate(); - name->set_direction(rtl ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); - name->add_string(text, font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - text_buf.push_back(name); - } -} - void TabContainer::_on_theme_changed() { - if (!_theme_changing) { + if (!theme_changing) { return; } - _refresh_texts(); - - update_minimum_size(); + tab_bar->add_theme_style_override(SNAME("tab_unselected"), get_theme_stylebox(SNAME("tab_unselected"))); + tab_bar->add_theme_style_override(SNAME("tab_selected"), get_theme_stylebox(SNAME("tab_selected"))); + tab_bar->add_theme_style_override(SNAME("tab_disabled"), get_theme_stylebox(SNAME("tab_disabled"))); + tab_bar->add_theme_icon_override(SNAME("increment"), get_theme_icon(SNAME("increment"))); + tab_bar->add_theme_icon_override(SNAME("increment_highlight"), get_theme_icon(SNAME("increment_highlight"))); + tab_bar->add_theme_icon_override(SNAME("decrement"), get_theme_icon(SNAME("decrement"))); + tab_bar->add_theme_icon_override(SNAME("decrement_highlight"), get_theme_icon(SNAME("decrement_highlight"))); + tab_bar->add_theme_color_override(SNAME("font_selected_color"), get_theme_color(SNAME("font_selected_color"))); + tab_bar->add_theme_color_override(SNAME("font_unselected_color"), get_theme_color(SNAME("font_unselected_color"))); + tab_bar->add_theme_color_override(SNAME("font_disabled_color"), get_theme_color(SNAME("font_disabled_color"))); + tab_bar->add_theme_color_override(SNAME("font_outline_color"), get_theme_color(SNAME("font_outline_color"))); + tab_bar->add_theme_font_override(SNAME("font"), get_theme_font(SNAME("font"))); + tab_bar->add_theme_constant_override(SNAME("font_size"), get_theme_constant(SNAME("font_size"))); + tab_bar->add_theme_constant_override(SNAME("icon_separation"), get_theme_constant(SNAME("icon_separation"))); + tab_bar->add_theme_constant_override(SNAME("outline_size"), get_theme_constant(SNAME("outline_size"))); + + _update_margins(); if (get_tab_count() > 0) { _repaint(); - update(); + } else { + update_minimum_size(); } - _theme_changing = false; + update(); + + theme_changing = false; } void TabContainer::_repaint() { Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel")); - Vector<Control *> tabs = _get_tabs(); - for (int i = 0; i < tabs.size(); i++) { - Control *c = tabs[i]; + Vector<Control *> controls = _get_tab_controls(); + int current = get_current_tab(); + + for (int i = 0; i < controls.size(); i++) { + Control *c = controls[i]; + if (i == current) { c->show(); c->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + if (tabs_visible) { c->set_offset(SIDE_TOP, _get_top_margin()); } + c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + sb->get_margin(SIDE_TOP)); c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + sb->get_margin(SIDE_LEFT)); c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - sb->get_margin(SIDE_RIGHT)); c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - sb->get_margin(SIDE_BOTTOM)); - } else { c->hide(); } } -} - -void TabContainer::_on_mouse_exited() { - if (menu_hovered || highlight_arrow > -1) { - menu_hovered = false; - highlight_arrow = -1; - update(); - } -} - -int TabContainer::_get_tab_width(int p_index) const { - ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0); - Control *control = get_tab_control(p_index); - if (!control || get_tab_hidden(p_index)) { - return 0; - } - - // Get the width of the text displayed on the tab. - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name())); - int width = font->get_string_size(text, font_size).width; - - // Add space for a tab icon. - if (control->has_meta("_tab_icon")) { - Ref<Texture2D> icon = control->get_meta("_tab_icon"); - if (icon.is_valid()) { - width += icon->get_width(); - if (!text.is_empty()) { - width += get_theme_constant(SNAME("icon_separation")); - } - } - } - - // Respect a minimum size. - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - if (get_tab_disabled(p_index)) { - width += tab_disabled->get_minimum_size().width; - } else if (p_index == current) { - width += tab_selected->get_minimum_size().width; - } else { - width += tab_unselected->get_minimum_size().width; - } - return width; + update_minimum_size(); } -Vector<Control *> TabContainer::_get_tabs() const { - Vector<Control *> controls; - for (int i = 0; i < get_child_count(); i++) { - Control *control = Object::cast_to<Control>(get_child(i)); - if (!control || control->is_set_as_top_level()) { - continue; - } - - controls.push_back(control); - } - return controls; -} +void TabContainer::_update_margins() { + int menu_width = get_theme_icon(SNAME("menu"))->get_width(); + int side_margin = get_theme_constant(SNAME("side_margin")); -void TabContainer::_child_renamed_callback() { - _refresh_texts(); - update(); -} + // Directly check for validity, to avoid errors when quitting. + bool has_popup = popup_obj_id.is_valid(); -void TabContainer::add_child_notify(Node *p_child) { - Container::add_child_notify(p_child); + if (get_tab_count() == 0) { + tab_bar->set_offset(SIDE_LEFT, 0); + tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0); - Control *c = Object::cast_to<Control>(p_child); - if (!c || c->is_set_as_top_level()) { return; } - _refresh_texts(); - call_deferred(SNAME("_repaint")); - update(); - - bool first = (_get_tabs().size() == 1); - if (first) { - current = 0; - previous = 0; - } - - p_child->connect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback)); - if (first && is_inside_tree()) { - emit_signal(SNAME("tab_changed"), current); - } -} - -void TabContainer::move_child_notify(Node *p_child) { - Container::move_child_notify(p_child); + switch (get_tab_alignment()) { + case TabBar::ALIGNMENT_LEFT: { + tab_bar->set_offset(SIDE_LEFT, side_margin); + tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0); + } break; - Control *c = Object::cast_to<Control>(p_child); - if (!c || c->is_set_as_top_level()) { - return; - } + case TabBar::ALIGNMENT_CENTER: { + tab_bar->set_offset(SIDE_LEFT, 0); + tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0); + } break; - _update_current_tab(); - update(); -} + case TabBar::ALIGNMENT_RIGHT: { + tab_bar->set_offset(SIDE_LEFT, 0); -int TabContainer::get_tab_count() const { - return _get_tabs().size(); -} - -void TabContainer::set_current_tab(int p_current) { - ERR_FAIL_INDEX(p_current, get_tab_count()); + if (has_popup) { + tab_bar->set_offset(SIDE_RIGHT, -menu_width); + return; + } - int pending_previous = current; - current = p_current; + int first_tab_pos = tab_bar->get_tab_rect(0).position.x; + Rect2 last_tab_rect = tab_bar->get_tab_rect(get_tab_count() - 1); + int total_tabs_width = last_tab_rect.position.x - first_tab_pos + last_tab_rect.size.width; - _repaint(); + // Calculate if all the tabs would still fit if the margin was present. + if (get_clip_tabs() && (tab_bar->get_offset_buttons_visible() || (get_tab_count() > 1 && (total_tabs_width + side_margin) > get_size().width))) { + tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0); + } else { + tab_bar->set_offset(SIDE_RIGHT, -side_margin); + } + } break; - if (pending_previous == current) { - emit_signal(SNAME("tab_selected"), current); - } else { - previous = pending_previous; - emit_signal(SNAME("tab_selected"), current); - emit_signal(SNAME("tab_changed"), current); + case TabBar::ALIGNMENT_MAX: + break; // Can't happen, but silences warning. } - - update(); -} - -int TabContainer::get_current_tab() const { - return current; } -int TabContainer::get_previous_tab() const { - return previous; -} - -Control *TabContainer::get_tab_control(int p_idx) const { - Vector<Control *> tabs = _get_tabs(); - if (p_idx >= 0 && p_idx < tabs.size()) { - return tabs[p_idx]; - } else { - return nullptr; +void TabContainer::_on_mouse_exited() { + if (menu_hovered) { + menu_hovered = false; + update(); } } -Control *TabContainer::get_current_tab_control() const { - return get_tab_control(current); -} - -void TabContainer::remove_child_notify(Node *p_child) { - Container::remove_child_notify(p_child); +Vector<Control *> TabContainer::_get_tab_controls() const { + Vector<Control *> controls; + for (int i = 0; i < get_child_count(); i++) { + Control *control = Object::cast_to<Control>(get_child(i)); + if (!control || control->is_set_as_top_level() || control == tab_bar) { + continue; + } - Control *c = Object::cast_to<Control>(p_child); - if (!c || c->is_set_as_top_level()) { - return; + controls.push_back(control); } - // Defer the call because tab is not yet removed (remove_child_notify is called right before p_child is actually removed). - call_deferred(SNAME("_update_current_tab")); - - p_child->disconnect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback)); - - update(); -} - -void TabContainer::_update_current_tab() { - _refresh_texts(); - - int tc = get_tab_count(); - if (current >= tc) { - current = tc - 1; - } - if (current < 0) { - current = 0; - } else { - set_current_tab(current); - } + return controls; } -Variant TabContainer::get_drag_data(const Point2 &p_point) { +Variant TabContainer::_get_drag_data_fw(const Point2 &p_point, Control *p_from_control) { if (!drag_to_rearrange_enabled) { return Variant(); } int tab_over = get_tab_idx_at_point(p_point); - if (tab_over < 0) { return Variant(); } @@ -825,18 +331,20 @@ Variant TabContainer::get_drag_data(const Point2 &p_point) { tf->set_texture(icon); drag_preview->add_child(tf); } + Label *label = memnew(Label(get_tab_title(tab_over))); - drag_preview->add_child(label); set_drag_preview(drag_preview); + drag_preview->add_child(label); Dictionary drag_data; drag_data["type"] = "tabc_element"; drag_data["tabc_element"] = tab_over; drag_data["from_path"] = get_path(); + return drag_data; } -bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) const { +bool TabContainer::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const { if (!drag_to_rearrange_enabled) { return false; } @@ -852,7 +360,7 @@ bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) c if (from_path == to_path) { return true; } else if (get_tabs_rearrange_group() != -1) { - // drag and drop between other TabContainers + // Drag and drop between other TabContainers. Node *from_node = get_node(from_path); TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node); if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { @@ -860,10 +368,11 @@ bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) c } } } + return false; } -void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) { +void TabContainer::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) { if (!drag_to_rearrange_enabled) { return; } @@ -883,85 +392,192 @@ void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) { if (hover_now < 0) { hover_now = get_tab_count() - 1; } - move_child(get_tab_control(tab_from_id), get_tab_control(hover_now)->get_index()); + + move_child(get_tab_control(tab_from_id), get_tab_control(hover_now)->get_index(false)); set_current_tab(hover_now); } else if (get_tabs_rearrange_group() != -1) { - // drag and drop between TabContainers + // Drag and drop between TabContainers. Node *from_node = get_node(from_path); TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node); if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { Control *moving_tabc = from_tabc->get_tab_control(tab_from_id); from_tabc->remove_child(moving_tabc); - add_child(moving_tabc, false, INTERNAL_MODE_FRONT); + add_child(moving_tabc, true); + if (hover_now < 0) { hover_now = get_tab_count() - 1; } - move_child(moving_tabc, get_tab_control(hover_now)->get_index()); + + move_child(moving_tabc, get_tab_control(hover_now)->get_index(false)); + set_current_tab(hover_now); - emit_signal(SNAME("tab_changed"), hover_now); } } } - update(); } -int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const { - if (get_tab_count() == 0) { - return -1; +void TabContainer::_on_tab_changed(int p_tab) { + call_deferred(SNAME("_repaint")); + + emit_signal(SNAME("tab_changed"), p_tab); +} + +void TabContainer::_on_tab_selected(int p_tab) { + if (p_tab != get_previous_tab()) { + call_deferred(SNAME("_repaint")); } - // must be on tabs in the tab header area. - if (p_point.y > _get_top_margin()) { - return -1; + emit_signal(SNAME("tab_selected"), p_tab); +} + +void TabContainer::_child_renamed_callback() { + Vector<Control *> controls = _get_tab_controls(); + for (int i = 0; i < controls.size(); i++) { + if (!controls[i]->has_meta("_tab_name") && String(controls[i]->get_name()) != get_tab_title(i)) { + tab_bar->set_tab_title(i, controls[i]->get_name()); + return; + } } +} - Size2 size = get_size(); - int button_ofs = 0; - int px = p_point.x; +void TabContainer::add_child_notify(Node *p_child) { + if (p_child == tab_bar) { + return; + } + + Container::add_child_notify(p_child); - if (is_layout_rtl()) { - px = size.width - px; + Control *c = Object::cast_to<Control>(p_child); + if (!c || c->is_set_as_top_level()) { + return; } + c->hide(); + + tab_bar->add_tab(p_child->get_name()); - if (px < tabs_ofs_cache) { - return -1; + _update_margins(); + + p_child->connect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback)); + + // TabBar won't emit the "tab_changed" signal when not inside the tree. + if (!is_inside_tree()) { + call_deferred("_repaint"); } +} - Popup *popup = get_popup(); - if (popup) { - Ref<Texture2D> menu = get_theme_icon(SNAME("menu")); - button_ofs += menu->get_width(); +void TabContainer::move_child_notify(Node *p_child) { + if (p_child == tab_bar) { + return; } - if (buttons_visible_cache) { - Ref<Texture2D> increment = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement")); - button_ofs += increment->get_width() + decrement->get_width(); + + Container::move_child_notify(p_child); + + Control *c = Object::cast_to<Control>(p_child); + if (c && !c->is_set_as_top_level()) { + int old_idx = -1; + String tab_name = c->has_meta("_tab_name") ? String(c->get_meta("_tab_name")) : String(c->get_name()); + + // Find the previous tab index of the control. + for (int i = 0; i < get_tab_count(); i++) { + if (get_tab_title(i) == tab_name) { + old_idx = i; + break; + } + } + + tab_bar->move_tab(old_idx, get_tab_idx_from_control(c)); } - if (px > size.width - button_ofs) { - return -1; +} + +void TabContainer::remove_child_notify(Node *p_child) { + if (p_child == tab_bar) { + return; } - // get the tab at the point - Vector<Control *> tabs = _get_tabs(); - px -= tabs_ofs_cache; - for (int i = first_tab_cache; i <= last_tab_cache; i++) { - int tab_width = _get_tab_width(i); - if (px < tab_width) { + Container::remove_child_notify(p_child); + + Control *c = Object::cast_to<Control>(p_child); + if (!c || c->is_set_as_top_level()) { + return; + } + + tab_bar->remove_tab(get_tab_idx_from_control(c)); + + _update_margins(); + + if (p_child->has_meta("_tab_name")) { + p_child->remove_meta("_tab_name"); + } + p_child->disconnect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback)); + + // TabBar won't emit the "tab_changed" signal when not inside the tree. + if (!is_inside_tree()) { + call_deferred("_repaint"); + } +} + +int TabContainer::get_tab_count() const { + return tab_bar->get_tab_count(); +} + +void TabContainer::set_current_tab(int p_current) { + tab_bar->set_current_tab(p_current); +} + +int TabContainer::get_current_tab() const { + return tab_bar->get_current_tab(); +} + +int TabContainer::get_previous_tab() const { + return tab_bar->get_previous_tab(); +} + +Control *TabContainer::get_tab_control(int p_idx) const { + Vector<Control *> controls = _get_tab_controls(); + if (p_idx >= 0 && p_idx < controls.size()) { + return controls[p_idx]; + } else { + return nullptr; + } +} + +Control *TabContainer::get_current_tab_control() const { + return get_tab_control(tab_bar->get_current_tab()); +} + +int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const { + return tab_bar->get_tab_idx_at_point(p_point); +} + +int TabContainer::get_tab_idx_from_control(Control *p_child) const { + ERR_FAIL_NULL_V(p_child, -1); + ERR_FAIL_COND_V(p_child->get_parent() != this, -1); + + Vector<Control *> controls = _get_tab_controls(); + for (int i = 0; i < controls.size(); i++) { + if (controls[i] == p_child) { return i; } - px -= tab_width; } + return -1; } -void TabContainer::set_tab_alignment(AlignmentMode p_alignment) { - ERR_FAIL_INDEX(p_alignment, 3); - alignment = p_alignment; - update(); +void TabContainer::set_tab_alignment(TabBar::AlignmentMode p_alignment) { + tab_bar->set_tab_alignment(p_alignment); + _update_margins(); } -TabContainer::AlignmentMode TabContainer::get_tab_alignment() const { - return alignment; +TabBar::AlignmentMode TabContainer::get_tab_alignment() const { + return tab_bar->get_tab_alignment(); +} + +void TabContainer::set_clip_tabs(bool p_clip_tabs) { + tab_bar->set_clip_tabs(p_clip_tabs); +} + +bool TabContainer::get_clip_tabs() const { + return tab_bar->get_clip_tabs(); } void TabContainer::set_tabs_visible(bool p_visible) { @@ -970,11 +586,12 @@ void TabContainer::set_tabs_visible(bool p_visible) { } tabs_visible = p_visible; + tab_bar->set_visible(tabs_visible); - Vector<Control *> tabs = _get_tabs(); - for (int i = 0; i < tabs.size(); i++) { - Control *c = tabs[i]; - if (p_visible) { + Vector<Control *> controls = _get_tab_controls(); + for (int i = 0; i < controls.size(); i++) { + Control *c = controls[i]; + if (tabs_visible) { c->set_offset(SIDE_TOP, _get_top_margin()); } else { c->set_offset(SIDE_TOP, 0); @@ -996,7 +613,8 @@ void TabContainer::set_all_tabs_in_front(bool p_in_front) { all_tabs_in_front = p_in_front; - update(); + remove_child(tab_bar); + add_child(tab_bar, false, all_tabs_in_front ? INTERNAL_MODE_BACK : INTERNAL_MODE_FRONT); } bool TabContainer::is_all_tabs_in_front() const { @@ -1006,95 +624,61 @@ bool TabContainer::is_all_tabs_in_front() const { void TabContainer::set_tab_title(int p_tab, const String &p_title) { Control *child = get_tab_control(p_tab); ERR_FAIL_COND(!child); - child->set_meta("_tab_name", p_title); - _refresh_texts(); - update(); -} -String TabContainer::get_tab_title(int p_tab) const { - Control *child = get_tab_control(p_tab); - ERR_FAIL_COND_V(!child, ""); - if (child->has_meta("_tab_name")) { - return child->get_meta("_tab_name"); + if (p_title.is_empty()) { + tab_bar->set_tab_title(p_tab, String(child->get_name())); + + if (child->has_meta("_tab_name")) { + child->remove_meta("_tab_name"); + } } else { - return child->get_name(); + tab_bar->set_tab_title(p_tab, p_title); + child->set_meta("_tab_name", p_title); } } +String TabContainer::get_tab_title(int p_tab) const { + return tab_bar->get_tab_title(p_tab); +} + void TabContainer::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) { - Control *child = get_tab_control(p_tab); - ERR_FAIL_COND(!child); - child->set_meta("_tab_icon", p_icon); - update(); + tab_bar->set_tab_icon(p_tab, p_icon); } Ref<Texture2D> TabContainer::get_tab_icon(int p_tab) const { - Control *child = get_tab_control(p_tab); - ERR_FAIL_COND_V(!child, Ref<Texture2D>()); - if (child->has_meta("_tab_icon")) { - return child->get_meta("_tab_icon"); - } else { - return Ref<Texture2D>(); - } + return tab_bar->get_tab_icon(p_tab); } void TabContainer::set_tab_disabled(int p_tab, bool p_disabled) { - Control *child = get_tab_control(p_tab); - ERR_FAIL_COND(!child); - child->set_meta("_tab_disabled", p_disabled); - update(); + tab_bar->set_tab_disabled(p_tab, p_disabled); } -bool TabContainer::get_tab_disabled(int p_tab) const { - Control *child = get_tab_control(p_tab); - ERR_FAIL_COND_V(!child, false); - if (child->has_meta("_tab_disabled")) { - return child->get_meta("_tab_disabled"); - } else { - return false; - } +bool TabContainer::is_tab_disabled(int p_tab) const { + return tab_bar->is_tab_disabled(p_tab); } void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) { Control *child = get_tab_control(p_tab); ERR_FAIL_COND(!child); - child->set_meta("_tab_hidden", p_hidden); - update(); - for (int i = 0; i < get_tab_count(); i++) { - int try_tab = (p_tab + 1 + i) % get_tab_count(); - if (get_tab_disabled(try_tab) || get_tab_hidden(try_tab)) { - continue; - } - - set_current_tab(try_tab); - return; - } - //assumed no other tab can be switched to, just hide + tab_bar->set_tab_hidden(p_tab, p_hidden); child->hide(); } -bool TabContainer::get_tab_hidden(int p_tab) const { - Control *child = get_tab_control(p_tab); - ERR_FAIL_COND_V(!child, false); - if (child->has_meta("_tab_hidden")) { - return child->get_meta("_tab_hidden"); - } else { - return false; - } +bool TabContainer::is_tab_hidden(int p_tab) const { + return tab_bar->is_tab_hidden(p_tab); } void TabContainer::get_translatable_strings(List<String> *p_strings) const { - Vector<Control *> tabs = _get_tabs(); - for (int i = 0; i < tabs.size(); i++) { - Control *c = tabs[i]; + Vector<Control *> controls = _get_tab_controls(); + for (int i = 0; i < controls.size(); i++) { + Control *c = controls[i]; if (!c->has_meta("_tab_name")) { continue; } String name = c->get_meta("_tab_name"); - if (!name.is_empty()) { p_strings->push_back(name); } @@ -1104,9 +688,26 @@ void TabContainer::get_translatable_strings(List<String> *p_strings) const { Size2 TabContainer::get_minimum_size() const { Size2 ms; - Vector<Control *> tabs = _get_tabs(); - for (int i = 0; i < tabs.size(); i++) { - Control *c = tabs[i]; + if (tabs_visible) { + ms = tab_bar->get_minimum_size(); + + if (!get_clip_tabs()) { + if (get_popup()) { + ms.x += get_theme_icon(SNAME("menu"))->get_width(); + } + + int side_margin = get_theme_constant(SNAME("side_margin")); + if (side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER && + (get_tab_alignment() != TabBar::ALIGNMENT_RIGHT || !get_popup())) { + ms.x += side_margin; + } + } + } + + Vector<Control *> controls = _get_tab_controls(); + int max_control_height = 0; + for (int i = 0; i < controls.size(); i++) { + Control *c = controls[i]; if (!c->is_visible_in_tree() && !use_hidden_tabs_for_min_size) { continue; @@ -1114,29 +715,28 @@ Size2 TabContainer::get_minimum_size() const { Size2 cms = c->get_combined_minimum_size(); ms.x = MAX(ms.x, cms.x); - ms.y = MAX(ms.y, cms.y); + max_control_height = MAX(max_control_height, cms.y); } + ms.y += max_control_height; - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - - if (tabs_visible) { - ms.y += MAX(MAX(tab_unselected->get_minimum_size().y, tab_selected->get_minimum_size().y), tab_disabled->get_minimum_size().y); - ms.y += _get_top_margin(); - } - - Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel")); - ms += sb->get_minimum_size(); + Size2 panel_ms = get_theme_stylebox(SNAME("panel"))->get_minimum_size(); + ms.x = MAX(ms.x, panel_ms.x); + ms.y += panel_ms.y; return ms; } void TabContainer::set_popup(Node *p_popup) { - ERR_FAIL_NULL(p_popup); + bool had_popup = get_popup(); + Popup *popup = Object::cast_to<Popup>(p_popup); popup_obj_id = popup ? popup->get_instance_id() : ObjectID(); - update(); + + if (had_popup != bool(popup)) { + update(); + _update_margins(); + update_minimum_size(); + } } Popup *TabContainer::get_popup() const { @@ -1151,6 +751,7 @@ Popup *TabContainer::get_popup() const { popup_obj_id = ObjectID(); } } + return nullptr; } @@ -1163,15 +764,16 @@ bool TabContainer::get_drag_to_rearrange_enabled() const { } void TabContainer::set_tabs_rearrange_group(int p_group_id) { - tabs_rearrange_group = p_group_id; + tab_bar->set_tabs_rearrange_group(p_group_id); } int TabContainer::get_tabs_rearrange_group() const { - return tabs_rearrange_group; + return tab_bar->get_tabs_rearrange_group(); } void TabContainer::set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs) { use_hidden_tabs_for_min_size = p_use_hidden_tabs; + update_minimum_size(); } bool TabContainer::get_use_hidden_tabs_for_min_size() const { @@ -1195,6 +797,8 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_control", "tab_idx"), &TabContainer::get_tab_control); ClassDB::bind_method(D_METHOD("set_tab_alignment", "alignment"), &TabContainer::set_tab_alignment); ClassDB::bind_method(D_METHOD("get_tab_alignment"), &TabContainer::get_tab_alignment); + ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &TabContainer::set_clip_tabs); + ClassDB::bind_method(D_METHOD("get_clip_tabs"), &TabContainer::get_clip_tabs); ClassDB::bind_method(D_METHOD("set_tabs_visible", "visible"), &TabContainer::set_tabs_visible); ClassDB::bind_method(D_METHOD("are_tabs_visible"), &TabContainer::are_tabs_visible); ClassDB::bind_method(D_METHOD("set_all_tabs_in_front", "is_front"), &TabContainer::set_all_tabs_in_front); @@ -1204,23 +808,25 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabContainer::set_tab_icon); ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabContainer::get_tab_icon); ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabContainer::set_tab_disabled); - ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabContainer::get_tab_disabled); + ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabContainer::is_tab_disabled); ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabContainer::set_tab_hidden); - ClassDB::bind_method(D_METHOD("get_tab_hidden", "tab_idx"), &TabContainer::get_tab_hidden); + ClassDB::bind_method(D_METHOD("is_tab_hidden", "tab_idx"), &TabContainer::is_tab_hidden); ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabContainer::get_tab_idx_at_point); + ClassDB::bind_method(D_METHOD("get_tab_idx_from_control", "control"), &TabContainer::get_tab_idx_from_control); ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup); ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup); ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabContainer::set_drag_to_rearrange_enabled); ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabContainer::get_drag_to_rearrange_enabled); ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabContainer::set_tabs_rearrange_group); ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabContainer::get_tabs_rearrange_group); - ClassDB::bind_method(D_METHOD("set_use_hidden_tabs_for_min_size", "enabled"), &TabContainer::set_use_hidden_tabs_for_min_size); ClassDB::bind_method(D_METHOD("get_use_hidden_tabs_for_min_size"), &TabContainer::get_use_hidden_tabs_for_min_size); ClassDB::bind_method(D_METHOD("_repaint"), &TabContainer::_repaint); ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed); - ClassDB::bind_method(D_METHOD("_update_current_tab"), &TabContainer::_update_current_tab); + ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &TabContainer::_get_drag_data_fw); + ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TabContainer::_can_drop_data_fw); + ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TabContainer::_drop_data_fw); ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab"))); @@ -1228,16 +834,20 @@ void TabContainer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "all_tabs_in_front"), "set_all_tabs_in_front", "is_all_tabs_in_front"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hidden_tabs_for_min_size"), "set_use_hidden_tabs_for_min_size", "get_use_hidden_tabs_for_min_size"); - - BIND_ENUM_CONSTANT(ALIGNMENT_LEFT); - BIND_ENUM_CONSTANT(ALIGNMENT_CENTER); - BIND_ENUM_CONSTANT(ALIGNMENT_RIGHT); } TabContainer::TabContainer() { + tab_bar = memnew(TabBar); + tab_bar->set_drag_forwarding(this); + add_child(tab_bar, false, INTERNAL_MODE_FRONT); + tab_bar->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE); + tab_bar->connect("tab_changed", callable_mp(this, &TabContainer::_on_tab_changed)); + tab_bar->connect("tab_selected", callable_mp(this, &TabContainer::_on_tab_selected)); + connect("mouse_exited", callable_mp(this, &TabContainer::_on_mouse_exited)); } diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index ee1b3fea51..1322f08206 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -33,46 +33,32 @@ #include "scene/gui/container.h" #include "scene/gui/popup.h" -#include "scene/resources/text_line.h" +#include "scene/gui/tab_bar.h" class TabContainer : public Container { GDCLASS(TabContainer, Container); -public: - enum AlignmentMode { - ALIGNMENT_LEFT, - ALIGNMENT_CENTER, - ALIGNMENT_RIGHT, - }; - -private: - int first_tab_cache = 0; - int tabs_ofs_cache = 0; - int last_tab_cache = 0; - int current = 0; - int previous = 0; + TabBar *tab_bar; bool tabs_visible = true; bool all_tabs_in_front = false; - bool buttons_visible_cache = false; bool menu_hovered = false; - int highlight_arrow = -1; - AlignmentMode alignment = ALIGNMENT_CENTER; - int _get_top_margin() const; mutable ObjectID popup_obj_id; bool drag_to_rearrange_enabled = false; bool use_hidden_tabs_for_min_size = false; - int tabs_rearrange_group = -1; + bool theme_changing = false; - Vector<Ref<TextLine>> text_buf; - Vector<Control *> _get_tabs() const; - int _get_tab_width(int p_index) const; - bool _theme_changing = false; + int _get_top_margin() const; + Vector<Control *> _get_tab_controls() const; void _on_theme_changed(); void _repaint(); + void _update_margins(); void _on_mouse_exited(); - void _update_current_tab(); - void _draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x); - void _refresh_texts(); + void _on_tab_changed(int p_tab); + void _on_tab_selected(int p_tab); + + Variant _get_drag_data_fw(const Point2 &p_point, Control *p_from_control); + bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const; + void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control); protected: void _child_renamed_callback(); @@ -81,17 +67,17 @@ protected: virtual void add_child_notify(Node *p_child) override; virtual void move_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; + static void _bind_methods(); - Variant get_drag_data(const Point2 &p_point) override; - bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override; - void drop_data(const Point2 &p_point, const Variant &p_data) override; +public: int get_tab_idx_at_point(const Point2 &p_point) const; + int get_tab_idx_from_control(Control *p_child) const; - static void _bind_methods(); + void set_tab_alignment(TabBar::AlignmentMode p_alignment); + TabBar::AlignmentMode get_tab_alignment() const; -public: - void set_tab_alignment(AlignmentMode p_alignment); - AlignmentMode get_tab_alignment() const; + void set_clip_tabs(bool p_clip_tabs); + bool get_clip_tabs() const; void set_tabs_visible(bool p_visible); bool are_tabs_visible() const; @@ -106,10 +92,10 @@ public: Ref<Texture2D> get_tab_icon(int p_tab) const; void set_tab_disabled(int p_tab, bool p_disabled); - bool get_tab_disabled(int p_tab) const; + bool is_tab_disabled(int p_tab) const; void set_tab_hidden(int p_tab, bool p_hidden); - bool get_tab_hidden(int p_tab) const; + bool is_tab_hidden(int p_tab) const; int get_tab_count() const; void set_current_tab(int p_current); @@ -139,6 +125,4 @@ public: TabContainer(); }; -VARIANT_ENUM_CAST(TabContainer::AlignmentMode); - #endif // TAB_CONTAINER_H diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 5a3c622c86..05fda7128c 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1283,7 +1283,8 @@ void TextEdit::_notification(int p_what) { } // Carets. - const int caret_width = get_theme_constant(SNAME("caret_width")) * get_theme_default_base_scale(); + // Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1). + const int caret_width = get_theme_constant(SNAME("caret_width")) * MAX(1, get_theme_default_base_scale()); if (!clipped && caret.line == line && line_wrap_index == caret_wrap_index) { caret.draw_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index); @@ -2374,7 +2375,7 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) { if (p_all_to_left) { int caret_current_column = caret.column; - caret.column = 0; + set_caret_column(0); _remove_text(caret.line, 0, caret.line, caret_current_column); return; } @@ -2920,15 +2921,20 @@ void TextEdit::_clear() { end_complex_operation(); return; } + // Cannot merge with above, as we are not part of the tree on creation. + int old_text_size = text.size(); + clear_undo_history(); text.clear(); - caret.column = 0; - caret.line = 0; + set_caret_line(0, false); + set_caret_column(0); caret.x_ofs = 0; caret.line_ofs = 0; caret.wrap_ofs = 0; caret.last_fit_x = 0; selection.active = false; + + emit_signal(SNAME("lines_edited_from"), old_text_size, 0); } void TextEdit::set_text(const String &p_text) { @@ -2987,14 +2993,16 @@ void TextEdit::set_line(int p_line, const String &p_new_text) { if (p_line < 0 || p_line >= text.size()) { return; } + begin_complex_operation(); _remove_text(p_line, 0, p_line, text[p_line].length()); _insert_text(p_line, 0, p_new_text); - if (caret.line == p_line) { - caret.column = MIN(caret.column, p_new_text.length()); + if (caret.line == p_line && caret.column > p_new_text.length()) { + set_caret_column(MIN(caret.column, p_new_text.length()), false); } if (has_selection() && p_line == selection.to_line && selection.to_column > text[p_line].length()) { selection.to_column = text[p_line].length(); } + end_complex_operation(); } String TextEdit::get_line(int p_line) const { @@ -3049,8 +3057,10 @@ void TextEdit::swap_lines(int p_from_line, int p_to_line) { String tmp = get_line(p_from_line); String tmp2 = get_line(p_to_line); + begin_complex_operation(); set_line(p_to_line, tmp); set_line(p_from_line, tmp2); + end_complex_operation(); } void TextEdit::insert_line_at(int p_at, const String &p_text) { @@ -3059,7 +3069,7 @@ void TextEdit::insert_line_at(int p_at, const String &p_text) { _insert_text(p_at, 0, p_text + "\n"); if (caret.line >= p_at) { // offset caret when located after inserted line - ++caret.line; + set_caret_line(caret.line + 1, false); } if (has_selection()) { if (selection.from_line >= p_at) { @@ -3964,6 +3974,7 @@ void TextEdit::set_caret_line(int p_line, bool p_adjust_viewport, bool p_can_be_ } } } + bool caret_moved = caret.line != p_line; caret.line = p_line; int n_col = _get_char_pos_for_line(caret.last_fit_x, p_line, p_wrap_index); @@ -3977,15 +3988,16 @@ void TextEdit::set_caret_line(int p_line, bool p_adjust_viewport, bool p_can_be_ n_col -= 1; } } + caret_moved = (caret_moved || caret.column != n_col); caret.column = n_col; - if (p_adjust_viewport) { + if (is_inside_tree() && p_adjust_viewport) { adjust_viewport_to_caret(); } setting_caret_line = false; - if (!caret_pos_dirty) { + if (caret_moved && !caret_pos_dirty) { if (is_inside_tree()) { MessageQueue::get_singleton()->push_call(this, "_emit_caret_changed"); } @@ -4002,6 +4014,7 @@ void TextEdit::set_caret_column(int p_col, bool p_adjust_viewport) { p_col = 0; } + bool caret_moved = caret.column != p_col; caret.column = p_col; if (caret.column > get_line(caret.line).length()) { caret.column = get_line(caret.line).length(); @@ -4009,11 +4022,11 @@ void TextEdit::set_caret_column(int p_col, bool p_adjust_viewport) { caret.last_fit_x = _get_column_x_offset_for_line(caret.column, caret.line); - if (p_adjust_viewport) { + if (is_inside_tree() && p_adjust_viewport) { adjust_viewport_to_caret(); } - if (!caret_pos_dirty) { + if (caret_moved && !caret_pos_dirty) { if (is_inside_tree()) { MessageQueue::get_singleton()->push_call(this, "_emit_caret_changed"); } @@ -5651,8 +5664,10 @@ void TextEdit::_generate_context_menu() { if (editable) { menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : Key::NONE); } - menu->add_separator(); - if (is_selecting_enabled()) { + if (selecting_enabled || editable) { + menu->add_separator(); + } + if (selecting_enabled) { menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE); } if (editable) { @@ -6566,7 +6581,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li emit_signal(SNAME("lines_edited_from"), p_to_line, p_from_line); } -TextEdit::TextEdit() { +TextEdit::TextEdit(const String &p_placeholder) { placeholder_data_buf.instantiate(); clear(); @@ -6608,5 +6623,7 @@ TextEdit::TextEdit() { undo_stack_max_size = GLOBAL_GET("gui/common/text_edit_undo_stack_max_size"); + set_placeholder(p_placeholder); + set_editable(true); } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 83a63ae40a..6deaf76e5e 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -940,7 +940,7 @@ public: void set_draw_spaces(bool p_enabled); bool is_drawing_spaces() const; - TextEdit(); + TextEdit(const String &p_placeholder = String()); }; VARIANT_ENUM_CAST(TextEdit::CaretType); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 73cf2b9c6e..5afc37061b 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3286,8 +3286,21 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { } else { Rect2 rect = get_selected()->get_meta("__focus_rect"); Point2 mpos = b->get_position(); + int icon_size_x = 0; + Ref<Texture2D> icon = get_selected()->get_icon(selected_col); + if (icon.is_valid()) { + Rect2i icon_region = get_selected()->get_icon_region(selected_col); + if (icon_region == Rect2i()) { + icon_size_x = icon->get_width(); + } else { + icon_size_x = icon_region.size.width; + } + } + // Icon is treated as if it is outside of the rect so that double clicking on it will emit the item_double_clicked signal. if (rtl) { - mpos.x = get_size().width - mpos.x; + mpos.x = get_size().width - (mpos.x + icon_size_x); + } else { + mpos.x -= icon_size_x; } if (rect.has_point(mpos)) { if (!edit_selected()) { diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 9ad711c596..d2f5b52dbf 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -56,32 +56,19 @@ Transform2D CanvasItem::_edit_get_transform() const { #endif bool CanvasItem::is_visible_in_tree() const { - return visible && visible_in_tree; + return visible && parent_visible_in_tree; } -void CanvasItem::_propagate_visibility_changed(bool p_visible, bool p_was_visible) { - if (p_visible && first_draw) { //avoid propagating it twice - first_draw = false; - } - visible_in_tree = p_visible; - notification(NOTIFICATION_VISIBILITY_CHANGED); - - if (visible && p_visible) { - update(); - } else if (!p_visible && (visible || p_was_visible)) { - emit_signal(SceneStringNames::get_singleton()->hidden); +void CanvasItem::_propagate_visibility_changed(bool p_parent_visible_in_tree) { + parent_visible_in_tree = p_parent_visible_in_tree; + if (!visible) { + return; } - _block(); - - for (int i = 0; i < get_child_count(); i++) { - CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i)); - - if (c && c->visible) { //should the top_levels stop propagation? i think so but.. - c->_propagate_visibility_changed(p_visible); - } + if (p_parent_visible_in_tree && first_draw) { // Avoid propagating it twice. + first_draw = false; } - _unblock(); + _handle_visibility_change(p_parent_visible_in_tree); } void CanvasItem::set_visible(bool p_visible) { @@ -90,13 +77,34 @@ void CanvasItem::set_visible(bool p_visible) { } visible = p_visible; - RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, p_visible); - if (!is_inside_tree()) { + if (!parent_visible_in_tree) { + notification(NOTIFICATION_VISIBILITY_CHANGED); return; } - _propagate_visibility_changed(p_visible, !p_visible); + _handle_visibility_change(p_visible); +} + +void CanvasItem::_handle_visibility_change(bool p_visible) { + RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, p_visible); + notification(NOTIFICATION_VISIBILITY_CHANGED); + + if (p_visible) { + update(); + } else { + emit_signal(SceneStringNames::get_singleton()->hidden); + } + + _block(); + for (int i = 0; i < get_child_count(); i++) { + CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i)); + + if (c) { // Should the top_levels stop propagation? I think so, but... + c->_propagate_visibility_changed(p_visible); + } + } + _unblock(); } void CanvasItem::show() { @@ -264,13 +272,13 @@ void CanvasItem::_notification(int p_what) { CanvasItem *ci = Object::cast_to<CanvasItem>(parent); if (ci) { - visible_in_tree = ci->is_visible_in_tree(); + parent_visible_in_tree = ci->is_visible_in_tree(); C = ci->children_items.push_back(this); } else { CanvasLayer *cl = Object::cast_to<CanvasLayer>(parent); if (cl) { - visible_in_tree = cl->is_visible(); + parent_visible_in_tree = cl->is_visible(); } else { // Look for a window. Viewport *viewport = nullptr; @@ -288,9 +296,9 @@ void CanvasItem::_notification(int p_what) { window = Object::cast_to<Window>(viewport); if (window) { window->connect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed)); - visible_in_tree = window->is_visible(); + parent_visible_in_tree = window->is_visible(); } else { - visible_in_tree = true; + parent_visible_in_tree = true; } } } @@ -333,7 +341,7 @@ void CanvasItem::_notification(int p_what) { window->disconnect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed)); } global_invalid = true; - visible_in_tree = false; + parent_visible_in_tree = false; } break; case NOTIFICATION_VISIBILITY_CHANGED: { diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 2a9e7bac3d..1b2c188fc0 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -85,7 +85,7 @@ private: Window *window = nullptr; bool first_draw = false; bool visible = true; - bool visible_in_tree = false; + bool parent_visible_in_tree = false; bool clip_children = false; bool pending_update = false; bool top_level = false; @@ -108,7 +108,8 @@ private: void _top_level_raise_self(); - void _propagate_visibility_changed(bool p_visible, bool p_was_visible = false); + void _propagate_visibility_changed(bool p_parent_visible_in_tree); + void _handle_visibility_change(bool p_visible); void _update_callback(); diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 6c627857fa..7aa4d391f8 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -58,11 +58,7 @@ void CanvasLayer::set_visible(bool p_visible) { if (c) { RenderingServer::get_singleton()->canvas_item_set_visible(c->get_canvas_item(), p_visible && c->is_visible()); - if (c->is_visible()) { - c->_propagate_visibility_changed(p_visible); - } else { - c->notification(CanvasItem::NOTIFICATION_VISIBILITY_CHANGED); - } + c->_propagate_visibility_changed(p_visible); } } } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 42a2e41a08..211667ce38 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1344,27 +1344,45 @@ bool Node::has_node(const NodePath &p_path) const { return get_node_or_null(p_path) != nullptr; } -Node *Node::find_node(const String &p_mask, bool p_recursive, bool p_owned) const { +TypedArray<Node> Node::find_nodes(const String &p_mask, const String &p_type, bool p_recursive, bool p_owned) const { + TypedArray<Node> ret; + ERR_FAIL_COND_V(p_mask.is_empty() && p_type.is_empty(), ret); + Node *const *cptr = data.children.ptr(); int ccount = data.children.size(); for (int i = 0; i < ccount; i++) { if (p_owned && !cptr[i]->data.owner) { continue; } - if (cptr[i]->data.name.operator String().match(p_mask)) { - return cptr[i]; + + if (!p_mask.is_empty()) { + if (!cptr[i]->data.name.operator String().match(p_mask)) { + continue; + } else if (p_type.is_empty()) { + ret.append(cptr[i]); + } } - if (!p_recursive) { - continue; + if (cptr[i]->is_class(p_type)) { + ret.append(cptr[i]); + } else if (cptr[i]->get_script_instance()) { + Ref<Script> script = cptr[i]->get_script_instance()->get_script(); + while (script.is_valid()) { + if ((ScriptServer::is_global_class(p_type) && ScriptServer::get_global_class_path(p_type) == script->get_path()) || p_type == script->get_path()) { + ret.append(cptr[i]); + break; + } + + script = script->get_base_script(); + } } - Node *ret = cptr[i]->find_node(p_mask, true, p_owned); - if (ret) { - return ret; + if (p_recursive) { + ret.append_array(cptr[i]->find_nodes(p_mask, p_type, true, p_owned)); } } - return nullptr; + + return ret; } Node *Node::get_parent() const { @@ -2706,7 +2724,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node); ClassDB::bind_method(D_METHOD("get_node_or_null", "path"), &Node::get_node_or_null); ClassDB::bind_method(D_METHOD("get_parent"), &Node::get_parent); - ClassDB::bind_method(D_METHOD("find_node", "mask", "recursive", "owned"), &Node::find_node, DEFVAL(true), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("find_nodes", "mask", "type", "recursive", "owned"), &Node::find_nodes, DEFVAL(""), DEFVAL(true), DEFVAL(true)); ClassDB::bind_method(D_METHOD("find_parent", "mask"), &Node::find_parent); ClassDB::bind_method(D_METHOD("has_node_and_resource", "path"), &Node::has_node_and_resource); ClassDB::bind_method(D_METHOD("get_node_and_resource", "path"), &Node::_get_node_and_resource); @@ -2845,6 +2863,9 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_WM_CLOSE_REQUEST); BIND_CONSTANT(NOTIFICATION_WM_GO_BACK_REQUEST); BIND_CONSTANT(NOTIFICATION_WM_SIZE_CHANGED); + BIND_CONSTANT(NOTIFICATION_WM_DPI_CHANGE); + BIND_CONSTANT(NOTIFICATION_VP_MOUSE_ENTER); + BIND_CONSTANT(NOTIFICATION_VP_MOUSE_EXIT); BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING); BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED); BIND_CONSTANT(NOTIFICATION_WM_ABOUT); diff --git a/scene/main/node.h b/scene/main/node.h index f2dcdf4b43..8e49f871a7 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -268,6 +268,8 @@ public: NOTIFICATION_WM_GO_BACK_REQUEST = 1007, NOTIFICATION_WM_SIZE_CHANGED = 1008, NOTIFICATION_WM_DPI_CHANGE = 1009, + NOTIFICATION_VP_MOUSE_ENTER = 1010, + NOTIFICATION_VP_MOUSE_EXIT = 1011, NOTIFICATION_OS_MEMORY_WARNING = MainLoop::NOTIFICATION_OS_MEMORY_WARNING, NOTIFICATION_TRANSLATION_CHANGED = MainLoop::NOTIFICATION_TRANSLATION_CHANGED, @@ -299,7 +301,7 @@ public: bool has_node(const NodePath &p_path) const; Node *get_node(const NodePath &p_path) const; Node *get_node_or_null(const NodePath &p_path) const; - Node *find_node(const String &p_mask, bool p_recursive = true, bool p_owned = true) const; + TypedArray<Node> find_nodes(const String &p_mask, const String &p_type = "", bool p_recursive = true, bool p_owned = true) const; bool has_node_and_resource(const NodePath &p_path) const; Node *get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property = true) const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 2b4d7d8331..69e7472cf2 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1256,8 +1256,6 @@ void SceneTree::_bind_methods() { ADD_SIGNAL(MethodInfo("process_frame")); ADD_SIGNAL(MethodInfo("physics_frame")); - ADD_SIGNAL(MethodInfo("files_dropped", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "screen"))); - BIND_ENUM_CONSTANT(GROUP_CALL_DEFAULT); BIND_ENUM_CONSTANT(GROUP_CALL_REVERSE); BIND_ENUM_CONSTANT(GROUP_CALL_REALTIME); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 2831e76fba..de6aa2b139 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -496,17 +496,17 @@ void Viewport::_notification(int p_what) { #endif // _3D_DISABLED } break; - case NOTIFICATION_WM_MOUSE_ENTER: { - gui.mouse_in_window = true; + case NOTIFICATION_VP_MOUSE_ENTER: { + gui.mouse_in_viewport = true; } break; - case NOTIFICATION_WM_MOUSE_EXIT: { - gui.mouse_in_window = false; + case NOTIFICATION_VP_MOUSE_EXIT: { + gui.mouse_in_viewport = false; _drop_physics_mouseover(); _drop_mouse_over(); - // When the mouse exits the window, we want to end mouse_over, but + // When the mouse exits the viewport, we want to end mouse_over, but // not mouse_focus, because, for example, we want to continue - // dragging a scrollbar even if the mouse has left the window. + // dragging a scrollbar even if the mouse has left the viewport. } break; case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { @@ -1239,6 +1239,7 @@ void Viewport::_gui_show_tooltip() { panel->set_transient(true); panel->set_flag(Window::FLAG_NO_FOCUS, true); + panel->set_flag(Window::FLAG_POPUP, false); panel->set_wrap_controls(true); panel->add_child(base_tooltip); @@ -1268,7 +1269,10 @@ void Viewport::_gui_show_tooltip() { gui.tooltip_popup->set_position(r.position); gui.tooltip_popup->set_size(r.size); - gui.tooltip_popup->show(); + DisplayServer::WindowID active_popup = DisplayServer::get_singleton()->window_get_active_popup(); + if (active_popup == DisplayServer::INVALID_WINDOW_ID || active_popup == window->get_window_id()) { + gui.tooltip_popup->show(); + } gui.tooltip_popup->child_controls_changed(); } @@ -1683,7 +1687,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Control *over = nullptr; if (gui.mouse_focus) { over = gui.mouse_focus; - } else if (gui.mouse_in_window) { + } else if (gui.mouse_in_viewport) { over = gui_find_control(mpos); } diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 3a71745f44..93e42f1838 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -335,7 +335,7 @@ private: // info used when this is a window bool forced_mouse_focus = false; //used for menu buttons - bool mouse_in_window = true; + bool mouse_in_viewport = true; bool key_event_accepted = false; Control *mouse_focus = nullptr; Control *last_mouse_focus = nullptr; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index e7a575f40a..6837fcae21 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -340,9 +340,11 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) { case DisplayServer::WINDOW_EVENT_MOUSE_ENTER: { _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER); emit_signal(SNAME("mouse_entered")); + notification(NOTIFICATION_VP_MOUSE_ENTER); DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape } break; case DisplayServer::WINDOW_EVENT_MOUSE_EXIT: { + notification(NOTIFICATION_VP_MOUSE_EXIT); _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT); emit_signal(SNAME("mouse_exited")); } break; @@ -434,8 +436,12 @@ void Window::set_visible(bool p_visible) { //update transient exclusive if (transient_parent) { if (exclusive && visible) { - ERR_FAIL_COND_MSG(transient_parent->exclusive_child && transient_parent->exclusive_child != this, "Transient parent has another exclusive child."); - transient_parent->exclusive_child = this; +#ifdef TOOLS_ENABLED + if (!(Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this))) { + ERR_FAIL_COND_MSG(transient_parent->exclusive_child && transient_parent->exclusive_child != this, "Transient parent has another exclusive child."); + transient_parent->exclusive_child = this; + } +#endif } else { if (transient_parent->exclusive_child == this) { transient_parent->exclusive_child = nullptr; @@ -949,7 +955,7 @@ bool Window::_can_consume_input_events() const { void Window::_window_input(const Ref<InputEvent> &p_ev) { if (EngineDebugger::is_active()) { - //quit from game window using F8 + // Quit from game window using F8. Ref<InputEventKey> k = p_ev; if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::F8) { EngineDebugger::get_singleton()->send_message("request_quit", Array()); @@ -957,15 +963,7 @@ void Window::_window_input(const Ref<InputEvent> &p_ev) { } if (exclusive_child != nullptr) { - /* - Window *focus_target = exclusive_child; - focus_target->grab_focus(); - while (focus_target->exclusive_child != nullptr) { - focus_target = focus_target->exclusive_child; - focus_target->grab_focus(); - }*/ - - if (!is_embedding_subwindows()) { //not embedding, no need for event + if (!is_embedding_subwindows()) { // Not embedding, no need for event. return; } } @@ -1585,6 +1583,7 @@ void Window::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "always_on_top"), "set_flag", "get_flag", FLAG_ALWAYS_ON_TOP); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_flag", "get_flag", FLAG_TRANSPARENT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "unfocusable"), "set_flag", "get_flag", FLAG_NO_FOCUS); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "popup_window"), "set_flag", "get_flag", FLAG_POPUP); ADD_GROUP("Limits", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "min_size"), "set_min_size", "get_min_size"); @@ -1628,6 +1627,7 @@ void Window::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_ALWAYS_ON_TOP); BIND_ENUM_CONSTANT(FLAG_TRANSPARENT); BIND_ENUM_CONSTANT(FLAG_NO_FOCUS); + BIND_ENUM_CONSTANT(FLAG_POPUP); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_DISABLED); diff --git a/scene/main/window.h b/scene/main/window.h index f37689f905..3d8e337b4a 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -55,6 +55,7 @@ public: FLAG_ALWAYS_ON_TOP = DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP, FLAG_TRANSPARENT = DisplayServer::WINDOW_FLAG_TRANSPARENT, FLAG_NO_FOCUS = DisplayServer::WINDOW_FLAG_NO_FOCUS, + FLAG_POPUP = DisplayServer::WINDOW_FLAG_POPUP, FLAG_MAX = DisplayServer::WINDOW_FLAG_MAX, }; diff --git a/scene/multiplayer/scene_cache_interface.cpp b/scene/multiplayer/scene_cache_interface.cpp index 2f278ed864..f05dc5a2da 100644 --- a/scene/multiplayer/scene_cache_interface.cpp +++ b/scene/multiplayer/scene_cache_interface.cpp @@ -139,74 +139,46 @@ void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_pack E->get() = true; } -bool SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) { - bool has_all_peers = true; - List<int> peers_to_add; // If one is missing, take note to add it. - - for (const Set<int>::Element *E = multiplayer->get_connected_peers().front(); E; E = E->next()) { - if (p_target < 0 && E->get() == -p_target) { - continue; // Continue, excluded. - } - - if (p_target > 0 && E->get() != p_target) { - continue; // Continue, not for this peer. - } - - Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - - if (!F || !F->get()) { - // Path was not cached, or was cached but is unconfirmed. - if (!F) { - // Not cached at all, take note. - peers_to_add.push_back(E->get()); - } +Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers) { + // Encode function name. + const CharString path = String(p_path).utf8(); + const int path_len = encode_cstring(path.get_data(), nullptr); - has_all_peers = false; - } - } - - if (peers_to_add.size() > 0) { - // Those that need to be added, send a message for this. - - // Encode function name. - const CharString path = String(p_path).utf8(); - const int path_len = encode_cstring(path.get_data(), nullptr); + // Extract MD5 from rpc methods list. + const String methods_md5 = multiplayer->get_rpc_md5(p_node); + const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. - // Extract MD5 from rpc methods list. - const String methods_md5 = multiplayer->get_rpc_md5(p_node); - const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. - - Vector<uint8_t> packet; - packet.resize(1 + 4 + path_len + methods_md5_len); - int ofs = 0; + Vector<uint8_t> packet; + packet.resize(1 + 4 + path_len + methods_md5_len); + int ofs = 0; - packet.write[ofs] = MultiplayerAPI::NETWORK_COMMAND_SIMPLIFY_PATH; - ofs += 1; + packet.write[ofs] = MultiplayerAPI::NETWORK_COMMAND_SIMPLIFY_PATH; + ofs += 1; - ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); + ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); - ofs += encode_uint32(psc->id, &packet.write[ofs]); + ofs += encode_uint32(psc->id, &packet.write[ofs]); - ofs += encode_cstring(path.get_data(), &packet.write[ofs]); + ofs += encode_cstring(path.get_data(), &packet.write[ofs]); - Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer(); - ERR_FAIL_COND_V(multiplayer_peer.is_null(), false); + Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer(); + ERR_FAIL_COND_V(multiplayer_peer.is_null(), ERR_BUG); #ifdef DEBUG_ENABLED - multiplayer->profile_bandwidth("out", packet.size() * peers_to_add.size()); + multiplayer->profile_bandwidth("out", packet.size() * p_peers.size()); #endif - for (int &E : peers_to_add) { - multiplayer_peer->set_target_peer(E); // To all of you. - multiplayer_peer->set_transfer_channel(0); - multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - multiplayer_peer->put_packet(packet.ptr(), packet.size()); - - psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed. - } + Error err = OK; + for (int peer_id : p_peers) { + multiplayer_peer->set_target_peer(peer_id); + multiplayer_peer->set_transfer_channel(0); + multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); + err = multiplayer_peer->put_packet(packet.ptr(), packet.size()); + ERR_FAIL_COND_V(err != OK, err); + // Insert into confirmed, but as false since it was not confirmed. + psc->confirmed_peers.insert(peer_id, false); } - - return has_all_peers; + return err; } bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) { @@ -230,7 +202,43 @@ bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int } r_id = psc->id; - return _send_confirm_path(node, p_path, psc, p_peer_id); + bool has_all_peers = true; + List<int> peers_to_add; // If one is missing, take note to add it. + + if (p_peer_id > 0) { + // Fast single peer check. + Map<int, bool>::Element *F = psc->confirmed_peers.find(p_peer_id); + if (!F) { + peers_to_add.push_back(p_peer_id); // Need to also be notified. + has_all_peers = false; + } else if (!F->get()) { + has_all_peers = false; + } + } else { + // Long and painful. + for (const Set<int>::Element *E = multiplayer->get_connected_peers().front(); E; E = E->next()) { + if (p_peer_id < 0 && E->get() == -p_peer_id) { + continue; // Continue, excluded. + } + if (p_peer_id > 0 && E->get() != p_peer_id) { + continue; // Continue, not for this peer. + } + + Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); + if (!F) { + peers_to_add.push_back(E->get()); // Need to also be notified. + has_all_peers = false; + } else if (!F->get()) { + has_all_peers = false; + } + } + } + + if (peers_to_add.size()) { + _send_confirm_path(node, p_path, psc, peers_to_add); + } + + return has_all_peers; } Object *SceneCacheInterface::get_cached_object(int p_from, uint32_t p_cache_id) { diff --git a/scene/multiplayer/scene_cache_interface.h b/scene/multiplayer/scene_cache_interface.h index 91a53cb948..c709d26b51 100644 --- a/scene/multiplayer/scene_cache_interface.h +++ b/scene/multiplayer/scene_cache_interface.h @@ -60,7 +60,7 @@ private: int last_send_cache_id = 1; protected: - bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target); + Error _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers); static MultiplayerCacheInterface *_create(MultiplayerAPI *p_multiplayer); public: diff --git a/scene/multiplayer/scene_replication_interface.cpp b/scene/multiplayer/scene_replication_interface.cpp index 25a704b37e..0764f136e4 100644 --- a/scene/multiplayer/scene_replication_interface.cpp +++ b/scene/multiplayer/scene_replication_interface.cpp @@ -350,11 +350,12 @@ void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) { } if (size) { uint32_t net_id = rep_state->get_net_id(oid); - if (net_id == 0) { + if (net_id == 0 || (net_id & 0x80000000)) { // First time path based ID. NodePath rel_path = multiplayer->get_root_path().rel_path_to(sync->get_path()); int path_id = 0; multiplayer->send_object_cache(sync, rel_path, p_peer, path_id); + ERR_CONTINUE_MSG(net_id && net_id != (uint32_t(path_id) | 0x80000000), "This should never happen!"); net_id = path_id; rep_state->set_net_id(oid, net_id | 0x80000000); } diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index bc533ff022..312a557602 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -2413,7 +2413,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol real_t c = 0.0; // prepare for all cases of interpolation - if ((loop_mode == LOOP_LINEAR || loop_mode == LOOP_PINGPONG) && p_loop_wrap) { + if (loop_mode == LOOP_LINEAR && p_loop_wrap) { // loop if (!p_backward) { // no backward @@ -2567,11 +2567,19 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol case INTERPOLATION_CUBIC: { int pre = idx - 1; if (pre < 0) { - pre = 0; + if (loop_mode == LOOP_LINEAR && p_loop_wrap) { + pre = len - 1; + } else { + pre = 0; + } } int post = next + 1; if (post >= len) { - post = next; + if (loop_mode == LOOP_LINEAR && p_loop_wrap) { + post = 0; + } else { + post = next; + } } return _cubic_interpolate(p_keys[pre].value, p_keys[idx].value, p_keys[next].value, p_keys[post].value, c); diff --git a/scene/resources/box_shape_3d.cpp b/scene/resources/box_shape_3d.cpp index a1ec9a2230..1abbf366fd 100644 --- a/scene/resources/box_shape_3d.cpp +++ b/scene/resources/box_shape_3d.cpp @@ -77,6 +77,7 @@ bool BoxShape3D::_get(const StringName &p_name, Variant &r_property) const { #endif // DISABLE_DEPRECATED void BoxShape3D::set_size(const Vector3 &p_size) { + ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0 || p_size.z < 0, "BoxShape3D size cannot be negative."); size = p_size; _update_shape(); notify_change_to_owners(); diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp index 4d2698d27d..a1ad487bff 100644 --- a/scene/resources/capsule_shape_2d.cpp +++ b/scene/resources/capsule_shape_2d.cpp @@ -59,6 +59,7 @@ void CapsuleShape2D::_update_shape() { } void CapsuleShape2D::set_radius(real_t p_radius) { + ERR_FAIL_COND_MSG(p_radius < 0, "CapsuleShape2D radius cannot be negative."); radius = p_radius; if (radius > height * 0.5) { height = radius * 2.0; @@ -71,6 +72,7 @@ real_t CapsuleShape2D::get_radius() const { } void CapsuleShape2D::set_height(real_t p_height) { + ERR_FAIL_COND_MSG(p_height < 0, "CapsuleShape2D height cannot be negative."); height = p_height; if (radius > height * 0.5) { radius = height * 0.5; diff --git a/scene/resources/capsule_shape_3d.cpp b/scene/resources/capsule_shape_3d.cpp index c16ddad984..2179ce82dd 100644 --- a/scene/resources/capsule_shape_3d.cpp +++ b/scene/resources/capsule_shape_3d.cpp @@ -79,6 +79,7 @@ void CapsuleShape3D::_update_shape() { } void CapsuleShape3D::set_radius(float p_radius) { + ERR_FAIL_COND_MSG(p_radius < 0, "CapsuleShape3D radius cannot be negative."); radius = p_radius; if (radius > height * 0.5) { height = radius * 2.0; @@ -92,6 +93,7 @@ float CapsuleShape3D::get_radius() const { } void CapsuleShape3D::set_height(float p_height) { + ERR_FAIL_COND_MSG(p_height < 0, "CapsuleShape3D height cannot be negative."); height = p_height; if (radius > height * 0.5) { radius = height * 0.5; diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp index 9c16ac2eed..de931fca7e 100644 --- a/scene/resources/circle_shape_2d.cpp +++ b/scene/resources/circle_shape_2d.cpp @@ -43,6 +43,7 @@ void CircleShape2D::_update_shape() { } void CircleShape2D::set_radius(real_t p_radius) { + ERR_FAIL_COND_MSG(p_radius < 0, "CircleShape2D radius cannot be negative."); radius = p_radius; _update_shape(); } diff --git a/scene/resources/cylinder_shape_3d.cpp b/scene/resources/cylinder_shape_3d.cpp index 5eeb62d17b..c4f1cba341 100644 --- a/scene/resources/cylinder_shape_3d.cpp +++ b/scene/resources/cylinder_shape_3d.cpp @@ -72,6 +72,7 @@ void CylinderShape3D::_update_shape() { } void CylinderShape3D::set_radius(float p_radius) { + ERR_FAIL_COND_MSG(p_radius < 0, "CylinderShape3D radius cannot be negative."); radius = p_radius; _update_shape(); notify_change_to_owners(); @@ -82,6 +83,7 @@ float CylinderShape3D::get_radius() const { } void CylinderShape3D::set_height(float p_height) { + ERR_FAIL_COND_MSG(p_height < 0, "CylinderShape3D height cannot be negative."); height = p_height; _update_shape(); notify_change_to_owners(); diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index 92ab091b86..30deb5ccd5 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -275,6 +275,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX]; Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL]; Vector<Vector2> uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV]; + Vector<Vector2> uv2s = surfaces[i].arrays[RS::ARRAY_TEX_UV2]; unsigned int index_count = indices.size(); unsigned int vertex_count = vertices.size(); @@ -287,7 +288,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli const int *indices_ptr = indices.ptr(); if (normals.is_empty()) { - normals.resize(vertices.size()); + normals.resize(index_count); Vector3 *n_ptr = normals.ptrw(); for (unsigned int j = 0; j < index_count; j += 3) { const Vector3 &v0 = vertices_ptr[indices_ptr[j + 0]]; @@ -313,6 +314,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli LocalVector<Vector3> merged_normals; LocalVector<int> merged_normals_counts; const Vector2 *uvs_ptr = uvs.ptr(); + const Vector2 *uv2s_ptr = uv2s.ptr(); for (unsigned int j = 0; j < vertex_count; j++) { const Vector3 &v = vertices_ptr[j]; @@ -327,8 +329,10 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli for (unsigned int k = 0; k < close_verts.size(); k++) { const Pair<int, int> &idx = close_verts[k]; - // TODO check more attributes? - if ((!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2) && normals[idx.second].dot(n) > normal_merge_threshold) { + bool is_uvs_close = (!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2); + bool is_uv2s_close = (!uv2s_ptr || uv2s_ptr[j].distance_squared_to(uv2s_ptr[idx.second]) < CMP_EPSILON2); + bool is_normals_close = normals[idx.second].dot(n) > normal_merge_threshold; + if (is_uvs_close && is_uv2s_close && is_normals_close) { vertex_remap.push_back(idx.first); merged_normals[idx.first] += normals[idx.second]; merged_normals_counts[idx.first]++; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index b74f44c52f..6fb0c0468d 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -330,7 +330,7 @@ void BaseMaterial3D::init_shaders() { shader_names->rim = "rim"; shader_names->rim_tint = "rim_tint"; shader_names->clearcoat = "clearcoat"; - shader_names->clearcoat_gloss = "clearcoat_gloss"; + shader_names->clearcoat_roughness = "clearcoat_roughness"; shader_names->anisotropy = "anisotropy_ratio"; shader_names->heightmap_scale = "heightmap_scale"; shader_names->subsurface_scattering_strength = "subsurface_scattering_strength"; @@ -541,12 +541,6 @@ void BaseMaterial3D::_update_shader() { case SPECULAR_SCHLICK_GGX: code += ",specular_schlick_ggx"; break; - case SPECULAR_BLINN: - code += ",specular_blinn"; - break; - case SPECULAR_PHONG: - code += ",specular_phong"; - break; case SPECULAR_TOON: code += ",specular_toon"; break; @@ -690,7 +684,7 @@ void BaseMaterial3D::_update_shader() { } if (features[FEATURE_CLEARCOAT]) { code += "uniform float clearcoat : hint_range(0,1);\n"; - code += "uniform float clearcoat_gloss : hint_range(0,1);\n"; + code += "uniform float clearcoat_roughness : hint_range(0,1);\n"; code += "uniform sampler2D texture_clearcoat : hint_white," + texfilter_str + ";\n"; } if (features[FEATURE_ANISOTROPY]) { @@ -1166,7 +1160,7 @@ void BaseMaterial3D::_update_shader() { code += " vec2 clearcoat_tex = texture(texture_clearcoat,base_uv).xy;\n"; } code += " CLEARCOAT = clearcoat*clearcoat_tex.x;"; - code += " CLEARCOAT_GLOSS = clearcoat_gloss*clearcoat_tex.y;\n"; + code += " CLEARCOAT_ROUGHNESS = clearcoat_roughness*clearcoat_tex.y;\n"; } if (features[FEATURE_ANISOTROPY]) { @@ -1408,13 +1402,13 @@ float BaseMaterial3D::get_clearcoat() const { return clearcoat; } -void BaseMaterial3D::set_clearcoat_gloss(float p_clearcoat_gloss) { - clearcoat_gloss = p_clearcoat_gloss; - RS::get_singleton()->material_set_param(_get_material(), shader_names->clearcoat_gloss, p_clearcoat_gloss); +void BaseMaterial3D::set_clearcoat_roughness(float p_clearcoat_roughness) { + clearcoat_roughness = p_clearcoat_roughness; + RS::get_singleton()->material_set_param(_get_material(), shader_names->clearcoat_roughness, p_clearcoat_roughness); } -float BaseMaterial3D::get_clearcoat_gloss() const { - return clearcoat_gloss; +float BaseMaterial3D::get_clearcoat_roughness() const { + return clearcoat_roughness; } void BaseMaterial3D::set_anisotropy(float p_anisotropy) { @@ -2271,8 +2265,8 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_clearcoat", "clearcoat"), &BaseMaterial3D::set_clearcoat); ClassDB::bind_method(D_METHOD("get_clearcoat"), &BaseMaterial3D::get_clearcoat); - ClassDB::bind_method(D_METHOD("set_clearcoat_gloss", "clearcoat_gloss"), &BaseMaterial3D::set_clearcoat_gloss); - ClassDB::bind_method(D_METHOD("get_clearcoat_gloss"), &BaseMaterial3D::get_clearcoat_gloss); + ClassDB::bind_method(D_METHOD("set_clearcoat_roughness", "clearcoat_roughness"), &BaseMaterial3D::set_clearcoat_roughness); + ClassDB::bind_method(D_METHOD("get_clearcoat_roughness"), &BaseMaterial3D::get_clearcoat_roughness); ClassDB::bind_method(D_METHOD("set_anisotropy", "anisotropy"), &BaseMaterial3D::set_anisotropy); ClassDB::bind_method(D_METHOD("get_anisotropy"), &BaseMaterial3D::get_anisotropy); @@ -2438,7 +2432,7 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Shading", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "shading_mode", PROPERTY_HINT_ENUM, "Unshaded,Per-Pixel,Per-Vertex"), "set_shading_mode", "get_shading_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "diffuse_mode", PROPERTY_HINT_ENUM, "Burley,Lambert,Lambert Wrap,Toon"), "set_diffuse_mode", "get_diffuse_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "specular_mode", PROPERTY_HINT_ENUM, "SchlickGGX,Blinn,Phong,Toon,Disabled"), "set_specular_mode", "get_specular_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "specular_mode", PROPERTY_HINT_ENUM, "SchlickGGX,Toon,Disabled"), "set_specular_mode", "get_specular_mode"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "disable_ambient_light"), "set_flag", "get_flag", FLAG_DISABLE_AMBIENT_LIGHT); ADD_GROUP("Vertex Color", "vertex_color"); @@ -2486,7 +2480,7 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Clearcoat", "clearcoat_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "clearcoat_enabled"), "set_feature", "get_feature", FEATURE_CLEARCOAT); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "clearcoat", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_clearcoat", "get_clearcoat"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "clearcoat_gloss", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_clearcoat_gloss", "get_clearcoat_gloss"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "clearcoat_roughness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_clearcoat_roughness", "get_clearcoat_roughness"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "clearcoat_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_CLEARCOAT); ADD_GROUP("Anisotropy", "anisotropy_"); @@ -2693,8 +2687,6 @@ void BaseMaterial3D::_bind_methods() { BIND_ENUM_CONSTANT(DIFFUSE_TOON); BIND_ENUM_CONSTANT(SPECULAR_SCHLICK_GGX); - BIND_ENUM_CONSTANT(SPECULAR_BLINN); - BIND_ENUM_CONSTANT(SPECULAR_PHONG); BIND_ENUM_CONSTANT(SPECULAR_TOON); BIND_ENUM_CONSTANT(SPECULAR_DISABLED); @@ -2732,7 +2724,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_rim(1.0); set_rim_tint(0.5); set_clearcoat(1); - set_clearcoat_gloss(0.5); + set_clearcoat_roughness(0.5); set_anisotropy(0); set_heightmap_scale(0.05); set_subsurface_scattering_strength(0); diff --git a/scene/resources/material.h b/scene/resources/material.h index 57591bee2f..a79f072f9a 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -252,8 +252,6 @@ public: enum SpecularMode { SPECULAR_SCHLICK_GGX, - SPECULAR_BLINN, - SPECULAR_PHONG, SPECULAR_TOON, SPECULAR_DISABLED, SPECULAR_MAX @@ -387,7 +385,7 @@ private: StringName rim; StringName rim_tint; StringName clearcoat; - StringName clearcoat_gloss; + StringName clearcoat_roughness; StringName anisotropy; StringName heightmap_scale; StringName subsurface_scattering_strength; @@ -454,7 +452,7 @@ private: float rim; float rim_tint; float clearcoat; - float clearcoat_gloss; + float clearcoat_roughness; float anisotropy; float heightmap_scale; float subsurface_scattering_strength; @@ -572,8 +570,8 @@ public: void set_clearcoat(float p_clearcoat); float get_clearcoat() const; - void set_clearcoat_gloss(float p_clearcoat_gloss); - float get_clearcoat_gloss() const; + void set_clearcoat_roughness(float p_clearcoat_roughness); + float get_clearcoat_roughness() const; void set_anisotropy(float p_anisotropy); float get_anisotropy() const; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 441e84eccc..6b44b05e02 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -1684,6 +1684,7 @@ Error ArrayMesh::lightmap_unwrap(const Transform3D &p_base_transform, float p_te Error ArrayMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache) { ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes."); + ERR_FAIL_COND_V_MSG(p_texel_size <= 0.0f, ERR_PARAMETER_RANGE_ERROR, "Texel size must be greater than 0."); LocalVector<float> vertices; LocalVector<float> normals; diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp index 27659f724e..5e88c9974c 100644 --- a/scene/resources/rectangle_shape_2d.cpp +++ b/scene/resources/rectangle_shape_2d.cpp @@ -58,6 +58,7 @@ bool RectangleShape2D::_get(const StringName &p_name, Variant &r_property) const #endif // DISABLE_DEPRECATED void RectangleShape2D::set_size(const Vector2 &p_size) { + ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0, "RectangleShape2D size cannot be negative."); size = p_size; _update_shape(); } diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index c03faa2c2d..d9ac967699 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -153,7 +153,7 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R RES res = ResourceLoader::load_threaded_get(path); if (res.is_null()) { if (ResourceLoader::get_abort_on_missing_resources()) { - error = ERR_FILE_CORRUPT; + error = ERR_FILE_MISSING_DEPENDENCIES; error_text = "[ext_resource] referenced nonexistent resource at: " + path; _printerr(); return error; @@ -165,7 +165,7 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R r_res = res; } } else { - error = ERR_FILE_CORRUPT; + error = ERR_FILE_MISSING_DEPENDENCIES; error_text = "[ext_resource] referenced non-loaded resource at: " + path; _printerr(); return error; @@ -265,7 +265,9 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &parser); if (error) { - if (error != ERR_FILE_EOF) { + if (error == ERR_FILE_MISSING_DEPENDENCIES) { + // Resource loading error, just skip it. + } else if (error != ERR_FILE_EOF) { _printerr(); return Ref<PackedScene>(); } else { diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp index 917aa40934..9cb6a16f5c 100644 --- a/scene/resources/sky.cpp +++ b/scene/resources/sky.cpp @@ -83,7 +83,7 @@ void Sky::_bind_methods() { ClassDB::bind_method(D_METHOD("get_material"), &Sky::get_material); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sky_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,PanoramaSkyMaterial,ProceduralSkyMaterial,PhysicalSkyMaterial"), "set_material", "get_material"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Automatic,High Quality (Slow),High Quality Incremental (Average),Real-Time (Fast)"), "set_process_mode", "get_process_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Automatic,HighQuality,HighQualityIncremental,RealTime"), "set_process_mode", "get_process_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "radiance_size", PROPERTY_HINT_ENUM, "32,64,128,256,512,1024,2048"), "set_radiance_size", "get_radiance_size"); BIND_ENUM_CONSTANT(RADIANCE_SIZE_32); diff --git a/scene/resources/sky.h b/scene/resources/sky.h index 3653568ac6..5e52239032 100644 --- a/scene/resources/sky.h +++ b/scene/resources/sky.h @@ -59,7 +59,7 @@ public: private: RID sky; - ProcessMode mode = PROCESS_MODE_REALTIME; + ProcessMode mode = PROCESS_MODE_AUTOMATIC; RadianceSize radiance_size = RADIANCE_SIZE_256; Ref<Material> sky_material; diff --git a/scene/resources/sphere_shape_3d.cpp b/scene/resources/sphere_shape_3d.cpp index ee789362c9..8282992401 100644 --- a/scene/resources/sphere_shape_3d.cpp +++ b/scene/resources/sphere_shape_3d.cpp @@ -63,6 +63,7 @@ void SphereShape3D::_update_shape() { } void SphereShape3D::set_radius(float p_radius) { + ERR_FAIL_COND_MSG(p_radius < 0, "SphereShape3D radius cannot be negative."); radius = p_radius; _update_shape(); notify_change_to_owners(); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 1e84947b87..1174117028 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -4432,6 +4432,10 @@ void TileSetAtlasSource::_update_padded_texture() { Ref<Image> src = texture->get_image(); + if (!src.is_valid()) { + return; + } + Ref<Image> image; image.instantiate(); image->create(size.x, size.y, false, src->get_format()); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index dae61c8609..47ca643029 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -3163,7 +3163,7 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim_tint", "RIM_TINT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat", "CLEARCOAT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat_gloss", "CLEARCOAT_GLOSS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat_roughness", "CLEARCOAT_ROUGHNESS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "anisotropy_flow", "ANISOTROPY_FLOW" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" }, diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index f2479199ee..a6aa6d8c49 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -2843,8 +2843,7 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade code += " vec3 c = " + p_input_vars[0] + ";\n"; code += " float max1 = max(c.r, c.g);\n"; code += " float max2 = max(max1, c.b);\n"; - code += " float max3 = max(max1, max2);\n"; - code += " " + p_output_vars[0] + " = vec3(max3, max3, max3);\n"; + code += " " + p_output_vars[0] + " = vec3(max2, max2, max2);\n"; code += " }\n"; break; case FUNC_SEPIA: |