mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
ui: feed computed text position into bevy_ui flex
and remove TextAlign because it is now redundant
This commit is contained in:
parent
1f006c348d
commit
cf9501a50e
14 changed files with 104 additions and 104 deletions
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()),
|
||||
|
|
Loading…
Reference in a new issue