diff options
Diffstat (limited to 'scene')
-rw-r--r-- | scene/2d/camera_2d.cpp | 4 | ||||
-rw-r--r-- | scene/2d/tile_map.cpp | 85 | ||||
-rw-r--r-- | scene/2d/tile_map.h | 4 | ||||
-rw-r--r-- | scene/2d/tile_map_layer.cpp | 96 | ||||
-rw-r--r-- | scene/2d/tile_map_layer.h | 4 | ||||
-rw-r--r-- | scene/3d/navigation_region_3d.cpp | 4 | ||||
-rw-r--r-- | scene/3d/skeleton_3d.cpp | 11 | ||||
-rw-r--r-- | scene/gui/button.cpp | 115 | ||||
-rw-r--r-- | scene/gui/button.h | 12 | ||||
-rw-r--r-- | scene/gui/check_box.cpp | 8 | ||||
-rw-r--r-- | scene/gui/check_button.cpp | 8 | ||||
-rw-r--r-- | scene/gui/option_button.cpp | 2 | ||||
-rw-r--r-- | scene/gui/popup.cpp | 12 | ||||
-rw-r--r-- | scene/gui/popup_menu.cpp | 7 | ||||
-rw-r--r-- | scene/gui/text_edit.cpp | 105 | ||||
-rw-r--r-- | scene/gui/tree.cpp | 26 | ||||
-rw-r--r-- | scene/gui/tree.h | 1 | ||||
-rw-r--r-- | scene/main/instance_placeholder.cpp | 131 | ||||
-rw-r--r-- | scene/main/instance_placeholder.h | 4 | ||||
-rw-r--r-- | scene/resources/packed_scene.cpp | 10 | ||||
-rw-r--r-- | scene/resources/portable_compressed_texture.cpp | 6 | ||||
-rw-r--r-- | scene/resources/shader.cpp | 4 | ||||
-rw-r--r-- | scene/theme/SCsub | 1 | ||||
-rw-r--r-- | scene/theme/icons/SCsub | 1 |
24 files changed, 455 insertions, 206 deletions
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 822f1b58fd..18ef2d8505 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -54,16 +54,18 @@ void Camera2D::_update_scroll() { if (is_current()) { ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id)); + Size2 screen_size = _get_camera_screen_size(); + Transform2D xform; if (is_physics_interpolated_and_enabled()) { xform = _interpolation_data.xform_prev.interpolate_with(_interpolation_data.xform_curr, Engine::get_singleton()->get_physics_interpolation_fraction()); + camera_screen_center = xform.affine_inverse().xform(0.5 * screen_size); } else { xform = get_camera_transform(); } viewport->set_canvas_transform(xform); - Size2 screen_size = _get_camera_screen_size(); Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2()); Point2 adj_screen_pos = camera_screen_center - (screen_size * 0.5); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 523d81e874..f7d672620d 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -225,91 +225,6 @@ int TileMap::get_rendering_quadrant_size() const { return rendering_quadrant_size; } -void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_normalized_animation_offset) { - ERR_FAIL_COND(!p_tile_set.is_valid()); - ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); - TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Check for the frame. - if (p_frame >= 0) { - ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords)); - } - - // Get the texture. - Ref<Texture2D> tex = atlas_source->get_runtime_texture(); - if (!tex.is_valid()) { - return; - } - - // Check if we are in the texture, return otherwise. - Vector2i grid_size = atlas_source->get_atlas_grid_size(); - if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { - return; - } - - // Get tile data. - const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile); - - // Get the tile modulation. - Color modulate = tile_data->get_modulate() * p_modulation; - - // Compute the offset. - Vector2 tile_offset = tile_data->get_texture_origin(); - - // Get destination rect. - Rect2 dest_rect; - dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size; - dest_rect.size.x += FP_ADJUST; - dest_rect.size.y += FP_ADJUST; - - bool transpose = tile_data->get_transpose() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); - if (transpose) { - dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); - } else { - dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); - } - - if (tile_data->get_flip_h() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) { - dest_rect.size.x = -dest_rect.size.x; - } - - if (tile_data->get_flip_v() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) { - dest_rect.size.y = -dest_rect.size.y; - } - - // Draw the tile. - if (p_frame >= 0) { - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) { - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - } else { - real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords); - real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed; - real_t animation_offset = p_normalized_animation_offset * animation_duration; - // Accumulate durations unaffected by the speed to avoid accumulating floating point division errors. - // Aka do `sum(duration[i]) / speed` instead of `sum(duration[i] / speed)`. - real_t time_unscaled = 0.0; - for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) { - real_t frame_duration_unscaled = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame); - real_t slice_start = time_unscaled / speed; - real_t slice_end = (time_unscaled + frame_duration_unscaled) / speed; - RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, slice_start, slice_end, animation_offset); - - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - - time_unscaled += frame_duration_unscaled; - } - RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0); - } - } -} - void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { if (p_tileset == tile_set) { return; diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 45604bfb8a..690102f730 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -63,8 +63,6 @@ private: // A compatibility enum to specify how is the data if formatted. mutable TileMapDataFormat format = TileMapDataFormat::TILE_MAP_DATA_FORMAT_3; - static constexpr float FP_ADJUST = 0.00001; - // Properties. Ref<TileSet> tile_set; int rendering_quadrant_size = 16; @@ -123,8 +121,6 @@ public: void set_rendering_quadrant_size(int p_size); int get_rendering_quadrant_size() const; - static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0); - // Accessors. void set_tileset(const Ref<TileSet> &p_tileset); Ref<TileSet> get_tileset() const; diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index 03fd7364c0..0ac236eaa7 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -340,7 +340,7 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) { } // Drawing the tile in the canvas item. - TileMap::draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, get_self_modulate(), tile_data, random_animation_offset); + draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, get_self_modulate(), tile_data, random_animation_offset); } // Reset physics interpolation for any recreated canvas items. @@ -603,6 +603,7 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) { rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder(occlusion_layer_index, flip_h, flip_v, transpose)->get_rid()); rs->canvas_light_occluder_attach_to_canvas(occluder, get_canvas()); rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index)); + rs->canvas_light_occluder_set_as_sdf_collision(occluder, tile_set->get_occlusion_layer_sdf_collision(occlusion_layer_index)); } else { // Clear occluder. if (occluder.is_valid()) { @@ -1695,18 +1696,18 @@ void TileMapLayer::_notification(int p_what) { _internal_update(true); } break; - case TileMap::NOTIFICATION_ENTER_CANVAS: { + case NOTIFICATION_ENTER_CANVAS: { dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true; _queue_internal_update(); } break; - case TileMap::NOTIFICATION_EXIT_CANVAS: { + case NOTIFICATION_EXIT_CANVAS: { dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true; // Update immediately on exiting, and force cleanup. _internal_update(true); } break; - case TileMap::NOTIFICATION_VISIBILITY_CHANGED: { + case NOTIFICATION_VISIBILITY_CHANGED: { dirty.flags[DIRTY_FLAGS_LAYER_VISIBILITY] = true; _queue_internal_update(); } break; @@ -2161,6 +2162,91 @@ TileMapCell TileMapLayer::get_cell(const Vector2i &p_coords) const { } } +void TileMapLayer::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_normalized_animation_offset) { + ERR_FAIL_COND(p_tile_set.is_null()); + ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); + TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Check for the frame. + if (p_frame >= 0) { + ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords)); + } + + // Get the texture. + Ref<Texture2D> tex = atlas_source->get_runtime_texture(); + if (tex.is_null()) { + return; + } + + // Check if we are in the texture, return otherwise. + Vector2i grid_size = atlas_source->get_atlas_grid_size(); + if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { + return; + } + + // Get tile data. + const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile); + + // Get the tile modulation. + Color modulate = tile_data->get_modulate() * p_modulation; + + // Compute the offset. + Vector2 tile_offset = tile_data->get_texture_origin(); + + // Get destination rect. + Rect2 dest_rect; + dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size; + dest_rect.size.x += FP_ADJUST; + dest_rect.size.y += FP_ADJUST; + + bool transpose = tile_data->get_transpose() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + if (transpose) { + dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); + } else { + dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); + } + + if (tile_data->get_flip_h() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) { + dest_rect.size.x = -dest_rect.size.x; + } + + if (tile_data->get_flip_v() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) { + dest_rect.size.y = -dest_rect.size.y; + } + + // Draw the tile. + if (p_frame >= 0) { + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) { + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else { + real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords); + real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed; + real_t animation_offset = p_normalized_animation_offset * animation_duration; + // Accumulate durations unaffected by the speed to avoid accumulating floating point division errors. + // Aka do `sum(duration[i]) / speed` instead of `sum(duration[i] / speed)`. + real_t time_unscaled = 0.0; + for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) { + real_t frame_duration_unscaled = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame); + real_t slice_start = time_unscaled / speed; + real_t slice_end = (time_unscaled + frame_duration_unscaled) / speed; + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, slice_start, slice_end, animation_offset); + + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + + time_unscaled += frame_duration_unscaled; + } + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0); + } + } +} + void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile) { // Set the current cell tile (using integer position). Vector2i pk(p_coords); @@ -2211,7 +2297,7 @@ void TileMapLayer::erase_cell(const Vector2i &p_coords) { } void TileMapLayer::fix_invalid_tiles() { - ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot call fix_invalid_tiles() on a TileMap without a valid TileSet."); + ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot call fix_invalid_tiles() on a TileMapLayer without a valid TileSet."); RBSet<Vector2i> coords; for (const KeyValue<Vector2i, CellData> &E : tile_map_layer_data) { diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h index 5861433c8a..57c83d7c4c 100644 --- a/scene/2d/tile_map_layer.h +++ b/scene/2d/tile_map_layer.h @@ -269,6 +269,8 @@ public: }; private: + static constexpr float FP_ADJUST = 0.00001; + // Properties. HashMap<Vector2i, CellData> tile_map_layer_data; @@ -407,6 +409,8 @@ public: // Not exposed to users. TileMapCell get_cell(const Vector2i &p_coords) const; + static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0); + ////////////// Exposed functions ////////////// // --- Cells manipulation --- diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 856c52b5d5..40e04f0fb4 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -474,7 +474,7 @@ void NavigationRegion3D::_update_debug_mesh() { return; } - if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { + if (!NavigationServer3D::get_singleton()->get_debug_enabled() || !NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { if (debug_instance.is_valid()) { RS::get_singleton()->instance_set_visible(debug_instance, false); } @@ -640,7 +640,7 @@ void NavigationRegion3D::_update_debug_mesh() { #ifdef DEBUG_ENABLED void NavigationRegion3D::_update_debug_edge_connections_mesh() { - if (!NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { + if (!NavigationServer3D::get_singleton()->get_debug_enabled() || !NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) { if (debug_edge_connections_instance.is_valid()) { RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false); } diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 498e101b3c..a4804e928a 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -847,12 +847,13 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { ERR_FAIL_INDEX(p_bone_idx, bone_size); Bone *bonesptr = bones.ptrw(); - List<int> bones_to_process = List<int>(); + thread_local LocalVector<int> bones_to_process; + bones_to_process.clear(); bones_to_process.push_back(p_bone_idx); - while (bones_to_process.size() > 0) { - int current_bone_idx = bones_to_process.front()->get(); - bones_to_process.erase(current_bone_idx); + uint32_t index = 0; + while (index < bones_to_process.size()) { + int current_bone_idx = bones_to_process[index]; Bone &b = bonesptr[current_bone_idx]; bool bone_enabled = b.enabled && !show_rest_only; @@ -905,6 +906,8 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { for (int i = 0; i < child_bone_size; i++) { bones_to_process.push_back(b.child_bones[i]); } + + index++; } } diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 15b7f62036..3bf9d79c7f 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -50,6 +50,83 @@ void Button::_set_internal_margin(Side p_side, float p_value) { void Button::_queue_update_size_cache() { } +void Button::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + const bool rtl = is_layout_rtl(); + if (rtl && has_theme_stylebox(SNAME("normal_mirrored"))) { + theme_cache.max_style_size = theme_cache.normal_mirrored->get_minimum_size(); + theme_cache.style_margin_left = theme_cache.normal_mirrored->get_margin(SIDE_LEFT); + theme_cache.style_margin_right = theme_cache.normal_mirrored->get_margin(SIDE_RIGHT); + theme_cache.style_margin_top = theme_cache.normal_mirrored->get_margin(SIDE_TOP); + theme_cache.style_margin_bottom = theme_cache.normal_mirrored->get_margin(SIDE_BOTTOM); + } else { + theme_cache.max_style_size = theme_cache.normal->get_minimum_size(); + theme_cache.style_margin_left = theme_cache.normal->get_margin(SIDE_LEFT); + theme_cache.style_margin_right = theme_cache.normal->get_margin(SIDE_RIGHT); + theme_cache.style_margin_top = theme_cache.normal->get_margin(SIDE_TOP); + theme_cache.style_margin_bottom = theme_cache.normal->get_margin(SIDE_BOTTOM); + } + if (has_theme_stylebox("hover_pressed")) { + if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover_pressed_mirrored->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover_pressed_mirrored->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover_pressed_mirrored->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover_pressed_mirrored->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover_pressed_mirrored->get_margin(SIDE_BOTTOM)); + } else { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover_pressed->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover_pressed->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover_pressed->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover_pressed->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover_pressed->get_margin(SIDE_BOTTOM)); + } + } + if (rtl && has_theme_stylebox(SNAME("pressed_mirrored"))) { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.pressed_mirrored->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.pressed_mirrored->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.pressed_mirrored->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.pressed_mirrored->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.pressed_mirrored->get_margin(SIDE_BOTTOM)); + } else { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.pressed->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.pressed->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.pressed->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.pressed->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.pressed->get_margin(SIDE_BOTTOM)); + } + if (rtl && has_theme_stylebox(SNAME("hover_mirrored"))) { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover_mirrored->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover_mirrored->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover_mirrored->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover_mirrored->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover_mirrored->get_margin(SIDE_BOTTOM)); + } else { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover->get_margin(SIDE_BOTTOM)); + } + if (rtl && has_theme_stylebox(SNAME("disabled_mirrored"))) { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.disabled_mirrored->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.disabled_mirrored->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.disabled_mirrored->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.disabled_mirrored->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.disabled_mirrored->get_margin(SIDE_BOTTOM)); + } else { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.disabled->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.disabled->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.disabled->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.disabled->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.disabled->get_margin(SIDE_BOTTOM)); + } +} + +Size2 Button::_get_largest_stylebox_size() const { + return theme_cache.max_style_size; +} + Ref<StyleBox> Button::_get_current_stylebox() const { Ref<StyleBox> stylebox = theme_cache.normal; const bool rtl = is_layout_rtl(); @@ -137,16 +214,13 @@ void Button::_notification(int p_what) { const RID ci = get_canvas_item(); const Size2 size = get_size(); - const Ref<StyleBox> style = _get_current_stylebox(); - { // Draws the stylebox in the current state. - if (!flat) { - style->draw(ci, Rect2(Point2(), size)); - } + // Draws the stylebox in the current state. + if (!flat) { + _get_current_stylebox()->draw(ci, Rect2(Point2(), size)); + } - if (has_focus()) { - Ref<StyleBox> style2 = theme_cache.focus; - style2->draw(ci, Rect2(Point2(), size)); - } + if (has_focus()) { + theme_cache.focus->draw(ci, Rect2(Point2(), size)); } Ref<Texture2D> _icon = icon; @@ -158,16 +232,11 @@ void Button::_notification(int p_what) { break; } - const float style_margin_left = style->get_margin(SIDE_LEFT); - const float style_margin_right = style->get_margin(SIDE_RIGHT); - const float style_margin_top = style->get_margin(SIDE_TOP); - const float style_margin_bottom = style->get_margin(SIDE_BOTTOM); - Size2 drawable_size_remained = size; { // The size after the stelybox is stripped. - drawable_size_remained.width -= style_margin_left + style_margin_right; - drawable_size_remained.height -= style_margin_top + style_margin_bottom; + drawable_size_remained.width -= theme_cache.style_margin_left + theme_cache.style_margin_right; + drawable_size_remained.height -= theme_cache.style_margin_top + theme_cache.style_margin_bottom; } const int h_separation = MAX(0, theme_cache.h_separation); @@ -312,12 +381,12 @@ void Button::_notification(int p_what) { [[fallthrough]]; case HORIZONTAL_ALIGNMENT_FILL: case HORIZONTAL_ALIGNMENT_LEFT: { - icon_ofs.x += style_margin_left; + icon_ofs.x += theme_cache.style_margin_left; icon_ofs.x += left_internal_margin_with_h_separation; } break; case HORIZONTAL_ALIGNMENT_RIGHT: { - icon_ofs.x = size.x - style_margin_right; + icon_ofs.x = size.x - theme_cache.style_margin_right; icon_ofs.x -= right_internal_margin_with_h_separation; icon_ofs.x -= icon_size.width; } break; @@ -330,11 +399,11 @@ void Button::_notification(int p_what) { [[fallthrough]]; case VERTICAL_ALIGNMENT_FILL: case VERTICAL_ALIGNMENT_TOP: { - icon_ofs.y += style_margin_top; + icon_ofs.y += theme_cache.style_margin_top; } break; case VERTICAL_ALIGNMENT_BOTTOM: { - icon_ofs.y = size.y - style_margin_bottom - icon_size.height; + icon_ofs.y = size.y - theme_cache.style_margin_bottom - icon_size.height; } break; } icon_ofs = icon_ofs.floor(); @@ -373,7 +442,7 @@ void Button::_notification(int p_what) { case HORIZONTAL_ALIGNMENT_FILL: case HORIZONTAL_ALIGNMENT_LEFT: case HORIZONTAL_ALIGNMENT_RIGHT: { - text_ofs.x += style_margin_left; + text_ofs.x += theme_cache.style_margin_left; text_ofs.x += left_internal_margin_with_h_separation; if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) { // Offset by the space's width that occupied by icon and h_separation together. @@ -382,7 +451,7 @@ void Button::_notification(int p_what) { } break; } - text_ofs.y = (drawable_size_remained.height - text_buf->get_size().height) / 2.0f + style_margin_top; + text_ofs.y = (drawable_size_remained.height - text_buf->get_size().height) / 2.0f + theme_cache.style_margin_top; if (vertical_icon_alignment == VERTICAL_ALIGNMENT_TOP) { text_ofs.y += custom_element_size.height - drawable_size_remained.height; // Offset by the icon's height. } @@ -452,7 +521,7 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu } } - return _get_current_stylebox()->get_minimum_size() + minsize; + return _get_largest_stylebox_size() + minsize; } void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) { diff --git a/scene/gui/button.h b/scene/gui/button.h index 86fdbd35d5..eefb690913 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -69,6 +69,12 @@ private: Ref<StyleBox> disabled_mirrored; Ref<StyleBox> focus; + Size2 max_style_size; + float style_margin_left = 0; + float style_margin_right = 0; + float style_margin_top = 0; + float style_margin_bottom = 0; + Color font_color; Color font_focus_color; Color font_pressed_color; @@ -94,16 +100,18 @@ private: int icon_max_width = 0; } theme_cache; - Size2 _fit_icon_size(const Size2 &p_size) const; - void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = ""); void _texture_changed(); protected: + virtual void _update_theme_item_cache() override; + void _set_internal_margin(Side p_side, float p_value); virtual void _queue_update_size_cache(); + Size2 _fit_icon_size(const Size2 &p_size) const; Ref<StyleBox> _get_current_stylebox() const; + Size2 _get_largest_stylebox_size() const; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp index af6696834e..99937aaf41 100644 --- a/scene/gui/check_box.cpp +++ b/scene/gui/check_box.cpp @@ -59,14 +59,14 @@ Size2 CheckBox::get_icon_size() const { if (!theme_cache.radio_unchecked_disabled.is_null()) { tex_size = tex_size.max(theme_cache.radio_unchecked_disabled->get_size()); } - return tex_size; + return _fit_icon_size(tex_size); } Size2 CheckBox::get_minimum_size() const { Size2 minsize = Button::get_minimum_size(); const Size2 tex_size = get_icon_size(); if (tex_size.width > 0 || tex_size.height > 0) { - const Size2 padding = _get_current_stylebox()->get_minimum_size(); + const Size2 padding = _get_largest_stylebox_size(); Size2 content_size = minsize - padding; if (content_size.width > 0 && tex_size.width > 0) { content_size.width += MAX(0, theme_cache.h_separation); @@ -127,9 +127,9 @@ void CheckBox::_notification(int p_what) { ofs.y = int((get_size().height - get_icon_size().height) / 2) + theme_cache.check_v_offset; if (is_pressed()) { - on_tex->draw(ci, ofs); + on_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(on_tex->get_size()))); } else { - off_tex->draw(ci, ofs); + off_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(off_tex->get_size()))); } } break; } diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index ab3b74a3c3..29b9504776 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -63,14 +63,14 @@ Size2 CheckButton::get_icon_size() const { tex_size = tex_size.max(off_tex->get_size()); } - return tex_size; + return _fit_icon_size(tex_size); } Size2 CheckButton::get_minimum_size() const { Size2 minsize = Button::get_minimum_size(); const Size2 tex_size = get_icon_size(); if (tex_size.width > 0 || tex_size.height > 0) { - const Size2 padding = _get_current_stylebox()->get_minimum_size(); + const Size2 padding = _get_largest_stylebox_size(); Size2 content_size = minsize - padding; if (content_size.width > 0 && tex_size.width > 0) { content_size.width += MAX(0, theme_cache.h_separation); @@ -134,9 +134,9 @@ void CheckButton::_notification(int p_what) { ofs.y = (get_size().height - tex_size.height) / 2 + theme_cache.check_v_offset; if (is_pressed()) { - on_tex->draw(ci, ofs); + on_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(on_tex->get_size()))); } else { - off_tex->draw(ci, ofs); + off_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(off_tex->get_size()))); } } break; } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 0f161a014a..68e72ea996 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -60,7 +60,7 @@ Size2 OptionButton::get_minimum_size() const { } if (has_theme_icon(SNAME("arrow"))) { - const Size2 padding = _get_current_stylebox()->get_minimum_size(); + const Size2 padding = _get_largest_stylebox_size(); const Size2 arrow_size = theme_cache.arrow_icon->get_size(); Size2 content_size = minsize - padding; diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 1d45a10d2a..38204af6d5 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -37,6 +37,7 @@ void Popup::_input_from_window(const Ref<InputEvent> &p_event) { if (get_flag(FLAG_POPUP) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) { + hide_reason = HIDE_REASON_CANCELED; // ESC pressed, mark as canceled unconditionally. _close_pressed(); } Window::_input_from_window(p_event); @@ -104,13 +105,18 @@ void Popup::_notification(int p_what) { case NOTIFICATION_WM_CLOSE_REQUEST: { if (!is_in_edited_scene_root()) { - hide_reason = HIDE_REASON_UNFOCUSED; + if (hide_reason == HIDE_REASON_NONE) { + hide_reason = HIDE_REASON_UNFOCUSED; + } _close_pressed(); } } break; case NOTIFICATION_APPLICATION_FOCUS_OUT: { if (!is_in_edited_scene_root() && get_flag(FLAG_POPUP)) { + if (hide_reason == HIDE_REASON_NONE) { + hide_reason = HIDE_REASON_UNFOCUSED; + } _close_pressed(); } } break; @@ -119,7 +125,9 @@ void Popup::_notification(int p_what) { void Popup::_parent_focused() { if (popped_up && get_flag(FLAG_POPUP)) { - hide_reason = HIDE_REASON_UNFOCUSED; + if (hide_reason == HIDE_REASON_NONE) { + hide_reason = HIDE_REASON_UNFOCUSED; + } _close_pressed(); } } diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index dbf1137bd6..bdd0102b63 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -1014,9 +1014,6 @@ void PopupMenu::_notification(int p_what) { float pm_delay = pm->get_submenu_popup_delay(); set_submenu_popup_delay(pm_delay); } - if (!is_embedded()) { - set_flag(FLAG_NO_FOCUS, true); - } if (system_menu_id != NativeMenu::INVALID_MENU_ID) { bind_global_menu(); } @@ -2829,6 +2826,8 @@ void PopupMenu::popup(const Rect2i &p_bounds) { if (native) { NativeMenu::get_singleton()->popup(global_menu, (p_bounds != Rect2i()) ? p_bounds.position : get_position()); } else { + set_flag(FLAG_NO_FOCUS, !is_embedded()); + moved = Vector2(); popup_time_msec = OS::get_singleton()->get_ticks_msec(); if (!is_embedded()) { @@ -2856,6 +2855,8 @@ void PopupMenu::set_visible(bool p_visible) { NativeMenu::get_singleton()->popup(global_menu, get_position()); } } else { + set_flag(FLAG_NO_FOCUS, !is_embedded()); + Popup::set_visible(p_visible); } } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 49cfa8a030..69b84da23d 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -558,8 +558,6 @@ void TextEdit::_notification(int p_what) { int visible_rows = get_visible_line_count() + 1; - Color color = !editable ? theme_cache.font_readonly_color : theme_cache.font_color; - if (theme_cache.background_color.a > 0.01) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), theme_cache.background_color); } @@ -734,7 +732,7 @@ void TextEdit::_notification(int p_what) { if (draw_minimap) { int minimap_visible_lines = get_minimap_visible_lines(); int minimap_line_height = (minimap_char_size.y + minimap_line_spacing); - int minimap_tab_size = minimap_char_size.x * text.get_tab_size(); + int tab_size = text.get_tab_size(); // Calculate viewport size and y offset. int viewport_height = (draw_amount - 1) * minimap_line_height; @@ -839,68 +837,74 @@ void TextEdit::_notification(int p_what) { } } - Color previous_color; + Color next_color = current_color; int characters = 0; - int tabs = 0; + int tab_alignment = 0; + int xpos = xmargin_end + 2 + indent_px; for (int j = 0; j < str.length(); j++) { - const Variant *color_data = color_map.getptr(last_wrap_column + j); - if (color_data != nullptr) { - current_color = (color_data->operator Dictionary()).get("color", theme_cache.font_color); - if (!editable) { - current_color.a = theme_cache.font_readonly_color.a; + bool next_is_whitespace = false; + bool next_is_tab = false; + // Get the number of characters to draw together. + for (characters = 0; j + characters < str.length(); characters++) { + int next_char_index = j + characters; + const Variant *color_data = color_map.getptr(last_wrap_column + next_char_index); + if (color_data != nullptr) { + next_color = (color_data->operator Dictionary()).get("color", theme_cache.font_color); + if (!editable) { + next_color.a = theme_cache.font_readonly_color.a; + } + next_color.a *= 0.6; } - } - color = current_color; - - if (j == 0) { - previous_color = color; - } - - int xpos = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * j)) + tabs; - bool out_of_bounds = (xpos >= xmargin_end + minimap_width); - - bool whitespace = is_whitespace(str[j]); - if (!whitespace) { - characters++; - - if (j < str.length() - 1 && color == previous_color && !out_of_bounds) { - continue; + if (characters == 0) { + current_color = next_color; } - - // If we've changed color we are at the start of a new section, therefore we need to go back to the end - // of the previous section to draw it, we'll also add the character back on. - if (color != previous_color) { - characters--; - j--; - - if (str[j] == '\t') { - tabs -= minimap_tab_size; + if (next_color != current_color) { + break; + } + next_is_whitespace = is_whitespace(str[next_char_index]); + if (next_is_whitespace) { + if (str[next_char_index] == '\t') { + next_is_tab = true; } + break; + } + bool out_of_bounds = xpos + minimap_char_size.x * characters >= xmargin_end + minimap_width; + if (out_of_bounds) { + break; } } + if (!next_is_whitespace && characters == 0) { + break; + } if (characters > 0) { - previous_color.a *= 0.6; - // Take one for zero indexing, and if we hit whitespace / the end of a word. - int chars = MAX(0, (j - (characters - 1)) - (whitespace ? 1 : 0)) + 1; - int char_x_ofs = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * chars)) + tabs; if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(size.width - char_x_ofs - minimap_char_size.x * characters, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(size.width - xpos - minimap_char_size.x * characters, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), current_color); } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_x_ofs, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(xpos, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), current_color); } } - if (out_of_bounds) { - break; - } + j += characters - 1; + xpos += minimap_char_size.x * characters; + tab_alignment += characters; - if (str[j] == '\t') { - tabs += minimap_tab_size; + if (next_is_whitespace) { + if (next_is_tab) { + tab_alignment %= tab_size; + xpos += minimap_char_size.x * (tab_size - tab_alignment); + tab_alignment = 0; + } else { + xpos += minimap_char_size.x; + tab_alignment += 1; + } + j += 1; } - previous_color = color; - characters = 0; + if (xpos >= xmargin_end + minimap_width) { + // Out of bounds. + break; + } } } } @@ -1188,6 +1192,7 @@ void TextEdit::_notification(int p_what) { if (!clipped && lookup_symbol_word.length() != 0) { // Highlight word if (is_ascii_alphabet_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '_' || lookup_symbol_word[0] == '.') { + Color highlight_underline_color = !editable ? theme_cache.font_readonly_color : theme_cache.font_color; int lookup_symbol_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); int lookup_symbol_word_len = lookup_symbol_word.length(); while (lookup_symbol_word_col != -1) { @@ -1205,7 +1210,7 @@ void TextEdit::_notification(int p_what) { } rect.position.y += ceil(TS->shaped_text_get_ascent(rid)) + ceil(theme_cache.font->get_underline_position(theme_cache.font_size)); rect.size.y = MAX(1, theme_cache.font->get_underline_thickness(theme_cache.font_size)); - draw_rect(rect, color); + draw_rect(rect, highlight_underline_color); } lookup_symbol_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, lookup_symbol_word_col + lookup_symbol_word_len); @@ -7973,7 +7978,7 @@ void TextEdit::_update_minimap_click() { Point2 mp = get_local_mouse_pos(); int xmargin_end = get_size().width - theme_cache.style_normal->get_margin(SIDE_RIGHT); - if (!dragging_minimap && (mp.x < xmargin_end - minimap_width || mp.y > xmargin_end)) { + if (!dragging_minimap && (mp.x < xmargin_end - minimap_width || mp.x > xmargin_end)) { minimap_clicked = false; return; } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 133515754b..fc5b942918 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3152,8 +3152,12 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int } void Tree::_text_editor_popup_modal_close() { + if (popup_edit_commited) { + return; // Already processed by LineEdit/TextEdit commit. + } + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { - return; + return; // ESC pressed, app focus lost, or forced close from code. } if (value_editor->has_point(value_editor->get_local_mouse_position())) { @@ -3172,9 +3176,18 @@ void Tree::_text_editor_popup_modal_close() { } void Tree::_text_editor_gui_input(const Ref<InputEvent> &p_event) { + if (popup_edit_commited) { + return; // Already processed by _text_editor_popup_modal_close + } + + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { + return; // ESC pressed, app focus lost, or forced close from code. + } + if (p_event->is_action_pressed("ui_text_newline_blank", true)) { accept_event(); } else if (p_event->is_action_pressed("ui_text_newline")) { + popup_edit_commited = true; // End edit popup processing. popup_editor->hide(); _apply_multiline_edit(); accept_event(); @@ -3205,6 +3218,15 @@ void Tree::_apply_multiline_edit() { } void Tree::_line_editor_submit(String p_text) { + if (popup_edit_commited) { + return; // Already processed by _text_editor_popup_modal_close + } + + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { + return; // ESC pressed, app focus lost, or forced close from code. + } + + popup_edit_commited = true; // End edit popup processing. popup_editor->hide(); if (!popup_edited_item) { @@ -4072,6 +4094,7 @@ bool Tree::edit_selected(bool p_force_edit) { if (!popup_editor->is_embedded()) { popup_editor->set_content_scale_factor(popup_scale); } + popup_edit_commited = false; // Start edit popup processing. popup_editor->popup(); popup_editor->child_controls_changed(); @@ -4091,6 +4114,7 @@ bool Tree::edit_selected(bool p_force_edit) { if (!popup_editor->is_embedded()) { popup_editor->set_content_scale_factor(popup_scale); } + popup_edit_commited = false; // Start edit popup processing. popup_editor->popup(); popup_editor->child_controls_changed(); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 311055a2f8..e9c93c6e03 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -479,6 +479,7 @@ private: VBoxContainer *popup_editor_vb = nullptr; + bool popup_edit_commited = true; Popup *popup_editor = nullptr; LineEdit *line_editor = nullptr; TextEdit *text_editor = nullptr; diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp index fe23ca1800..f36bbe9395 100644 --- a/scene/main/instance_placeholder.cpp +++ b/scene/main/instance_placeholder.cpp @@ -88,16 +88,16 @@ Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene if (!ps.is_valid()) { return nullptr; } - Node *scene = ps->instantiate(); - if (!scene) { + Node *instance = ps->instantiate(); + if (!instance) { return nullptr; } - scene->set_name(get_name()); - scene->set_multiplayer_authority(get_multiplayer_authority()); + instance->set_name(get_name()); + instance->set_multiplayer_authority(get_multiplayer_authority()); int pos = get_index(); for (const PropSet &E : stored_values) { - scene->set(E.name, E.value); + set_value_on_instance(this, instance, E); } if (p_replace) { @@ -105,10 +105,125 @@ Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene base->remove_child(this); } - base->add_child(scene); - base->move_child(scene, pos); + base->add_child(instance); + base->move_child(instance, pos); - return scene; + return instance; +} + +// This method will attempt to set the correct values on the placeholder instance +// for regular types this is trivial and unnecessary. +// For nodes however this becomes a bit tricky because they might now have existed until the instantiation, +// so this method will try to find the correct nodes and resolve them. +void InstancePlaceholder::set_value_on_instance(InstancePlaceholder *p_placeholder, Node *p_instance, const PropSet &p_set) { + bool is_valid; + + // If we don't have any info, we can't do anything, + // so try setting the value directly. + Variant current = p_instance->get(p_set.name, &is_valid); + if (!is_valid) { + p_instance->set(p_set.name, p_set.value, &is_valid); + return; + } + + Variant::Type current_type = current.get_type(); + Variant::Type placeholder_type = p_set.value.get_type(); + + // Arrays are a special case, because their containing type might be different. + if (current_type != Variant::Type::ARRAY) { + // Check if the variant types match. + if (Variant::evaluate(Variant::OP_EQUAL, current_type, placeholder_type)) { + p_instance->set(p_set.name, p_set.value, &is_valid); + if (is_valid) { + return; + } + // Types match but setting failed? This is strange, so let's print a warning! + WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name())); + return; + } + } else { + // We are dealing with an Array. + // Let's check if the subtype of the array matches first. + // This is needed because the set method of ScriptInstance checks for type, + // but the ClassDB set method doesn't! So we cannot reliably know what actually happens. + Array current_array = current; + Array placeholder_array = p_set.value; + if (current_array.is_same_typed(placeholder_array)) { + p_instance->set(p_set.name, p_set.value, &is_valid); + if (is_valid) { + return; + } + // Internal array types match but setting failed? This is strange, so let's print a warning! + WARN_PRINT(vformat("Array Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(Variant::Type(current_array.get_typed_builtin())), p_placeholder->get_name())); + } + // Arrays are not the same internal type. This should be happening because we have a NodePath Array, + // but the instance wants a Node Array. + } + + switch (current_type) { + case Variant::Type::NIL: + if (placeholder_type != Variant::Type::NODE_PATH) { + break; + } + // If it's nil but we have a NodePath, we guess what works. + + p_instance->set(p_set.name, p_set.value, &is_valid); + if (is_valid) { + break; + } + + p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value), &is_valid); + break; + case Variant::Type::OBJECT: + if (placeholder_type != Variant::Type::NODE_PATH) { + break; + } + // Easiest case, we want a node, but we have a deferred NodePath. + p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value)); + break; + case Variant::Type::ARRAY: { + // If we have reached here it means our array types don't match, + // so we will convert the placeholder array into the correct type + // and resolve nodes if necessary. + Array current_array = current; + Array converted_array; + Array placeholder_array = p_set.value; + converted_array = current_array.duplicate(); + converted_array.resize(placeholder_array.size()); + + if (Variant::evaluate(Variant::OP_EQUAL, current_array.get_typed_builtin(), Variant::Type::NODE_PATH)) { + // We want a typed NodePath array. + for (int i = 0; i < placeholder_array.size(); i++) { + converted_array.set(i, placeholder_array[i]); + } + } else { + // We want Nodes, convert NodePaths. + for (int i = 0; i < placeholder_array.size(); i++) { + converted_array.set(i, try_get_node(p_placeholder, p_instance, placeholder_array[i])); + } + } + + p_instance->set(p_set.name, converted_array, &is_valid); + if (!is_valid) { + WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name())); + } + break; + } + default: + WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name())); + break; + } +} + +Node *InstancePlaceholder::try_get_node(InstancePlaceholder *p_placeholder, Node *p_instance, const NodePath &p_path) { + // First try to resolve internally, + // if that fails try resolving externally. + Node *node = p_instance->get_node_or_null(p_path); + if (node == nullptr) { + node = p_placeholder->get_node_or_null(p_path); + } + + return node; } Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) { diff --git a/scene/main/instance_placeholder.h b/scene/main/instance_placeholder.h index 480474d0bd..ccf1e63a16 100644 --- a/scene/main/instance_placeholder.h +++ b/scene/main/instance_placeholder.h @@ -46,6 +46,10 @@ class InstancePlaceholder : public Node { List<PropSet> stored_values; +private: + void set_value_on_instance(InstancePlaceholder *p_placeholder, Node *p_instance, const PropSet &p_set); + Node *try_get_node(InstancePlaceholder *p_placeholder, Node *p_instance, const NodePath &p_path); + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 5e50b9a240..0c57c6b7ba 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -314,6 +314,16 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { ERR_FAIL_INDEX_V(nprops[j].value, prop_count, nullptr); if (nprops[j].name & FLAG_PATH_PROPERTY_IS_NODE) { + if (!Engine::get_singleton()->is_editor_hint() && node->get_scene_instance_load_placeholder()) { + // We cannot know if the referenced nodes exist yet, so instead of deferring, we write the NodePaths directly. + + uint32_t name_idx = nprops[j].name & (FLAG_PATH_PROPERTY_IS_NODE - 1); + ERR_FAIL_UNSIGNED_INDEX_V(name_idx, (uint32_t)sname_count, nullptr); + + node->set(snames[name_idx], props[nprops[j].value], &valid); + continue; + } + uint32_t name_idx = nprops[j].name & (FLAG_PATH_PROPERTY_IS_NODE - 1); ERR_FAIL_UNSIGNED_INDEX_V(name_idx, (uint32_t)sname_count, nullptr); diff --git a/scene/resources/portable_compressed_texture.cpp b/scene/resources/portable_compressed_texture.cpp index 918b5c0b41..002db30379 100644 --- a/scene/resources/portable_compressed_texture.cpp +++ b/scene/resources/portable_compressed_texture.cpp @@ -153,14 +153,14 @@ void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, C for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { Vector<uint8_t> data; if (p_compression_mode == COMPRESSION_MODE_LOSSY) { - data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality); + data = Image::webp_lossy_packer(i ? p_image->get_image_from_mipmap(i) : p_image, p_lossy_quality); encode_uint16(DATA_FORMAT_WEBP, buffer.ptrw() + 2); } else { if (use_webp) { - data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i)); + data = Image::webp_lossless_packer(i ? p_image->get_image_from_mipmap(i) : p_image); encode_uint16(DATA_FORMAT_WEBP, buffer.ptrw() + 2); } else { - data = Image::png_packer(p_image->get_image_from_mipmap(i)); + data = Image::png_packer(i ? p_image->get_image_from_mipmap(i) : p_image); encode_uint16(DATA_FORMAT_PNG, buffer.ptrw() + 2); } } diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 5e9510c1ac..dfe5bd4a47 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -155,7 +155,7 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr DocData::PropertyDoc prop_doc; prop_doc.name = "shader_parameter/" + pi.name; #ifdef MODULE_REGEX_ENABLED - const RegEx pattern("/\\*\\*([^*]|[\\r\\n]|(\\*+([^*/]|[\\r\\n])))*\\*+/\\s*uniform\\s+\\w+\\s+" + pi.name + "(?=[\\s:;=])"); + const RegEx pattern("/\\*\\*\\s([^*]|[\\r\\n]|(\\*+([^*/]|[\\r\\n])))*\\*+/\\s*uniform\\s+\\w+\\s+" + pi.name + "(?=[\\s:;=])"); Ref<RegExMatch> pattern_ref = pattern.search(code); if (pattern_ref != nullptr) { RegExMatch *match = pattern_ref.ptr(); @@ -173,7 +173,7 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr } } #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && !class_doc.name.is_empty() && p_params) { + if (EditorHelp::get_doc_data() != nullptr && Engine::get_singleton()->is_editor_hint() && !class_doc.name.is_empty() && p_params) { EditorHelp::get_doc_data()->add_doc(class_doc); } #endif diff --git a/scene/theme/SCsub b/scene/theme/SCsub index 5f62ae4b05..2372d1820a 100644 --- a/scene/theme/SCsub +++ b/scene/theme/SCsub @@ -4,7 +4,6 @@ Import("env") import default_theme_builders - env.add_source_files(env.scene_sources, "*.cpp") SConscript("icons/SCsub") diff --git a/scene/theme/icons/SCsub b/scene/theme/icons/SCsub index 46133ccceb..1f3b7f6d17 100644 --- a/scene/theme/icons/SCsub +++ b/scene/theme/icons/SCsub @@ -4,7 +4,6 @@ Import("env") import default_theme_icons_builders - env["BUILDERS"]["MakeDefaultThemeIconsBuilder"] = Builder( action=env.Run(default_theme_icons_builders.make_default_theme_icons_action), suffix=".h", |