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

View File

@ -1,4 +1,5 @@
use bevy::{
asset::{AssetPath, AssetServer},
math::Quat,
render::{color::Color, view::Visibility},
text::{Text, TextAlignment},
@ -18,6 +19,8 @@ pub fn set_attribute(
visibility: &mut Visibility,
z_index: &mut ZIndex,
text: Option<&mut Text>,
image: Option<&mut UiImage>,
asset_server: &AssetServer,
) {
#[allow(unused_variables, unreachable_code)]
match (name, value) {
@ -198,6 +201,9 @@ pub fn set_attribute(
("text_color", value) if text.is_some() => {
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}`."),
}
}

View File

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