ui: text alignment and more complete button example event handling
This commit is contained in:
parent
a531c906a6
commit
6db82714dc
@ -1,7 +1,7 @@
|
||||
use crate::{Font, FontAtlasSet};
|
||||
use ab_glyph::{Glyph, PxScale, ScaleFont};
|
||||
use ab_glyph::{FontVec, Glyph, PxScale, PxScaleFont, ScaleFont};
|
||||
use bevy_asset::Assets;
|
||||
use bevy_math::{Mat4, Vec3};
|
||||
use bevy_math::{Mat4, Vec3, Vec2};
|
||||
use bevy_render::{
|
||||
color::Color,
|
||||
draw::{Draw, DrawContext, DrawError, Drawable},
|
||||
@ -17,41 +17,61 @@ use bevy_sprite::{TextureAtlas, TextureAtlasSprite};
|
||||
pub struct TextStyle {
|
||||
pub font_size: f32,
|
||||
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> {
|
||||
font: &'a Font,
|
||||
font_atlas_set: &'a FontAtlasSet,
|
||||
texture_atlases: &'a Assets<TextureAtlas>,
|
||||
render_resource_bindings: &'a mut RenderResourceBindings,
|
||||
asset_render_resource_bindings: &'a mut AssetRenderResourceBindings,
|
||||
position: Vec3,
|
||||
style: &'a TextStyle,
|
||||
text: &'a str,
|
||||
pub font: &'a Font,
|
||||
pub font_atlas_set: &'a FontAtlasSet,
|
||||
pub texture_atlases: &'a Assets<TextureAtlas>,
|
||||
pub render_resource_bindings: &'a mut RenderResourceBindings,
|
||||
pub asset_render_resource_bindings: &'a mut AssetRenderResourceBindings,
|
||||
pub position: Vec3,
|
||||
pub container_size: Vec2,
|
||||
pub style: &'a TextStyle,
|
||||
pub text: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> DrawableText<'a> {
|
||||
pub fn new(
|
||||
font: &'a Font,
|
||||
font_atlas_set: &'a FontAtlasSet,
|
||||
texture_atlases: &'a Assets<TextureAtlas>,
|
||||
render_resource_bindings: &'a mut RenderResourceBindings,
|
||||
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,
|
||||
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> {
|
||||
@ -89,6 +109,14 @@ 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
|
||||
|
@ -11,7 +11,7 @@ pub use font_atlas_set::*;
|
||||
pub use font_loader::*;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{Font, TextStyle};
|
||||
pub use crate::{Font, TextStyle, TextAlign};
|
||||
}
|
||||
|
||||
use bevy_app::prelude::*;
|
||||
|
@ -42,11 +42,11 @@ impl<'a> ChildBuilder<'a> {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 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],
|
||||
};
|
||||
|
||||
spawn_children(&mut builder);
|
||||
parent(&mut builder);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ pub struct LabelComponents {
|
||||
pub node: Node,
|
||||
pub draw: Draw,
|
||||
pub label: Label,
|
||||
pub focus_policy: FocusPolicy,
|
||||
pub transform: Transform,
|
||||
pub translation: Translation,
|
||||
pub rotation: Rotation,
|
||||
@ -76,6 +77,7 @@ impl Default for LabelComponents {
|
||||
LabelComponents {
|
||||
label: Label::default(),
|
||||
node: Default::default(),
|
||||
focus_policy: FocusPolicy::Pass,
|
||||
draw: Draw {
|
||||
is_transparent: true,
|
||||
..Default::default()
|
||||
|
@ -2,7 +2,6 @@ use crate::Node;
|
||||
use bevy_asset::{Assets, Handle};
|
||||
use bevy_ecs::{Query, Res, ResMut};
|
||||
use bevy_render::{
|
||||
color::Color,
|
||||
draw::{Draw, DrawContext, Drawable},
|
||||
renderer::{AssetRenderResourceBindings, RenderResourceBindings},
|
||||
texture::Texture,
|
||||
@ -11,25 +10,13 @@ use bevy_sprite::TextureAtlas;
|
||||
use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle};
|
||||
use bevy_transform::prelude::Transform;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Label {
|
||||
pub text: String,
|
||||
pub font: Handle<Font>,
|
||||
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 {
|
||||
pub fn label_system(
|
||||
mut textures: ResMut<Assets<Texture>>,
|
||||
@ -69,21 +56,21 @@ impl Label {
|
||||
mut query: Query<(&mut Draw, &Label, &Node, &Transform)>,
|
||||
) {
|
||||
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 mut drawable_text = DrawableText::new(
|
||||
fonts.get(&label.font).unwrap(),
|
||||
font_atlas_sets
|
||||
let mut drawable_text = DrawableText {
|
||||
font: fonts.get(&label.font).unwrap(),
|
||||
font_atlas_set: font_atlas_sets
|
||||
.get(&label.font.as_handle::<FontAtlasSet>())
|
||||
.unwrap(),
|
||||
&texture_atlases,
|
||||
&mut render_resource_bindings,
|
||||
&mut asset_render_resource_bindings,
|
||||
texture_atlases: &texture_atlases,
|
||||
render_resource_bindings: &mut render_resource_bindings,
|
||||
asset_render_resource_bindings: &mut asset_render_resource_bindings,
|
||||
position,
|
||||
&label.style,
|
||||
&label.text,
|
||||
);
|
||||
style: &label.style,
|
||||
text: &label.text,
|
||||
container_size: node.size,
|
||||
};
|
||||
drawable_text.draw(&mut draw, &mut draw_context).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ fn setup(
|
||||
style: TextStyle {
|
||||
color: Color::rgb(0.2, 0.2, 0.8).into(),
|
||||
font_size: 40.0,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
node: Node::new(Anchors::TOP_LEFT, Margins::new(10.0, 50.0, 10.0, 50.0)),
|
||||
|
@ -31,18 +31,41 @@ fn button_system(
|
||||
mut click_query: Query<(
|
||||
&Button,
|
||||
Changed<Click>,
|
||||
Option<&Hover>,
|
||||
&mut Handle<ColorMaterial>,
|
||||
&Children,
|
||||
)>,
|
||||
mut hover_query: Query<(
|
||||
&Button,
|
||||
Changed<Hover>,
|
||||
Option<&Click>,
|
||||
&mut Handle<ColorMaterial>,
|
||||
&Children,
|
||||
)>,
|
||||
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();
|
||||
match *click {
|
||||
Click::Pressed => {
|
||||
@ -50,22 +73,13 @@ fn button_system(
|
||||
*material = button_materials.pressed;
|
||||
}
|
||||
Click::Released => {
|
||||
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;
|
||||
if let Some(Hover::Hovered) = hover {
|
||||
label.text = "Hover".to_string();
|
||||
*material = button_materials.hovered;
|
||||
} else {
|
||||
label.text = "Button".to_string();
|
||||
*material = button_materials.normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -86,13 +100,14 @@ fn setup(
|
||||
})
|
||||
.with_children(|parent| {
|
||||
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 {
|
||||
text: "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()
|
||||
|
@ -75,6 +75,7 @@ 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()
|
||||
|
@ -36,6 +36,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
style: TextStyle {
|
||||
font_size: 60.0,
|
||||
color: Color::WHITE,
|
||||
align: TextAlign::Left,
|
||||
},
|
||||
},
|
||||
..Default::default()
|
||||
|
@ -47,6 +47,7 @@ fn setup(
|
||||
style: TextStyle {
|
||||
font_size: 30.0,
|
||||
color: Color::WHITE,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
..Default::default()
|
||||
|
Loading…
Reference in New Issue
Block a user