use std::collections::HashMap;
use bevy::color::Srgba;
use bevy::math::{Rect, Vec2};
use bevy::prelude::*;
use crate::font_id::FontId;
#[derive(Debug, Clone, Reflect)]
pub struct TextSegment {
pub text: String,
pub style: SegmentStyle,
}
#[derive(Debug, Clone, Default, Reflect)]
pub struct SegmentStyle {
pub color: Option<Srgba>,
}
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
#[require(TextBlockStyling, TextBlockLayout)]
pub struct TextBlock {
pub segments: Vec<TextSegment>,
}
impl TextBlock {
pub fn new(text: impl Into<String>) -> Self {
Self {
segments: vec![TextSegment {
text: text.into(),
style: SegmentStyle::default(),
}],
}
}
pub fn from_segments(segments: Vec<TextSegment>) -> Self {
Self { segments }
}
pub fn full_text(&self) -> String {
self.segments.iter().map(|s| s.text.as_str()).collect()
}
pub fn get_single(&self) -> Option<&str> {
if self.segments.len() == 1 {
Some(&self.segments[0].text)
} else {
None
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Reflect)]
pub enum TextAlign {
#[default]
Left,
Center,
Right,
}
impl TextAlign {
pub fn factor(&self) -> f32 {
match self {
TextAlign::Left => 0.0,
TextAlign::Center => 0.5,
TextAlign::Right => 1.0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Reflect)]
pub struct TextAnchor(pub Vec2);
impl Default for TextAnchor {
fn default() -> Self {
Self(Vec2::new(0.5, -0.5))
}
}
impl TextAnchor {
pub const TOP_LEFT: Self = Self(Vec2::new(-0.5, 0.5));
pub const TOP_CENTER: Self = Self(Vec2::new(0.0, 0.5));
pub const TOP_RIGHT: Self = Self(Vec2::new(0.5, 0.5));
pub const CENTER_LEFT: Self = Self(Vec2::new(-0.5, 0.0));
pub const CENTER: Self = Self(Vec2::new(0.0, 0.0));
pub const CENTER_RIGHT: Self = Self(Vec2::new(0.5, 0.0));
pub const BOTTOM_LEFT: Self = Self(Vec2::new(-0.5, -0.5));
pub const BOTTOM_CENTER: Self = Self(Vec2::new(0.0, -0.5));
pub const BOTTOM_RIGHT: Self = Self(Vec2::new(0.5, -0.5));
}
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
pub struct TextBlockStyling {
pub font: FontId,
pub size_px: u32,
pub world_scale: f32,
pub color: Srgba,
pub align: TextAlign,
pub anchor: TextAnchor,
pub line_height: f32,
pub char_spacing: f32,
pub word_spacing: f32,
pub max_width: Option<f32>,
}
impl Default for TextBlockStyling {
fn default() -> Self {
Self {
font: FontId::from_name("default"),
size_px: 32,
world_scale: 1.0,
color: Srgba::WHITE,
align: TextAlign::default(),
anchor: TextAnchor::default(),
line_height: 1.375,
char_spacing: 0.0,
word_spacing: 0.0,
max_width: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct FontLayoutOverride {
pub offset_factor: Vec2,
}
impl Default for FontLayoutOverride {
fn default() -> Self {
Self {
offset_factor: Vec2::ZERO,
}
}
}
#[derive(Resource, Debug, Clone, Default)]
pub struct FontLayoutOverrides {
offsets: HashMap<FontId, FontLayoutOverride>,
}
impl FontLayoutOverrides {
pub fn insert(&mut self, font: FontId, layout: FontLayoutOverride) {
self.offsets.insert(font, layout);
}
pub fn get(&self, font: &FontId) -> Option<&FontLayoutOverride> {
self.offsets.get(font)
}
}
#[derive(Component, Debug, Clone, Default, Reflect)]
#[reflect(Component)]
pub struct TextBlockLayout {
pub glyphs: Vec<LayoutGlyph>,
pub dimension: Vec2,
}
#[derive(Debug, Clone, Reflect)]
pub struct LayoutGlyph {
pub char_index: usize,
pub character: char,
pub position: Vec2,
pub size: Vec2,
pub uv_rect: Rect,
pub color: Srgba,
pub entity: Option<Entity>,
}
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
pub struct GlyphEntity {
pub char_index: usize,
pub character: char,
}
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
pub struct GlyphBaseOffset(pub Vec2);
#[derive(Component, Debug, Clone, Default, Reflect)]
#[reflect(Component)]
pub struct GlyphReveal {
pub visible_count: usize,
}
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
pub struct ShakeEffect {
pub intensity: f32,
pub elapsed: f32,
}
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
pub struct WaveEffect {
pub amplitude: f32,
pub frequency: f32,
pub elapsed: f32,
}