add children on initial build and add sorted slice macro

This commit is contained in:
Evan Almloff 2022-04-13 12:49:53 -05:00
parent 3b06059834
commit 0fb9aed273
7 changed files with 221 additions and 414 deletions

View file

@ -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<String, LitStr>,
}
// impl Parse for StrSlice {
// fn parse(input: ParseStream) -> Result<Self> {
// input.parse::<Token![[]>()?;
// let str: LitStr = input.parse()?;
// let name: Ident = input.parse()?;
// input.parse::<Token![,]>()?;
// input.parse::<Token![]]>()?;
// Ok(LazyStatic {
// visibility,
// name,
// ty,
// init,
// })
// }
// }
impl Parse for StrSlice {
fn parse(input: ParseStream) -> Result<Self> {
let content;
bracketed!(content in input);
let mut map = BTreeMap::new();
while let Ok(s) = content.parse::<LitStr>() {
map.insert(s.value(), s);
#[allow(unused_must_use)]
{
content.parse::<Token![,]>();
}
}
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 {

View file

@ -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<Self> {
// input.parse::<Token![[]>()?;
// let str: LitStr = input.parse()?;
// let name: Ident = input.parse()?;
// input.parse::<Token![,]>()?;
// input.parse::<Token![]]>()?;
// 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<std::any::TypeId>{
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<std::any::TypeId>{
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<std::any::TypeId>{
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<Member>,
}
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<StateMember<'a>>,
}
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<Type>,
dep_ty: Option<Type>,
}
impl Parse for DepTypes {
fn parse(input: ParseStream) -> Result<Self> {
let dep_ty = input.parse().ok();
let comma: Option<Token![,]> = input.parse().ok();
let ctx_ty = input.parse().ok();
Ok(Self {
ctx_ty: comma.and(ctx_ty),
dep_ty,
})
}
}
struct NodeDepTypes {
ctx_ty: Option<Type>,
}
impl Parse for NodeDepTypes {
fn parse(input: ParseStream) -> Result<Self> {
let ctx_ty = input.parse().ok();
Ok(Self { ctx_ty })
}
}
impl From<NodeDepTypes> 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<Self> {
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<Type>,
}
impl<'a> StateMember<'a> {
fn parse(field: &Field, mem: &'a Member, parent: &'a Struct) -> Option<StateMember<'a>> {
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::<NodeDepTypes>().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
})
}
}

View file

@ -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

View file

@ -384,13 +384,7 @@ impl<S: State> RealDom<S> {
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);
}
}

View file

@ -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<T> {
Uninitalized,
Initialized(T),
}
impl<T> PossiblyUninitalized<T> {
pub fn unwrap(self) -> T {
match self {
Self::Initialized(i) => i,
_ => panic!(),
}
}
}
impl<T> Default for PossiblyUninitalized<T> {
fn default() -> Self {
Self::Uninitalized
}
}
#[derive(Clone, PartialEq, Default, Debug)]
pub struct StretchLayout {
pub(crate) struct StretchLayout {
pub style: Style,
pub node: Option<Node>,
pub node: PossiblyUninitalized<Node>,
}
impl ChildDepState for StretchLayout {
type Ctx = Rc<RefCell<Stretch>>;
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"
]);

View file

@ -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));

View file

@ -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;