use crate::events::ElementSpawn;
use crate::props::Properties;
use crate::widgets::Widget;
use bevy::asset::AssetServer;
use bevy::color::Color;
use bevy::ecs::relationship::RelatedSpawnerCommands;
use bevy::prelude::{
BorderColor, ChildOf, Component, Entity, GlobalTransform, Interaction, Transform,
};
use bevy::ui::{BackgroundColor, Node};
use rustc_hash::FxHashMap;
use smol_str::SmolStr;
#[derive(Debug)]
pub struct Element {
pub widget: Box<dyn Widget>,
pub props: Properties<ElementProps>,
pub id: Option<ElementId>,
pub children: Vec<Element>,
}
impl Element {
pub(crate) fn spawn(
&self,
parent: &mut RelatedSpawnerCommands<ChildOf>,
assets: &AssetServer,
reg: &mut ElementRegistry,
) -> Entity {
let props = &self.props.default;
let mut entity_cmd = parent.spawn((
self.props.clone(),
props.node.clone(),
Interaction::None,
Transform::default(),
GlobalTransform::default(),
BackgroundColor(props.bg_color.unwrap_or(Color::NONE)),
props.border_color.unwrap_or(BorderColor::DEFAULT),
));
if let Some(id) = &self.id {
entity_cmd.insert(id.clone());
}
let root_entity = entity_cmd.id();
let target_entity = self.widget.spawn(&mut entity_cmd, assets);
if target_entity == root_entity {
entity_cmd.with_children(|p| {
for child in &self.children {
child.spawn(p, assets, reg);
}
});
} else {
parent
.commands_mut()
.entity(target_entity)
.with_children(|p| {
for child in &self.children {
child.spawn(p, assets, reg);
}
});
}
if let Some(id) = self.id() {
reg.register_element(id, root_entity);
}
parent.commands_mut().trigger(ElementSpawn {
entity: root_entity,
id: self.id(),
});
root_entity
}
pub fn id(&self) -> Option<ElementId> {
self.id.clone()
}
}
#[derive(Clone, Debug, Default)]
pub struct ElementProps {
pub node: Node,
pub bg_color: Option<Color>,
pub border_color: Option<BorderColor>,
}
#[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()
}
}