2020-12-27 19:19:03 +00:00
|
|
|
use bevy_asset::Handle;
|
2021-12-14 03:58:23 +00:00
|
|
|
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
|
2022-05-03 19:20:13 +00:00
|
|
|
use bevy_reflect::{prelude::*, FromReflect};
|
2021-01-25 01:07:43 +00:00
|
|
|
use bevy_render::color::Color;
|
2021-12-14 03:58:23 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2020-12-27 19:19:03 +00:00
|
|
|
|
2021-01-25 01:07:43 +00:00
|
|
|
use crate::Font;
|
2020-12-27 19:19:03 +00:00
|
|
|
|
2021-12-14 03:58:23 +00:00
|
|
|
#[derive(Component, Debug, Default, Clone, Reflect)]
|
2022-05-03 19:20:13 +00:00
|
|
|
#[reflect(Component, Default)]
|
2020-12-27 19:19:03 +00:00
|
|
|
pub struct Text {
|
2021-01-25 01:07:43 +00:00
|
|
|
pub sections: Vec<TextSection>,
|
|
|
|
pub alignment: TextAlignment,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Text {
|
2022-07-20 14:14:29 +00:00
|
|
|
/// Constructs a [`Text`] with a single section.
|
2021-01-25 01:07:43 +00:00
|
|
|
///
|
|
|
|
/// ```
|
2022-07-20 14:14:29 +00:00
|
|
|
/// # use bevy_asset::Handle;
|
2021-01-25 01:07:43 +00:00
|
|
|
/// # use bevy_render::color::Color;
|
2021-12-14 03:58:23 +00:00
|
|
|
/// # use bevy_text::{Font, Text, TextAlignment, TextStyle, HorizontalAlign, VerticalAlign};
|
2021-01-25 01:07:43 +00:00
|
|
|
/// #
|
|
|
|
/// # let font_handle: Handle<Font> = Default::default();
|
|
|
|
/// #
|
2022-07-20 14:14:29 +00:00
|
|
|
/// // Basic usage.
|
|
|
|
/// let hello_world = Text::from_section(
|
|
|
|
/// // Accepts a String or any type that converts into a String, such as &str.
|
|
|
|
/// "hello world!",
|
2021-01-25 01:07:43 +00:00
|
|
|
/// TextStyle {
|
|
|
|
/// font: font_handle.clone(),
|
|
|
|
/// font_size: 60.0,
|
|
|
|
/// color: Color::WHITE,
|
|
|
|
/// },
|
|
|
|
/// );
|
|
|
|
///
|
2022-07-20 14:14:29 +00:00
|
|
|
/// let hello_bevy = Text::from_section(
|
2021-01-25 01:07:43 +00:00
|
|
|
/// "hello bevy!",
|
|
|
|
/// TextStyle {
|
|
|
|
/// font: font_handle,
|
|
|
|
/// font_size: 60.0,
|
|
|
|
/// color: Color::WHITE,
|
|
|
|
/// },
|
2022-07-20 14:14:29 +00:00
|
|
|
/// ) // You can still add an alignment.
|
|
|
|
/// .with_alignment(TextAlignment::CENTER);
|
|
|
|
/// ```
|
|
|
|
pub fn from_section(value: impl Into<String>, style: TextStyle) -> Self {
|
|
|
|
Self {
|
|
|
|
sections: vec![TextSection::new(value, style)],
|
|
|
|
alignment: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Constructs a [`Text`] from a list of sections.
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use bevy_asset::Handle;
|
|
|
|
/// # use bevy_render::color::Color;
|
|
|
|
/// # use bevy_text::{Font, Text, TextStyle, TextSection};
|
|
|
|
/// #
|
|
|
|
/// # let font_handle: Handle<Font> = Default::default();
|
|
|
|
/// #
|
|
|
|
/// let hello_world = Text::from_sections([
|
|
|
|
/// TextSection::new(
|
|
|
|
/// "Hello, ",
|
|
|
|
/// TextStyle {
|
|
|
|
/// font: font_handle.clone(),
|
|
|
|
/// font_size: 60.0,
|
|
|
|
/// color: Color::BLUE,
|
|
|
|
/// },
|
|
|
|
/// ),
|
|
|
|
/// TextSection::new(
|
|
|
|
/// "World!",
|
|
|
|
/// TextStyle {
|
|
|
|
/// font: font_handle,
|
|
|
|
/// font_size: 60.0,
|
|
|
|
/// color: Color::RED,
|
|
|
|
/// },
|
|
|
|
/// ),
|
|
|
|
/// ]);
|
2021-01-25 01:07:43 +00:00
|
|
|
/// ```
|
2022-07-20 14:14:29 +00:00
|
|
|
pub fn from_sections(sections: impl IntoIterator<Item = TextSection>) -> Self {
|
2021-01-25 01:07:43 +00:00
|
|
|
Self {
|
2022-07-20 14:14:29 +00:00
|
|
|
sections: sections.into_iter().collect(),
|
|
|
|
alignment: Default::default(),
|
2021-01-25 01:07:43 +00:00
|
|
|
}
|
|
|
|
}
|
2022-07-20 14:14:29 +00:00
|
|
|
|
|
|
|
/// Returns this [`Text`] with a new [`TextAlignment`].
|
|
|
|
pub const fn with_alignment(mut self, alignment: TextAlignment) -> Self {
|
|
|
|
self.alignment = alignment;
|
|
|
|
self
|
|
|
|
}
|
2021-01-25 01:07:43 +00:00
|
|
|
}
|
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
#[derive(Debug, Default, Clone, FromReflect, Reflect)]
|
2021-01-25 01:07:43 +00:00
|
|
|
pub struct TextSection {
|
2020-12-27 19:19:03 +00:00
|
|
|
pub value: String,
|
|
|
|
pub style: TextStyle,
|
|
|
|
}
|
|
|
|
|
2022-07-20 14:14:29 +00:00
|
|
|
impl TextSection {
|
|
|
|
/// Create a new [`TextSection`].
|
|
|
|
pub fn new(value: impl Into<String>, style: TextStyle) -> Self {
|
|
|
|
Self {
|
|
|
|
value: value.into(),
|
|
|
|
style,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create an empty [`TextSection`] from a style. Useful when the value will be set dynamically.
|
|
|
|
pub const fn from_style(style: TextStyle) -> Self {
|
|
|
|
Self {
|
|
|
|
value: String::new(),
|
|
|
|
style,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-14 03:58:23 +00:00
|
|
|
#[derive(Debug, Clone, Copy, Reflect)]
|
2021-01-25 01:07:43 +00:00
|
|
|
pub struct TextAlignment {
|
|
|
|
pub vertical: VerticalAlign,
|
|
|
|
pub horizontal: HorizontalAlign,
|
|
|
|
}
|
|
|
|
|
2022-07-20 14:14:29 +00:00
|
|
|
impl TextAlignment {
|
|
|
|
/// A [`TextAlignment`] set to the top-left.
|
|
|
|
pub const TOP_LEFT: Self = TextAlignment {
|
|
|
|
vertical: VerticalAlign::Top,
|
|
|
|
horizontal: HorizontalAlign::Left,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A [`TextAlignment`] set to the top-center.
|
|
|
|
pub const TOP_CENTER: Self = TextAlignment {
|
|
|
|
vertical: VerticalAlign::Top,
|
|
|
|
horizontal: HorizontalAlign::Center,
|
|
|
|
};
|
|
|
|
|
2022-07-21 20:46:54 +00:00
|
|
|
/// A [`TextAlignment`] set to the top-right.
|
2022-07-20 14:14:29 +00:00
|
|
|
pub const TOP_RIGHT: Self = TextAlignment {
|
|
|
|
vertical: VerticalAlign::Top,
|
|
|
|
horizontal: HorizontalAlign::Right,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A [`TextAlignment`] set to center the center-left.
|
|
|
|
pub const CENTER_LEFT: Self = TextAlignment {
|
|
|
|
vertical: VerticalAlign::Center,
|
|
|
|
horizontal: HorizontalAlign::Left,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A [`TextAlignment`] set to center on both axes.
|
|
|
|
pub const CENTER: Self = TextAlignment {
|
|
|
|
vertical: VerticalAlign::Center,
|
|
|
|
horizontal: HorizontalAlign::Center,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A [`TextAlignment`] set to the center-right.
|
|
|
|
pub const CENTER_RIGHT: Self = TextAlignment {
|
|
|
|
vertical: VerticalAlign::Center,
|
|
|
|
horizontal: HorizontalAlign::Right,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A [`TextAlignment`] set to the bottom-left.
|
|
|
|
pub const BOTTOM_LEFT: Self = TextAlignment {
|
|
|
|
vertical: VerticalAlign::Bottom,
|
|
|
|
horizontal: HorizontalAlign::Left,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A [`TextAlignment`] set to the bottom-center.
|
|
|
|
pub const BOTTOM_CENTER: Self = TextAlignment {
|
|
|
|
vertical: VerticalAlign::Bottom,
|
|
|
|
horizontal: HorizontalAlign::Center,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A [`TextAlignment`] set to the bottom-right.
|
|
|
|
pub const BOTTOM_RIGHT: Self = TextAlignment {
|
|
|
|
vertical: VerticalAlign::Bottom,
|
|
|
|
horizontal: HorizontalAlign::Right,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-01-25 01:07:43 +00:00
|
|
|
impl Default for TextAlignment {
|
|
|
|
fn default() -> Self {
|
2022-07-20 14:14:29 +00:00
|
|
|
TextAlignment::TOP_LEFT
|
2021-01-25 01:07:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-14 03:58:23 +00:00
|
|
|
/// Describes horizontal alignment preference for positioning & bounds.
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
|
|
|
|
#[reflect_value(Serialize, Deserialize)]
|
|
|
|
pub enum HorizontalAlign {
|
|
|
|
/// Leftmost character is immediately to the right of the render position.<br/>
|
|
|
|
/// Bounds start from the render position and advance rightwards.
|
|
|
|
Left,
|
|
|
|
/// Leftmost & rightmost characters are equidistant to the render position.<br/>
|
|
|
|
/// Bounds start from the render position and advance equally left & right.
|
|
|
|
Center,
|
2022-07-21 20:46:54 +00:00
|
|
|
/// Rightmost character is immediately to the left of the render position.<br/>
|
2021-12-14 03:58:23 +00:00
|
|
|
/// Bounds start from the render position and advance leftwards.
|
|
|
|
Right,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<HorizontalAlign> for glyph_brush_layout::HorizontalAlign {
|
|
|
|
fn from(val: HorizontalAlign) -> Self {
|
|
|
|
match val {
|
|
|
|
HorizontalAlign::Left => glyph_brush_layout::HorizontalAlign::Left,
|
|
|
|
HorizontalAlign::Center => glyph_brush_layout::HorizontalAlign::Center,
|
|
|
|
HorizontalAlign::Right => glyph_brush_layout::HorizontalAlign::Right,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Describes vertical alignment preference for positioning & bounds. Currently a placeholder
|
|
|
|
/// for future functionality.
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
|
|
|
|
#[reflect_value(Serialize, Deserialize)]
|
|
|
|
pub enum VerticalAlign {
|
|
|
|
/// Characters/bounds start underneath the render position and progress downwards.
|
|
|
|
Top,
|
|
|
|
/// Characters/bounds center at the render position and progress outward equally.
|
|
|
|
Center,
|
|
|
|
/// Characters/bounds start above the render position and progress upward.
|
|
|
|
Bottom,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<VerticalAlign> for glyph_brush_layout::VerticalAlign {
|
|
|
|
fn from(val: VerticalAlign) -> Self {
|
|
|
|
match val {
|
|
|
|
VerticalAlign::Top => glyph_brush_layout::VerticalAlign::Top,
|
|
|
|
VerticalAlign::Center => glyph_brush_layout::VerticalAlign::Center,
|
|
|
|
VerticalAlign::Bottom => glyph_brush_layout::VerticalAlign::Bottom,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
#[derive(Clone, Debug, Reflect, FromReflect)]
|
2021-01-25 01:07:43 +00:00
|
|
|
pub struct TextStyle {
|
|
|
|
pub font: Handle<Font>,
|
|
|
|
pub font_size: f32,
|
|
|
|
pub color: Color,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TextStyle {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
font: Default::default(),
|
|
|
|
font_size: 12.0,
|
|
|
|
color: Color::WHITE,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|