Skip to content
Open
Changes from all commits
Commits
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
225 changes: 85 additions & 140 deletions crates/bevy_ui/src/widget/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use bevy_ecs::{
query::With,
reflect::ReflectComponent,
system::{Query, Res, ResMut},
world::{Mut, Ref},
world::Ref,
};
use bevy_image::prelude::*;
use bevy_math::Vec2;
Expand Down Expand Up @@ -215,49 +215,6 @@ impl Measure for TextMeasure {
}
}

#[inline]
fn create_text_measure<'a>(
entity: Entity,
fonts: &Assets<Font>,
scale_factor: f64,
spans: impl Iterator<Item = (Entity, usize, &'a str, &'a TextFont, Color)>,
block: Ref<TextLayout>,
text_pipeline: &mut TextPipeline,
mut content_size: Mut<ContentSize>,
mut text_flags: Mut<TextNodeFlags>,
mut computed: Mut<ComputedTextBlock>,
font_system: &mut CosmicFontSystem,
) {
match text_pipeline.create_text_measure(
entity,
fonts,
spans,
scale_factor,
&block,
computed.as_mut(),
font_system,
) {
Ok(measure) => {
if block.linebreak == LineBreak::NoWrap {
content_size.set(NodeMeasure::Fixed(FixedMeasure { size: measure.max }));
} else {
content_size.set(NodeMeasure::Text(TextMeasure { info: measure }));
}

// Text measure func created successfully, so set `TextNodeFlags` to schedule a recompute
text_flags.needs_measure_fn = false;
text_flags.needs_recompute = true;
}
Err(TextError::NoSuchFont) => {
// Try again next frame
text_flags.needs_measure_fn = true;
}
Err(e @ (TextError::FailedToAddGlyph(_) | TextError::FailedToGetGlyphImage(_))) => {
panic!("Fatal error when processing text: {e}.");
}
};
}

/// Generates a new [`Measure`] for a text node on changes to its [`Text`] component.
///
/// A `Measure` is used by the UI's layout algorithm to determine the appropriate amount of space
Expand Down Expand Up @@ -286,92 +243,55 @@ pub fn measure_text_system(
mut text_pipeline: ResMut<TextPipeline>,
mut font_system: ResMut<CosmicFontSystem>,
) {
for (entity, block, content_size, text_flags, computed, computed_target, computed_node) in
&mut text_query
for (
entity,
block,
mut content_size,
mut text_flags,
mut computed,
computed_target,
computed_node,
) in &mut text_query
{
// Note: the ComputedTextBlock::needs_rerender bool is cleared in create_text_measure().
// 1e-5 epsilon to ignore tiny scale factor float errors
if 1e-5
if !(1e-5
< (computed_target.scale_factor() - computed_node.inverse_scale_factor.recip()).abs()
|| computed.needs_rerender()
|| text_flags.needs_measure_fn
|| content_size.is_added()
|| content_size.is_added())
{
create_text_measure(
entity,
&fonts,
computed_target.scale_factor.into(),
text_reader.iter(entity),
block,
&mut text_pipeline,
content_size,
text_flags,
computed,
&mut font_system,
);
continue;
}
}
}

