Add image element

This commit is contained in:
JMS55 2023-12-27 12:51:10 -08:00
parent 3d222ec2aa
commit d75930218c
5 changed files with 139 additions and 40 deletions

View file

@ -3,6 +3,7 @@ use crate::{
parse_attributes::set_attribute, parse_attributes::set_attribute,
}; };
use bevy::{ use bevy::{
asset::AssetServer,
ecs::{entity::Entity, system::Command, world::World}, ecs::{entity::Entity, system::Command, world::World},
hierarchy::{BuildWorldChildren, Children, DespawnRecursive, Parent}, hierarchy::{BuildWorldChildren, Children, DespawnRecursive, Parent},
prelude::default, prelude::default,
@ -10,7 +11,7 @@ use bevy::{
text::{Text, TextLayoutInfo, TextStyle}, text::{Text, TextLayoutInfo, TextStyle},
transform::components::Transform, transform::components::Transform,
ui::{ ui::{
node_bundles::{NodeBundle, TextBundle}, node_bundles::{ImageBundle, NodeBundle, TextBundle},
widget::TextFlags, widget::TextFlags,
*, *,
}, },
@ -28,11 +29,12 @@ pub fn apply_mutations(
templates: &mut HashMap<String, BevyTemplate>, templates: &mut HashMap<String, BevyTemplate>,
root_entity: Entity, root_entity: Entity,
world: &mut World, world: &mut World,
asset_server: &AssetServer,
) { ) {
for new_template in mutations.templates { for new_template in mutations.templates {
templates.insert( templates.insert(
new_template.name.to_owned(), new_template.name.to_owned(),
BevyTemplate::from_dioxus(&new_template), BevyTemplate::from_dioxus(&new_template, asset_server),
); );
} }
@ -63,8 +65,11 @@ pub fn apply_mutations(
stack.push(entity); stack.push(entity);
} }
Mutation::CreateTextNode { value, id } => { Mutation::CreateTextNode { value, id } => {
let entity = let entity = BevyTemplateNode::from_dioxus(
BevyTemplateNode::from_dioxus(&TemplateNode::Text { text: value }).spawn(world); &TemplateNode::Text { text: value },
asset_server,
)
.spawn(world);
element_id_to_bevy_ui_entity.insert(id, entity); element_id_to_bevy_ui_entity.insert(id, entity);
bevy_ui_entity_to_element_id.insert(entity, id); bevy_ui_entity_to_element_id.insert(entity, id);
stack.push(entity); stack.push(entity);
@ -177,6 +182,7 @@ pub fn apply_mutations(
mut visibility, mut visibility,
mut z_index, mut z_index,
mut text, mut text,
mut image,
) = world ) = world
.query::<( .query::<(
&mut Style, &mut Style,
@ -187,6 +193,7 @@ pub fn apply_mutations(
&mut Visibility, &mut Visibility,
&mut ZIndex, &mut ZIndex,
Option<&mut Text>, Option<&mut Text>,
Option<&mut UiImage>,
)>() )>()
.get_mut(world, element_id_to_bevy_ui_entity[&id]) .get_mut(world, element_id_to_bevy_ui_entity[&id])
.unwrap(); .unwrap();
@ -202,6 +209,8 @@ pub fn apply_mutations(
&mut visibility, &mut visibility,
&mut z_index, &mut z_index,
text.as_deref_mut(), text.as_deref_mut(),
image.as_deref_mut(),
asset_server,
); );
} }
Mutation::SetText { value, id } => { Mutation::SetText { value, id } => {
@ -242,23 +251,28 @@ enum BevyTemplateNode {
style: StyleComponents, style: StyleComponents,
children: Box<[Self]>, children: Box<[Self]>,
}, },
ImageNode {
image: UiImage,
style: StyleComponents,
children: Box<[Self]>,
},
IntrinsicTextNode(Text), IntrinsicTextNode(Text),
} }
impl BevyTemplate { impl BevyTemplate {
fn from_dioxus(template: &Template) -> Self { fn from_dioxus(template: &Template, asset_server: &AssetServer) -> Self {
Self { Self {
roots: template roots: template
.roots .roots
.iter() .iter()
.map(BevyTemplateNode::from_dioxus) .map(|node| BevyTemplateNode::from_dioxus(node, asset_server))
.collect(), .collect(),
} }
} }
} }
impl BevyTemplateNode { impl BevyTemplateNode {
fn from_dioxus(node: &TemplateNode) -> Self { fn from_dioxus(node: &TemplateNode, asset_server: &AssetServer) -> Self {
match node { match node {
TemplateNode::Element { TemplateNode::Element {
tag: "node", tag: "node",
@ -266,10 +280,13 @@ impl BevyTemplateNode {
attrs, attrs,
children, children,
} => { } => {
let (style, _) = parse_template_attributes(attrs); let (style, _, _) = parse_template_attributes(attrs, Color::NONE, asset_server);
Self::Node { Self::Node {
style, style,
children: children.iter().map(Self::from_dioxus).collect(), children: children
.iter()
.map(|node| Self::from_dioxus(node, asset_server))
.collect(),
} }
} }
TemplateNode::Element { TemplateNode::Element {
@ -278,11 +295,31 @@ impl BevyTemplateNode {
attrs, attrs,
children, children,
} => { } => {
let (style, text) = parse_template_attributes(attrs); let (style, text, _) = parse_template_attributes(attrs, Color::NONE, asset_server);
Self::TextNode { Self::TextNode {
text, text,
style, style,
children: children.iter().map(Self::from_dioxus).collect(), 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(),
} }
} }
TemplateNode::Text { text } => { TemplateNode::Text { text } => {
@ -364,6 +401,35 @@ impl BevyTemplateNode {
.push_children(&children) .push_children(&children)
.id() .id()
} }
BevyTemplateNode::ImageNode {
image,
style,
children,
} => {
let children = children
.iter()
.map(|child| child.spawn(world))
.collect::<Box<[_]>>();
world
.spawn(NodeBundle {
border_color: style.border_color.clone(),
..default()
})
.insert((
ImageBundle {
image: image.clone(),
style: style.style.clone(),
background_color: style.background_color.clone(),
transform: style.transform.clone(),
visibility: style.visibility.clone(),
z_index: style.z_index.clone(),
..default()
},
style.outline.clone(),
))
.push_children(&children)
.id()
}
Self::IntrinsicTextNode(text) => world Self::IntrinsicTextNode(text) => world
.spawn(TextBundle { .spawn(TextBundle {
text: text.clone(), text: text.clone(),
@ -374,9 +440,17 @@ impl BevyTemplateNode {
} }
} }
fn parse_template_attributes(attributes: &[TemplateAttribute]) -> (StyleComponents, Text) { fn parse_template_attributes(
let mut style = StyleComponents::default(); attributes: &[TemplateAttribute],
background_color: Color,
asset_server: &AssetServer,
) -> (StyleComponents, Text, UiImage) {
let mut style = StyleComponents {
background_color: BackgroundColor(background_color),
..default()
};
let mut text = Text::from_section("", TextStyle::default()); let mut text = Text::from_section("", TextStyle::default());
let mut image = UiImage::default();
for attribute in attributes { for attribute in attributes {
if let TemplateAttribute::Static { if let TemplateAttribute::Static {
name, name,
@ -395,12 +469,15 @@ fn parse_template_attributes(attributes: &[TemplateAttribute]) -> (StyleComponen
&mut style.visibility, &mut style.visibility,
&mut style.z_index, &mut style.z_index,
Some(&mut text), Some(&mut text),
Some(&mut image),
asset_server,
); );
} }
} }
(style, text) (style, text, image)
} }
#[derive(Default)]
struct StyleComponents { struct StyleComponents {
style: Style, style: Style,
border_color: BorderColor, border_color: BorderColor,
@ -410,17 +487,3 @@ struct StyleComponents {
visibility: Visibility, visibility: Visibility,
z_index: ZIndex, z_index: ZIndex,
} }
impl Default for StyleComponents {
fn default() -> Self {
Self {
style: Default::default(),
border_color: Default::default(),
outline: Default::default(),
background_color: Color::NONE.into(),
transform: Default::default(),
visibility: Default::default(),
z_index: Default::default(),
}
}
}

View file

@ -93,4 +93,12 @@ pub mod dioxus_elements {
pub const text_color: AttributeDescription = ("text_color", None, false); pub const text_color: AttributeDescription = ("text_color", None, false);
node_attributes!(); node_attributes!();
} }
pub struct image;
impl image {
pub const TAG_NAME: &'static str = "image";
pub const NAME_SPACE: Option<&'static str> = NAME_SPACE;
pub const image_asset_path: AttributeDescription = ("image_asset_path", None, false);
node_attributes!();
}
} }

View file

@ -43,6 +43,15 @@ impl HotReloadingContext for HotReloadContext {
return Some(attribute); return Some(attribute);
} }
} }
if element_name_rust == dioxus_elements::image::TAG_NAME {
let attribute = match attribute_name_rust {
"image_asset_path" => Some(("image_asset_path", None)),
_ => None,
};
if let Some(attribute) = attribute {
return Some(attribute);
}
}
if let dioxus_elements::node::TAG_NAME | dioxus_elements::text::TAG_NAME = element_name_rust if let dioxus_elements::node::TAG_NAME | dioxus_elements::text::TAG_NAME = element_name_rust
{ {
match attribute_name_rust { match attribute_name_rust {
@ -127,6 +136,10 @@ impl HotReloadingContext for HotReloadContext {
dioxus_elements::text::TAG_NAME, dioxus_elements::text::TAG_NAME,
dioxus_elements::text::NAME_SPACE, dioxus_elements::text::NAME_SPACE,
)), )),
dioxus_elements::image::TAG_NAME => Some((
dioxus_elements::image::TAG_NAME,
dioxus_elements::image::NAME_SPACE,
)),
_ => None, _ => None,
} }
} }

View file

@ -1,4 +1,5 @@
use bevy::{ use bevy::{
asset::{AssetPath, AssetServer},
math::Quat, math::Quat,
render::{color::Color, view::Visibility}, render::{color::Color, view::Visibility},
text::{Text, TextAlignment}, text::{Text, TextAlignment},
@ -18,6 +19,8 @@ pub fn set_attribute(
visibility: &mut Visibility, visibility: &mut Visibility,
z_index: &mut ZIndex, z_index: &mut ZIndex,
text: Option<&mut Text>, text: Option<&mut Text>,
image: Option<&mut UiImage>,
asset_server: &AssetServer,
) { ) {
#[allow(unused_variables, unreachable_code)] #[allow(unused_variables, unreachable_code)]
match (name, value) { match (name, value) {
@ -198,6 +201,9 @@ pub fn set_attribute(
("text_color", value) if text.is_some() => { ("text_color", value) if text.is_some() => {
text.unwrap().sections[0].style.color = parse_color(value); text.unwrap().sections[0].style.color = parse_color(value);
} }
("image_asset_path", value) if image.is_some() => {
image.unwrap().texture = asset_server.load(AssetPath::parse(value));
}
_ => panic!("Encountered unsupported bevy_dioxus attribute `{name}: {value}`."), _ => panic!("Encountered unsupported bevy_dioxus attribute `{name}: {value}`."),
} }
} }

View file

@ -6,6 +6,7 @@ use crate::{
DioxusUiRoot, UiContext, UiRoot, DioxusUiRoot, UiContext, UiRoot,
}; };
use bevy::{ use bevy::{
asset::AssetServer,
ecs::{ ecs::{
entity::Entity, entity::Entity,
world::{Mut, World}, world::{Mut, World},
@ -104,23 +105,31 @@ fn render_ui(root_entity: Entity, ui_root: &mut UiRoot, world: &mut World) {
crate::hot_reload::update_templates(world, &mut ui_root.virtual_dom); crate::hot_reload::update_templates(world, &mut ui_root.virtual_dom);
if ui_root.needs_rebuild { if ui_root.needs_rebuild {
let mutations = ui_root.virtual_dom.rebuild();
world.resource_scope(|world, asset_server: Mut<AssetServer>| {
apply_mutations( apply_mutations(
ui_root.virtual_dom.rebuild(), mutations,
&mut ui_root.element_id_to_bevy_ui_entity, &mut ui_root.element_id_to_bevy_ui_entity,
&mut ui_root.bevy_ui_entity_to_element_id, &mut ui_root.bevy_ui_entity_to_element_id,
&mut ui_root.templates, &mut ui_root.templates,
root_entity, root_entity,
world, world,
&asset_server,
); );
});
ui_root.needs_rebuild = false; ui_root.needs_rebuild = false;
} }
let mutations = ui_root.virtual_dom.render_immediate();
world.resource_scope(|world, asset_server: Mut<AssetServer>| {
apply_mutations( apply_mutations(
ui_root.virtual_dom.render_immediate(), mutations,
&mut ui_root.element_id_to_bevy_ui_entity, &mut ui_root.element_id_to_bevy_ui_entity,
&mut ui_root.bevy_ui_entity_to_element_id, &mut ui_root.bevy_ui_entity_to_element_id,
&mut ui_root.templates, &mut ui_root.templates,
root_entity, root_entity,
world, world,
&asset_server,
); );
});
} }