From 1bd7306a3ab9602007fdb7a3b049982f8a3790bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Fri, 24 Feb 2023 02:21:07 +0000 Subject: [PATCH] make bevy_text optional again (#7801) # Objective - `bevy_text` used to be "optional". the feature could be disabled, which meant that the systems were not added but `bevy_text` was still compiled because of a hard dependency in `bevy_ui` - Running something without `bevy_text` enabled and with `bevy_ui` enabled now crashes: ``` thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', /bevy/crates/bevy_ecs/src/schedule/schedule.rs:1147:34 ``` - This is because `bevy_ui` declares some of its systems in ambiguity sets with systems from `bevy_text`, which were not added if `bevy_text` is disabled ## Solution - Make `bevy_text` completely optional ## Migration Guide - feature `bevy_text` now completely removes `bevy_text` from the dependencies when not enabled. Enable feature `bevy_text` if you use Bevy to render text --- crates/bevy_internal/Cargo.toml | 2 + crates/bevy_ui/Cargo.toml | 2 +- crates/bevy_ui/src/lib.rs | 85 ++++++++++++++++-------------- crates/bevy_ui/src/node_bundles.rs | 4 ++ crates/bevy_ui/src/render/mod.rs | 8 ++- crates/bevy_ui/src/widget/image.rs | 11 ++-- crates/bevy_ui/src/widget/mod.rs | 2 + 7 files changed, 68 insertions(+), 46 deletions(-) diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index be6ca62646..0166d797f7 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -82,6 +82,8 @@ dynamic_linking = ["bevy_diagnostic/dynamic_linking"] # Enable using a shared stdlib for cxx on Android. android_shared_stdcxx = ["bevy_audio/android_shared_stdcxx"] +bevy_text = ["dep:bevy_text", "bevy_ui?/bevy_text"] + [dependencies] # bevy bevy_app = { path = "../bevy_app", version = "0.9.0" } diff --git a/crates/bevy_ui/Cargo.toml b/crates/bevy_ui/Cargo.toml index f0724e65d5..697fee3149 100644 --- a/crates/bevy_ui/Cargo.toml +++ b/crates/bevy_ui/Cargo.toml @@ -24,7 +24,7 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.9.0", features = [ ] } bevy_render = { path = "../bevy_render", version = "0.9.0" } bevy_sprite = { path = "../bevy_sprite", version = "0.9.0" } -bevy_text = { path = "../bevy_text", version = "0.9.0" } +bevy_text = { path = "../bevy_text", version = "0.9.0", optional = true } bevy_transform = { path = "../bevy_transform", version = "0.9.0" } bevy_window = { path = "../bevy_window", version = "0.9.0" } bevy_utils = { path = "../bevy_utils", version = "0.9.0" } diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 8ea3b45410..4f5c0acea2 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -14,7 +14,9 @@ pub mod node_bundles; pub mod update; pub mod widget; -use bevy_render::{camera::CameraUpdateSystem, extract_component::ExtractComponentPlugin}; +#[cfg(feature = "bevy_text")] +use bevy_render::camera::CameraUpdateSystem; +use bevy_render::extract_component::ExtractComponentPlugin; pub use flex::*; pub use focus::*; pub use geometry::*; @@ -103,44 +105,49 @@ impl Plugin for UiPlugin { .configure_set(UiSystem::Focus.in_base_set(CoreSet::PreUpdate)) .configure_set(UiSystem::Flex.in_base_set(CoreSet::PostUpdate)) .configure_set(UiSystem::Stack.in_base_set(CoreSet::PostUpdate)) - .add_system(ui_focus_system.in_set(UiSystem::Focus).after(InputSystem)) - // add these systems to front because these must run before transform update systems - .add_system( - widget::text_system - .in_base_set(CoreSet::PostUpdate) - .before(UiSystem::Flex) - // Potential conflict: `Assets` - // In practice, they run independently since `bevy_render::camera_update_system` - // will only ever observe its own render target, and `widget::text_system` - // will never modify a pre-existing `Image` asset. - .ambiguous_with(CameraUpdateSystem) - // Potential conflict: `Assets` - // Since both systems will only ever insert new [`Image`] assets, - // they will never observe each other's effects. - .ambiguous_with(bevy_text::update_text2d_layout), - ) - .add_system( - widget::update_image_calculated_size_system - .in_base_set(CoreSet::PostUpdate) - .before(UiSystem::Flex) - // Potential conflicts: `Assets` - // They run independently since `widget::image_node_system` will only ever observe - // its own UiImage, and `widget::text_system` & `bevy_text::update_text2d_layout` - // will never modify a pre-existing `Image` asset. - .ambiguous_with(bevy_text::update_text2d_layout) - .ambiguous_with(widget::text_system), - ) - .add_system( - flex_node_system - .in_set(UiSystem::Flex) - .before(TransformSystem::TransformPropagate), - ) - .add_system(ui_stack_system.in_set(UiSystem::Stack)) - .add_system( - update_clipping_system - .after(TransformSystem::TransformPropagate) - .in_base_set(CoreSet::PostUpdate), - ); + .add_system(ui_focus_system.in_set(UiSystem::Focus).after(InputSystem)); + // add these systems to front because these must run before transform update systems + #[cfg(feature = "bevy_text")] + app.add_system( + widget::text_system + .in_base_set(CoreSet::PostUpdate) + .before(UiSystem::Flex) + // Potential conflict: `Assets` + // In practice, they run independently since `bevy_render::camera_update_system` + // will only ever observe its own render target, and `widget::text_system` + // will never modify a pre-existing `Image` asset. + .ambiguous_with(CameraUpdateSystem) + // Potential conflict: `Assets` + // Since both systems will only ever insert new [`Image`] assets, + // they will never observe each other's effects. + .ambiguous_with(bevy_text::update_text2d_layout), + ); + app.add_system({ + let system = widget::update_image_calculated_size_system + .in_base_set(CoreSet::PostUpdate) + .before(UiSystem::Flex); + // Potential conflicts: `Assets` + // They run independently since `widget::image_node_system` will only ever observe + // its own UiImage, and `widget::text_system` & `bevy_text::update_text2d_layout` + // will never modify a pre-existing `Image` asset. + #[cfg(feature = "bevy_text")] + let system = system + .ambiguous_with(bevy_text::update_text2d_layout) + .ambiguous_with(widget::text_system); + + system + }) + .add_system( + flex_node_system + .in_set(UiSystem::Flex) + .before(TransformSystem::TransformPropagate), + ) + .add_system(ui_stack_system.in_set(UiSystem::Stack)) + .add_system( + update_clipping_system + .after(TransformSystem::TransformPropagate) + .in_base_set(CoreSet::PostUpdate), + ); crate::render::build_ui_render(app); } diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index f6a2433396..c5c3e3171f 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -9,6 +9,7 @@ use bevy_render::{ prelude::{Color, ComputedVisibility}, view::Visibility, }; +#[cfg(feature = "bevy_text")] use bevy_text::{Text, TextAlignment, TextSection, TextStyle}; use bevy_transform::prelude::{GlobalTransform, Transform}; @@ -95,6 +96,7 @@ pub struct ImageBundle { pub z_index: ZIndex, } +#[cfg(feature = "bevy_text")] /// A UI node that is text #[derive(Bundle, Clone, Debug)] pub struct TextBundle { @@ -128,6 +130,7 @@ pub struct TextBundle { pub background_color: BackgroundColor, } +#[cfg(feature = "bevy_text")] impl Default for TextBundle { fn default() -> Self { Self { @@ -147,6 +150,7 @@ impl Default for TextBundle { } } +#[cfg(feature = "bevy_text")] impl TextBundle { /// Create a [`TextBundle`] from a single section. /// diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index f213118a01..0bc8a7ce39 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -3,6 +3,7 @@ mod render_pass; use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d}; use bevy_render::ExtractSchedule; +#[cfg(feature = "bevy_text")] use bevy_window::{PrimaryWindow, Window}; pub use pipeline::*; pub use render_pass::*; @@ -26,7 +27,10 @@ use bevy_render::{ view::{ComputedVisibility, ExtractedView, ViewUniforms}, Extract, RenderApp, RenderSet, }; -use bevy_sprite::{SpriteAssetEvents, TextureAtlas}; +use bevy_sprite::SpriteAssetEvents; +#[cfg(feature = "bevy_text")] +use bevy_sprite::TextureAtlas; +#[cfg(feature = "bevy_text")] use bevy_text::{Text, TextLayoutInfo}; use bevy_transform::components::GlobalTransform; use bevy_utils::FloatOrd; @@ -78,6 +82,7 @@ pub fn build_ui_render(app: &mut App) { extract_default_ui_camera_view::, extract_default_ui_camera_view::, extract_uinodes.in_set(RenderUiSystem::ExtractNode), + #[cfg(feature = "bevy_text")] extract_text_uinodes.after(RenderUiSystem::ExtractNode), ), ) @@ -288,6 +293,7 @@ pub fn extract_default_ui_camera_view( } } +#[cfg(feature = "bevy_text")] pub fn extract_text_uinodes( mut extracted_uinodes: ResMut, texture_atlases: Extract>>, diff --git a/crates/bevy_ui/src/widget/image.rs b/crates/bevy_ui/src/widget/image.rs index 232be96eb4..4b5777c841 100644 --- a/crates/bevy_ui/src/widget/image.rs +++ b/crates/bevy_ui/src/widget/image.rs @@ -1,17 +1,18 @@ use crate::{CalculatedSize, UiImage}; use bevy_asset::Assets; -use bevy_ecs::{ - query::Without, - system::{Query, Res}, -}; +#[cfg(feature = "bevy_text")] +use bevy_ecs::query::Without; +use bevy_ecs::system::{Query, Res}; use bevy_math::Vec2; use bevy_render::texture::Image; +#[cfg(feature = "bevy_text")] use bevy_text::Text; /// Updates calculated size of the node based on the image provided pub fn update_image_calculated_size_system( textures: Res>, - mut query: Query<(&mut CalculatedSize, &UiImage), Without>, + #[cfg(feature = "bevy_text")] mut query: Query<(&mut CalculatedSize, &UiImage), Without>, + #[cfg(not(feature = "bevy_text"))] mut query: Query<(&mut CalculatedSize, &UiImage)>, ) { for (mut calculated_size, image) in &mut query { if let Some(texture) = textures.get(&image.texture) { diff --git a/crates/bevy_ui/src/widget/mod.rs b/crates/bevy_ui/src/widget/mod.rs index 12ed55620e..e5c26ae689 100644 --- a/crates/bevy_ui/src/widget/mod.rs +++ b/crates/bevy_ui/src/widget/mod.rs @@ -2,8 +2,10 @@ mod button; mod image; +#[cfg(feature = "bevy_text")] mod text; pub use button::*; pub use image::*; +#[cfg(feature = "bevy_text")] pub use text::*;