2020-11-13 00:21:48 +00:00
|
|
|
use crate::{CalculatedSize, Node, Style, Val};
|
2020-07-20 03:33:55 +00:00
|
|
|
use bevy_asset::{Assets, Handle};
|
2020-11-13 00:21:48 +00:00
|
|
|
use bevy_ecs::{Changed, Entity, Local, Or, Query, QuerySet, Res, ResMut};
|
2020-09-15 01:20:20 +00:00
|
|
|
use bevy_math::Size;
|
2020-07-20 03:33:55 +00:00
|
|
|
use bevy_render::{
|
|
|
|
draw::{Draw, DrawContext, Drawable},
|
2020-10-31 02:21:53 +00:00
|
|
|
mesh::Mesh,
|
2020-08-16 03:27:41 +00:00
|
|
|
prelude::Msaa,
|
2020-07-20 03:33:55 +00:00
|
|
|
renderer::{AssetRenderResourceBindings, RenderResourceBindings},
|
2020-08-16 03:27:41 +00:00
|
|
|
texture::Texture,
|
2020-07-20 03:33:55 +00:00
|
|
|
};
|
2020-10-31 02:21:53 +00:00
|
|
|
use bevy_sprite::{TextureAtlas, QUAD_HANDLE};
|
2020-11-13 00:21:48 +00:00
|
|
|
use bevy_text::{DefaultTextPipeline, DrawableText, Font, FontAtlasSet, TextError, TextStyle};
|
2020-09-14 21:00:32 +00:00
|
|
|
use bevy_transform::prelude::GlobalTransform;
|
2020-09-19 21:22:01 +00:00
|
|
|
|
2020-10-08 18:43:01 +00:00
|
|
|
#[derive(Debug, Default)]
|
2020-09-22 03:23:09 +00:00
|
|
|
pub struct QueuedText {
|
|
|
|
entities: Vec<Entity>,
|
2020-09-19 21:22:01 +00:00
|
|
|
}
|
2020-07-20 03:33:55 +00:00
|
|
|
|
2020-10-08 18:43:01 +00:00
|
|
|
#[derive(Debug, Default, Clone)]
|
2020-07-20 03:33:55 +00:00
|
|
|
pub struct Text {
|
|
|
|
pub value: String,
|
|
|
|
pub font: Handle<Font>,
|
|
|
|
pub style: TextStyle,
|
|
|
|
}
|
|
|
|
|
2020-11-13 00:21:48 +00:00
|
|
|
/// 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) -> f32 {
|
|
|
|
// Needs support for percentages
|
|
|
|
match (min_size, size, max_size) {
|
|
|
|
(_, _, Val::Px(max)) => max,
|
|
|
|
(Val::Px(min), _, _) => min,
|
|
|
|
(Val::Undefined, Val::Px(size), Val::Undefined) => size,
|
|
|
|
(Val::Auto, Val::Px(size), Val::Auto) => size,
|
|
|
|
_ => f32::MAX,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Computes the size of a text block and updates the TextGlyphs with the
|
|
|
|
/// new computed glyphs from the layout
|
2020-07-20 03:33:55 +00:00
|
|
|
pub fn text_system(
|
2020-09-22 03:23:09 +00:00
|
|
|
mut queued_text: Local<QueuedText>,
|
2020-07-20 03:33:55 +00:00
|
|
|
mut textures: ResMut<Assets<Texture>>,
|
|
|
|
fonts: Res<Assets<Font>>,
|
|
|
|
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
|
2020-11-13 00:21:48 +00:00
|
|
|
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)>,
|
2020-10-30 06:39:55 +00:00
|
|
|
)>,
|
2020-07-20 03:33:55 +00:00
|
|
|
) {
|
2020-11-13 00:21:48 +00:00
|
|
|
// 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();
|
2020-09-22 03:23:09 +00:00
|
|
|
for entity in queued_text.entities.drain(..) {
|
2020-11-13 00:21:48 +00:00
|
|
|
if let Ok((text, style, mut calculated_size)) = query.get_mut(entity) {
|
|
|
|
match add_text_to_pipeline(
|
|
|
|
entity,
|
|
|
|
&*text,
|
|
|
|
&*style,
|
|
|
|
&mut *textures,
|
|
|
|
&*fonts,
|
|
|
|
&mut *texture_atlases,
|
|
|
|
&mut *font_atlas_set_storage,
|
|
|
|
&mut *text_pipeline,
|
2020-10-30 06:39:55 +00:00
|
|
|
) {
|
2020-11-13 00:21:48 +00:00
|
|
|
TextPipelineResult::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 = text_layout_info.size;
|
|
|
|
}
|
|
|
|
TextPipelineResult::Reschedule => {
|
|
|
|
// There was an error processing the text layout, let's add this entity to the queue for further processing
|
|
|
|
new_queue.push(entity);
|
|
|
|
}
|
2020-09-19 21:22:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-13 00:21:48 +00:00
|
|
|
queued_text.entities = new_queue;
|
|
|
|
}
|
2020-09-22 03:23:09 +00:00
|
|
|
|
2020-11-13 00:21:48 +00:00
|
|
|
enum TextPipelineResult {
|
|
|
|
Ok,
|
|
|
|
Reschedule,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Computes the text layout and stores it in the TextPipeline resource.
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
fn add_text_to_pipeline(
|
|
|
|
entity: Entity,
|
|
|
|
text: &Text,
|
|
|
|
style: &Style,
|
|
|
|
textures: &mut Assets<Texture>,
|
|
|
|
fonts: &Assets<Font>,
|
|
|
|
texture_atlases: &mut Assets<TextureAtlas>,
|
|
|
|
font_atlas_set_storage: &mut Assets<FontAtlasSet>,
|
|
|
|
text_pipeline: &mut DefaultTextPipeline,
|
|
|
|
) -> TextPipelineResult {
|
|
|
|
let node_size = Size::new(
|
|
|
|
text_constraint(style.min_size.width, style.size.width, style.max_size.width),
|
|
|
|
text_constraint(
|
|
|
|
style.min_size.height,
|
|
|
|
style.size.height,
|
|
|
|
style.max_size.height,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
match text_pipeline.queue_text(
|
|
|
|
entity,
|
|
|
|
text.font.clone(),
|
|
|
|
&fonts,
|
|
|
|
&text.value,
|
|
|
|
text.style.font_size,
|
|
|
|
text.style.alignment,
|
|
|
|
node_size,
|
|
|
|
font_atlas_set_storage,
|
|
|
|
texture_atlases,
|
|
|
|
textures,
|
|
|
|
) {
|
|
|
|
Err(TextError::NoSuchFont) => TextPipelineResult::Reschedule,
|
|
|
|
Err(e @ TextError::FailedToAddGlyph(_)) => {
|
|
|
|
panic!("Fatal error when processing text: {}", e);
|
2020-09-19 21:22:01 +00:00
|
|
|
}
|
2020-11-13 00:21:48 +00:00
|
|
|
Ok(()) => TextPipelineResult::Ok,
|
2020-07-20 03:33:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-16 07:30:04 +00:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2020-07-20 03:33:55 +00:00
|
|
|
pub fn draw_text_system(
|
2020-11-13 00:21:48 +00:00
|
|
|
mut context: DrawContext,
|
2020-07-30 01:15:15 +00:00
|
|
|
msaa: Res<Msaa>,
|
2020-10-31 02:21:53 +00:00
|
|
|
meshes: Res<Assets<Mesh>>,
|
2020-07-20 03:33:55 +00:00
|
|
|
mut render_resource_bindings: ResMut<RenderResourceBindings>,
|
|
|
|
mut asset_render_resource_bindings: ResMut<AssetRenderResourceBindings>,
|
2020-11-13 00:21:48 +00:00
|
|
|
text_pipeline: Res<DefaultTextPipeline>,
|
|
|
|
mut query: Query<(Entity, &mut Draw, &Text, &Node, &GlobalTransform)>,
|
2020-07-20 03:33:55 +00:00
|
|
|
) {
|
2020-11-02 21:15:07 +00:00
|
|
|
let font_quad = meshes.get(&QUAD_HANDLE).unwrap();
|
|
|
|
let vertex_buffer_descriptor = font_quad.get_vertex_buffer_descriptor();
|
2020-10-31 02:21:53 +00:00
|
|
|
|
2020-11-13 00:21:48 +00:00
|
|
|
for (entity, mut draw, text, node, global_transform) in query.iter_mut() {
|
2020-11-22 19:57:52 +00:00
|
|
|
if !draw.is_visible {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-11-13 00:21:48 +00:00
|
|
|
if let Some(text_glyphs) = text_pipeline.get_glyphs(&entity) {
|
2020-10-18 20:03:16 +00:00
|
|
|
let position = global_transform.translation - (node.size / 2.0).extend(0.0);
|
2020-11-13 00:21:48 +00:00
|
|
|
|
2020-09-19 19:57:52 +00:00
|
|
|
let mut drawable_text = DrawableText {
|
|
|
|
render_resource_bindings: &mut render_resource_bindings,
|
|
|
|
asset_render_resource_bindings: &mut asset_render_resource_bindings,
|
|
|
|
position,
|
|
|
|
msaa: &msaa,
|
2020-11-13 00:21:48 +00:00
|
|
|
text_glyphs: &text_glyphs.glyphs,
|
2020-11-02 21:15:07 +00:00
|
|
|
font_quad_vertex_descriptor: &vertex_buffer_descriptor,
|
2020-11-13 00:21:48 +00:00
|
|
|
style: &text.style,
|
2020-09-19 19:57:52 +00:00
|
|
|
};
|
2020-11-13 00:21:48 +00:00
|
|
|
|
|
|
|
drawable_text.draw(&mut draw, &mut context).unwrap();
|
2020-09-19 19:57:52 +00:00
|
|
|
}
|
2020-07-20 03:33:55 +00:00
|
|
|
}
|
2020-07-26 19:10:18 +00:00
|
|
|
}
|