From bd49112977cdc1d07493df9ed5c00d6df93c82bf Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sun, 24 Dec 2023 16:29:22 -0800 Subject: [PATCH] Add support for most remaining attributes --- src/apply_mutations.rs | 144 +++++++++++++++++++++-------- src/parse_attributes.rs | 199 ++++++++++++++++++++++++++++++++-------- 2 files changed, 268 insertions(+), 75 deletions(-) diff --git a/src/apply_mutations.rs b/src/apply_mutations.rs index 161395b..7b99a44 100644 --- a/src/apply_mutations.rs +++ b/src/apply_mutations.rs @@ -6,8 +6,9 @@ use bevy::{ ecs::{entity::Entity, system::Command, world::World}, hierarchy::{BuildWorldChildren, Children, DespawnRecursive, Parent}, prelude::default, - render::color::Color, + render::{color::Color, view::Visibility}, text::{Text, TextLayoutInfo, TextStyle}, + transform::components::Transform, ui::{ node_bundles::{NodeBundle, TextBundle}, widget::TextFlags, @@ -167,8 +168,26 @@ pub fn apply_mutations( } }; - let (mut style, mut background_color, mut text) = world - .query::<(&mut Style, &mut BackgroundColor, Option<&mut Text>)>() + let ( + mut style, + mut border_color, + mut outline, + mut background_color, + mut transform, + mut visibility, + mut z_index, + mut text, + ) = world + .query::<( + &mut Style, + &mut BorderColor, + &mut Outline, + &mut BackgroundColor, + &mut Transform, + &mut Visibility, + &mut ZIndex, + Option<&mut Text>, + )>() .get_mut(world, element_id_to_bevy_ui_entity[&id]) .unwrap(); @@ -176,7 +195,12 @@ pub fn apply_mutations( name, value, &mut style, + &mut border_color, + &mut outline, &mut background_color, + &mut transform, + &mut visibility, + &mut z_index, text.as_deref_mut(), ); } @@ -210,12 +234,12 @@ pub struct BevyTemplate { enum BevyTemplateNode { Node { - style: (Style, BackgroundColor), + style: StyleComponents, children: Box<[Self]>, }, TextNode { text: Text, - style: (Style, BackgroundColor), + style: StyleComponents, children: Box<[Self]>, }, IntrinsicTextNode(Text), @@ -242,9 +266,9 @@ impl BevyTemplateNode { attrs, children, } => { - let (style, background_color, _) = parse_template_attributes(attrs); + let (style, _) = parse_template_attributes(attrs); Self::Node { - style: (style, background_color), + style, children: children.iter().map(Self::from_dioxus).collect(), } } @@ -254,10 +278,10 @@ impl BevyTemplateNode { attrs, children, } => { - let (style, background_color, text) = parse_template_attributes(attrs); + let (style, text) = parse_template_attributes(attrs); Self::TextNode { text, - style: (style, background_color), + style, children: children.iter().map(Self::from_dioxus).collect(), } } @@ -265,7 +289,7 @@ impl BevyTemplateNode { Self::IntrinsicTextNode(Text::from_section(*text, TextStyle::default())) } TemplateNode::Dynamic { id: _ } => Self::Node { - style: (Style::default(), Color::NONE.into()), + style: StyleComponents::default(), children: Box::new([]), }, TemplateNode::DynamicText { id: _ } => { @@ -290,8 +314,30 @@ impl BevyTemplateNode { fn spawn(&self, world: &mut World) -> Entity { match self { - BevyTemplateNode::Node { - style: (style, background_color), + BevyTemplateNode::Node { style, children } => { + let children = children + .iter() + .map(|child| child.spawn(world)) + .collect::>(); + world + .spawn(( + NodeBundle { + style: style.style.clone(), + border_color: style.border_color.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() + } + BevyTemplateNode::TextNode { + text, + style, children, } => { let children = children @@ -300,29 +346,21 @@ impl BevyTemplateNode { .collect::>(); world .spawn(NodeBundle { - style: style.clone(), - background_color: background_color.clone(), - ..default() - }) - .push_children(&children) - .id() - } - BevyTemplateNode::TextNode { - text, - style: (style, background_color), - children, - } => { - let children = children - .iter() - .map(|child| child.spawn(world)) - .collect::>(); - world - .spawn(TextBundle { - text: text.clone(), - style: style.clone(), - background_color: background_color.clone(), + border_color: style.border_color.clone(), ..default() }) + .insert(( + TextBundle { + text: text.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() } @@ -336,9 +374,8 @@ impl BevyTemplateNode { } } -fn parse_template_attributes(attributes: &[TemplateAttribute]) -> (Style, BackgroundColor, Text) { - let mut style = Style::default(); - let mut background_color = Color::NONE.into(); +fn parse_template_attributes(attributes: &[TemplateAttribute]) -> (StyleComponents, Text) { + let mut style = StyleComponents::default(); let mut text = Text::from_section("", TextStyle::default()); for attribute in attributes { if let TemplateAttribute::Static { @@ -350,11 +387,40 @@ fn parse_template_attributes(attributes: &[TemplateAttribute]) -> (Style, Backgr set_attribute( name, value, - &mut style, - &mut background_color, + &mut style.style, + &mut style.border_color, + &mut style.outline, + &mut style.background_color, + &mut style.transform, + &mut style.visibility, + &mut style.z_index, Some(&mut text), ); } } - (style, background_color, text) + (style, text) +} + +struct StyleComponents { + style: Style, + border_color: BorderColor, + outline: Outline, + background_color: BackgroundColor, + transform: Transform, + 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/parse_attributes.rs b/src/parse_attributes.rs index 25e0b06..b52cb0a 100644 --- a/src/parse_attributes.rs +++ b/src/parse_attributes.rs @@ -1,17 +1,27 @@ use bevy::{ - render::color::Color, + math::Quat, + render::{color::Color, view::Visibility}, text::{Text, TextAlignment}, + transform::components::Transform, ui::*, }; +use std::f32::consts::PI; pub fn set_attribute( name: &str, value: &str, style: &mut Style, + border_color: &mut BorderColor, + outline: &mut Outline, background_color: &mut BackgroundColor, + transform: &mut Transform, + visibility: &mut Visibility, + z_index: &mut ZIndex, text: Option<&mut Text>, ) { + #[allow(unused_variables, unreachable_code)] match (name, value) { + ("animate", value) => todo!(), ("display", "flex") => style.display = Display::Flex, ("display", "grid") => style.display = Display::Grid, ("display", "none") => style.display = Display::None, @@ -23,24 +33,18 @@ pub fn set_attribute( ("overflow_x", "clip") => style.overflow.x = OverflowAxis::Clip, ("overflow_y", "visible") => style.overflow.y = OverflowAxis::Visible, ("overflow_y", "clip") => style.overflow.y = OverflowAxis::Clip, - ("left", val) => style.left = parse_val(val), - ("right", val) => style.right = parse_val(val), - ("top", val) => style.top = parse_val(val), - ("bottom", val) => style.bottom = parse_val(val), - ("width", val) => style.width = parse_val(val), - ("height", val) => style.height = parse_val(val), - ("min_width", val) => style.min_width = parse_val(val), - ("min_height", val) => style.min_height = parse_val(val), - ("max_width", val) => style.max_width = parse_val(val), - ("max_height", val) => style.max_height = parse_val(val), + ("left", value) => style.left = parse_val(value), + ("right", value) => style.right = parse_val(value), + ("top", value) => style.top = parse_val(value), + ("bottom", value) => style.bottom = parse_val(value), + ("width", value) => style.width = parse_val(value), + ("height", value) => style.height = parse_val(value), + ("min_width", value) => style.min_width = parse_val(value), + ("min_height", value) => style.min_height = parse_val(value), + ("max_width", value) => style.max_width = parse_val(value), + ("max_height", value) => style.max_height = parse_val(value), ("aspect_ratio", "none") => style.aspect_ratio = None, - ("aspect_ratio", float) => { - style.aspect_ratio = Some( - float - .parse::() - .unwrap_or_else(|val| panic!("Encountered invalid bevy_dioxus f32 `{val}`.")), - ); - } + ("aspect_ratio", value) => style.aspect_ratio = Some(parse_f32(value)), ("align_items", "default") => style.align_items = AlignItems::Default, ("align_items", "start") => style.align_items = AlignItems::Start, ("align_items", "end") => style.align_items = AlignItems::End, @@ -49,19 +53,129 @@ pub fn set_attribute( ("align_items", "center") => style.align_items = AlignItems::Center, ("align_items", "baseline") => style.align_items = AlignItems::Baseline, ("align_items", "stretch") => style.align_items = AlignItems::Stretch, - // TODO: The rest of the attributes from here on out - ("flex_direction", "column") => style.flex_direction = FlexDirection::Column, - ("background_color", hex) => { - background_color.0 = Color::hex(hex).expect(&format!( - "Encountered invalid bevy_dioxus Color hex `{hex}`." - )); - } - ("padding", val) => style.padding = UiRect::all(parse_val(val)), + ("justify_items", "default") => style.justify_items = JustifyItems::Default, + ("justify_items", "start") => style.justify_items = JustifyItems::Start, + ("justify_items", "end") => style.justify_items = JustifyItems::End, + ("justify_items", "center") => style.justify_items = JustifyItems::Center, + ("justify_items", "baseline") => style.justify_items = JustifyItems::Baseline, + ("justify_items", "stretch") => style.justify_items = JustifyItems::Stretch, + ("align_self", "auto") => style.align_self = AlignSelf::Auto, + ("align_self", "start") => style.align_self = AlignSelf::Start, + ("align_self", "end") => style.align_self = AlignSelf::End, + ("align_self", "flex_start") => style.align_self = AlignSelf::FlexStart, + ("align_self", "flex_end") => style.align_self = AlignSelf::FlexEnd, + ("align_self", "center") => style.align_self = AlignSelf::Center, + ("align_self", "baseline") => style.align_self = AlignSelf::Baseline, + ("align_self", "stretch") => style.align_self = AlignSelf::Stretch, + ("justify_self", "auto") => style.justify_self = JustifySelf::Auto, + ("justify_self", "start") => style.justify_self = JustifySelf::Start, + ("justify_self", "end") => style.justify_self = JustifySelf::End, + ("justify_self", "center") => style.justify_self = JustifySelf::Center, + ("justify_self", "baseline") => style.justify_self = JustifySelf::Baseline, + ("justify_self", "stretch") => style.justify_self = JustifySelf::Stretch, + ("align_content", "default") => style.align_content = AlignContent::Default, + ("align_content", "start") => style.align_content = AlignContent::Start, + ("align_content", "end") => style.align_content = AlignContent::End, + ("align_content", "flex_start") => style.align_content = AlignContent::FlexStart, + ("align_content", "flex_end") => style.align_content = AlignContent::FlexEnd, + ("align_content", "center") => style.align_content = AlignContent::Center, + ("align_content", "stretch") => style.align_content = AlignContent::Stretch, + ("align_content", "space_between") => style.align_content = AlignContent::SpaceBetween, + ("align_content", "space_evenly") => style.align_content = AlignContent::SpaceEvenly, + ("align_content", "space_around") => style.align_content = AlignContent::SpaceAround, + ("justify_content", "default") => style.justify_content = JustifyContent::Default, + ("justify_content", "start") => style.justify_content = JustifyContent::Start, + ("justify_content", "end") => style.justify_content = JustifyContent::End, + ("justify_content", "flex_start") => style.justify_content = JustifyContent::FlexStart, + ("justify_content", "flex_end") => style.justify_content = JustifyContent::FlexEnd, + ("justify_content", "center") => style.justify_content = JustifyContent::Center, + ("justify_content", "stretch") => style.justify_content = JustifyContent::Stretch, ("justify_content", "space_between") => { style.justify_content = JustifyContent::SpaceBetween; } - ("align_content", "space_between") => style.align_content = AlignContent::SpaceBetween, - ("text", new_text) if text.is_some() => text.unwrap().sections[0] = new_text.into(), + ("justify_content", "space_evenly") => style.justify_content = JustifyContent::SpaceEvenly, + ("justify_content", "space_around") => style.justify_content = JustifyContent::SpaceAround, + ("margin", value) => style.margin = UiRect::all(parse_val(value)), + ("margin_left", value) => style.margin.left = parse_val(value), + ("margin_right", value) => style.margin.right = parse_val(value), + ("margin_top", value) => style.margin.top = parse_val(value), + ("margin_bottom", value) => style.margin.bottom = parse_val(value), + ("padding", value) => style.padding = UiRect::all(parse_val(value)), + ("padding_left", value) => style.padding.left = parse_val(value), + ("padding_right", value) => style.padding.right = parse_val(value), + ("padding_top", value) => style.padding.top = parse_val(value), + ("padding_bottom", value) => style.padding.bottom = parse_val(value), + ("border_width", value) => style.border = UiRect::all(parse_val(value)), + ("border_width_left", value) => style.border.left = parse_val(value), + ("border_width_right", value) => style.border.right = parse_val(value), + ("border_width_top", value) => style.border.top = parse_val(value), + ("border_width_bottom", value) => style.border.bottom = parse_val(value), + ("border_color", value) => border_color.0 = parse_color(value), + ("outline_width", value) => outline.width = parse_val(value), + ("outline_offset", value) => outline.offset = parse_val(value), + ("outline_color", value) => outline.color = parse_color(value), + ("flex_direction", "row") => style.flex_direction = FlexDirection::Row, + ("flex_direction", "column") => style.flex_direction = FlexDirection::Column, + ("flex_direction", "row_reverse") => style.flex_direction = FlexDirection::RowReverse, + ("flex_direction", "column_reverse") => style.flex_direction = FlexDirection::ColumnReverse, + ("flex_wrap", "no_wrap") => style.flex_wrap = FlexWrap::NoWrap, + ("flex_wrap", "wrap") => style.flex_wrap = FlexWrap::Wrap, + ("flex_wrap", "wrap_reverse") => style.flex_wrap = FlexWrap::WrapReverse, + ("flex_grow", value) => style.flex_grow = parse_f32(value), + ("flex_shrink", value) => style.flex_shrink = parse_f32(value), + ("flex_basis", value) => style.flex_basis = parse_val(value), + ("row_gap", value) => style.row_gap = parse_val(value), + ("column_gap", value) => style.column_gap = parse_val(value), + ("grid_auto_flow", "row") => style.grid_auto_flow = GridAutoFlow::Row, + ("grid_auto_flow", "column") => style.grid_auto_flow = GridAutoFlow::Column, + ("grid_auto_flow", "row_dense") => style.grid_auto_flow = GridAutoFlow::RowDense, + ("grid_auto_flow", "column_dense") => style.grid_auto_flow = GridAutoFlow::ColumnDense, + ("grid_template_rows", value) => { + style.grid_template_rows = todo!(); + } + ("grid_template_columns", value) => { + style.grid_template_columns = todo!(); + } + ("grid_auto_rows", value) => { + style.grid_auto_rows = todo!(); + } + ("grid_auto_columns", value) => { + style.grid_auto_columns = todo!(); + } + ("grid_row", value) => { + style.grid_row = todo!(); + } + ("grid_column", value) => { + style.grid_column = todo!(); + } + ("background_color", value) => background_color.0 = parse_color(value), + ("translation", value) => { + let value = parse_f32(value); + transform.translation.x = value; + transform.translation.y = value; + } + ("translation_x", value) => transform.translation.x = parse_f32(value), + ("translation_y", value) => transform.translation.y = parse_f32(value), + ("rotation", value) => { + transform.rotation = Quat::from_rotation_y(parse_f32(value) * (180.0 / PI)); + } + ("scale", value) => { + let value = parse_f32(value); + transform.scale.x = value; + transform.scale.y = value; + } + ("scale_x", value) => transform.scale.x = parse_f32(value), + ("scale_y", value) => transform.scale.y = parse_f32(value), + ("visibility", "inherited") => *visibility = Visibility::Inherited, + ("visibility", "hidden") => *visibility = Visibility::Hidden, + ("visibility", "visible") => *visibility = Visibility::Visible, + ("z_index", value) => match value.split_once(":") { + Some(("local", value)) => *z_index = ZIndex::Local(parse_i32(value)), + Some(("global", value)) => *z_index = ZIndex::Global(parse_i32(value)), + None => *z_index = ZIndex::Local(parse_i32(value)), + _ => panic!("Encountered invalid bevy_dioxus ZIndex `{value}`."), + }, + ("text", value) if text.is_some() => text.unwrap().sections[0] = value.into(), ("text_direction", "inherit") if text.is_some() => style.direction = Direction::Inherit, ("text_direction", "left_to_right") if text.is_some() => { style.direction = Direction::LeftToRight; @@ -78,20 +192,33 @@ pub fn set_attribute( ("text_alignment", "right") if text.is_some() => { text.unwrap().alignment = TextAlignment::Right; } - ("text_size", val) if text.is_some() => { - text.unwrap().sections[0].style.font_size = val - .parse::() - .unwrap_or_else(|val| panic!("Encountered invalid bevy_dioxus f32 `{val}`.")); + ("text_size", value) if text.is_some() => { + text.unwrap().sections[0].style.font_size = parse_f32(value); } - ("text_color", hex) if text.is_some() => { - text.unwrap().sections[0].style.color = Color::hex(hex).expect(&format!( - "Encountered invalid bevy_dioxus Color hex `{hex}`." - )); + ("text_color", value) if text.is_some() => { + text.unwrap().sections[0].style.color = parse_color(value); } _ => panic!("Encountered unsupported bevy_dioxus attribute `{name}: {value}`."), } } +fn parse_color(hex: &str) -> Color { + Color::hex(hex).expect(&format!( + "Encountered invalid bevy_dioxus Color hex `{hex}`." + )) +} + +fn parse_f32(float: &str) -> f32 { + float + .parse::() + .unwrap_or_else(|val| panic!("Encountered invalid bevy_dioxus f32 `{val}`.")) +} + +fn parse_i32(int: &str) -> i32 { + int.parse::() + .unwrap_or_else(|val| panic!("Encountered invalid bevy_dioxus i32 `{val}`.")) +} + fn parse_val(val: &str) -> Val { if let Ok(val) = val.parse::() { return Val::Px(val);