use bevy_app::prelude::*;
use bevy_ecs::{prelude::*, system::EntityCommands, world::Command};
use bevy_hierarchy::despawn_with_children_recursive;
#[doc(hidden)]
pub use bevy_ui;
#[doc(hidden)]
pub use bevy_ecs;
#[doc(hidden)]
pub use bevy_hierarchy;
#[doc(hidden)]
pub use bevy_text;
#[doc(hidden)]
pub use replace_ident::replace_ident;
pub mod class;
mod class_list;
pub use class_list::BsmlClasses;
pub mod prelude {
pub use crate::class::prelude::*;
pub use crate::{bsml, BsmlClasses, BsmlPlugin, SpawnBsml};
}
#[derive(Debug, Clone, Copy)]
pub struct BsmlPlugin;
impl Plugin for BsmlPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
PostUpdate,
class_list::apply_class_system.before(bevy_ui::UiSystem::Layout),
);
}
fn is_unique(&self) -> bool {
true
}
}
pub trait SpawnBsml {
fn spawn_bsml<'a, T: BsmlElement>(&'a mut self, node: T) -> EntityCommands<'a>;
fn despawn_bsml(&mut self, entity: Entity);
}
impl<'w, 's> SpawnBsml for bevy_ecs::system::Commands<'w, 's> {
fn spawn_bsml<'a, T: BsmlElement>(&'a mut self, node: T) -> EntityCommands<'a> {
let entity = node.spawn(self, &[]);
self.entity(entity)
}
fn despawn_bsml(&mut self, entity: Entity) {
struct BsmlCommand(Entity);
impl Command for BsmlCommand {
fn apply(self, world: &mut World) {
despawn_with_children_recursive(world, self.0);
}
}
self.add(BsmlCommand(entity));
}
}
pub trait BsmlElement {
fn spawn(self, commands: &mut Commands, slot: &[Entity]) -> Entity;
fn taking_slot(&self) -> bool {
false
}
}
#[derive(Debug, Clone, Component)]
pub struct Bsml;
#[macro_export]
macro_rules! bsml {
(($itag:ident $($attr:tt)*) $({$($content:tt)*})?) => {{
#[derive($crate::bevy_ecs::component::Component, Clone, Copy)]
pub struct __BsmlTag;
$crate::bsml!(__BsmlTag; ($itag $($attr)*) $({$($content)*})?);
__BsmlTag
}};
($tag:ident; ($itag:ident $($attr:tt)*) $({$($content:tt)*})?) => {
impl $crate::BsmlElement for $tag {
#[allow(unused_variables)]
fn spawn(self, commands: &mut $crate::bevy_ecs::system::Commands, slot: &[$crate::bevy_ecs::entity::Entity]) -> $crate::bevy_ecs::entity::Entity {
#[allow(unused_imports)]
use $crate::class::ApplyClass;
use $crate::bevy_hierarchy::BuildChildren;
let this = &self;
let __entity = $crate::bsml!(@element(this, commands, slot) ($itag $($attr)*) $({$($content)*})?);
commands.entity(__entity).insert(self);
__entity
}
$crate::bsml!(@taking_slot ($itag) $({$($content)*})?);
}
};
(@element($this:ident, $commands:ident, $slot:ident) (node $($attr:tt)*) $({$(($($def:tt)+) $({$($imp:tt)*})?)*})?) => {{
#[allow(unused_mut)]
let mut __bundle = $crate::bevy_ui::node_bundles::NodeBundle::default();
let __entity = $crate::bsml!(@spawn($this, $commands, $slot, __bundle) $($attr)*);
let __children = [$($(
$crate::bsml!(@element($this, $commands, $slot) ($($def)+) $({$($imp)*})?),
)*)?];
$commands.entity(__entity).push_children(&__children);
__entity
}};
(@element($this:ident, $commands:ident, $slot:ident) (for {$i:ident, $v:ident in $iter:expr} $($attr:tt)*) {$(($($def:tt)+) $({$($imp:tt)*})?)*}) => {{
#[allow(unused_mut)]
let mut __bundle = $crate::bevy_ui::node_bundles::NodeBundle::default();
let __entity = $crate::bsml!(@spawn($this, $commands, $slot) $($attr)*);
for ($i, $v) in ($crate::replace_ident!(self, $this, $iter)).into_iter().enumerate() {
let __children = [$(
$crate::bsml!(@element($this, $commands, $slot) ($($def)+) $({$($imp)*})?),
)*];
$commands.entity(__entity).push_children(&__children);
}
__entity
}};
(@element($this:ident, $commands:ident, $slot:ident) (for {$v:ident in $iter:expr} $($attr:tt)*) {$(($($def:tt)+) $({$($imp:tt)*})?)*}) => {{
#[allow(unused_mut)]
let mut __bundle = $crate::bevy_ui::node_bundles::NodeBundle::default();
let __entity = $crate::bsml!(@spawn($this, $commands, $slot, __bundle) $($attr)*);
for $v in $crate::replace_ident!(labels, labels_ref, $crate::replace_ident!(self, $this, $iter)) {
let __children = [$(
$crate::bsml!(@element($this, $commands, $slot) ($($def)+) $({$($imp)*})?),
)*];
$commands.entity(__entity).push_children(&__children);
}
__entity
}};
(@element($this:ident, $commands:ident, $slot:ident) (text $($attr:tt)*) $({$($literal:expr $(,$value:expr)*)?})?) => {{
let mut __bundle = $crate::bevy_ui::node_bundles::TextBundle::from_section(
$crate::bsml!(@text_value($this) $({$($literal $(,$value)*)?})?),
$crate::bevy_text::TextStyle::default()
);
$crate::bsml!(@spawn($this, $commands, $slot, __bundle) $($attr)*)
}};
(@text_value($this:ident)) => { String::new() };
(@text_value($this:ident) {}) => { String::new() };
(@text_value($this:ident) {$literal:expr $(,$value:expr)*}) => {{
format!($literal $(, $crate::replace_ident!(labels, labels_ref, $crate::replace_ident!(self, $this, $value)))*)
}};
(@element($this:ident, $commands:ident, $slot:ident) (img $($attr:tt)*) {$image:expr}) => {{
let mut __bundle = $crate::bevy_ui::ImageBundle::default();
__bundle.image = $image .into();
$crate::bsml!(@spawn($this, $commands, $slot, __bundle) $($attr)*)
}};
(@element($this:ident, $commands:ident, $slot:ident) (material $($attr:tt)*) {$material:expr}) => {{
let mut __bundle = $crate::bevy_ui::MaterialBundle::default();
__bundle.material = $material .into();
$crate::bsml!(@spawn($this, $commands, $slot, __bundle) $($attr)*)
}};
(@element($this:ident, $commands:ident, $slot:ident) (slot $($attr:tt)*) $({$(($($def:tt)+) $({$($imp:tt)*})?)*})?) => {{
#[allow(unused_mut)]
let mut __bundle = $crate::bevy_ui::node_bundles::NodeBundle::default();
let __entity = $crate::bsml!(@spawn($this, $commands, $slot, __bundle) $($attr)*);
if $slot .is_empty() {
let __children = [
$($(
$crate::bsml!(@element($this, $commands, $slot) ($($def)+) $({$($imp)*})?),
)*)?
];
$commands.entity(__entity).push_children(&__children);
} else {
$commands.entity(__entity).push_children(&$slot);
}
__entity
}};
(@element($this:ident, $commands:ident, $_slot:ident) ($component:expr $(; labels=[$($label:expr),* $(,)?])?) $({$(($($def:tt)+) $({$($imp:tt)*})?)*})?) => {{
let __tag = $crate::replace_ident!(self, $this, $component);
let __entity = if __tag.taking_slot() {
let __children = [$($(
$crate::bsml!(@element($this, $commands, $_slot) ($($def)+) $({$($imp)*})?),
)*)?];
__tag.spawn($commands, &__children)
} else {
__tag.spawn($commands, &[])
};
$(
$commands.entity(__entity).insert(($($label),*));
)?
__entity
}};
(@spawn($this:ident, $commands:ident, $slot:ident, $bundle:ident) $(labels=[$($label:expr),* $(,)?])? $(class=[$($class:expr),* $(,)?])?) => {{
#[allow(unused_mut)]
let mut __class_map = $crate::BsmlClasses::default();
let labels = ($($(
$crate::replace_ident!(self, $this, $label),
)*)?);
let labels_ref = &labels;
$({
use $crate::class::WithInteraction;
$(
let (__interaction, mut __class) = $crate::replace_ident!(labels, labels_ref, $crate::replace_ident!(self, $this, $class)).with_interaction();
if __interaction == $crate::bevy_ui::Interaction::None {
$bundle .apply_class(&__class);
}
__class_map.insert(__interaction, __class);
)*
})?
let __entity = $commands.spawn((
$bundle,
$crate::bevy_ui::Interaction::None,
$crate::Bsml,
labels,
))
.insert(__class_map)
.id();
__entity
}};
(@spawn($this:ident, $commands:ident, $slot:ident, $bundle:ident) class=[$($class:expr),* $(,)?] labels=[$($label:expr),* $(,)?]) => {
$crate::bsml!(@spawn($this, $commands, $slot, $bundle) labels=[$($label),*] class=[$($class),*])
};
(@taking_slot (slot $($_:tt)*) $($_1:tt)?) => {
fn taking_slot(&self) -> bool {
true
}
};
(@taking_slot (text $($_:tt)*) $($_1:tt)?) => {};
(@taking_slot (node $($_:tt)*) $($_1:tt)?) => {};
(@taking_slot ($($_:tt)*) $({$(($($def:tt)+) $({$($imp:tt)*})?)*})?) => {
$($(
$crate::bsml!(@taking_slot ($($def)+) $({$($imp)*})? );
)*)?
};
}