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 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
|
||||||
|
@ -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::*;
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)),
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user