use crate::element::{ElementProps, ElementWidget};
use crate::parser::values::parse_attribute;
use crate::widgets::Widget;
use bevy::asset::AssetServer;
use bevy::color::Color;
use bevy::prelude::{
Component, Entity, EntityCommands, FontSize, FontSource, FontStyle, FontWeight, FontWidth,
Text, TextColor, TextFont,
};
use roxmltree::Node;
#[derive(Clone, Debug, Component)]
pub struct TextWidget {
pub content: String,
pub font: Option<String>,
pub font_weight: Option<FontWeight>,
pub font_width: Option<FontWidth>,
pub font_size: Option<FontSize>,
pub font_style: Option<FontStyle>,
pub color: Option<Color>,
}
impl Widget for TextWidget {
fn spawn(&self, commands: &mut EntityCommands, assets: &AssetServer) -> Entity {
let text = Text::new(&self.content);
let text_font = TextFont {
font: self
.font
.as_ref()
.map(|f| FontSource::Handle(assets.load(f)))
.unwrap_or_default(),
font_size: self.font_size.unwrap_or_default(),
weight: self.font_weight.unwrap_or_default(),
width: self.font_width.unwrap_or_default(),
style: self.font_style.unwrap_or_default(),
..Default::default()
};
let text_color = TextColor(self.color.unwrap_or(Color::WHITE));
commands.insert((text, text_font, text_color));
commands.id()
}
fn parse(
node: &Node,
prefix: Option<&str>,
base: Option<&ElementWidget>,
) -> Result<Self, String>
where
Self: Sized,
{
let base_text = match base {
Some(ElementWidget::Text(TextWidget {
content,
font,
font_weight,
font_width,
font_size,
font_style,
color,
})) => Some((
content,
font,
font_weight,
font_width,
font_size,
font_style,
color,
)),
_ => None,
};
let content = parse_attribute(node, "content", prefix, |s| Ok(s.to_string()))?
.or_else(|| {
if prefix.is_none() {
node.text().map(str::trim).map(str::to_string)
} else {
None
}
})
.or_else(|| base_text.map(|b| b.0.clone()))
.unwrap_or_default();
let font = parse_attribute(node, "font", prefix, |s| Ok(s.to_string()))?
.or_else(|| base_text.and_then(|b| b.1.clone()));
let font_weight = parse_attribute(node, "font-weight", prefix, |s| {
crate::parser::values::parse_int(s)
.map(|i| Ok(FontWeight(i as u16)))
.unwrap_or_else(|_| {
crate::parser::values::parse_matches(
s,
&[
("thin", || Ok(FontWeight::THIN)),
("extra_light", || Ok(FontWeight::EXTRA_LIGHT)),
("light", || Ok(FontWeight::LIGHT)),
("normal", || Ok(FontWeight::NORMAL)),
("medium", || Ok(FontWeight::MEDIUM)),
("semibold", || Ok(FontWeight::SEMIBOLD)),
("bold", || Ok(FontWeight::BOLD)),
("extra_bold", || Ok(FontWeight::EXTRA_BOLD)),
("black", || Ok(FontWeight::BLACK)),
("extra_black", || Ok(FontWeight::EXTRA_BLACK)),
],
)
})
})?
.or_else(|| base_text.and_then(|b| *b.2));
let font_width = parse_attribute(node, "font-width", prefix, |s| {
crate::parser::values::parse_float(s).map(FontWidth)
})?
.or_else(|| base_text.and_then(|b| *b.3));
let font_size = parse_attribute(
node,
"font-size",
prefix,
crate::parser::values::parse_font_size,
)?
.or_else(|| base_text.and_then(|b| *b.4));
let font_style = parse_attribute(node, "font-style", prefix, |s| {
crate::parser::values::parse_matches(
s,
&[
("normal", || Ok(FontStyle::Normal)),
("italic", || Ok(FontStyle::Italic)),
("oblique", || Ok(FontStyle::Oblique(None))),
],
)
})?
.or_else(|| base_text.and_then(|b| *b.5));
let color = parse_attribute(node, "color", prefix, crate::parser::color::parse_color)?
.or_else(|| base_text.and_then(|b| *b.6));
Ok(Self {
content,
font,
font_weight,
font_width,
font_size,
font_style,
color,
})
}
fn apply_defaults(_: &Node, _: &mut ElementProps, _: &mut ElementProps, _: &mut ElementProps) {}
}