From 0fb9aed273b9ac32a24dff0e57e3d3afa5b18005 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 13 Apr 2022 12:49:53 -0500 Subject: [PATCH] add children on initial build and add sorted slice macro --- packages/native-core-macro/src/lib.rs | 49 ++- .../src/lib.rs~Stashed changes | 368 ------------------ packages/native-core/src/layout_attributes.rs | 2 - packages/native-core/src/real_dom.rs | 8 +- packages/tui/src/layout.rs | 206 +++++++++- packages/tui/src/lib.rs | 1 - packages/tui/src/render.rs | 1 - 7 files changed, 221 insertions(+), 414 deletions(-) delete mode 100644 packages/native-core-macro/src/lib.rs~Stashed changes diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index 084349831..a4c7fed7c 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -1,37 +1,44 @@ extern crate proc_macro; +use std::collections::BTreeMap; + use proc_macro::TokenStream; use quote::{quote, ToTokens}; use syn::{ - self, + self, bracketed, parse::{Parse, ParseStream, Result}, + parse_macro_input, punctuated::Punctuated, token::Paren, Field, Ident, LitStr, Token, Type, TypeTuple, }; -// struct StrSlice { -// init: LitStr, -// } +struct StrSlice { + map: BTreeMap, +} -// impl Parse for StrSlice { -// fn parse(input: ParseStream) -> Result { -// input.parse::()?; -// let str: LitStr = input.parse()?; -// let name: Ident = input.parse()?; -// input.parse::()?; -// input.parse::()?; -// Ok(LazyStatic { -// visibility, -// name, -// ty, -// init, -// }) -// } -// } +impl Parse for StrSlice { + fn parse(input: ParseStream) -> Result { + let content; + bracketed!(content in input); + let mut map = BTreeMap::new(); + while let Ok(s) = content.parse::() { + map.insert(s.value(), s); + #[allow(unused_must_use)] + { + content.parse::(); + } + } + Ok(StrSlice { map }) + } +} -// #[proc_macro] -// pub fn sorted_str_slice(input: TokenStream) -> TokenStream {} +#[proc_macro] +pub fn sorted_str_slice(input: TokenStream) -> TokenStream { + let slice: StrSlice = parse_macro_input!(input as StrSlice); + let strings = slice.map.values(); + quote!([#(#strings, )*]).into() +} #[derive(PartialEq)] enum DepKind { diff --git a/packages/native-core-macro/src/lib.rs~Stashed changes b/packages/native-core-macro/src/lib.rs~Stashed changes deleted file mode 100644 index 084349831..000000000 --- a/packages/native-core-macro/src/lib.rs~Stashed changes +++ /dev/null @@ -1,368 +0,0 @@ -extern crate proc_macro; - -use proc_macro::TokenStream; -use quote::{quote, ToTokens}; -use syn::{ - self, - parse::{Parse, ParseStream, Result}, - punctuated::Punctuated, - token::Paren, - Field, Ident, LitStr, Token, Type, TypeTuple, -}; - -// struct StrSlice { -// init: LitStr, -// } - -// impl Parse for StrSlice { -// fn parse(input: ParseStream) -> Result { -// input.parse::()?; -// let str: LitStr = input.parse()?; -// let name: Ident = input.parse()?; -// input.parse::()?; -// input.parse::()?; -// Ok(LazyStatic { -// visibility, -// name, -// ty, -// init, -// }) -// } -// } - -// #[proc_macro] -// pub fn sorted_str_slice(input: TokenStream) -> TokenStream {} - -#[derive(PartialEq)] -enum DepKind { - NodeDepState, - ChildDepState, - ParentDepState, -} - -// macro that streams data from the State for any attributes that end with _ -#[proc_macro_derive(State, attributes(node_dep_state, child_dep_state, parent_dep_state))] -pub fn state_macro_derive(input: TokenStream) -> TokenStream { - let ast = syn::parse(input).unwrap(); - impl_derive_macro(&ast) -} - -fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { - let type_name = &ast.ident; - let fields: Vec<_> = match &ast.data { - syn::Data::Struct(data) => match &data.fields { - syn::Fields::Named(e) => &e.named, - syn::Fields::Unnamed(_) => todo!("unnamed fields"), - syn::Fields::Unit => todo!("unit fields"), - } - .iter() - .collect(), - _ => unimplemented!(), - }; - let strct = Struct::parse(&fields); - let state_strct = StateStruct::parse(&fields, &strct); - - let node_dep_state_fields = quote::__private::TokenStream::from_iter( - state_strct - .state_members - .iter() - .filter(|f| f.dep_kind == DepKind::NodeDepState) - .map(|f| { - let ty_id = &f.type_id(); - let reduce = &f.reduce_self(); - quote! { - else if ty == #ty_id { - #reduce - } - } - }), - ); - let child_dep_state_fields = quote::__private::TokenStream::from_iter( - state_strct - .state_members - .iter() - .filter(|f| f.dep_kind == DepKind::ChildDepState) - .map(|f| { - let ty_id = &f.type_id(); - let reduce = &f.reduce_self(); - quote! { - else if ty == #ty_id { - #reduce - } - } - }), - ); - let parent_dep_state_fields = quote::__private::TokenStream::from_iter( - state_strct - .state_members - .iter() - .filter(|f| f.dep_kind == DepKind::ParentDepState) - .map(|f| { - let ty_id = &f.type_id(); - let reduce = &f.reduce_self(); - quote! { - else if ty == #ty_id { - #reduce - } - } - }), - ); - - let node_types = state_strct - .state_members - .iter() - .filter(|f| f.dep_kind == DepKind::NodeDepState) - .map(|f| &f.mem.ty); - let child_types = state_strct - .state_members - .iter() - .filter(|f| f.dep_kind == DepKind::ChildDepState) - .map(|f| &f.mem.ty); - let parent_types = state_strct - .state_members - .iter() - .filter(|f| f.dep_kind == DepKind::ParentDepState) - .map(|f| &f.mem.ty); - - let type_name_str = type_name.to_string(); - - let gen = quote! { - impl State for #type_name{ - fn update_node_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, ctx: &anymap::AnyMap) -> bool{ - use dioxus_native_core::state::NodeDepState as _; - // println!("called update_node_dep_state with ty: {:?}", ty); - if false { - unreachable!(); - } - #node_dep_state_fields - else{ - panic!("{:?} not in {}", ty, #type_name_str) - } - } - - fn update_parent_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, parent: Option<&Self>, ctx: &anymap::AnyMap) -> bool{ - use dioxus_native_core::state::ParentDepState as _; - // println!("called update_parent_dep_state with ty: {:?}", ty); - if false { - unreachable!(); - } - #parent_dep_state_fields - else{ - panic!("{:?} not in {}", ty, #type_name_str) - } - } - - fn update_child_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, children: &[&Self], ctx: &anymap::AnyMap) -> bool{ - use dioxus_native_core::state::ChildDepState as _; - // println!("called update_child_dep_state with ty: {:?}", ty); - if false { - unreachable!() - } - #child_dep_state_fields - else{ - panic!("{:?} not in {}", ty, #type_name_str) - } - } - - fn child_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec{ - let mut dep_types = Vec::new(); - #(if #child_types::NODE_MASK.overlaps(mask) { - dep_types.push(std::any::TypeId::of::<#child_types>()); - })* - dep_types - } - - fn parent_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec{ - let mut dep_types = Vec::new(); - #(if #parent_types::NODE_MASK.overlaps(mask) { - dep_types.push(std::any::TypeId::of::<#parent_types>()); - })* - dep_types - } - - fn node_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec{ - let mut dep_types = Vec::new(); - #(if #node_types::NODE_MASK.overlaps(mask) { - dep_types.push(std::any::TypeId::of::<#node_types>()); - })* - dep_types - } - } - }; - gen.into() -} - -struct Struct { - members: Vec, -} - -impl Struct { - fn parse(fields: &[&Field]) -> Self { - let members = fields.iter().filter_map(|f| Member::parse(f)).collect(); - Self { members } - } -} - -struct StateStruct<'a> { - state_members: Vec>, -} - -impl<'a> StateStruct<'a> { - fn parse(fields: &[&'a Field], strct: &'a Struct) -> Self { - let state_members = strct - .members - .iter() - .zip(fields.iter()) - .filter_map(|(m, f)| StateMember::parse(f, m, &strct)) - .collect(); - - // todo: sort members - - Self { state_members } - } -} - -struct DepTypes { - ctx_ty: Option, - dep_ty: Option, -} - -impl Parse for DepTypes { - fn parse(input: ParseStream) -> Result { - let dep_ty = input.parse().ok(); - let comma: Option = input.parse().ok(); - let ctx_ty = input.parse().ok(); - Ok(Self { - ctx_ty: comma.and(ctx_ty), - dep_ty, - }) - } -} - -struct NodeDepTypes { - ctx_ty: Option, -} - -impl Parse for NodeDepTypes { - fn parse(input: ParseStream) -> Result { - let ctx_ty = input.parse().ok(); - Ok(Self { ctx_ty }) - } -} - -impl From for DepTypes { - fn from(node_dep_types: NodeDepTypes) -> Self { - Self { - ctx_ty: node_dep_types.ctx_ty, - dep_ty: None, - } - } -} - -struct Member { - ty: Type, - ident: Ident, -} - -impl Member { - fn parse(field: &Field) -> Option { - Some(Self { - ty: field.ty.clone(), - ident: field.ident.as_ref()?.clone(), - }) - } -} - -struct StateMember<'a> { - mem: &'a Member, - dep_kind: DepKind, - dep_mem: Option<&'a Member>, - ctx_ty: Option, -} - -impl<'a> StateMember<'a> { - fn parse(field: &Field, mem: &'a Member, parent: &'a Struct) -> Option> { - field.attrs.iter().find_map(|a| { - let dep_kind = a - .path - .get_ident() - .map(|i| match i.to_string().as_str() { - "node_dep_state" => Some(DepKind::NodeDepState), - "child_dep_state" => Some(DepKind::ChildDepState), - "parent_dep_state" => Some(DepKind::ParentDepState), - _ => None, - }) - .flatten()?; - let deps: DepTypes = match dep_kind { - DepKind::NodeDepState => a.parse_args::().ok()?.into(), - _ => a.parse_args().ok()?, - }; - - Some(Self { - mem, - dep_kind, - dep_mem: deps - .dep_ty - .map(|ty| parent.members.iter().find(|m| m.ty == ty)) - .flatten(), - ctx_ty: deps.ctx_ty, - }) - }) - } - - fn reduce_self(&self) -> quote::__private::TokenStream { - let ident = &self.mem.ident; - let get_ctx = if let Some(ctx_ty) = &self.ctx_ty { - if ctx_ty - == &Type::Tuple(TypeTuple { - paren_token: Paren { - span: quote::__private::Span::call_site(), - }, - elems: Punctuated::new(), - }) - { - quote! {&()} - } else { - let msg = ctx_ty.to_token_stream().to_string() + " not found in context"; - quote! {ctx.get().expect(#msg)} - } - } else { - quote! {&()} - }; - let ty = &self.mem.ty; - let node_view = quote!(NodeView::new(node, #ty::NODE_MASK, vdom)); - if let Some(dep_ident) = &self.dep_mem.map(|m| &m.ident) { - match self.dep_kind { - DepKind::NodeDepState => { - quote!(self.#ident.reduce(#node_view, #get_ctx)) - } - DepKind::ChildDepState => { - quote!(self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident), #get_ctx)) - } - DepKind::ParentDepState => { - quote!(self.#ident.reduce(#node_view, parent.as_ref().map(|p| &p.#dep_ident), #get_ctx)) - } - } - } else { - match self.dep_kind { - DepKind::NodeDepState => { - quote!(self.#ident.reduce(#node_view, #get_ctx)) - } - DepKind::ChildDepState => { - quote!(self.#ident.reduce(#node_view, &(), #get_ctx)) - } - DepKind::ParentDepState => { - quote!(self.#ident.reduce(#node_view, Some(&()), #get_ctx)) - } - } - } - } - - fn type_id(&self) -> quote::__private::TokenStream { - let ty = &self.mem.ty; - quote!({ - let type_id = std::any::TypeId::of::<#ty>(); - type_id - }) - } -} diff --git a/packages/native-core/src/layout_attributes.rs b/packages/native-core/src/layout_attributes.rs index b675183a7..45461a3f9 100644 --- a/packages/native-core/src/layout_attributes.rs +++ b/packages/native-core/src/layout_attributes.rs @@ -29,8 +29,6 @@ - [ ] pub aspect_ratio: Number, */ -// align-content,align-items,align-self,animation,animation-delay,animation-direction,animation-duration,animation-fill-mode,animation-iteration-count,animation-name,animation-play-state,animation-timing-function,backface-visibility,border,border-bottom,border-bottom-color,border-bottom-left-radius,border-bottom-right-radius,border-bottom-style,border-bottom-width,border-collapse,border-color,border-image,border-image-outset,border-image-repeat,border-image-slice,border-image-source,border-image-width,border-left,border-left-color,border-left-style,border-left-width,border-radius,border-right,border-right-color,border-right-style,border-right-width,border-spacing,border-style,border-top,border-top-color,border-top-left-radius,border-top-right-radius,border-top-style,border-top-width,border-width,bottom,box-shadow,box-sizing,caption-side,clear,clip,column-count,column-fill,column-gap,column-rule,column-rule-color,column-rule-style,column-rule-width,column-span,column-width,columns,content,counter-increment,counter-reset,cursor,direction,ltr,rtl,display,empty-cells,flex,flex-basis,flex-direction,flex-flow,flex-grow,flex-shrink,flex-wrap,float,height,justify-content,flex-start,flex-end,center,space-between,space-around,space-evenly,left,letter-spacing,line-height,list-style,list-style-image,list-style-position,list-style-type,margin,margin-bottom,margin-left,margin-right,margin-top,max-height,max-width,min-height,min-width,opacity,order,outline,outline-color,outline-offset,outline-style,outline-width,overflow,overflow-x,overflow-y,padding,padding-bottom,padding-left,padding-right,padding-top,page-break-after,page-break-before,page-break-inside,perspective,perspective-origin,position,static,relative,fixed,absolute,sticky,pointer-events,quotes,resize,right,tab-size,table-layout,top,transform,transform-origin,transform-style,transition,transition-delay,transition-duration,transition-property,transition-timing-function,vertical-align,visibility,white-space,width,word-break,word-spacing,word-wrap,z-index - use stretch2::{prelude::*, style::PositionType}; /// applies the entire html namespace defined in dioxus-html diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 8c2a05e9f..ecb165c26 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -384,13 +384,7 @@ impl RealDom { let vnode = node.element(vdom); let parent = parent.as_deref(); let state = &mut node.state; - if state.update_parent_dep_state( - ty, - vnode, - vdom, - parent.filter(|n| n.id.0 != 0).map(|n| &n.state), - &ctx, - ) { + if state.update_parent_dep_state(ty, vnode, vdom, parent.map(|n| &n.state), &ctx) { changed.push(ty); } } diff --git a/packages/tui/src/layout.rs b/packages/tui/src/layout.rs index f2a26c65e..de4f13569 100644 --- a/packages/tui/src/layout.rs +++ b/packages/tui/src/layout.rs @@ -2,21 +2,46 @@ use std::cell::RefCell; use std::rc::Rc; use dioxus_core::*; +use dioxus_native_core::dioxus_native_core_macro::sorted_str_slice; use dioxus_native_core::layout_attributes::apply_layout_attributes; use dioxus_native_core::state::{AttributeMask, ChildDepState, NodeMask, NodeView}; use stretch2::prelude::*; +#[derive(Debug, Clone, Copy, PartialEq)] +pub(crate) enum PossiblyUninitalized { + Uninitalized, + Initialized(T), +} +impl PossiblyUninitalized { + pub fn unwrap(self) -> T { + match self { + Self::Initialized(i) => i, + _ => panic!(), + } + } +} +impl Default for PossiblyUninitalized { + fn default() -> Self { + Self::Uninitalized + } +} + #[derive(Clone, PartialEq, Default, Debug)] -pub struct StretchLayout { +pub(crate) struct StretchLayout { pub style: Style, - pub node: Option, + pub node: PossiblyUninitalized, } impl ChildDepState for StretchLayout { type Ctx = Rc>; type DepState = Self; // todo: update mask - const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, false, false, true); + const NODE_MASK: NodeMask = NodeMask::new( + AttributeMask::Static(SORTED_LAYOUT_ATTRS), + false, + false, + true, + ); /// Setup the layout fn reduce<'a>( @@ -44,16 +69,18 @@ impl ChildDepState for StretchLayout { }, ..Default::default() }; - if let Some(n) = self.node { + if let PossiblyUninitalized::Initialized(n) = self.node { if self.style != style { stretch.set_style(n, style).unwrap(); } } else { - self.node = Some(stretch.new_node(style, &[]).unwrap()); + self.node = + PossiblyUninitalized::Initialized(stretch.new_node(style, &[]).unwrap()); } } else { // gather up all the styles from the attribute list for &Attribute { name, value, .. } in node.attributes() { + assert!(SORTED_LAYOUT_ATTRS.binary_search(&name).is_ok()); apply_layout_attributes(name, value, &mut style); } @@ -69,14 +96,7 @@ impl ChildDepState for StretchLayout { child_layout.push(l.node.unwrap()); } - if let Some(n) = self.node { - if self.style != style { - stretch.set_style(n, style).unwrap(); - } - } else { - self.node = Some(stretch.new_node(style, &[]).unwrap()); - } - if let Some(n) = self.node { + if let PossiblyUninitalized::Initialized(n) = self.node { if self.style != style { stretch.set_style(n, style).unwrap(); } @@ -84,7 +104,9 @@ impl ChildDepState for StretchLayout { stretch.set_children(n, &child_layout).unwrap(); } } else { - self.node = Some(stretch.new_node(style, &[]).unwrap()); + self.node = PossiblyUninitalized::Initialized( + stretch.new_node(style, &child_layout).unwrap(), + ); } } if self.style != style { @@ -94,3 +116,159 @@ impl ChildDepState for StretchLayout { changed } } + +// these are the attributes in layout_attiributes in native-core +const SORTED_LAYOUT_ATTRS: &'static [&'static str] = &sorted_str_slice!([ + "align-content", + "align-items", + "align-self", + "animation", + "animation-delay", + "animation-direction", + "animation-duration", + "animation-fill-mode", + "animation-iteration-count", + "animation-name", + "animation-play-state", + "animation-timing-function", + "backface-visibility", + "border", + "border-bottom", + "border-bottom-color", + "border-bottom-left-radius", + "border-bottom-right-radius", + "border-bottom-style", + "border-bottom-width", + "border-collapse", + "border-color", + "border-image", + "border-image-outset", + "border-image-repeat", + "border-image-slice", + "border-image-source", + "border-image-width", + "border-left", + "border-left-color", + "border-left-style", + "border-left-width", + "border-radius", + "border-right", + "border-right-color", + "border-right-style", + "border-right-width", + "border-spacing", + "border-style", + "border-top", + "border-top-color", + "border-top-left-radius", + "border-top-right-radius", + "border-top-style", + "border-top-width", + "border-width", + "bottom", + "box-shadow", + "box-sizing", + "caption-side", + "clear", + "clip", + "column-count", + "column-fill", + "column-gap", + "column-rule", + "column-rule-color", + "column-rule-style", + "column-rule-width", + "column-span", + "column-width", + "columns", + "content", + "counter-increment", + "counter-reset", + "cursor", + "direction", + "ltr", + "rtl", + "display", + "empty-cells", + "flex", + "flex-basis", + "flex-direction", + "flex-flow", + "flex-grow", + "flex-shrink", + "flex-wrap", + "float", + "height", + "justify-content", + "flex-start", + "flex-end", + "center", + "space-between", + "space-around", + "space-evenly", + "left", + "letter-spacing", + "line-height", + "list-style", + "list-style-image", + "list-style-position", + "list-style-type", + "margin", + "margin-bottom", + "margin-left", + "margin-right", + "margin-top", + "max-height", + "max-width", + "min-height", + "min-width", + "opacity", + "order", + "outline", + "outline-color", + "outline-offset", + "outline-style", + "outline-width", + "overflow", + "overflow-x", + "overflow-y", + "padding", + "padding-bottom", + "padding-left", + "padding-right", + "padding-top", + "page-break-after", + "page-break-before", + "page-break-inside", + "perspective", + "perspective-origin", + "position", + "static", + "relative", + "fixed", + "absolute", + "sticky", + "pointer-events", + "quotes", + "resize", + "right", + "tab-size", + "table-layout", + "top", + "transform", + "transform-origin", + "transform-style", + "transition", + "transition-delay", + "transition-duration", + "transition-property", + "transition-timing-function", + "vertical-align", + "visibility", + "white-space", + "width", + "word-break", + "word-spacing", + "word-wrap", + "z-index" +]); diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index 2c1844eb5..1eb3f7612 100644 --- a/packages/tui/src/lib.rs +++ b/packages/tui/src/lib.rs @@ -215,7 +215,6 @@ fn render_vdom( } { - // resolve events before rendering let evts = handler.get_events(&stretch.borrow(), &mut rdom); for e in evts { vdom.handle_message(SchedulerMsg::Event(e)); diff --git a/packages/tui/src/render.rs b/packages/tui/src/render.rs index 4a69b7901..e3f03f0d8 100644 --- a/packages/tui/src/render.rs +++ b/packages/tui/src/render.rs @@ -30,7 +30,6 @@ pub(crate) fn render_vnode( } let Layout { location, size, .. } = layout.layout(node.state.layout.node.unwrap()).unwrap(); - // println!("rendering {node:?} {location:?} {size:?}"); let Point { x, y } = location; let Size { width, height } = size;