ui: text alignment and more complete button example event handling

This commit is contained in:
Carter Anderson 2020-07-18 17:03:37 -07:00
parent a531c906a6
commit 6db82714dc
10 changed files with 112 additions and 76 deletions

View File

@ -1,7 +1,7 @@
use crate::{Font, FontAtlasSet}; use crate::{Font, FontAtlasSet};
use ab_glyph::{Glyph, PxScale, ScaleFont}; use ab_glyph::{FontVec, Glyph, PxScale, PxScaleFont, ScaleFont};
use bevy_asset::Assets; use bevy_asset::Assets;
use bevy_math::{Mat4, Vec3}; use bevy_math::{Mat4, Vec3, Vec2};
use bevy_render::{ use bevy_render::{
color::Color, color::Color,
draw::{Draw, DrawContext, DrawError, Drawable}, draw::{Draw, DrawContext, DrawError, Drawable},
@ -17,41 +17,61 @@ use bevy_sprite::{TextureAtlas, TextureAtlasSprite};
pub struct TextStyle { pub struct TextStyle {
pub font_size: f32, pub font_size: f32,
pub color: Color, pub color: Color,
pub align: TextAlign,
}
impl Default for TextStyle {
fn default() -> Self {
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 struct DrawableText<'a> {
font: &'a Font, pub font: &'a Font,
font_atlas_set: &'a FontAtlasSet, pub font_atlas_set: &'a FontAtlasSet,
texture_atlases: &'a Assets<TextureAtlas>, pub texture_atlases: &'a Assets<TextureAtlas>,
render_resource_bindings: &'a mut RenderResourceBindings, pub render_resource_bindings: &'a mut RenderResourceBindings,
asset_render_resource_bindings: &'a mut AssetRenderResourceBindings, pub asset_render_resource_bindings: &'a mut AssetRenderResourceBindings,
position: Vec3, pub position: Vec3,
style: &'a TextStyle, pub container_size: Vec2,
text: &'a str, pub style: &'a TextStyle,
pub text: &'a str,
} }
impl<'a> DrawableText<'a> { fn get_text_width(text: &str, scaled_font: &PxScaleFont<&&FontVec>) -> f32 {
pub fn new( let mut last_glyph: Option<Glyph> = None;
font: &'a Font, let mut position = 0.0;
font_atlas_set: &'a FontAtlasSet, for character in text.chars() {
texture_atlases: &'a Assets<TextureAtlas>, if character.is_control() {
render_resource_bindings: &'a mut RenderResourceBindings, continue;
asset_render_resource_bindings: &'a mut AssetRenderResourceBindings,
position: Vec3,
style: &'a TextStyle,
text: &'a str,
) -> Self {
Self {
font,
font_atlas_set,
texture_atlases,
render_resource_bindings,
asset_render_resource_bindings,
position,
style,
text,
} }
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> { impl<'a> Drawable for DrawableText<'a> {
@ -89,6 +109,14 @@ impl<'a> Drawable for DrawableText<'a> {
let scale = PxScale::from(self.style.font_size); let scale = PxScale::from(self.style.font_size);
let scaled_font = ab_glyph::Font::as_scaled(&font, scale); let scaled_font = ab_glyph::Font::as_scaled(&font, scale);
let mut caret = self.position; 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; let mut last_glyph: Option<Glyph> = None;
// set local per-character bindings // set local per-character bindings

View File

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

View File

@ -42,11 +42,11 @@ impl<'a> ChildBuilder<'a> {
} }
pub trait BuildChildren { pub trait BuildChildren {
fn with_children(&mut self, spawn_children: impl FnMut(&mut ChildBuilder)) -> &mut Self; fn with_children(&mut self, parent: impl FnMut(&mut ChildBuilder)) -> &mut Self;
} }
impl BuildChildren for Commands { impl BuildChildren for Commands {
fn with_children(&mut self, mut spawn_children: impl FnMut(&mut ChildBuilder)) -> &mut Self { fn with_children(&mut self, mut parent: impl FnMut(&mut ChildBuilder)) -> &mut Self {
{ {
let mut commands = self.commands.lock().unwrap(); let mut commands = self.commands.lock().unwrap();
let current_entity = commands.current_entity.expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); let current_entity = commands.current_entity.expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first.");
@ -55,7 +55,7 @@ impl BuildChildren for Commands {
parent_entities: vec![current_entity], parent_entities: vec![current_entity],
}; };
spawn_children(&mut builder); parent(&mut builder);
} }
self self
} }

View File

@ -65,6 +65,7 @@ pub struct LabelComponents {
pub node: Node, pub node: Node,
pub draw: Draw, pub draw: Draw,
pub label: Label, pub label: Label,
pub focus_policy: FocusPolicy,
pub transform: Transform, pub transform: Transform,
pub translation: Translation, pub translation: Translation,
pub rotation: Rotation, pub rotation: Rotation,
@ -76,6 +77,7 @@ impl Default for LabelComponents {
LabelComponents { LabelComponents {
label: Label::default(), label: Label::default(),
node: Default::default(), node: Default::default(),
focus_policy: FocusPolicy::Pass,
draw: Draw { draw: Draw {
is_transparent: true, is_transparent: true,
..Default::default() ..Default::default()

View File

@ -2,7 +2,6 @@ use crate::Node;
use bevy_asset::{Assets, Handle}; use bevy_asset::{Assets, Handle};
use bevy_ecs::{Query, Res, ResMut}; use bevy_ecs::{Query, Res, ResMut};
use bevy_render::{ use bevy_render::{
color::Color,
draw::{Draw, DrawContext, Drawable}, draw::{Draw, DrawContext, Drawable},
renderer::{AssetRenderResourceBindings, RenderResourceBindings}, renderer::{AssetRenderResourceBindings, RenderResourceBindings},
texture::Texture, texture::Texture,
@ -11,25 +10,13 @@ use bevy_sprite::TextureAtlas;
use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle}; use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle};
use bevy_transform::prelude::Transform; use bevy_transform::prelude::Transform;
#[derive(Default)]
pub struct Label { pub struct Label {
pub text: String, pub text: String,
pub font: Handle<Font>, pub font: Handle<Font>,
pub style: TextStyle, pub style: TextStyle,
} }
impl Default for Label {
fn default() -> Self {
Label {
text: String::new(),
style: TextStyle {
color: Color::WHITE,
font_size: 12.0,
},
font: Handle::default(),
}
}
}
impl Label { impl Label {
pub fn label_system( pub fn label_system(
mut textures: ResMut<Assets<Texture>>, mut textures: ResMut<Assets<Texture>>,
@ -69,21 +56,21 @@ impl Label {
mut query: Query<(&mut Draw, &Label, &Node, &Transform)>, mut query: Query<(&mut Draw, &Label, &Node, &Transform)>,
) { ) {
for (mut draw, label, node, transform) in &mut query.iter() { for (mut draw, label, node, transform) in &mut query.iter() {
// let position = transform.0 - quad.size / 2.0;
let position = transform.value.w_axis().truncate() - (node.size / 2.0).extend(0.0); let position = transform.value.w_axis().truncate() - (node.size / 2.0).extend(0.0);
let mut drawable_text = DrawableText::new( let mut drawable_text = DrawableText {
fonts.get(&label.font).unwrap(), font: fonts.get(&label.font).unwrap(),
font_atlas_sets font_atlas_set: font_atlas_sets
.get(&label.font.as_handle::<FontAtlasSet>()) .get(&label.font.as_handle::<FontAtlasSet>())
.unwrap(), .unwrap(),
&texture_atlases, texture_atlases: &texture_atlases,
&mut render_resource_bindings, render_resource_bindings: &mut render_resource_bindings,
&mut asset_render_resource_bindings, asset_render_resource_bindings: &mut asset_render_resource_bindings,
position, position,
&label.style, style: &label.style,
&label.text, text: &label.text,
); container_size: node.size,
};
drawable_text.draw(&mut draw, &mut draw_context).unwrap(); drawable_text.draw(&mut draw, &mut draw_context).unwrap();
} }
} }

View File

@ -71,6 +71,7 @@ fn setup(
style: TextStyle { style: TextStyle {
color: Color::rgb(0.2, 0.2, 0.8).into(), color: Color::rgb(0.2, 0.2, 0.8).into(),
font_size: 40.0, font_size: 40.0,
..Default::default()
}, },
}, },
node: Node::new(Anchors::TOP_LEFT, Margins::new(10.0, 50.0, 10.0, 50.0)), node: Node::new(Anchors::TOP_LEFT, Margins::new(10.0, 50.0, 10.0, 50.0)),

View File

@ -31,18 +31,41 @@ fn button_system(
mut click_query: Query<( mut click_query: Query<(
&Button, &Button,
Changed<Click>, Changed<Click>,
Option<&Hover>,
&mut Handle<ColorMaterial>, &mut Handle<ColorMaterial>,
&Children, &Children,
)>, )>,
mut hover_query: Query<( mut hover_query: Query<(
&Button, &Button,
Changed<Hover>, Changed<Hover>,
Option<&Click>,
&mut Handle<ColorMaterial>, &mut Handle<ColorMaterial>,
&Children, &Children,
)>, )>,
label_query: Query<&mut Label>, label_query: Query<&mut Label>,
) { ) {
for (_button, click, mut material, children) in &mut click_query.iter() { for (_button, hover, click, mut material, children) in &mut hover_query.iter() {
let mut label = label_query.get_mut::<Label>(children[0]).unwrap();
match *hover {
Hover::Hovered => {
if let Some(Click::Released) = click {
label.text = "Hover".to_string();
*material = button_materials.hovered;
}
}
Hover::NotHovered => {
if let Some(Click::Pressed) = click {
label.text = "Press".to_string();
*material = button_materials.pressed;
} else {
label.text = "Button".to_string();
*material = button_materials.normal;
}
}
}
}
for (_button, click, hover, mut material, children) in &mut click_query.iter() {
let mut label = label_query.get_mut::<Label>(children[0]).unwrap(); let mut label = label_query.get_mut::<Label>(children[0]).unwrap();
match *click { match *click {
Click::Pressed => { Click::Pressed => {
@ -50,22 +73,13 @@ fn button_system(
*material = button_materials.pressed; *material = button_materials.pressed;
} }
Click::Released => { Click::Released => {
label.text = "Button".to_string(); if let Some(Hover::Hovered) = hover {
*material = button_materials.normal; label.text = "Hover".to_string();
} *material = button_materials.hovered;
} } else {
} label.text = "Button".to_string();
*material = button_materials.normal;
for (_button, hover, mut material, children) in &mut hover_query.iter() { }
let mut label = label_query.get_mut::<Label>(children[0]).unwrap();
match *hover {
Hover::Hovered => {
label.text = "Hover".to_string();
*material = button_materials.hovered;
}
Hover::NotHovered => {
label.text = "Button".to_string();
*material = button_materials.normal;
} }
} }
} }
@ -86,13 +100,14 @@ fn setup(
}) })
.with_children(|parent| { .with_children(|parent| {
parent.spawn(LabelComponents { parent.spawn(LabelComponents {
node: Node::new(Anchors::CENTER, Margins::new(52.0, 10.0, 20.0, 20.0)), node: Node::new(Anchors::FULL, Margins::new(0.0, 0.0, 12.0, 0.0)),
label: Label { label: Label {
text: "Button".to_string(), text: "Button".to_string(),
font: asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap(), font: asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap(),
style: TextStyle { style: TextStyle {
font_size: 40.0, font_size: 40.0,
color: Color::rgb(0.8, 0.8, 0.8), color: Color::rgb(0.8, 0.8, 0.8),
align: TextAlign::Center,
}, },
}, },
..Default::default() ..Default::default()

View File

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

View File

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

View File

@ -47,6 +47,7 @@ fn setup(
style: TextStyle { style: TextStyle {
font_size: 30.0, font_size: 30.0,
color: Color::WHITE, color: Color::WHITE,
..Default::default()
}, },
}, },
..Default::default() ..Default::default()