2021-01-03 20:39:11 +00:00
use ab_glyph ::{ Font as _ , FontArc , Glyph , ScaleFont as _ } ;
2020-11-13 00:21:48 +00:00
use bevy_asset ::{ Assets , Handle } ;
2022-04-25 13:54:46 +00:00
use bevy_math ::Vec2 ;
2021-12-14 03:58:23 +00:00
use bevy_render ::texture ::Image ;
2020-11-13 00:21:48 +00:00
use bevy_sprite ::TextureAtlas ;
2022-11-25 23:49:25 +00:00
use bevy_utils ::tracing ::warn ;
2020-11-13 00:21:48 +00:00
use glyph_brush_layout ::{
2023-01-21 00:17:11 +00:00
BuiltInLineBreaker , FontId , GlyphPositioner , Layout , SectionGeometry , SectionGlyph ,
SectionText , ToSectionText ,
2020-11-13 00:21:48 +00:00
} ;
2022-10-18 13:28:34 +00:00
use crate ::{
2023-01-21 00:17:11 +00:00
error ::TextError , BreakLineOn , Font , FontAtlasSet , FontAtlasWarning , GlyphAtlasInfo ,
TextAlignment , TextSettings , YAxisOrientation ,
2022-10-18 13:28:34 +00:00
} ;
2020-11-13 00:21:48 +00:00
pub struct GlyphBrush {
fonts : Vec < FontArc > ,
handles : Vec < Handle < Font > > ,
latest_font_id : FontId ,
}
impl Default for GlyphBrush {
fn default ( ) -> Self {
GlyphBrush {
fonts : Vec ::new ( ) ,
handles : Vec ::new ( ) ,
latest_font_id : FontId ( 0 ) ,
}
}
}
impl GlyphBrush {
pub fn compute_glyphs < S : ToSectionText > (
& self ,
sections : & [ S ] ,
2022-04-25 13:54:46 +00:00
bounds : Vec2 ,
2020-11-13 00:21:48 +00:00
text_alignment : TextAlignment ,
2023-04-05 21:25:53 +00:00
linebreak_behavior : BreakLineOn ,
2020-11-13 00:21:48 +00:00
) -> Result < Vec < SectionGlyph > , TextError > {
let geom = SectionGeometry {
2022-04-25 13:54:46 +00:00
bounds : ( bounds . x , bounds . y ) ,
2020-11-13 00:21:48 +00:00
.. Default ::default ( )
} ;
2023-01-21 00:17:11 +00:00
2023-04-05 21:25:53 +00:00
let lbb : BuiltInLineBreaker = linebreak_behavior . into ( ) ;
2023-01-21 00:17:11 +00:00
2020-11-13 00:21:48 +00:00
let section_glyphs = Layout ::default ( )
Remove VerticalAlign from TextAlignment (#6807)
# Objective
Remove the `VerticalAlign` enum.
Text's alignment field should only affect the text's internal text alignment, not its position. The only way to control a `TextBundle`'s position and bounds should be through the manipulation of the constraints in the `Style` components of the nodes in the Bevy UI's layout tree.
`Text2dBundle` should have a separate `Anchor` component that sets its position relative to its transform.
Related issues: #676, #1490, #5502, #5513, #5834, #6717, #6724, #6741, #6748
## Changelog
* Changed `TextAlignment` into an enum with `Left`, `Center`, and `Right` variants.
* Removed the `HorizontalAlign` and `VerticalAlign` types.
* Added an `Anchor` component to `Text2dBundle`
* Added `Component` derive to `Anchor`
* Use `f32::INFINITY` instead of `f32::MAX` to represent unbounded text in Text2dBounds
## Migration Guide
The `alignment` field of `Text` now only affects the text's internal alignment.
### Change `TextAlignment` to TextAlignment` which is now an enum. Replace:
* `TextAlignment::TOP_LEFT`, `TextAlignment::CENTER_LEFT`, `TextAlignment::BOTTOM_LEFT` with `TextAlignment::Left`
* `TextAlignment::TOP_CENTER`, `TextAlignment::CENTER_LEFT`, `TextAlignment::BOTTOM_CENTER` with `TextAlignment::Center`
* `TextAlignment::TOP_RIGHT`, `TextAlignment::CENTER_RIGHT`, `TextAlignment::BOTTOM_RIGHT` with `TextAlignment::Right`
### Changes for `Text2dBundle`
`Text2dBundle` has a new field 'text_anchor' that takes an `Anchor` component that controls its position relative to its transform.
2023-01-18 02:19:17 +00:00
. h_align ( text_alignment . into ( ) )
2023-01-21 00:17:11 +00:00
. line_breaker ( lbb )
2020-11-13 00:21:48 +00:00
. calculate_glyphs ( & self . fonts , & geom , sections ) ;
Ok ( section_glyphs )
}
2022-09-19 16:12:12 +00:00
#[ allow(clippy::too_many_arguments) ]
2020-11-13 00:21:48 +00:00
pub fn process_glyphs (
& self ,
glyphs : Vec < SectionGlyph > ,
2021-01-25 01:07:43 +00:00
sections : & [ SectionText ] ,
2020-11-13 00:21:48 +00:00
font_atlas_set_storage : & mut Assets < FontAtlasSet > ,
fonts : & Assets < Font > ,
texture_atlases : & mut Assets < TextureAtlas > ,
2021-12-14 03:58:23 +00:00
textures : & mut Assets < Image > ,
2022-09-19 16:12:12 +00:00
text_settings : & TextSettings ,
2022-11-25 23:49:25 +00:00
font_atlas_warning : & mut FontAtlasWarning ,
2022-10-18 13:28:34 +00:00
y_axis_orientation : YAxisOrientation ,
2020-11-13 00:21:48 +00:00
) -> Result < Vec < PositionedGlyph > , TextError > {
if glyphs . is_empty ( ) {
return Ok ( Vec ::new ( ) ) ;
}
2021-01-25 01:07:43 +00:00
let sections_data = sections
. iter ( )
. map ( | section | {
let handle = & self . handles [ section . font_id . 0 ] ;
let font = fonts . get ( handle ) . ok_or ( TextError ::NoSuchFont ) ? ;
let font_size = section . scale . y ;
Ok ( (
handle ,
font ,
font_size ,
ab_glyph ::Font ::as_scaled ( & font . font , font_size ) ,
) )
} )
. collect ::< Result < Vec < _ > , _ > > ( ) ? ;
2020-11-13 00:21:48 +00:00
let mut min_x = std ::f32 ::MAX ;
2022-10-11 12:51:44 +00:00
let mut min_y = std ::f32 ::MAX ;
2022-10-18 13:28:34 +00:00
let mut max_y = std ::f32 ::MIN ;
2022-02-13 22:33:55 +00:00
for sg in & glyphs {
2021-01-25 01:07:43 +00:00
let glyph = & sg . glyph ;
2022-10-11 12:51:44 +00:00
2021-01-25 01:07:43 +00:00
let scaled_font = sections_data [ sg . section_index ] . 3 ;
2020-11-13 00:21:48 +00:00
min_x = min_x . min ( glyph . position . x ) ;
2022-10-11 12:51:44 +00:00
min_y = min_y . min ( glyph . position . y - scaled_font . ascent ( ) ) ;
2022-10-18 13:28:34 +00:00
max_y = max_y . max ( glyph . position . y - scaled_font . descent ( ) ) ;
2020-11-13 00:21:48 +00:00
}
min_x = min_x . floor ( ) ;
2022-10-11 12:51:44 +00:00
min_y = min_y . floor ( ) ;
2022-10-18 13:28:34 +00:00
max_y = max_y . floor ( ) ;
2020-11-13 00:21:48 +00:00
let mut positioned_glyphs = Vec ::new ( ) ;
for sg in glyphs {
2021-01-01 21:36:00 +00:00
let SectionGlyph {
section_index : _ ,
2021-01-26 19:53:55 +00:00
byte_index ,
2021-01-01 21:36:00 +00:00
mut glyph ,
font_id : _ ,
} = sg ;
let glyph_id = glyph . id ;
2021-01-03 20:39:11 +00:00
let glyph_position = glyph . position ;
let adjust = GlyphPlacementAdjuster ::new ( & mut glyph ) ;
2021-01-25 01:07:43 +00:00
let section_data = sections_data [ sg . section_index ] ;
if let Some ( outlined_glyph ) = section_data . 1. font . outline_glyph ( glyph ) {
2020-11-13 00:21:48 +00:00
let bounds = outlined_glyph . px_bounds ( ) ;
2022-10-28 22:43:14 +00:00
let handle_font_atlas : Handle < FontAtlasSet > = section_data . 0. cast_weak ( ) ;
2020-11-13 00:21:48 +00:00
let font_atlas_set = font_atlas_set_storage
. get_or_insert_with ( handle_font_atlas , FontAtlasSet ::default ) ;
let atlas_info = font_atlas_set
2021-01-25 01:07:43 +00:00
. get_glyph_atlas_info ( section_data . 2 , glyph_id , glyph_position )
2020-11-13 00:21:48 +00:00
. map ( Ok )
. unwrap_or_else ( | | {
2022-11-25 23:49:25 +00:00
font_atlas_set . add_glyph_to_atlas ( texture_atlases , textures , outlined_glyph )
2020-11-13 00:21:48 +00:00
} ) ? ;
2022-11-25 23:49:25 +00:00
if ! text_settings . allow_dynamic_font_size
& & ! font_atlas_warning . warned
& & font_atlas_set . num_font_atlases ( ) > text_settings . max_font_atlases . get ( )
{
warn! ( " warning[B0005]: Number of font atlases has exceeded the maximum of {}. Performance and memory usage may suffer. " , text_settings . max_font_atlases . get ( ) ) ;
font_atlas_warning . warned = true ;
}
2020-11-13 00:21:48 +00:00
let texture_atlas = texture_atlases . get ( & atlas_info . texture_atlas ) . unwrap ( ) ;
2022-10-28 21:03:01 +00:00
let glyph_rect = texture_atlas . textures [ atlas_info . glyph_index ] ;
2021-01-26 19:53:55 +00:00
let size = Vec2 ::new ( glyph_rect . width ( ) , glyph_rect . height ( ) ) ;
2020-11-13 00:21:48 +00:00
2021-01-26 19:53:55 +00:00
let x = bounds . min . x + size . x / 2.0 - min_x ;
2022-10-18 13:28:34 +00:00
let y = match y_axis_orientation {
YAxisOrientation ::BottomToTop = > max_y - bounds . max . y + size . y / 2.0 ,
YAxisOrientation ::TopToBottom = > bounds . min . y + size . y / 2.0 - min_y ,
} ;
2021-01-03 20:39:11 +00:00
let position = adjust . position ( Vec2 ::new ( x , y ) ) ;
2020-11-13 00:21:48 +00:00
positioned_glyphs . push ( PositionedGlyph {
position ,
2021-01-26 19:53:55 +00:00
size ,
2020-11-13 00:21:48 +00:00
atlas_info ,
2021-01-25 01:07:43 +00:00
section_index : sg . section_index ,
2021-01-26 19:53:55 +00:00
byte_index ,
2020-11-13 00:21:48 +00:00
} ) ;
}
}
Ok ( positioned_glyphs )
}
pub fn add_font ( & mut self , handle : Handle < Font > , font : FontArc ) -> FontId {
self . fonts . push ( font ) ;
self . handles . push ( handle ) ;
let font_id = self . latest_font_id ;
self . latest_font_id = FontId ( font_id . 0 + 1 ) ;
font_id
}
}
#[ derive(Debug, Clone) ]
pub struct PositionedGlyph {
pub position : Vec2 ,
2021-01-26 19:53:55 +00:00
pub size : Vec2 ,
2020-11-13 00:21:48 +00:00
pub atlas_info : GlyphAtlasInfo ,
2021-01-25 01:07:43 +00:00
pub section_index : usize ,
2021-01-26 19:53:55 +00:00
pub byte_index : usize ,
2020-11-13 00:21:48 +00:00
}
2021-01-03 20:39:11 +00:00
#[ cfg(feature = " subpixel_glyph_atlas " ) ]
struct GlyphPlacementAdjuster ;
#[ cfg(feature = " subpixel_glyph_atlas " ) ]
impl GlyphPlacementAdjuster {
#[ inline(always) ]
pub fn new ( _ : & mut Glyph ) -> Self {
Self
}
#[ inline(always) ]
pub fn position ( & self , p : Vec2 ) -> Vec2 {
p
}
}
#[ cfg(not(feature = " subpixel_glyph_atlas " )) ]
struct GlyphPlacementAdjuster ( f32 ) ;
#[ cfg(not(feature = " subpixel_glyph_atlas " )) ]
impl GlyphPlacementAdjuster {
#[ inline(always) ]
pub fn new ( glyph : & mut Glyph ) -> Self {
let v = glyph . position . x . round ( ) ;
glyph . position . x = 0. ;
glyph . position . y = glyph . position . y . ceil ( ) ;
Self ( v )
}
#[ inline(always) ]
pub fn position ( & self , v : Vec2 ) -> Vec2 {
Vec2 ::new ( self . 0 , 0. ) + v
}
}