bevy/crates/bevy_ui/src/widget/text.rs

169 lines
5.7 KiB
Rust
Raw Normal View History

use crate::{Node, Style, Val};
use bevy_asset::Assets;
use bevy_ecs::{Changed, Entity, Local, Or, Query, QuerySet, Res, ResMut};
use bevy_math::Size;
use bevy_render::{
draw::{Draw, DrawContext, Drawable},
mesh::Mesh,
prelude::{Msaa, Visible},
renderer::RenderResourceBindings,
2020-08-16 03:27:41 +00:00
texture::Texture,
};
use bevy_sprite::{TextureAtlas, QUAD_HANDLE};
use bevy_text::{
CalculatedSize, DefaultTextPipeline, DrawableText, Font, FontAtlasSet, Text, TextError,
};
use bevy_transform::prelude::GlobalTransform;
use bevy_window::Windows;
2020-09-19 21:22:01 +00:00
#[derive(Debug, Default)]
pub struct QueuedText {
entities: Vec<Entity>,
2020-09-19 21:22:01 +00:00
}
fn scale_value(value: f32, factor: f64) -> f32 {
(value as f64 * factor) as f32
}
/// Defines how min_size, size, and max_size affects the bounds of a text
/// block.
pub fn text_constraint(min_size: Val, size: Val, max_size: Val, scale_factor: f64) -> f32 {
// Needs support for percentages
match (min_size, size, max_size) {
(_, _, Val::Px(max)) => scale_value(max, scale_factor),
(Val::Px(min), _, _) => scale_value(min, scale_factor),
(Val::Undefined, Val::Px(size), Val::Undefined) => scale_value(size, scale_factor),
(Val::Auto, Val::Px(size), Val::Auto) => scale_value(size, scale_factor),
_ => f32::MAX,
}
}
/// Computes the size of a text block and updates the TextGlyphs with the
/// new computed glyphs from the layout
#[allow(clippy::too_many_arguments)]
pub fn text_system(
mut queued_text: Local<QueuedText>,
mut textures: ResMut<Assets<Texture>>,
fonts: Res<Assets<Font>>,
windows: Res<Windows>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
mut text_pipeline: ResMut<DefaultTextPipeline>,
mut text_queries: QuerySet<(
Query<Entity, Or<(Changed<Text>, Changed<Style>)>>,
Query<(&Text, &Style, &mut CalculatedSize)>,
)>,
) {
let scale_factor = if let Some(window) = windows.get_primary() {
window.scale_factor()
} else {
1.
};
let inv_scale_factor = 1. / scale_factor;
// Adds all entities where the text or the style has changed to the local queue
for entity in text_queries.q0_mut().iter_mut() {
queued_text.entities.push(entity);
}
if queued_text.entities.is_empty() {
return;
}
// Computes all text in the local queue
let mut new_queue = Vec::new();
let query = text_queries.q1_mut();
for entity in queued_text.entities.drain(..) {
if let Ok((text, style, mut calculated_size)) = query.get_mut(entity) {
let node_size = Size::new(
text_constraint(
style.min_size.width,
style.size.width,
style.max_size.width,
scale_factor,
),
text_constraint(
style.min_size.height,
style.size.height,
style.max_size.height,
scale_factor,
),
);
match text_pipeline.queue_text(
entity,
&fonts,
&text.sections,
scale_factor,
text.alignment,
node_size,
&mut *font_atlas_set_storage,
&mut *texture_atlases,
&mut *textures,
) {
Err(TextError::NoSuchFont) => {
// There was an error processing the text layout, let's add this entity to the queue for further processing
new_queue.push(entity);
}
Err(e @ TextError::FailedToAddGlyph(_)) => {
panic!("Fatal error when processing text: {}.", e);
}
Ok(()) => {
let text_layout_info = text_pipeline.get_glyphs(&entity).expect(
"Failed to get glyphs from the pipeline that have just been computed",
);
calculated_size.size = Size {
width: scale_value(text_layout_info.size.width, inv_scale_factor),
height: scale_value(text_layout_info.size.height, inv_scale_factor),
};
}
2020-09-19 21:22:01 +00:00
}
}
}
queued_text.entities = new_queue;
}
#[allow(clippy::too_many_arguments)]
pub fn draw_text_system(
mut context: DrawContext,
2020-07-30 01:15:15 +00:00
msaa: Res<Msaa>,
windows: Res<Windows>,
meshes: Res<Assets<Mesh>>,
mut render_resource_bindings: ResMut<RenderResourceBindings>,
text_pipeline: Res<DefaultTextPipeline>,
mut query: Query<(Entity, &mut Draw, &Visible, &Text, &Node, &GlobalTransform)>,
) {
let scale_factor = if let Some(window) = windows.get_primary() {
window.scale_factor()
} else {
1.
};
let font_quad = meshes.get(&QUAD_HANDLE).unwrap();
let vertex_buffer_layout = font_quad.get_vertex_buffer_layout();
for (entity, mut draw, visible, text, node, global_transform) in query.iter_mut() {
if !visible.is_visible {
continue;
}
if let Some(text_glyphs) = text_pipeline.get_glyphs(&entity) {
let position = global_transform.translation - (node.size / 2.0).extend(0.0);
let mut drawable_text = DrawableText {
render_resource_bindings: &mut render_resource_bindings,
position,
scale_factor: scale_factor as f32,
msaa: &msaa,
text_glyphs: &text_glyphs.glyphs,
font_quad_vertex_layout: &vertex_buffer_layout,
sections: &text.sections,
};
drawable_text.draw(&mut draw, &mut context).unwrap();
}
}
2020-07-26 19:10:18 +00:00
}