use crate::{prepare::family, MeshExport, StrokeJoin, Style, TextAlign, TextAnchor, Weight};
use bevy::asset::AssetId;
use bevy::image::Image;
use bevy::{color::Srgba, ecs::component::Component, math::Vec2};
use cosmic_text::Metrics;
use cosmic_text::{fontdb::ID, Attrs};
use std::fmt::Debug;
use std::{num::NonZeroU32, sync::Arc};
#[cfg(feature = "reflect")]
use bevy::prelude::{Reflect, ReflectComponent, ReflectDefault};
#[derive(Debug, Component, Clone)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "reflect", reflect(Component, Default))]
pub struct Text3dStyling {
pub size: f32,
pub font: Arc<str>,
pub style: Style,
pub weight: Weight,
pub align: TextAlign,
pub anchor: TextAnchor,
pub line_height: f32,
pub color: Srgba,
pub stroke_color: Srgba,
pub fill: bool,
pub stroke: Option<NonZeroU32>,
pub stroke_in_front: bool,
pub stroke_join: StrokeJoin,
pub layer_offset: f32,
pub export: MeshExport,
pub tab_width: u16,
pub world_scale: Option<Vec2>,
pub text_shadow: Option<(Srgba, Vec2)>,
}
impl Default for Text3dStyling {
fn default() -> Self {
Self {
size: 16.,
color: Srgba::WHITE,
font: Default::default(),
style: Default::default(),
weight: Default::default(),
align: Default::default(),
anchor: TextAnchor::CENTER,
stroke_color: Srgba::WHITE,
fill: true,
stroke: Default::default(),
line_height: 1.0,
layer_offset: 0.01,
stroke_in_front: false,
stroke_join: StrokeJoin::Round,
export: MeshExport::None,
tab_width: 4,
world_scale: None,
text_shadow: None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum SegmentSize {
Flat(f32),
Multiply(f32),
}
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
pub struct SegmentStyle {
pub font: Option<Arc<str>>,
pub size: Option<SegmentSize>,
pub fill_color: Option<Srgba>,
pub stroke_color: Option<Srgba>,
pub fill: Option<bool>,
pub stroke: Option<NonZeroU32>,
pub weight: Option<Weight>,
pub style: Option<Style>,
pub underline: Option<bool>,
pub strikethrough: Option<bool>,
pub magic_number: Option<f32>,
}
impl SegmentStyle {
pub fn as_attr<'t>(&'t self, base: &'t Text3dStyling) -> Attrs<'t> {
let family_name = self.font.as_ref().map(Arc::as_ref).unwrap_or(&base.font);
let family = family(family_name);
match self.size {
Some(SegmentSize::Flat(size)) => {
Attrs::new().metrics(Metrics::new(size, size * base.line_height))
}
Some(SegmentSize::Multiply(size)) => {
let size = base.size * size;
Attrs::new().metrics(Metrics::new(size, size * base.line_height))
}
None => Attrs::new(),
}
.weight(self.weight.unwrap_or(base.weight).into())
.style(self.style.unwrap_or(base.style).into())
.family(family)
}
pub fn join(&self, other: Self) -> Self {
SegmentStyle {
font: other.font.or_else(|| self.font.clone()),
size: other.size.or(self.size),
fill_color: other.fill_color.or(self.fill_color),
stroke_color: other.stroke_color.or(self.stroke_color),
fill: other.fill.or(self.fill),
stroke: other.stroke.or(self.stroke),
weight: other.weight.or(self.weight),
underline: other.underline.or(self.underline),
strikethrough: other.strikethrough.or(self.strikethrough),
style: other.style.or(self.style),
magic_number: other.magic_number.or(self.magic_number),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GlyphEntry {
Glyph {
font: ID,
glyph_id: GlyphTextureOf,
join: StrokeJoin,
real_size: FloatDecimal,
weight: Weight,
stroke: Option<NonZeroU32>,
},
Image(AssetId<Image>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GlyphTextureOf {
Id(u16),
UnderlineTexture,
StrikethroughTexture,
}
impl From<u16> for GlyphTextureOf {
fn from(id: u16) -> Self {
GlyphTextureOf::Id(id)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct FloatDecimal(i32);
impl FloatDecimal {
pub fn new(input: f32) -> Self {
Self((input * 100.).round() as i32)
}
}
impl Debug for FloatDecimal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let value = self.0 as f32 / 100.;
f.debug_tuple("FloatDecimal").field(&value).finish()
}
}