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

120 lines
5.2 KiB
Rust
Raw Normal View History

use crate::{CalculatedSize, Node};
use bevy_asset::{Assets, Handle};
use bevy_ecs::{Changed, Entity, Local, Query, QuerySet, Res, ResMut};
use bevy_math::Size;
use bevy_render::{
draw::{Draw, DrawContext, Drawable},
2020-08-16 03:27:41 +00:00
prelude::Msaa,
renderer::{AssetRenderResourceBindings, RenderResourceBindings},
2020-08-16 03:27:41 +00:00
texture::Texture,
};
use bevy_sprite::TextureAtlas;
use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle};
use bevy_transform::prelude::GlobalTransform;
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
}
#[derive(Debug, Default, Clone)]
pub struct Text {
pub value: String,
pub font: Handle<Font>,
pub style: TextStyle,
}
pub fn text_system(
mut queued_text: Local<QueuedText>,
mut textures: ResMut<Assets<Texture>>,
fonts: Res<Assets<Font>>,
mut font_atlas_sets: ResMut<Assets<FontAtlasSet>>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
mut queries: QuerySet<(
Query<(Entity, Changed<Text>, &mut CalculatedSize)>,
Query<(&Text, &mut CalculatedSize)>,
)>,
) {
// add queued text to atlases
let mut new_queued_text = Vec::new();
for entity in queued_text.entities.drain(..) {
if let Ok((text, mut calculated_size)) = queries.q1_mut().get_mut(entity) {
let font_atlases = font_atlas_sets
.get_or_insert_with(text.font.id, || FontAtlasSet::new(text.font.clone_weak()));
// TODO: this call results in one or more TextureAtlases, whose render resources are created in the RENDER_GRAPH_SYSTEMS
// stage. That logic runs _before_ the DRAW stage, which means we cant call add_glyphs_to_atlas in the draw stage
// without our render resources being a frame behind. Therefore glyph atlasing either needs its own system or the TextureAtlas
// resource generation needs to happen AFTER the render graph systems. maybe draw systems should execute within the
// render graph so ordering like this can be taken into account? Maybe the RENDER_GRAPH_SYSTEMS stage should be removed entirely
// in favor of node.update()? Regardless, in the immediate short term the current approach is fine.
if let Some(width) = font_atlases.add_glyphs_to_atlas(
&fonts,
&mut texture_atlases,
&mut textures,
text.style.font_size,
&text.value,
) {
calculated_size.size = Size::new(width, text.style.font_size);
} else {
new_queued_text.push(entity);
2020-09-19 21:22:01 +00:00
}
}
}
queued_text.entities = new_queued_text;
// add changed text to atlases
for (entity, text, mut calculated_size) in queries.q0_mut().iter_mut() {
let font_atlases = font_atlas_sets
.get_or_insert_with(text.font.id, || FontAtlasSet::new(text.font.clone_weak()));
// TODO: this call results in one or more TextureAtlases, whose render resources are created in the RENDER_GRAPH_SYSTEMS
// stage. That logic runs _before_ the DRAW stage, which means we cant call add_glyphs_to_atlas in the draw stage
// without our render resources being a frame behind. Therefore glyph atlasing either needs its own system or the TextureAtlas
// resource generation needs to happen AFTER the render graph systems. maybe draw systems should execute within the
// render graph so ordering like this can be taken into account? Maybe the RENDER_GRAPH_SYSTEMS stage should be removed entirely
// in favor of node.update()? Regardless, in the immediate short term the current approach is fine.
2020-09-19 21:22:01 +00:00
if let Some(width) = font_atlases.add_glyphs_to_atlas(
&fonts,
&mut texture_atlases,
&mut textures,
text.style.font_size,
&text.value,
2020-09-19 21:22:01 +00:00
) {
calculated_size.size = Size::new(width, text.style.font_size);
} else {
queued_text.entities.push(entity);
2020-09-19 21:22:01 +00:00
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn draw_text_system(
mut draw_context: DrawContext,
fonts: Res<Assets<Font>>,
2020-07-30 01:15:15 +00:00
msaa: Res<Msaa>,
font_atlas_sets: Res<Assets<FontAtlasSet>>,
texture_atlases: Res<Assets<TextureAtlas>>,
mut render_resource_bindings: ResMut<RenderResourceBindings>,
mut asset_render_resource_bindings: ResMut<AssetRenderResourceBindings>,
mut query: Query<(&mut Draw, &Text, &Node, &GlobalTransform)>,
) {
for (mut draw, text, node, global_transform) in query.iter_mut() {
if let Some(font) = fonts.get(&text.font) {
let position = global_transform.translation - (node.size / 2.0).extend(0.0);
let mut drawable_text = DrawableText {
font,
font_atlas_set: font_atlas_sets.get(text.font.id).unwrap(),
texture_atlases: &texture_atlases,
render_resource_bindings: &mut render_resource_bindings,
asset_render_resource_bindings: &mut asset_render_resource_bindings,
position,
msaa: &msaa,
style: &text.style,
text: &text.value,
container_size: node.size,
};
drawable_text.draw(&mut draw, &mut draw_context).unwrap();
}
}
2020-07-26 19:10:18 +00:00
}