Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
be8e768
Added style module
ickshonpe Sep 23, 2025
41ce2f1
* New component `ComputedTextStyle`.
ickshonpe Sep 23, 2025
2b8fbf9
Fix tests
ickshonpe Sep 23, 2025
35eae7f
Added `DefaultTextStyle` to test app
ickshonpe Sep 23, 2025
be52501
Remove more schedule ambiguities
ickshonpe Sep 23, 2025
c83d383
In testbed_ui explicitly use default fonts as needed.
ickshonpe Sep 23, 2025
a4c5dea
Replaced queries for `detect_text_needs_rerender`
ickshonpe Sep 23, 2025
2f7956a
Add comments
ickshonpe Sep 23, 2025
176cd1d
Updated the docs for `DefaultTextStyle` and `ComputedTextStyle`
ickshonpe Sep 24, 2025
4555bad
Removed textspan types and systems
ickshonpe Sep 25, 2025
baa6e45
Merge branch 'text-style-propagation' into remove-textspan
ickshonpe Sep 25, 2025
9d95d75
fix sprite and UI text systems
ickshonpe Sep 25, 2025
8eac71f
Updated `Text2d` example.
ickshonpe Sep 26, 2025
4df5ba9
Added `update_text_roots` to the schedule.
ickshonpe Sep 26, 2025
6893c74
updated more examples
ickshonpe Sep 26, 2025
0ba91b4
update many_buttons example
ickshonpe Sep 26, 2025
6490db1
update change detection
ickshonpe Sep 26, 2025
f7a379e
Only update `TextRoot`s on changes.
ickshonpe Sep 26, 2025
7882215
Added query filters for `TextRoot` to text extraction functions
ickshonpe Sep 26, 2025
7cbbe08
Changed detect_text_needs_rerender to use `Or` on query filter.
ickshonpe Sep 26, 2025
eea4fb0
FIxed degenerate child text nodes.
ickshonpe Sep 26, 2025
2bb6e6c
Merge branch 'main' into remove-textspan
ickshonpe Sep 26, 2025
cf8e44b
Replaced `TextSpan`s in examples.
ickshonpe Sep 26, 2025
cf69abb
Merge branch 'remove-textspan' of https://github.com/ickshonpe/bevy i…
ickshonpe Sep 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 14 additions & 19 deletions crates/bevy_dev_tools/src/fps_overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use bevy_color::Color;
use bevy_diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};
use bevy_ecs::{
component::Component,
entity::Entity,
prelude::Local,
query::{With, Without},
resource::Resource,
Expand All @@ -15,12 +14,9 @@ use bevy_ecs::{
};
use bevy_picking::Pickable;
use bevy_render::storage::ShaderStorageBuffer;
use bevy_text::{Font, TextColor, TextFont, TextSpan};
use bevy_text::{Font, TextColor, TextFont};
use bevy_time::Time;
use bevy_ui::{
widget::{Text, TextUiWriter},
FlexDirection, GlobalZIndex, Node, PositionType, Val,
};
use bevy_ui::{widget::Text, FlexDirection, GlobalZIndex, Node, PositionType, Val};
use bevy_ui_render::prelude::MaterialNode;
use core::time::Duration;

Expand Down Expand Up @@ -146,6 +142,9 @@ impl Default for FrameTimeGraphConfig {
#[derive(Component)]
struct FpsText;

#[derive(Component)]
struct FpsCounter;

#[derive(Component)]
struct FrameTimeGraph;

Expand All @@ -172,10 +171,10 @@ fn setup(
Text::new("FPS: "),
overlay_config.text_config.clone(),
TextColor(overlay_config.text_color),
FpsText,
Pickable::IGNORE,
FpsText,
))
.with_child((TextSpan::default(), overlay_config.text_config.clone()));
.with_child((Text::default(), FpsCounter));

let font_size = overlay_config.text_config.font_size;
p.spawn((
Expand Down Expand Up @@ -211,35 +210,31 @@ fn setup(

fn update_text(
diagnostic: Res<DiagnosticsStore>,
query: Query<Entity, With<FpsText>>,
mut writer: TextUiWriter,
mut query: Query<&mut Text, With<FpsText>>,
time: Res<Time>,
config: Res<FpsOverlayConfig>,
mut time_since_rerender: Local<Duration>,
) {
*time_since_rerender += time.delta();
if *time_since_rerender >= config.refresh_interval {
*time_since_rerender = Duration::ZERO;
for entity in &query {
for mut text in &mut query {
if let Some(fps) = diagnostic.get(&FrameTimeDiagnosticsPlugin::FPS)
&& let Some(value) = fps.smoothed()
{
*writer.text(entity, 1) = format!("{value:.2}");
text.0 = format!("{value:.2}");
}
}
}
}

fn customize_overlay(
overlay_config: Res<FpsOverlayConfig>,
query: Query<Entity, With<FpsText>>,
mut writer: TextUiWriter,
mut query: Query<(&mut TextFont, &mut TextColor), With<FpsText>>,
) {
for entity in &query {
writer.for_each_font(entity, |mut font| {
*font = overlay_config.text_config.clone();
});
writer.for_each_color(entity, |mut color| color.0 = overlay_config.text_color);
for (mut font, mut color) in &mut query {
*font = overlay_config.text_config.clone();
color.0 = overlay_config.text_color;
}
}

Expand Down
7 changes: 3 additions & 4 deletions crates/bevy_feathers/src/font_styles.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! A framework for inheritable font styles.
use bevy_app::{Propagate, PropagateOver};
use bevy_asset::{AssetServer, Handle};
use bevy_ecs::{
component::Component,
Expand All @@ -17,7 +16,7 @@ use crate::{handle_or_path::HandleOrPath, theme::ThemedText};
/// downward to any child text entity that has the [`ThemedText`] marker.
#[derive(Component, Default, Clone, Debug, Reflect)]
#[reflect(Component, Default)]
#[require(ThemedText, PropagateOver::<TextFont>::default())]
#[require(ThemedText)]
pub struct InheritableFont {
/// The font handle or path.
pub font: HandleOrPath<Font>,
Expand Down Expand Up @@ -57,10 +56,10 @@ pub(crate) fn on_changed_font(
HandleOrPath::Path(ref p) => Some(assets.load::<Font>(p)),
}
{
commands.entity(insert.entity).insert(Propagate(TextFont {
commands.entity(insert.entity).insert(TextFont {
font,
font_size: style.font_size,
..Default::default()
}));
});
}
}
18 changes: 2 additions & 16 deletions crates/bevy_feathers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,17 @@
//! Please report issues, submit fixes and propose changes.
//! Thanks for stress-testing; let's build something better together.

use bevy_app::{
HierarchyPropagatePlugin, Plugin, PluginGroup, PluginGroupBuilder, PostUpdate, PropagateSet,
};
use bevy_app::{Plugin, PluginGroup, PluginGroupBuilder, PostUpdate};
use bevy_asset::embedded_asset;
use bevy_ecs::{query::With, schedule::IntoScheduleConfigs};
use bevy_input_focus::{tab_navigation::TabNavigationPlugin, InputDispatchPlugin};
use bevy_text::{TextColor, TextFont};
use bevy_ui::UiSystems;
use bevy_ui_render::UiMaterialPlugin;
use bevy_ui_widgets::UiWidgetsPlugins;

use crate::{
alpha_pattern::{AlphaPatternMaterial, AlphaPatternResource},
controls::ControlsPlugin,
cursor::{CursorIconPlugin, DefaultCursor, EntityCursor},
theme::{ThemedText, UiTheme},
theme::UiTheme,
};

mod alpha_pattern;
Expand Down Expand Up @@ -68,18 +63,9 @@ impl Plugin for FeathersPlugin {
app.add_plugins((
ControlsPlugin,
CursorIconPlugin,
HierarchyPropagatePlugin::<TextColor, With<ThemedText>>::new(PostUpdate),
HierarchyPropagatePlugin::<TextFont, With<ThemedText>>::new(PostUpdate),
UiMaterialPlugin::<AlphaPatternMaterial>::default(),
));

// This needs to run in UiSystems::Propagate so the fonts are up-to-date for `measure_text_system`
// and `detect_text_needs_rerender` in UiSystems::Content
app.configure_sets(
PostUpdate,
PropagateSet::<TextFont>::default().in_set(UiSystems::Propagate),
);

app.insert_resource(DefaultCursor(EntityCursor::System(
bevy_window::SystemCursorIcon::Default,
)));
Expand Down
7 changes: 2 additions & 5 deletions crates/bevy_feathers/src/theme.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! A framework for theming.
use bevy_app::{Propagate, PropagateOver};
use bevy_color::{palettes, Color};
use bevy_ecs::{
change_detection::DetectChanges,
Expand Down Expand Up @@ -105,7 +104,7 @@ pub struct ThemeBorderColor(pub ThemeToken);
#[component(immutable)]
#[derive(Reflect)]
#[reflect(Component, Clone)]
#[require(ThemedText, PropagateOver::<TextColor>::default())]
#[require(ThemedText)]
pub struct ThemeFontColor(pub ThemeToken);

/// A marker component that is used to indicate that the text entity wants to opt-in to using
Expand Down Expand Up @@ -167,8 +166,6 @@ pub(crate) fn on_changed_font_color(
) {
if let Ok(token) = font_color.get(insert.entity) {
let color = theme.color(&token.0);
commands
.entity(insert.entity)
.insert(Propagate(TextColor(color)));
commands.entity(insert.entity).insert(TextColor(color));
}
}
9 changes: 7 additions & 2 deletions crates/bevy_sprite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub mod prelude {
};
#[cfg(feature = "bevy_text")]
#[doc(hidden)]
pub use crate::text2d::{Text2d, Text2dReader, Text2dWriter};
pub use crate::text2d::Text2d;
#[doc(hidden)]
pub use crate::{
sprite::{Sprite, SpriteImageMode},
Expand All @@ -44,6 +44,8 @@ use bevy_camera::{
visibility::VisibilitySystems,
};
use bevy_mesh::{Mesh, Mesh2d};
#[cfg(feature = "bevy_text")]
use bevy_text::{detect_text_needs_rerender, update_text_roots, update_text_styles};
#[cfg(feature = "bevy_sprite_picking_backend")]
pub use picking_backend::*;
pub use sprite::*;
Expand Down Expand Up @@ -84,9 +86,12 @@ impl Plugin for SpritePlugin {
app.add_systems(
PostUpdate,
(
bevy_text::detect_text_needs_rerender::<Text2d>,
detect_text_needs_rerender::<Text2d>
.after(update_text_styles)
.after(update_text_roots),
update_text2d_layout
.after(bevy_camera::CameraUpdateSystems)
.after(update_text_styles)
.after(bevy_text::remove_dropped_font_atlas_sets),
calculate_bounds_text2d.in_set(VisibilitySystems::CalculateBounds),
)
Expand Down
61 changes: 19 additions & 42 deletions crates/bevy_sprite/src/text2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ use bevy_math::{FloatOrd, Vec2, Vec3};
use bevy_reflect::{prelude::ReflectDefault, Reflect};
use bevy_text::{
ComputedTextBlock, CosmicFontSystem, Font, FontAtlasSets, LineBreak, SwashCache, TextBounds,
TextColor, TextError, TextFont, TextLayout, TextLayoutInfo, TextPipeline, TextReader, TextRoot,
TextSpanAccess, TextWriter,
TextError, TextLayout, TextLayoutInfo, TextPipeline,
};
use bevy_text::{ComputedTextStyle, TextRoot};
use bevy_transform::components::Transform;
use core::any::TypeId;

Expand Down Expand Up @@ -81,12 +81,11 @@ use core::any::TypeId;
#[reflect(Component, Default, Debug, Clone)]
#[require(
TextLayout,
TextFont,
TextColor,
TextBounds,
Anchor,
Visibility,
VisibilityClass,
ComputedTextStyle,
Transform
)]
#[component(on_add = visibility::add_visibility_class::<Sprite>)]
Expand All @@ -98,36 +97,6 @@ impl Text2d {
Self(text.into())
}
}

impl TextRoot for Text2d {}

impl TextSpanAccess for Text2d {
fn read_span(&self) -> &str {
self.as_str()
}
fn write_span(&mut self) -> &mut String {
&mut *self
}
}

impl From<&str> for Text2d {
fn from(value: &str) -> Self {
Self(String::from(value))
}
}

impl From<String> for Text2d {
fn from(value: String) -> Self {
Self(value)
}
}

/// 2d alias for [`TextReader`].
pub type Text2dReader<'w, 's> = TextReader<'w, 's, Text2d>;

/// 2d alias for [`TextWriter`].
pub type Text2dWriter<'w, 's> = TextWriter<'w, 's, Text2d>;

/// Adds a shadow behind `Text2d` text
///
/// Use `TextShadow` for text drawn with `bevy_ui`
Expand Down Expand Up @@ -167,15 +136,16 @@ pub fn update_text2d_layout(
mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
mut font_atlas_sets: ResMut<FontAtlasSets>,
mut text_pipeline: ResMut<TextPipeline>,
mut text_query: Query<(
text_query: Query<(&Text2d, &ComputedTextStyle)>,
mut text_root_query: Query<(
Entity,
Option<&RenderLayers>,
Ref<TextLayout>,
Ref<TextBounds>,
&mut TextLayoutInfo,
&mut ComputedTextBlock,
&TextRoot,
)>,
mut text_reader: Text2dReader,
mut font_system: ResMut<CosmicFontSystem>,
mut swash_cache: ResMut<SwashCache>,
) {
Expand All @@ -196,8 +166,8 @@ pub fn update_text2d_layout(
let mut previous_scale_factor = 0.;
let mut previous_mask = &RenderLayers::none();

for (entity, maybe_entity_mask, block, bounds, text_layout_info, mut computed) in
&mut text_query
for (entity, maybe_entity_mask, block, bounds, text_layout_info, mut computed, text_root) in
&mut text_root_query
{
let entity_mask = maybe_entity_mask.unwrap_or_default();

Expand Down Expand Up @@ -232,11 +202,18 @@ pub fn update_text2d_layout(
height: bounds.height.map(|height| height * scale_factor),
};

let spans = text_root.0.iter().cloned().filter_map(|entity| {
text_query
.get(entity)
.map(|(text, style)| (entity, 0, text.0.as_str(), style))
.ok()
});

let text_layout_info = text_layout_info.into_inner();
match text_pipeline.queue_text(
text_layout_info,
&fonts,
text_reader.iter(entity),
spans,
scale_factor as f64,
&block,
text_bounds,
Expand Down Expand Up @@ -309,7 +286,7 @@ mod tests {
use bevy_camera::{ComputedCameraValues, RenderTargetInfo};
use bevy_ecs::schedule::IntoScheduleConfigs;
use bevy_math::UVec2;
use bevy_text::{detect_text_needs_rerender, TextIterScratch};
use bevy_text::{update_text_styles, DefaultTextStyle};

use super::*;

Expand All @@ -325,11 +302,11 @@ mod tests {
.init_resource::<TextPipeline>()
.init_resource::<CosmicFontSystem>()
.init_resource::<SwashCache>()
.init_resource::<TextIterScratch>()
.init_resource::<DefaultTextStyle>()
.add_systems(
Update,
(
detect_text_needs_rerender::<Text2d>,
update_text_styles,
update_text2d_layout,
calculate_bounds_text2d,
)
Expand Down
7 changes: 4 additions & 3 deletions crates/bevy_sprite_render/src/text2d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ use bevy_render::sync_world::TemporaryRenderEntity;
use bevy_render::Extract;
use bevy_sprite::{Anchor, Text2dShadow};
use bevy_text::{
ComputedTextBlock, PositionedGlyph, TextBackgroundColor, TextBounds, TextColor, TextLayoutInfo,
ComputedTextBlock, ComputedTextStyle, PositionedGlyph, TextBackgroundColor, TextBounds,
TextLayoutInfo,
};
use bevy_transform::prelude::GlobalTransform;

Expand All @@ -37,7 +38,7 @@ pub fn extract_text2d_sprite(
&GlobalTransform,
)>,
>,
text_colors: Extract<Query<&TextColor>>,
text_colors: Extract<Query<&ComputedTextStyle>>,
text_background_colors_query: Extract<Query<&TextBackgroundColor>>,
) {
let mut start = extracted_slices.slices.len();
Expand Down Expand Up @@ -170,7 +171,7 @@ pub fn extract_text2d_sprite(
.map(|t| t.entity)
.unwrap_or(Entity::PLACEHOLDER),
)
.map(|text_color| LinearRgba::from(text_color.0))
.map(|style| LinearRgba::from(style.color()))
.unwrap_or_default();
current_span = *span_index;
}
Expand Down
Loading
Loading