bevy_dioxus/src/apply_mutations.rs

490 lines
18 KiB
Rust
Raw Normal View History

2023-12-23 20:09:09 +00:00
use crate::{
events::{insert_event_listener, remove_event_listener},
parse_attributes::set_attribute,
};
2023-12-07 19:17:42 +00:00
use bevy::{
2023-12-27 20:51:10 +00:00
asset::AssetServer,
2024-03-25 02:38:08 +00:00
ecs::{entity::{Entity, EntityHashMap}, system::Command, world::World},
2023-12-16 01:46:19 +00:00
hierarchy::{BuildWorldChildren, Children, DespawnRecursive, Parent},
prelude::default,
render::{color::Color, view::Visibility},
2023-12-21 06:53:55 +00:00
text::{Text, TextLayoutInfo, TextStyle},
transform::components::Transform,
2023-12-10 23:44:27 +00:00
ui::{
2023-12-27 20:51:10 +00:00
node_bundles::{ImageBundle, NodeBundle, TextBundle},
2023-12-16 04:31:00 +00:00
widget::TextFlags,
2023-12-10 23:44:27 +00:00
*,
},
2024-03-25 02:38:08 +00:00
utils::HashMap,
2023-12-07 19:17:42 +00:00
};
2023-12-14 07:23:30 +00:00
use dioxus::core::{
BorrowedAttributeValue, ElementId, Mutation, Mutations, Template, TemplateAttribute,
TemplateNode,
};
2023-12-07 02:46:50 +00:00
2023-12-07 19:17:42 +00:00
pub fn apply_mutations(
mutations: Mutations,
element_id_to_bevy_ui_entity: &mut HashMap<ElementId, Entity>,
2024-03-25 02:38:08 +00:00
bevy_ui_entity_to_element_id: &mut EntityHashMap<ElementId>,
templates: &mut HashMap<String, BevyTemplate>,
2023-12-07 19:17:42 +00:00
root_entity: Entity,
world: &mut World,
2023-12-27 20:51:10 +00:00
asset_server: &AssetServer,
2023-12-07 19:17:42 +00:00
) {
for new_template in mutations.templates {
templates.insert(
new_template.name.to_owned(),
2023-12-27 20:51:10 +00:00
BevyTemplate::from_dioxus(&new_template, asset_server),
);
2023-12-07 19:17:42 +00:00
}
2023-12-10 23:44:27 +00:00
element_id_to_bevy_ui_entity.insert(ElementId(0), root_entity);
2023-12-12 08:12:06 +00:00
bevy_ui_entity_to_element_id.insert(root_entity, ElementId(0));
2023-12-07 19:17:42 +00:00
let mut stack = vec![root_entity];
2023-12-10 21:05:01 +00:00
2023-12-07 19:17:42 +00:00
for edit in mutations.edits {
match edit {
Mutation::AppendChildren { id, m } => {
let mut parent = world.entity_mut(element_id_to_bevy_ui_entity[&id]);
for child in stack.drain((stack.len() - m)..) {
2023-12-10 21:50:10 +00:00
parent.add_child(child);
2023-12-07 19:17:42 +00:00
}
}
2023-12-17 05:33:13 +00:00
Mutation::AssignId { path, id } => {
let mut entity = *stack.last().unwrap();
for index in path {
entity = world.entity(entity).get::<Children>().unwrap()[*index as usize];
}
element_id_to_bevy_ui_entity.insert(id, entity);
bevy_ui_entity_to_element_id.insert(entity, id);
}
2023-12-14 07:45:30 +00:00
Mutation::CreatePlaceholder { id } => {
2023-12-16 01:42:22 +00:00
let entity = world.spawn(NodeBundle::default()).id();
2023-12-14 07:45:30 +00:00
element_id_to_bevy_ui_entity.insert(id, entity);
bevy_ui_entity_to_element_id.insert(entity, id);
stack.push(entity);
}
2023-12-10 21:05:01 +00:00
Mutation::CreateTextNode { value, id } => {
2023-12-27 20:51:10 +00:00
let entity = BevyTemplateNode::from_dioxus(
&TemplateNode::Text { text: value },
asset_server,
)
.spawn(world);
2023-12-10 23:44:27 +00:00
element_id_to_bevy_ui_entity.insert(id, entity);
2023-12-12 08:12:06 +00:00
bevy_ui_entity_to_element_id.insert(entity, id);
2023-12-10 21:05:01 +00:00
stack.push(entity);
}
2023-12-10 21:50:10 +00:00
Mutation::HydrateText { path, value, id } => {
let mut entity = *stack.last().unwrap();
for index in path {
entity = world.entity(entity).get::<Children>().unwrap()[*index as usize];
2023-12-10 21:50:10 +00:00
}
2023-12-16 04:31:00 +00:00
world.entity_mut(entity).insert((
Text::from_section(value, TextStyle::default()),
TextLayoutInfo::default(),
TextFlags::default(),
ContentSize::default(),
));
2023-12-10 23:44:27 +00:00
element_id_to_bevy_ui_entity.insert(id, entity);
2023-12-12 08:12:06 +00:00
bevy_ui_entity_to_element_id.insert(entity, id);
2023-12-10 21:50:10 +00:00
}
Mutation::LoadTemplate { name, index, id } => {
let entity = templates[name].roots[index].spawn(world);
2023-12-10 23:44:27 +00:00
element_id_to_bevy_ui_entity.insert(id, entity);
2023-12-12 08:12:06 +00:00
bevy_ui_entity_to_element_id.insert(entity, id);
stack.push(entity);
2023-12-07 19:17:42 +00:00
}
Mutation::ReplaceWith { id, m } => {
let existing = element_id_to_bevy_ui_entity[&id];
2023-12-17 20:19:08 +00:00
let existing_parent = world.entity(existing).get::<Parent>().unwrap().get();
let mut existing_parent = world.entity_mut(existing_parent);
2023-12-17 20:19:08 +00:00
let existing_index = existing_parent
.get::<Children>()
.unwrap()
.iter()
.position(|child| *child == existing)
.unwrap();
2023-12-17 20:19:08 +00:00
existing_parent.insert_children(existing_index, &stack.split_off(stack.len() - m));
DespawnRecursive { entity: existing }.apply(world);
// TODO: We're not removing child entities from the element maps
if let Some(existing_element_id) = bevy_ui_entity_to_element_id.remove(&existing) {
element_id_to_bevy_ui_entity.remove(&existing_element_id);
2023-12-17 20:19:08 +00:00
}
}
Mutation::ReplacePlaceholder { path, m } => {
let mut existing = stack[stack.len() - m - 1];
for index in path {
existing = world.entity(existing).get::<Children>().unwrap()[*index as usize];
}
let existing_parent = world.entity(existing).get::<Parent>().unwrap().get();
let mut existing_parent = world.entity_mut(existing_parent);
2023-12-16 01:51:05 +00:00
let existing_index = existing_parent
.get::<Children>()
.unwrap()
.iter()
.position(|child| *child == existing)
.unwrap();
2023-12-17 20:19:08 +00:00
existing_parent.insert_children(existing_index, &stack.split_off(stack.len() - m));
2023-12-16 01:51:05 +00:00
2023-12-16 01:46:19 +00:00
DespawnRecursive { entity: existing }.apply(world);
2023-12-16 04:31:00 +00:00
// TODO: We're not removing child entities from the element maps
2023-12-16 01:53:14 +00:00
if let Some(existing_element_id) = bevy_ui_entity_to_element_id.remove(&existing) {
element_id_to_bevy_ui_entity.remove(&existing_element_id);
2023-12-17 20:19:08 +00:00
}
}
2023-12-17 05:33:13 +00:00
Mutation::InsertAfter { id, m } => {
let entity = element_id_to_bevy_ui_entity[&id];
let parent = world.entity(entity).get::<Parent>().unwrap().get();
let mut parent = world.entity_mut(parent);
let index = parent
.get::<Children>()
.unwrap()
.iter()
.position(|child| *child == entity)
.unwrap();
2023-12-17 20:19:08 +00:00
parent.insert_children(index + 1, &stack.split_off(stack.len() - m));
2023-12-17 05:33:13 +00:00
}
Mutation::InsertBefore { id, m } => {
let existing = element_id_to_bevy_ui_entity[&id];
let parent = world.entity(existing).get::<Parent>().unwrap().get();
let mut parent = world.entity_mut(parent);
let index = parent
.get::<Children>()
.unwrap()
.iter()
.position(|child| *child == existing)
.unwrap();
2023-12-17 20:19:08 +00:00
parent.insert_children(index, &stack.split_off(stack.len() - m));
}
2023-12-07 19:17:42 +00:00
Mutation::SetAttribute {
name,
value,
id,
2023-12-14 07:23:30 +00:00
ns: _,
} => {
let value = match value {
BorrowedAttributeValue::Text(value) => value,
2023-12-22 06:19:35 +00:00
BorrowedAttributeValue::None => todo!("Remove the attribute"),
value => {
2023-12-14 07:23:30 +00:00
panic!("Encountered unsupported bevy_dioxus attribute `{name}: {value:?}`.")
}
};
2023-12-16 01:51:05 +00:00
let (
mut style,
mut border_color,
mut outline,
mut background_color,
mut transform,
mut visibility,
mut z_index,
mut text,
2023-12-27 20:51:10 +00:00
mut image,
) = world
.query::<(
&mut Style,
&mut BorderColor,
&mut Outline,
&mut BackgroundColor,
&mut Transform,
&mut Visibility,
&mut ZIndex,
Option<&mut Text>,
2023-12-27 20:51:10 +00:00
Option<&mut UiImage>,
)>()
.get_mut(world, element_id_to_bevy_ui_entity[&id])
.unwrap();
2023-12-19 18:55:41 +00:00
set_attribute(
name,
value,
&mut style,
&mut border_color,
&mut outline,
2023-12-19 18:55:41 +00:00
&mut background_color,
&mut transform,
&mut visibility,
&mut z_index,
2023-12-19 18:55:41 +00:00
text.as_deref_mut(),
2023-12-27 20:51:10 +00:00
image.as_deref_mut(),
asset_server,
2023-12-19 18:55:41 +00:00
);
2023-12-14 07:23:30 +00:00
}
2023-12-12 08:12:06 +00:00
Mutation::SetText { value, id } => {
world
.entity_mut(element_id_to_bevy_ui_entity[&id])
2023-12-12 08:12:06 +00:00
.insert(Text::from_section(value, TextStyle::default()));
2023-12-10 23:44:27 +00:00
}
Mutation::NewEventListener { name, id } => {
2023-12-23 20:09:09 +00:00
insert_event_listener(name, world.entity_mut(element_id_to_bevy_ui_entity[&id]));
}
Mutation::RemoveEventListener { name, id } => {
2023-12-23 20:09:09 +00:00
remove_event_listener(name, world.entity_mut(element_id_to_bevy_ui_entity[&id]));
2023-12-10 23:44:27 +00:00
}
Mutation::Remove { id } => {
2023-12-17 20:19:08 +00:00
let entity = element_id_to_bevy_ui_entity[&id];
DespawnRecursive { entity }.apply(world);
// TODO: We're not removing child entities from the element maps
2023-12-17 20:19:08 +00:00
if let Some(existing_element_id) = bevy_ui_entity_to_element_id.remove(&entity) {
element_id_to_bevy_ui_entity.remove(&existing_element_id);
}
}
2023-12-10 23:44:27 +00:00
Mutation::PushRoot { id } => stack.push(element_id_to_bevy_ui_entity[&id]),
2023-12-07 19:17:42 +00:00
}
}
2023-12-07 02:46:50 +00:00
}
2023-12-08 04:27:42 +00:00
pub struct BevyTemplate {
2023-12-10 20:55:49 +00:00
roots: Box<[BevyTemplateNode]>,
2023-12-08 04:27:42 +00:00
}
enum BevyTemplateNode {
2023-12-13 03:47:14 +00:00
Node {
style: StyleComponents,
2023-12-13 03:47:14 +00:00
children: Box<[Self]>,
},
2023-12-19 18:55:41 +00:00
TextNode {
text: Text,
style: StyleComponents,
2023-12-19 18:55:41 +00:00
children: Box<[Self]>,
},
2023-12-27 20:51:10 +00:00
ImageNode {
image: UiImage,
style: StyleComponents,
children: Box<[Self]>,
},
2023-12-19 18:55:41 +00:00
IntrinsicTextNode(Text),
}
impl BevyTemplate {
2023-12-27 20:51:10 +00:00
fn from_dioxus(template: &Template, asset_server: &AssetServer) -> Self {
Self {
roots: template
.roots
.iter()
2023-12-27 20:51:10 +00:00
.map(|node| BevyTemplateNode::from_dioxus(node, asset_server))
.collect(),
}
}
}
2023-12-08 04:27:42 +00:00
impl BevyTemplateNode {
2023-12-27 20:51:10 +00:00
fn from_dioxus(node: &TemplateNode, asset_server: &AssetServer) -> Self {
match node {
TemplateNode::Element {
2023-12-19 05:24:06 +00:00
tag: "node",
namespace: Some("bevy_ui"),
2023-12-10 23:44:27 +00:00
attrs,
children,
2023-12-19 18:55:41 +00:00
} => {
2023-12-27 20:51:10 +00:00
let (style, _, _) = parse_template_attributes(attrs, Color::NONE, asset_server);
2023-12-19 18:55:41 +00:00
Self::Node {
style,
2023-12-27 20:51:10 +00:00
children: children
.iter()
.map(|node| Self::from_dioxus(node, asset_server))
.collect(),
2023-12-19 18:55:41 +00:00
}
}
TemplateNode::Element {
tag: "text",
namespace: Some("bevy_ui"),
attrs,
children,
} => {
2023-12-27 20:51:10 +00:00
let (style, text, _) = parse_template_attributes(attrs, Color::NONE, asset_server);
2023-12-19 18:55:41 +00:00
Self::TextNode {
text,
style,
2023-12-27 20:51:10 +00:00
children: children
.iter()
.map(|node| Self::from_dioxus(node, asset_server))
.collect(),
}
}
TemplateNode::Element {
tag: "image",
namespace: Some("bevy_ui"),
attrs,
children,
} => {
let (style, _, image) =
parse_template_attributes(attrs, Color::WHITE, asset_server);
Self::ImageNode {
image,
style,
children: children
.iter()
.map(|node| Self::from_dioxus(node, asset_server))
.collect(),
2023-12-19 18:55:41 +00:00
}
}
TemplateNode::Text { text } => {
2023-12-19 18:55:41 +00:00
Self::IntrinsicTextNode(Text::from_section(*text, TextStyle::default()))
2023-12-08 04:27:42 +00:00
}
2023-12-10 21:50:10 +00:00
TemplateNode::Dynamic { id: _ } => Self::Node {
style: StyleComponents::default(),
2023-12-10 21:50:10 +00:00
children: Box::new([]),
},
TemplateNode::DynamicText { id: _ } => {
2023-12-19 18:55:41 +00:00
Self::IntrinsicTextNode(Text::from_section("", TextStyle::default()))
2023-12-10 21:50:10 +00:00
}
2023-12-19 05:24:06 +00:00
TemplateNode::Element {
tag,
namespace: None,
..
} => {
panic!("Encountered unsupported bevy_dioxus tag `{tag}`.")
}
TemplateNode::Element {
tag,
namespace: Some(namespace),
..
} => {
panic!("Encountered unsupported bevy_dioxus tag `{namespace}::{tag}`.")
}
}
}
fn spawn(&self, world: &mut World) -> Entity {
match self {
BevyTemplateNode::Node { style, children } => {
2023-12-10 20:55:49 +00:00
let children = children
.iter()
.map(|child| child.spawn(world))
2023-12-10 20:55:49 +00:00
.collect::<Box<[_]>>();
world
.spawn((
NodeBundle {
style: style.style.clone(),
2024-01-02 21:50:29 +00:00
border_color: style.border_color,
background_color: style.background_color,
transform: style.transform,
visibility: style.visibility,
z_index: style.z_index,
..default()
},
2024-01-02 21:50:29 +00:00
style.outline,
))
2023-12-10 20:55:49 +00:00
.push_children(&children)
.id()
2023-12-10 20:55:49 +00:00
}
2023-12-19 18:55:41 +00:00
BevyTemplateNode::TextNode {
text,
style,
2023-12-19 18:55:41 +00:00
children,
} => {
let children = children
.iter()
.map(|child| child.spawn(world))
.collect::<Box<[_]>>();
world
.spawn(NodeBundle {
2024-01-02 21:50:29 +00:00
border_color: style.border_color,
2023-12-19 18:55:41 +00:00
..default()
})
.insert((
TextBundle {
text: text.clone(),
style: style.style.clone(),
2024-01-02 21:50:29 +00:00
background_color: style.background_color,
transform: style.transform,
visibility: style.visibility,
z_index: style.z_index,
..default()
},
2024-01-02 21:50:29 +00:00
style.outline,
))
2023-12-19 18:55:41 +00:00
.push_children(&children)
.id()
}
2023-12-27 20:51:10 +00:00
BevyTemplateNode::ImageNode {
image,
style,
children,
} => {
let children = children
.iter()
.map(|child| child.spawn(world))
.collect::<Box<[_]>>();
world
.spawn(NodeBundle {
2024-01-02 21:50:29 +00:00
border_color: style.border_color,
2023-12-27 20:51:10 +00:00
..default()
})
.insert((
ImageBundle {
image: image.clone(),
style: style.style.clone(),
2024-01-02 21:50:29 +00:00
background_color: style.background_color,
transform: style.transform,
visibility: style.visibility,
z_index: style.z_index,
2023-12-27 20:51:10 +00:00
..default()
},
2024-01-02 21:50:29 +00:00
style.outline,
2023-12-27 20:51:10 +00:00
))
.push_children(&children)
.id()
}
2023-12-19 18:55:41 +00:00
Self::IntrinsicTextNode(text) => world
2023-12-23 20:09:09 +00:00
.spawn(TextBundle {
text: text.clone(),
..default()
})
2023-12-10 20:55:49 +00:00
.id(),
}
2023-12-08 04:27:42 +00:00
}
}
2023-12-10 23:44:27 +00:00
2023-12-27 20:51:10 +00:00
fn parse_template_attributes(
attributes: &[TemplateAttribute],
background_color: Color,
asset_server: &AssetServer,
) -> (StyleComponents, Text, UiImage) {
let mut style = StyleComponents {
background_color: BackgroundColor(background_color),
..default()
};
2023-12-19 18:55:41 +00:00
let mut text = Text::from_section("", TextStyle::default());
2023-12-27 20:51:10 +00:00
let mut image = UiImage::default();
2023-12-10 23:44:27 +00:00
for attribute in attributes {
if let TemplateAttribute::Static {
name,
value,
namespace: _,
} = attribute
{
2023-12-19 18:55:41 +00:00
set_attribute(
name,
value,
&mut style.style,
&mut style.border_color,
&mut style.outline,
&mut style.background_color,
&mut style.transform,
&mut style.visibility,
&mut style.z_index,
2023-12-19 18:55:41 +00:00
Some(&mut text),
2023-12-27 20:51:10 +00:00
Some(&mut image),
asset_server,
2023-12-19 18:55:41 +00:00
);
2023-12-10 23:44:27 +00:00
}
}
2023-12-27 20:51:10 +00:00
(style, text, image)
}
2023-12-27 20:51:10 +00:00
#[derive(Default)]
struct StyleComponents {
style: Style,
border_color: BorderColor,
outline: Outline,
background_color: BackgroundColor,
transform: Transform,
visibility: Visibility,
z_index: ZIndex,
}