
# Objective - Field `id` of `Handle<T>` is public: https://docs.rs/bevy/latest/bevy/asset/struct.Handle.html#structfield.id - Changing the value of this field doesn't make sense as it could mean changing the previous handle without dropping it, breaking asset cleanup detection for the old handle and the new one ## Solution - Make the field private, and add a public getter Opened after discussion in #6171. Pinging @zicklag --- ## Migration Guide - If you were accessing the value `handle.id`, you can now do so with `handle.id()`
113 lines
3.5 KiB
Rust
113 lines
3.5 KiB
Rust
use ab_glyph::{PxScale, ScaleFont};
|
|
use bevy_asset::{Assets, Handle, HandleId};
|
|
use bevy_ecs::component::Component;
|
|
use bevy_ecs::system::Resource;
|
|
use bevy_math::Vec2;
|
|
use bevy_render::texture::Image;
|
|
use bevy_sprite::TextureAtlas;
|
|
use bevy_utils::HashMap;
|
|
|
|
use glyph_brush_layout::{FontId, SectionText};
|
|
|
|
use crate::{
|
|
error::TextError, glyph_brush::GlyphBrush, scale_value, Font, FontAtlasSet, PositionedGlyph,
|
|
TextAlignment, TextSection, TextSettings,
|
|
};
|
|
|
|
#[derive(Default, Resource)]
|
|
pub struct TextPipeline {
|
|
brush: GlyphBrush,
|
|
map_font_id: HashMap<HandleId, FontId>,
|
|
}
|
|
|
|
/// Render information for a corresponding [`Text`](crate::Text) component.
|
|
///
|
|
/// Contains scaled glyphs and their size. Generated via [`TextPipeline::queue_text`].
|
|
#[derive(Component, Clone, Default, Debug)]
|
|
pub struct TextLayoutInfo {
|
|
pub glyphs: Vec<PositionedGlyph>,
|
|
pub size: Vec2,
|
|
}
|
|
|
|
impl TextPipeline {
|
|
pub fn get_or_insert_font_id(&mut self, handle: &Handle<Font>, font: &Font) -> FontId {
|
|
let brush = &mut self.brush;
|
|
*self
|
|
.map_font_id
|
|
.entry(handle.id())
|
|
.or_insert_with(|| brush.add_font(handle.clone(), font.font.clone()))
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn queue_text(
|
|
&mut self,
|
|
fonts: &Assets<Font>,
|
|
sections: &[TextSection],
|
|
scale_factor: f64,
|
|
text_alignment: TextAlignment,
|
|
bounds: Vec2,
|
|
font_atlas_set_storage: &mut Assets<FontAtlasSet>,
|
|
texture_atlases: &mut Assets<TextureAtlas>,
|
|
textures: &mut Assets<Image>,
|
|
text_settings: &TextSettings,
|
|
) -> Result<TextLayoutInfo, TextError> {
|
|
let mut scaled_fonts = Vec::new();
|
|
let sections = sections
|
|
.iter()
|
|
.map(|section| {
|
|
let font = fonts
|
|
.get(§ion.style.font)
|
|
.ok_or(TextError::NoSuchFont)?;
|
|
let font_id = self.get_or_insert_font_id(§ion.style.font, font);
|
|
let font_size = scale_value(section.style.font_size, scale_factor);
|
|
|
|
scaled_fonts.push(ab_glyph::Font::as_scaled(&font.font, font_size));
|
|
|
|
let section = SectionText {
|
|
font_id,
|
|
scale: PxScale::from(font_size),
|
|
text: §ion.value,
|
|
};
|
|
|
|
Ok(section)
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
let section_glyphs = self
|
|
.brush
|
|
.compute_glyphs(§ions, bounds, text_alignment)?;
|
|
|
|
if section_glyphs.is_empty() {
|
|
return Ok(TextLayoutInfo::default());
|
|
}
|
|
|
|
let mut min_x: f32 = std::f32::MAX;
|
|
let mut min_y: f32 = std::f32::MAX;
|
|
let mut max_x: f32 = std::f32::MIN;
|
|
let mut max_y: f32 = std::f32::MIN;
|
|
|
|
for sg in §ion_glyphs {
|
|
let scaled_font = scaled_fonts[sg.section_index];
|
|
let glyph = &sg.glyph;
|
|
min_x = min_x.min(glyph.position.x);
|
|
min_y = min_y.min(glyph.position.y - scaled_font.ascent());
|
|
max_x = max_x.max(glyph.position.x + scaled_font.h_advance(glyph.id));
|
|
max_y = max_y.max(glyph.position.y - scaled_font.descent());
|
|
}
|
|
|
|
let size = Vec2::new(max_x - min_x, max_y - min_y);
|
|
|
|
let glyphs = self.brush.process_glyphs(
|
|
section_glyphs,
|
|
§ions,
|
|
font_atlas_set_storage,
|
|
fonts,
|
|
texture_atlases,
|
|
textures,
|
|
text_settings,
|
|
)?;
|
|
|
|
Ok(TextLayoutInfo { glyphs, size })
|
|
}
|
|
}
|