#[inline]
fn queue_text(
entity: Entity,
fonts: &Assets<Font>,
text_pipeline: &mut TextPipeline,
font_atlas_set: &mut FontAtlasSet,
texture_atlases: &mut Assets<TextureAtlasLayout>,
textures: &mut Assets<Image>,
scale_factor: f32,
inverse_scale_factor: f32,
block: &TextLayout,
node: Ref<ComputedNode>,
mut text_flags: Mut<TextNodeFlags>,
text_layout_info: Mut<TextLayoutInfo>,
computed: &mut ComputedTextBlock,
text_reader: &mut TextUiReader,
font_system: &mut CosmicFontSystem,
swash_cache: &mut SwashCache,
) {
// Skip the text node if it is waiting for a new measure func
if text_flags.needs_measure_fn {
return;
}

let physical_node_size = if block.linebreak == LineBreak::NoWrap {
// With `NoWrap` set, no constraints are placed on the width of the text.
TextBounds::UNBOUNDED
} else {
// `scale_factor` is already multiplied by `UiScale`
TextBounds::new(node.unrounded_size.x, node.unrounded_size.y)
};
match text_pipeline.create_text_measure(
entity,
fonts.as_ref(),
text_reader.iter(entity),
computed_target.scale_factor.into(),
&block,
computed.as_mut(),
&mut font_system,
) {
Ok(measure) => {
if block.linebreak == LineBreak::NoWrap {
content_size.set(NodeMeasure::Fixed(FixedMeasure { size: measure.max }));
} else {
content_size.set(NodeMeasure::Text(TextMeasure { info: measure }));
}

let text_layout_info = text_layout_info.into_inner();
match text_pipeline.queue_text(
text_layout_info,
fonts,
text_reader.iter(entity),
scale_factor.into(),
block,
physical_node_size,
font_atlas_set,
texture_atlases,
textures,
computed,
font_system,
swash_cache,
) {
Err(TextError::NoSuchFont) => {
// There was an error processing the text layout, try again next frame
text_flags.needs_recompute = true;
}
Err(e @ (TextError::FailedToAddGlyph(_) | TextError::FailedToGetGlyphImage(_))) => {
panic!("Fatal error when processing text: {e}.");
}
Ok(()) => {
text_layout_info.scale_factor = scale_factor;
text_layout_info.size *= inverse_scale_factor;
text_flags.needs_recompute = false;
}
// Text measure func created successfully, so set `TextNodeFlags` to schedule a recompute
text_flags.needs_measure_fn = false;
text_flags.needs_recompute = true;
}
Err(TextError::NoSuchFont) => {
// Try again next frame
text_flags.needs_measure_fn = true;
}
Err(e @ (TextError::FailedToAddGlyph(_) | TextError::FailedToGetGlyphImage(_))) => {
panic!("Fatal error when processing text: {e}.");
}
};
}
}

Expand Down Expand Up @@ -401,26 +321,51 @@ pub fn text_system(
mut font_system: ResMut<CosmicFontSystem>,
mut swash_cache: ResMut<SwashCache>,
) {
for (entity, node, block, text_layout_info, text_flags, mut computed) in &mut text_query {
if node.is_changed() || text_flags.needs_recompute {
queue_text(
entity,
&fonts,
&mut text_pipeline,
&mut font_atlas_set,
&mut texture_atlases,
&mut textures,
node.inverse_scale_factor.recip(),
node.inverse_scale_factor,
block,
node,
text_flags,
text_layout_info,
computed.as_mut(),
&mut text_reader,
&mut font_system,
&mut swash_cache,
);
for (entity, node, block, text_layout_info, mut text_flags, mut computed) in &mut text_query {
// Skip the text node if it is waiting for a new measure func
if text_flags.needs_measure_fn {
continue;
}

if !(node.is_changed() || text_flags.needs_recompute) {
continue;
}

let physical_node_size = if block.linebreak == LineBreak::NoWrap {
// With `NoWrap` set, no constraints are placed on the width of the text.
TextBounds::UNBOUNDED
} else {
// `scale_factor` is already multiplied by `UiScale`
TextBounds::new(node.unrounded_size.x, node.unrounded_size.y)
};

let text_layout_info = text_layout_info.into_inner();
match text_pipeline.queue_text(
text_layout_info,
&fonts,
text_reader.iter(entity),
node.inverse_scale_factor.recip() as f64,
block,
physical_node_size,
&mut font_atlas_set,
&mut texture_atlases,
&mut textures,
&mut computed,
&mut font_system,
&mut swash_cache,
) {
Err(TextError::NoSuchFont) => {
// There was an error processing the text layout, try again next frame
text_flags.needs_recompute = true;
}
Err(e @ (TextError::FailedToAddGlyph(_) | TextError::FailedToGetGlyphImage(_))) => {
panic!("Fatal error when processing text: {e}.");
}
Ok(()) => {
text_layout_info.scale_factor = node.inverse_scale_factor.recip();
text_layout_info.size *= node.inverse_scale_factor;
text_flags.needs_recompute = false;
}
}
}
}