use crate::events::ElementSpawn;
use bevy::asset::AssetServer;
use bevy::color::Color;
use bevy::ecs::relationship::RelatedSpawnerCommands;
use bevy::math::Rect;
use bevy::prelude::{
Button, ChildOf, Component, Entity, FontSize, FontSource, FontStyle, FontWeight, FontWidth,
ImageNode, Node, NodeImageMode, Text, TextColor, TextFont, VisualBox,
};
use rustc_hash::FxHashMap;
use smol_str::SmolStr;
#[derive(Clone, Debug)]
pub enum Element {
Node {
node: Node,
id: Option<ElementId>,
children: Vec<Element>,
},
Button {
node: Node,
id: Option<ElementId>,
children: Vec<Element>,
},
Text {
node: Node,
id: Option<ElementId>,
content: String,
font: Option<String>,
font_weight: Option<FontWeight>,
font_width: Option<FontWidth>,
font_size: Option<FontSize>,
font_style: Option<FontStyle>,
color: Option<Color>,
},
Image {
node: Node,
id: Option<ElementId>,
src: String,
color: Option<Color>,
flip_x: Option<bool>,
flip_y: Option<bool>,
rect: Option<Rect>,
mode: Option<NodeImageMode>,
visual_box: Option<VisualBox>,
},
}
impl Element {
pub(crate) fn spawn(
&self,
parent: &mut RelatedSpawnerCommands<ChildOf>,
assets: &AssetServer,
reg: &mut ElementRegistry,
) -> Entity {
let entity = match self {
Element::Node { node, id, children } => {
let mut entity = parent.spawn((node.clone(), Button));
if let Some(id) = id {
entity.insert(id.clone());
}
entity.with_children(|p| {
for child in children {
child.spawn(p, assets, reg);
}
});
entity.id()
}
Element::Button { node, id, children } => {
let mut entity = parent.spawn((node.clone(), Button));
if let Some(id) = id {
entity.insert(id.clone());
}
entity.with_children(|p| {
for child in children {
child.spawn(p, assets, reg);
}
});
entity.id()
}
Element::Text {
node,
id,
content,
font,
font_weight,
font_width,
font_size,
font_style,
color,
} => {
let text = Text::new(content);
let font = TextFont {
font: if let Some(font) = font {
FontSource::Handle(assets.load(font))
} else {
FontSource::default()
},
font_size: font_size.unwrap_or_default(),
weight: font_weight.unwrap_or_default(),
width: font_width.unwrap_or_default(),
style: font_style.unwrap_or_default(),
..Default::default()
};
let text_color = TextColor(color.unwrap_or(Color::WHITE));
let mut entity = parent.spawn((node.clone(), Button, text, font, text_color));
if let Some(id) = id {
entity.insert(id.clone());
}
entity.id()
}
Element::Image {
node,
id,
src,
color,
flip_x,
flip_y,
rect,
mode,
visual_box,
} => {
let image = ImageNode {
color: color.unwrap_or(Color::WHITE),
image: assets.load(src),
texture_atlas: None,
flip_x: flip_x.unwrap_or_default(),
flip_y: flip_y.unwrap_or_default(),
rect: *rect,
image_mode: mode.clone().unwrap_or_default(),
visual_box: visual_box.unwrap_or_default(),
};
let mut entity = parent.spawn((node.clone(), Button, image));
if let Some(id) = id {
entity.insert(id.clone());
}
entity.id()
}
};
if let Some(id) = self.get_id() {
reg.register_element(id, entity);
}
parent.commands_mut().trigger(ElementSpawn {
entity,
id: self.get_id(),
});
entity
}
pub fn get_id(&self) -> Option<ElementId> {
match self {
Element::Node { id, .. } => id.clone(),
Element::Button { id, .. } => id.clone(),
Element::Text { id, .. } => id.clone(),
Element::Image { id, .. } => id.clone(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, Component)]
pub struct ElementId(SmolStr);
impl ElementId {
pub fn new(s: impl AsRef<str>) -> Self {
Self(SmolStr::new(s))
}
pub fn new_static(s: &'static str) -> Self {
Self(SmolStr::new_static(s))
}
}
impl From<&'static str> for ElementId {
fn from(s: &'static str) -> Self {
Self::new_static(s)
}
}
#[derive(Clone, Debug)]
pub(crate) struct ElementRegistry {
ids: FxHashMap<ElementId, Entity>,
}
impl ElementRegistry {
pub(crate) fn new(cap: usize) -> Self {
Self {
ids: FxHashMap::with_capacity_and_hasher(cap, Default::default()),
}
}
pub(crate) fn register_element(&mut self, id: ElementId, entity: Entity) {
self.ids.insert(id, entity);
}
pub(crate) fn get_element(&self, id: ElementId) -> Option<Entity> {
self.ids.get(&id).cloned()
}
}