From d75930218c53af68a626c1949887e070fdc6453b Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Wed, 27 Dec 2023 12:51:10 -0800 Subject: [PATCH] Add image element --- src/apply_mutations.rs | 119 ++++++++++++++++++++++++++++++---------- src/elements.rs | 8 +++ src/hot_reload.rs | 13 +++++ src/parse_attributes.rs | 6 ++ src/tick.rs | 33 +++++++---- 5 files changed, 139 insertions(+), 40 deletions(-) diff --git a/src/apply_mutations.rs b/src/apply_mutations.rs index 7b99a44..489d0b4 100644 --- a/src/apply_mutations.rs +++ b/src/apply_mutations.rs @@ -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, 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::>(); + 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(), - } - } -} diff --git a/src/elements.rs b/src/elements.rs index ca35689..5753e8f 100644 --- a/src/elements.rs +++ b/src/elements.rs @@ -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!(); + } } diff --git a/src/hot_reload.rs b/src/hot_reload.rs index f236294..0f22fa9 100644 --- a/src/hot_reload.rs +++ b/src/hot_reload.rs @@ -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, } } diff --git a/src/parse_attributes.rs b/src/parse_attributes.rs index 1efd23f..a213faf 100644 --- a/src/parse_attributes.rs +++ b/src/parse_attributes.rs @@ -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}`."), } } diff --git a/src/tick.rs b/src/tick.rs index 3835107..31d5ddb 100644 --- a/src/tick.rs +++ b/src/tick.rs @@ -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| { + 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| { 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, - ); + }); }