ui: feed computed text position into bevy_ui flex

and remove TextAlign because it is now redundant
This commit is contained in:
Carter Anderson 2020-07-27 21:04:04 -07:00
parent 1f006c348d
commit cf9501a50e
14 changed files with 104 additions and 104 deletions

View file

@ -2,7 +2,7 @@ use std::ops::{Add, AddAssign};
use glam::Vec2;
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Size<T> {
pub struct Size<T=f32> {
pub width: T,
pub height: T,
}

View file

@ -1,5 +1,5 @@
use crate::{Font, FontAtlasSet};
use ab_glyph::{FontVec, Glyph, PxScale, PxScaleFont, ScaleFont};
use ab_glyph::{Glyph, PxScale, ScaleFont};
use bevy_asset::Assets;
use bevy_math::{Mat4, Vec2, Vec3};
use bevy_render::{
@ -17,7 +17,6 @@ use bevy_sprite::{TextureAtlas, TextureAtlasSprite};
pub struct TextStyle {
pub font_size: f32,
pub color: Color,
pub align: TextAlign,
}
impl Default for TextStyle {
@ -25,23 +24,10 @@ impl Default for TextStyle {
Self {
color: Color::WHITE,
font_size: 12.0,
align: TextAlign::default(),
}
}
}
pub enum TextAlign {
Left,
Center,
Right,
}
impl Default for TextAlign {
fn default() -> Self {
TextAlign::Left
}
}
pub struct DrawableText<'a> {
pub font: &'a Font,
pub font_atlas_set: &'a FontAtlasSet,
@ -54,26 +40,6 @@ pub struct DrawableText<'a> {
pub text: &'a str,
}
fn get_text_width(text: &str, scaled_font: &PxScaleFont<&&FontVec>) -> f32 {
let mut last_glyph: Option<Glyph> = None;
let mut position = 0.0;
for character in text.chars() {
if character.is_control() {
continue;
}
let glyph = scaled_font.scaled_glyph(character);
if let Some(last_glyph) = last_glyph.take() {
position += scaled_font.kern(last_glyph.id, glyph.id);
}
position += scaled_font.h_advance(glyph.id);
last_glyph = Some(glyph);
}
position
}
impl<'a> Drawable for DrawableText<'a> {
fn draw(&mut self, draw: &mut Draw, context: &mut DrawContext) -> Result<(), DrawError> {
context.set_pipeline(
@ -109,17 +75,6 @@ impl<'a> Drawable for DrawableText<'a> {
let scale = PxScale::from(self.style.font_size);
let scaled_font = ab_glyph::Font::as_scaled(&font, scale);
let mut caret = self.position;
match self.style.align {
TextAlign::Left => { /* already aligned left by default */ }
TextAlign::Center => {
*caret.x_mut() +=
self.container_size.x() / 2.0 - get_text_width(&self.text, &scaled_font) / 2.0
}
TextAlign::Right => {
*caret.x_mut() += self.container_size.x() - get_text_width(&self.text, &scaled_font)
}
}
let mut last_glyph: Option<Glyph> = None;
// set local per-character bindings

View file

@ -1,5 +1,5 @@
use crate::{Font, FontAtlas};
use ab_glyph::ScaleFont;
use ab_glyph::{Glyph, ScaleFont};
use bevy_asset::{Assets, Handle};
use bevy_core::FloatOrd;
use bevy_math::Vec2;
@ -49,24 +49,35 @@ impl FontAtlasSet {
textures: &mut Assets<Texture>,
font_size: f32,
text: &str,
) {
) -> f32 {
let font = fonts.get(&self.font).unwrap();
let scaled_font = ab_glyph::Font::as_scaled(&font.font, font_size);
let font_atlas = self
.font_atlases
.entry(FloatOrd(font_size))
.or_insert_with(|| FontAtlas::new(textures, texture_atlases, Vec2::new(512.0, 512.0)));
let mut last_glyph: Option<Glyph> = None;
let mut width = 0.0;
for character in text.chars() {
if character.is_control() || font_atlas.get_char_index(character).is_some() {
if character.is_control() {
continue;
}
let glyph = scaled_font.scaled_glyph(character);
if let Some(outlined_glyph) = scaled_font.outline_glyph(glyph) {
let glyph_texture = Font::get_outlined_glyph_texture(outlined_glyph);
font_atlas.add_char(textures, texture_atlases, character, &glyph_texture);
if let Some(last_glyph) = last_glyph.take() {
width += scaled_font.kern(last_glyph.id, glyph.id);
}
if font_atlas.get_char_index(character).is_none() {
if let Some(outlined_glyph) = scaled_font.outline_glyph(glyph.clone()) {
let glyph_texture = Font::get_outlined_glyph_texture(outlined_glyph);
font_atlas.add_char(textures, texture_atlases, character, &glyph_texture);
}
}
width += scaled_font.h_advance(glyph.id);
last_glyph = Some(glyph);
}
width
}
pub fn get_glyph_atlas_info(&self, font_size: f32, character: char) -> Option<GlyphAtlasInfo> {

View file

@ -11,7 +11,7 @@ pub use font_atlas_set::*;
pub use font_loader::*;
pub mod prelude {
pub use crate::{Font, TextAlign, TextStyle};
pub use crate::{Font, TextStyle};
}
use bevy_app::prelude::*;

View file

@ -2,7 +2,7 @@ use super::Node;
use crate::{
render::UI_PIPELINE_HANDLE,
widget::{Button, Text},
Click, FocusPolicy, Hover, Style,
Click, FocusPolicy, Hover, Style, CalculatedSize,
};
use bevy_asset::Handle;
use bevy_ecs::Bundle;
@ -68,6 +68,7 @@ pub struct TextComponents {
pub style: Style,
pub draw: Draw,
pub text: Text,
pub calculated_size: CalculatedSize,
pub focus_policy: FocusPolicy,
pub transform: Transform,
pub local_transform: LocalTransform,
@ -83,6 +84,7 @@ impl Default for TextComponents {
},
text: Default::default(),
node: Default::default(),
calculated_size: Default::default(),
style: Default::default(),
transform: Default::default(),
local_transform: Default::default(),

View file

@ -1,6 +1,6 @@
mod convert;
use crate::{Style, Node};
use crate::{CalculatedSize, Node, Style};
use bevy_ecs::{Changed, Entity, Query, Res, ResMut, With, Without};
use bevy_math::Vec2;
use bevy_transform::prelude::{Children, LocalTransform, Parent};
@ -10,7 +10,6 @@ use stretch::Stretch;
pub struct FlexSurface {
entity_to_stretch: HashMap<Entity, stretch::node::Node>,
stretch_to_entity: HashMap<stretch::node::Node, Entity>,
window_nodes: HashMap<WindowId, stretch::node::Node>,
stretch: Stretch,
}
@ -19,7 +18,6 @@ impl Default for FlexSurface {
fn default() -> Self {
Self {
entity_to_stretch: Default::default(),
stretch_to_entity: Default::default(),
window_nodes: Default::default(),
stretch: Stretch::new(),
}
@ -30,12 +28,10 @@ impl FlexSurface {
pub fn upsert_node(&mut self, entity: Entity, style: &Style) {
let mut added = false;
let stretch = &mut self.stretch;
let stretch_to_entity = &mut self.stretch_to_entity;
let stretch_style = style.into();
let stretch_node = self.entity_to_stretch.entry(entity).or_insert_with(|| {
added = true;
let stretch_node = stretch.new_node(stretch_style, Vec::new()).unwrap();
stretch_to_entity.insert(stretch_node, entity);
stretch_node
});
@ -46,6 +42,44 @@ impl FlexSurface {
}
}
pub fn upsert_leaf(&mut self, entity: Entity, style: &Style, calculated_size: CalculatedSize) {
let mut added = false;
let stretch = &mut self.stretch;
let stretch_style = style.into();
let stretch_node = self.entity_to_stretch.entry(entity).or_insert_with(|| {
added = true;
let stretch_node = stretch
.new_leaf(
stretch_style,
Box::new(move |_| {
Ok(stretch::geometry::Size {
width: calculated_size.size.width,
height: calculated_size.size.height,
})
}),
)
.unwrap();
stretch_node
});
if !added {
self.stretch
.set_style(*stretch_node, stretch_style)
.unwrap();
self.stretch
.set_measure(
*stretch_node,
Some(Box::new(move |_| {
Ok(stretch::geometry::Size {
width: calculated_size.size.width,
height: calculated_size.size.height,
})
})),
)
.unwrap();
}
}
pub fn update_children(&mut self, entity: Entity, children: &Children) {
let mut stretch_children = Vec::with_capacity(children.len());
for child in children.iter() {
@ -63,10 +97,7 @@ impl FlexSurface {
let stretch = &mut self.stretch;
let node = self.window_nodes.entry(window.id).or_insert_with(|| {
stretch
.new_node(
stretch::style::Style::default(),
Vec::new(),
)
.new_node(stretch::style::Style::default(), Vec::new())
.unwrap()
});
@ -120,7 +151,8 @@ pub fn flex_node_system(
windows: Res<Windows>,
mut flex_surface: ResMut<FlexSurface>,
mut root_node_query: Query<With<Node, Without<Parent, Entity>>>,
mut node_query: Query<With<Node, (Entity, Changed<Style>)>>,
mut node_query: Query<With<Node, (Entity, Changed<Style>, Option<&CalculatedSize>)>>,
mut changed_size_query: Query<With<Node, (Entity, &Style, Changed<CalculatedSize>)>>,
mut children_query: Query<With<Node, (Entity, Changed<Children>)>>,
mut node_transform_query: Query<(Entity, &mut Node, &mut LocalTransform, Option<&Parent>)>,
) {
@ -130,9 +162,17 @@ pub fn flex_node_system(
}
// update changed nodes
for (entity, style) in &mut node_query.iter() {
for (entity, style, calculated_size) in &mut node_query.iter() {
// TODO: remove node from old hierarchy if its root has changed
flex_surface.upsert_node(entity, &style);
if let Some(calculated_size) = calculated_size {
flex_surface.upsert_leaf(entity, &style, *calculated_size);
} else {
flex_surface.upsert_node(entity, &style);
}
}
for (entity, style, calculated_size) in &mut changed_size_query.iter() {
flex_surface.upsert_leaf(entity, &style, *calculated_size);
}
// TODO: handle removed nodes

View file

@ -39,7 +39,7 @@ impl AppPlugin for UiPlugin {
// add these stages to front because these must run before transform update systems
.add_system_to_stage_front(stage::POST_UPDATE, flex_node_system.system())
.add_system_to_stage_front(stage::POST_UPDATE, ui_z_system.system())
.add_system_to_stage(stage::POST_UPDATE, widget::text_system.system())
.add_system_to_stage_front(stage::POST_UPDATE, widget::text_system.system())
.add_system_to_stage(bevy_render::stage::DRAW, widget::draw_text_system.system());
let resources = app.resources();

View file

@ -41,7 +41,11 @@ impl AddAssign<f32> for Val {
Val::Percent(value) => *value += rhs,
}
}
}
#[derive(Default, Copy, Clone)]
pub struct CalculatedSize {
pub size: Size,
}
#[derive(Clone, PartialEq, Debug)]

View file

@ -1,6 +1,7 @@
use crate::Node;
use crate::{CalculatedSize, Node};
use bevy_asset::{Assets, Handle};
use bevy_ecs::{Query, Res, ResMut};
use bevy_ecs::{Changed, Query, Res, ResMut};
use bevy_math::Size;
use bevy_render::{
draw::{Draw, DrawContext, Drawable},
renderer::{AssetRenderResourceBindings, RenderResourceBindings},
@ -22,9 +23,9 @@ pub fn text_system(
fonts: Res<Assets<Font>>,
mut font_atlas_sets: ResMut<Assets<FontAtlasSet>>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
mut query: Query<&Text>,
mut query: Query<(Changed<Text>, &mut CalculatedSize)>,
) {
for text in &mut query.iter() {
for (text, mut calculated_size) in &mut query.iter() {
let font_atlases = font_atlas_sets
.get_or_insert_with(Handle::from_id(text.font.id), || {
FontAtlasSet::new(text.font)
@ -35,13 +36,15 @@ pub fn text_system(
// 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.
font_atlases.add_glyphs_to_atlas(
let 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);
}
}

View file

@ -71,7 +71,6 @@ fn setup(
style: TextStyle {
color: Color::rgb(0.2, 0.2, 0.8).into(),
font_size: 40.0,
..Default::default()
},
},
style: Style {

View file

@ -95,13 +95,13 @@ fn setup(
.spawn(UiCameraComponents::default())
.spawn(ButtonComponents {
style: Style {
size: Size::new(Val::Px(150.0), Val::Px(70.0)),
align_self: AlignSelf::Center,
margin: Rect {
left: Val::Auto,
right: Val::Auto,
..Default::default()
},
size: Size::new(Val::Px(150.0), Val::Px(65.0)),
// center button
margin: Rect::all(Val::Auto),
// horizontally center child text
justify_content: JustifyContent::Center,
// vertically center child text
align_items: AlignItems::Center,
..Default::default()
},
material: button_materials.normal,
@ -109,21 +109,12 @@ fn setup(
})
.with_children(|parent| {
parent.spawn(TextComponents {
style: Style {
size: Size::new(Val::Percent(100.0), Val::Percent(100.0)),
margin: Rect {
bottom: Val::Px(10.0),
..Default::default()
},
..Default::default()
},
text: Text {
value: "Button".to_string(),
font: asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap(),
style: TextStyle {
font_size: 40.0,
color: Color::rgb(0.8, 0.8, 0.8),
align: TextAlign::Center,
},
},
..Default::default()

View file

@ -81,7 +81,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut state: ResM
style: TextStyle {
font_size: 60.0,
color: Color::WHITE,
..Default::default()
},
},
..Default::default()

View file

@ -26,17 +26,19 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let font_handle = asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap();
commands
// 2d camera
.spawn(Camera2dComponents::default())
.spawn(UiCameraComponents::default())
// texture
.spawn(TextComponents {
node: Node::default(),
style: Style {
align_self: AlignSelf::FlexEnd,
..Default::default()
},
text: Text {
value: "FPS:".to_string(),
font: font_handle,
style: TextStyle {
font_size: 60.0,
color: Color::WHITE,
align: TextAlign::Left,
},
},
..Default::default()

View file

@ -61,12 +61,7 @@ fn setup(
// text
parent.spawn(TextComponents {
style: Style {
size: Size::new(Val::Px(100.0), Val::Px(30.0)),
margin: Rect {
left: Val::Px(5.0),
top: Val::Px(5.0),
..Default::default()
},
margin: Rect::all(Val::Px(5.0)),
..Default::default()
},
text: Text {
@ -77,7 +72,6 @@ fn setup(
style: TextStyle {
font_size: 30.0,
color: Color::WHITE,
..Default::default()
},
},
..Default::default()
@ -103,7 +97,7 @@ fn setup(
bottom: Val::Px(10.0),
..Default::default()
},
border: Rect::all(Val::Px(10.0)),
border: Rect::all(Val::Px(20.0)),
..Default::default()
},
material: materials.add(Color::rgb(0.1, 0.1, 1.0).into()),