mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-23 20:53:06 +00:00
add node dependancy
This commit is contained in:
parent
0fb9aed273
commit
436c6a02f7
18 changed files with 1312 additions and 575 deletions
|
@ -10,3 +10,13 @@ proc-macro = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
syn = { version = "1.0.11", features = ["extra-traits"] }
|
syn = { version = "1.0.11", features = ["extra-traits"] }
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
|
dioxus-native-core = { path = "../native-core" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
dioxus-core = { path = "../core", version = "^0.2.0" }
|
||||||
|
dioxus-html = { path = "../html", version = "^0.2.0" }
|
||||||
|
dioxus-core-macro = { path = "../core-macro", version = "^0.2.0" }
|
||||||
|
|
||||||
|
smallvec = "1.6"
|
||||||
|
fxhash = "0.2"
|
||||||
|
anymap = "0.12.1"
|
|
@ -1,38 +1,17 @@
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
mod sorted_slice;
|
||||||
|
|
||||||
|
use dioxus_native_core::state::MemberId;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens, __private::Span};
|
||||||
|
use sorted_slice::StrSlice;
|
||||||
use syn::{
|
use syn::{
|
||||||
self, bracketed,
|
self,
|
||||||
parse::{Parse, ParseStream, Result},
|
parse::{Parse, ParseStream, Result},
|
||||||
parse_macro_input,
|
parse_macro_input, parse_quote, Error, Field, Ident, Token, Type,
|
||||||
punctuated::Punctuated,
|
|
||||||
token::Paren,
|
|
||||||
Field, Ident, LitStr, Token, Type, TypeTuple,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StrSlice {
|
|
||||||
map: BTreeMap<String, LitStr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
#[proc_macro]
|
||||||
pub fn sorted_str_slice(input: TokenStream) -> TokenStream {
|
pub fn sorted_str_slice(input: TokenStream) -> TokenStream {
|
||||||
let slice: StrSlice = parse_macro_input!(input as StrSlice);
|
let slice: StrSlice = parse_macro_input!(input as StrSlice);
|
||||||
|
@ -40,14 +19,13 @@ pub fn sorted_str_slice(input: TokenStream) -> TokenStream {
|
||||||
quote!([#(#strings, )*]).into()
|
quote!([#(#strings, )*]).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
enum DepKind {
|
enum DepKind {
|
||||||
NodeDepState,
|
NodeDepState,
|
||||||
ChildDepState,
|
ChildDepState,
|
||||||
ParentDepState,
|
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))]
|
#[proc_macro_derive(State, attributes(node_dep_state, child_dep_state, parent_dep_state))]
|
||||||
pub fn state_macro_derive(input: TokenStream) -> TokenStream {
|
pub fn state_macro_derive(input: TokenStream) -> TokenStream {
|
||||||
let ast = syn::parse(input).unwrap();
|
let ast = syn::parse(input).unwrap();
|
||||||
|
@ -60,153 +38,149 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
syn::Data::Struct(data) => match &data.fields {
|
syn::Data::Struct(data) => match &data.fields {
|
||||||
syn::Fields::Named(e) => &e.named,
|
syn::Fields::Named(e) => &e.named,
|
||||||
syn::Fields::Unnamed(_) => todo!("unnamed fields"),
|
syn::Fields::Unnamed(_) => todo!("unnamed fields"),
|
||||||
syn::Fields::Unit => todo!("unit fields"),
|
syn::Fields::Unit => todo!("unit structs"),
|
||||||
}
|
}
|
||||||
.iter()
|
.iter()
|
||||||
.collect(),
|
.collect(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
let strct = Struct::parse(&fields);
|
let strct = Struct::new(type_name.clone(), &fields);
|
||||||
let state_strct = StateStruct::parse(&fields, &strct);
|
match StateStruct::parse(&fields, &strct) {
|
||||||
|
Ok(state_strct) => {
|
||||||
|
let node_dep_state_fields = state_strct
|
||||||
|
.state_members
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.dep_kind == DepKind::NodeDepState)
|
||||||
|
.map(|f| f.reduce_self());
|
||||||
|
let child_dep_state_fields = state_strct
|
||||||
|
.state_members
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.dep_kind == DepKind::ChildDepState)
|
||||||
|
.map(|f| f.reduce_self());
|
||||||
|
let parent_dep_state_fields = state_strct
|
||||||
|
.state_members
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.dep_kind == DepKind::ParentDepState)
|
||||||
|
.map(|f| f.reduce_self());
|
||||||
|
|
||||||
let node_dep_state_fields = quote::__private::TokenStream::from_iter(
|
let node_iter = state_strct
|
||||||
state_strct
|
.state_members
|
||||||
.state_members
|
.iter()
|
||||||
.iter()
|
.filter(|m| m.dep_kind == DepKind::NodeDepState);
|
||||||
.filter(|f| f.dep_kind == DepKind::NodeDepState)
|
let node_ids = node_iter.clone().map(|m| m.member_id.0);
|
||||||
.map(|f| {
|
let node_ids_clone = node_ids.clone();
|
||||||
let ty_id = &f.type_id();
|
let node_types = node_iter.map(|f| &f.mem.ty);
|
||||||
let reduce = &f.reduce_self();
|
|
||||||
quote! {
|
let child_iter = state_strct
|
||||||
else if ty == #ty_id {
|
.state_members
|
||||||
#reduce
|
.iter()
|
||||||
|
.filter(|m| m.dep_kind == DepKind::ChildDepState);
|
||||||
|
let child_ids = child_iter.clone().map(|m| m.member_id.0);
|
||||||
|
let child_ids_clone = child_ids.clone();
|
||||||
|
let child_types = child_iter.map(|f| &f.mem.ty);
|
||||||
|
|
||||||
|
let parent_iter = state_strct
|
||||||
|
.state_members
|
||||||
|
.iter()
|
||||||
|
.filter(|m| m.dep_kind == DepKind::ParentDepState);
|
||||||
|
let parent_ids = parent_iter.clone().map(|m| m.member_id.0);
|
||||||
|
let parent_ids_clone = parent_ids.clone();
|
||||||
|
let parent_types = parent_iter.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: dioxus_native_core::state::MemberId,
|
||||||
|
node: &'a dioxus_core::VNode<'a>,
|
||||||
|
vdom: &'a dioxus_core::VirtualDom,
|
||||||
|
ctx: &anymap::AnyMap,
|
||||||
|
) -> Option<dioxus_native_core::state::NodeStatesChanged>{
|
||||||
|
use dioxus_native_core::state::NodeDepState as _;
|
||||||
|
match ty.0{
|
||||||
|
#(
|
||||||
|
#node_ids => #node_dep_state_fields,
|
||||||
|
)*
|
||||||
|
_ => panic!("{:?} not in {}", ty, #type_name_str),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_parent_dep_state<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
ty: dioxus_native_core::state::MemberId,
|
||||||
|
node: &'a dioxus_core::VNode<'a>,
|
||||||
|
vdom: &'a dioxus_core::VirtualDom,
|
||||||
|
parent: Option<&Self>,
|
||||||
|
ctx: &anymap::AnyMap,
|
||||||
|
) -> Option<dioxus_native_core::state::ParentStatesChanged>{
|
||||||
|
use dioxus_native_core::state::ParentDepState as _;
|
||||||
|
match ty.0{
|
||||||
|
#(
|
||||||
|
#parent_ids => #parent_dep_state_fields,
|
||||||
|
)*
|
||||||
|
_ => panic!("{:?} not in {}", ty, #type_name_str),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_child_dep_state<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
ty: dioxus_native_core::state::MemberId,
|
||||||
|
node: &'a dioxus_core::VNode<'a>,
|
||||||
|
vdom: &'a dioxus_core::VirtualDom,
|
||||||
|
children: &[&Self],
|
||||||
|
ctx: &anymap::AnyMap,
|
||||||
|
) -> Option<dioxus_native_core::state::ChildStatesChanged>{
|
||||||
|
use dioxus_native_core::state::ChildDepState as _;
|
||||||
|
match ty.0{
|
||||||
|
#(
|
||||||
|
#child_ids => {#child_dep_state_fields},
|
||||||
|
)*
|
||||||
|
_ => panic!("{:?} not in {}", ty, #type_name_str),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn child_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
|
||||||
|
let mut dep_types = Vec::new();
|
||||||
|
#(if #child_types::NODE_MASK.overlaps(mask) {
|
||||||
|
dep_types.push(dioxus_native_core::state::MemberId(#child_ids_clone));
|
||||||
|
})*
|
||||||
|
dep_types
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
|
||||||
|
let mut dep_types = Vec::new();
|
||||||
|
#(if #parent_types::NODE_MASK.overlaps(mask) {
|
||||||
|
dep_types.push(dioxus_native_core::state::MemberId(#parent_ids_clone));
|
||||||
|
})*
|
||||||
|
dep_types
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
|
||||||
|
let mut dep_types = Vec::new();
|
||||||
|
#(if #node_types::NODE_MASK.overlaps(mask) {
|
||||||
|
dep_types.push(dioxus_native_core::state::MemberId(#node_ids_clone));
|
||||||
|
})*
|
||||||
|
dep_types
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
};
|
||||||
);
|
gen.into()
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
Err(e) => e.into_compile_error().into(),
|
||||||
gen.into()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Struct {
|
struct Struct {
|
||||||
|
name: Ident,
|
||||||
members: Vec<Member>,
|
members: Vec<Member>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Struct {
|
impl Struct {
|
||||||
fn parse(fields: &[&Field]) -> Self {
|
fn new(name: Ident, fields: &[&Field]) -> Self {
|
||||||
let members = fields.iter().filter_map(|f| Member::parse(f)).collect();
|
let members = fields.iter().filter_map(|f| Member::parse(f)).collect();
|
||||||
Self { members }
|
Self { name, members }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,57 +189,198 @@ struct StateStruct<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StateStruct<'a> {
|
impl<'a> StateStruct<'a> {
|
||||||
fn parse(fields: &[&'a Field], strct: &'a Struct) -> Self {
|
fn parse(fields: &[&'a Field], strct: &'a Struct) -> Result<Self> {
|
||||||
let state_members = strct
|
let mut parse_err = Ok(());
|
||||||
|
let state_members: Vec<_> = strct
|
||||||
.members
|
.members
|
||||||
.iter()
|
.iter()
|
||||||
.zip(fields.iter())
|
.zip(fields.iter())
|
||||||
.filter_map(|(m, f)| StateMember::parse(f, m, &strct))
|
.filter_map(|(m, f)| match StateMember::parse(f, m, &strct) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(err) => {
|
||||||
|
parse_err = Err(err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
parse_err?;
|
||||||
|
|
||||||
// todo: sort members
|
#[derive(Debug, Clone)]
|
||||||
|
struct DepNode<'a> {
|
||||||
|
state_mem: StateMember<'a>,
|
||||||
|
depandants: Vec<Box<DepNode<'a>>>,
|
||||||
|
}
|
||||||
|
impl<'a> DepNode<'a> {
|
||||||
|
fn new(state_mem: StateMember<'a>) -> Self {
|
||||||
|
Self {
|
||||||
|
state_mem,
|
||||||
|
depandants: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Self { state_members }
|
/// flattens the node in pre order
|
||||||
}
|
fn flatten(self) -> Vec<StateMember<'a>> {
|
||||||
}
|
let DepNode {
|
||||||
|
state_mem,
|
||||||
|
depandants,
|
||||||
|
} = self;
|
||||||
|
let mut flat = vec![state_mem];
|
||||||
|
for d in depandants {
|
||||||
|
flat.append(&mut d.flatten());
|
||||||
|
}
|
||||||
|
flat
|
||||||
|
}
|
||||||
|
|
||||||
struct DepTypes {
|
fn set_ids(&mut self, current_id: &mut usize) {
|
||||||
ctx_ty: Option<Type>,
|
self.state_mem.member_id = dioxus_native_core::state::MemberId(*current_id);
|
||||||
dep_ty: Option<Type>,
|
// if the node depends on itself, we need to add the dependency seperately
|
||||||
}
|
if let Some(dep) = self.state_mem.dep_mem {
|
||||||
|
if dep == self.state_mem.mem {
|
||||||
|
self.state_mem
|
||||||
|
.dependants
|
||||||
|
.push((MemberId(*current_id), self.state_mem.dep_kind.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*current_id += 1;
|
||||||
|
for d in &mut self.depandants {
|
||||||
|
self.state_mem
|
||||||
|
.dependants
|
||||||
|
.push((MemberId(*current_id), d.state_mem.dep_kind.clone()));
|
||||||
|
d.set_ids(current_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Parse for DepTypes {
|
fn contains_member(&self, member: &Member) -> bool {
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
if self.state_mem.mem == member {
|
||||||
let dep_ty = input.parse().ok();
|
true
|
||||||
let comma: Option<Token![,]> = input.parse().ok();
|
} else {
|
||||||
let ctx_ty = input.parse().ok();
|
self.depandants.iter().any(|d| d.contains_member(member))
|
||||||
Ok(Self {
|
}
|
||||||
ctx_ty: comma.and(ctx_ty),
|
}
|
||||||
dep_ty,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NodeDepTypes {
|
// check if there are any mixed child/parent dependancies
|
||||||
ctx_ty: Option<Type>,
|
fn check(&self) -> Option<Error> {
|
||||||
}
|
self.kind().err()
|
||||||
|
}
|
||||||
|
|
||||||
impl Parse for NodeDepTypes {
|
fn kind(&self) -> Result<&DepKind> {
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
fn reduce_kind<'a>(dk1: &'a DepKind, dk2: &'a DepKind) -> Result<&'a DepKind> {
|
||||||
let ctx_ty = input.parse().ok();
|
match (dk1, dk2) {
|
||||||
Ok(Self { ctx_ty })
|
(DepKind::ChildDepState, DepKind::ParentDepState)
|
||||||
}
|
| (DepKind::ParentDepState, DepKind::ChildDepState) => Err(Error::new(
|
||||||
}
|
Span::call_site(),
|
||||||
|
"There is a ChildDepState that depends on a ParentDepState",
|
||||||
|
)),
|
||||||
|
// node dep state takes the lowest priority
|
||||||
|
(DepKind::NodeDepState, important) | (important, DepKind::NodeDepState) => {
|
||||||
|
Ok(important)
|
||||||
|
}
|
||||||
|
// they are the same
|
||||||
|
(fst, _) => Ok(fst),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reduce_kind(
|
||||||
|
self.depandants
|
||||||
|
.iter()
|
||||||
|
.try_fold(&DepKind::NodeDepState, |dk1, dk2| {
|
||||||
|
reduce_kind(dk1, dk2.kind()?)
|
||||||
|
})?,
|
||||||
|
&self.state_mem.dep_kind,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl From<NodeDepTypes> for DepTypes {
|
fn insert_dependant(&mut self, other: DepNode<'a>) -> bool {
|
||||||
fn from(node_dep_types: NodeDepTypes) -> Self {
|
let dep = other.state_mem.dep_mem.unwrap();
|
||||||
Self {
|
if self.contains_member(dep) {
|
||||||
ctx_ty: node_dep_types.ctx_ty,
|
if self.state_mem.mem == dep {
|
||||||
dep_ty: None,
|
self.depandants.push(Box::new(other));
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
self.depandants
|
||||||
|
.iter_mut()
|
||||||
|
.find(|d| d.contains_member(dep))
|
||||||
|
.unwrap()
|
||||||
|
.insert_dependant(other)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// members need to be sorted so that members are updated after the members they depend on
|
||||||
|
let mut roots: Vec<DepNode> = vec![];
|
||||||
|
for m in state_members.into_iter() {
|
||||||
|
if let Some(dep) = m.dep_mem {
|
||||||
|
let root_depends_on = roots
|
||||||
|
.iter()
|
||||||
|
.filter_map(|m| m.state_mem.dep_mem)
|
||||||
|
.any(|d| m.mem == d);
|
||||||
|
|
||||||
|
if let Some(r) = roots.iter_mut().find(|r| r.contains_member(dep)) {
|
||||||
|
let new = DepNode::new(m);
|
||||||
|
if root_depends_on {
|
||||||
|
return Err(Error::new(
|
||||||
|
new.state_mem.mem.ident.span(),
|
||||||
|
format!("{} has a circular dependancy", new.state_mem.mem.ident),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// return Err(Error::new(new.state_mem.mem.ident.span(), "stuff"));
|
||||||
|
r.insert_dependant(new);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut new = DepNode::new(m);
|
||||||
|
let mut i = 0;
|
||||||
|
while i < roots.len() {
|
||||||
|
if roots[i].state_mem.dep_mem == Some(new.state_mem.mem) {
|
||||||
|
let child = roots.remove(i);
|
||||||
|
new.insert_dependant(child);
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
roots.push(new);
|
||||||
|
}
|
||||||
|
let mut current_id = 0;
|
||||||
|
for r in &mut roots {
|
||||||
|
r.set_ids(&mut current_id);
|
||||||
|
}
|
||||||
|
if let Some(err) = roots.iter().find_map(DepNode::check) {
|
||||||
|
Err(err)
|
||||||
|
} else {
|
||||||
|
let state_members: Vec<_> = roots
|
||||||
|
.into_iter()
|
||||||
|
.map(|r| r.flatten().into_iter())
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(Self { state_members })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Dependancy {
|
||||||
|
ctx_ty: Option<Type>,
|
||||||
|
dep: Option<Ident>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Dependancy {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let dep = input
|
||||||
|
.parse()
|
||||||
|
.ok()
|
||||||
|
.filter(|i: &Ident| format!("{}", i) != "NONE");
|
||||||
|
let comma: Option<Token![,]> = input.parse().ok();
|
||||||
|
let ctx_ty = input.parse().ok();
|
||||||
|
Ok(Self {
|
||||||
|
ctx_ty: comma.and(ctx_ty),
|
||||||
|
dep,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
struct Member {
|
struct Member {
|
||||||
ty: Type,
|
ty: Type,
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
|
@ -280,16 +395,25 @@ impl Member {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
struct StateMember<'a> {
|
struct StateMember<'a> {
|
||||||
mem: &'a Member,
|
mem: &'a Member,
|
||||||
dep_kind: DepKind,
|
dep_kind: DepKind,
|
||||||
dep_mem: Option<&'a Member>,
|
dep_mem: Option<&'a Member>,
|
||||||
ctx_ty: Option<Type>,
|
ctx_ty: Option<Type>,
|
||||||
|
dependants: Vec<(dioxus_native_core::state::MemberId, DepKind)>,
|
||||||
|
// This is just the index of the final order of the struct it is used to communicate which parts need updated and what order to update them in.
|
||||||
|
member_id: dioxus_native_core::state::MemberId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StateMember<'a> {
|
impl<'a> StateMember<'a> {
|
||||||
fn parse(field: &Field, mem: &'a Member, parent: &'a Struct) -> Option<StateMember<'a>> {
|
fn parse(
|
||||||
field.attrs.iter().find_map(|a| {
|
field: &Field,
|
||||||
|
mem: &'a Member,
|
||||||
|
parent: &'a Struct,
|
||||||
|
) -> Result<Option<StateMember<'a>>> {
|
||||||
|
let mut err = Ok(());
|
||||||
|
let member = field.attrs.iter().find_map(|a| {
|
||||||
let dep_kind = a
|
let dep_kind = a
|
||||||
.path
|
.path
|
||||||
.get_ident()
|
.get_ident()
|
||||||
|
@ -300,34 +424,44 @@ impl<'a> StateMember<'a> {
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.flatten()?;
|
.flatten()?;
|
||||||
let deps: DepTypes = match dep_kind {
|
match a.parse_args::<Dependancy>() {
|
||||||
DepKind::NodeDepState => a.parse_args::<NodeDepTypes>().ok()?.into(),
|
Ok(dependancy) => {
|
||||||
_ => a.parse_args().ok()?,
|
let dep_mem = if let Some(name) = &dependancy.dep {
|
||||||
};
|
if let Some(found) = parent.members.iter().find(|m| &m.ident == name) {
|
||||||
|
Some(found)
|
||||||
Some(Self {
|
} else {
|
||||||
mem,
|
err = Err(Error::new(
|
||||||
dep_kind,
|
name.span(),
|
||||||
dep_mem: deps
|
format!("{} not found in {}", name, parent.name),
|
||||||
.dep_ty
|
));
|
||||||
.map(|ty| parent.members.iter().find(|m| m.ty == ty))
|
None
|
||||||
.flatten(),
|
}
|
||||||
ctx_ty: deps.ctx_ty,
|
} else {
|
||||||
})
|
None
|
||||||
})
|
};
|
||||||
|
Some(Self {
|
||||||
|
mem,
|
||||||
|
dep_kind,
|
||||||
|
dep_mem,
|
||||||
|
ctx_ty: dependancy.ctx_ty,
|
||||||
|
dependants: Vec::new(),
|
||||||
|
member_id: dioxus_native_core::state::MemberId(0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
err = Err(e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
err?;
|
||||||
|
Ok(member)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reduce_self(&self) -> quote::__private::TokenStream {
|
fn reduce_self(&self) -> quote::__private::TokenStream {
|
||||||
let ident = &self.mem.ident;
|
let ident = &self.mem.ident;
|
||||||
let get_ctx = if let Some(ctx_ty) = &self.ctx_ty {
|
let get_ctx = if let Some(ctx_ty) = &self.ctx_ty {
|
||||||
if ctx_ty
|
if ctx_ty == &parse_quote!(()) {
|
||||||
== &Type::Tuple(TypeTuple {
|
|
||||||
paren_token: Paren {
|
|
||||||
span: quote::__private::Span::call_site(),
|
|
||||||
},
|
|
||||||
elems: Punctuated::new(),
|
|
||||||
})
|
|
||||||
{
|
|
||||||
quote! {&()}
|
quote! {&()}
|
||||||
} else {
|
} else {
|
||||||
let msg = ctx_ty.to_token_stream().to_string() + " not found in context";
|
let msg = ctx_ty.to_token_stream().to_string() + " not found in context";
|
||||||
|
@ -336,40 +470,119 @@ impl<'a> StateMember<'a> {
|
||||||
} else {
|
} else {
|
||||||
quote! {&()}
|
quote! {&()}
|
||||||
};
|
};
|
||||||
|
let states_changed = {
|
||||||
|
let child_dep = self
|
||||||
|
.dependants
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, kind)| kind == &DepKind::ChildDepState)
|
||||||
|
.map(|(id, _)| id.0);
|
||||||
|
let parent_dep = self
|
||||||
|
.dependants
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, kind)| kind == &DepKind::ParentDepState)
|
||||||
|
.map(|(id, _)| id.0);
|
||||||
|
let node_dep = self
|
||||||
|
.dependants
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, kind)| kind == &DepKind::NodeDepState)
|
||||||
|
.map(|(id, _)| id.0);
|
||||||
|
match self.dep_kind {
|
||||||
|
DepKind::NodeDepState => {
|
||||||
|
quote! {
|
||||||
|
dioxus_native_core::state::NodeStatesChanged{
|
||||||
|
node_dep: &[#(dioxus_native_core::state::MemberId(#node_dep), )*],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DepKind::ChildDepState => {
|
||||||
|
quote! {
|
||||||
|
dioxus_native_core::state::ChildStatesChanged{
|
||||||
|
node_dep: &[#(dioxus_native_core::state::MemberId(#node_dep), )*],
|
||||||
|
child_dep: &[#(dioxus_native_core::state::MemberId(#child_dep), )*],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DepKind::ParentDepState => {
|
||||||
|
quote! {
|
||||||
|
dioxus_native_core::state::ParentStatesChanged{
|
||||||
|
node_dep: &[#(dioxus_native_core::state::MemberId(#node_dep), )*],
|
||||||
|
parent_dep: &[#(dioxus_native_core::state::MemberId(#parent_dep), )*],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let ty = &self.mem.ty;
|
let ty = &self.mem.ty;
|
||||||
let node_view = quote!(NodeView::new(node, #ty::NODE_MASK, vdom));
|
let node_view =
|
||||||
|
quote!(dioxus_native_core::node_ref::NodeView::new(node, #ty::NODE_MASK, vdom));
|
||||||
|
let id = self.member_id.0;
|
||||||
if let Some(dep_ident) = &self.dep_mem.map(|m| &m.ident) {
|
if let Some(dep_ident) = &self.dep_mem.map(|m| &m.ident) {
|
||||||
match self.dep_kind {
|
match self.dep_kind {
|
||||||
DepKind::NodeDepState => {
|
DepKind::NodeDepState => {
|
||||||
quote!(self.#ident.reduce(#node_view, #get_ctx))
|
quote!({
|
||||||
|
// println!("node: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
|
||||||
|
if self.#ident.reduce(#node_view, &self.#dep_ident, #get_ctx){
|
||||||
|
Some(#states_changed)
|
||||||
|
} else{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
DepKind::ChildDepState => {
|
DepKind::ChildDepState => {
|
||||||
quote!(self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident), #get_ctx))
|
quote!({
|
||||||
|
// println!("child: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
|
||||||
|
if self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident), #get_ctx){
|
||||||
|
Some(#states_changed)
|
||||||
|
} else{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
DepKind::ParentDepState => {
|
DepKind::ParentDepState => {
|
||||||
quote!(self.#ident.reduce(#node_view, parent.as_ref().map(|p| &p.#dep_ident), #get_ctx))
|
quote!({
|
||||||
|
// println!("parent: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
|
||||||
|
if self.#ident.reduce(#node_view, parent.as_ref().map(|p| &p.#dep_ident), #get_ctx){
|
||||||
|
Some(#states_changed)
|
||||||
|
} else{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match self.dep_kind {
|
match self.dep_kind {
|
||||||
DepKind::NodeDepState => {
|
DepKind::NodeDepState => {
|
||||||
quote!(self.#ident.reduce(#node_view, #get_ctx))
|
quote!({
|
||||||
|
// println!("node: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
|
||||||
|
if self.#ident.reduce(#node_view, &(), #get_ctx){
|
||||||
|
Some(#states_changed)
|
||||||
|
} else{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
DepKind::ChildDepState => {
|
DepKind::ChildDepState => {
|
||||||
quote!(self.#ident.reduce(#node_view, &(), #get_ctx))
|
quote!({
|
||||||
|
// println!("child: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
|
||||||
|
if self.#ident.reduce(#node_view, std::iter::empty(), #get_ctx){
|
||||||
|
Some(#states_changed)
|
||||||
|
} else{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
DepKind::ParentDepState => {
|
DepKind::ParentDepState => {
|
||||||
quote!(self.#ident.reduce(#node_view, Some(&()), #get_ctx))
|
quote!({
|
||||||
|
println!("parent: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
|
||||||
|
if self.#ident.reduce(#node_view, Some(&()), #get_ctx){
|
||||||
|
Some(#states_changed)
|
||||||
|
} else{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_id(&self) -> quote::__private::TokenStream {
|
|
||||||
let ty = &self.mem.ty;
|
|
||||||
quote!({
|
|
||||||
let type_id = std::any::TypeId::of::<#ty>();
|
|
||||||
type_id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
28
packages/native-core-macro/src/sorted_slice.rs
Normal file
28
packages/native-core-macro/src/sorted_slice.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use syn::{
|
||||||
|
self, bracketed,
|
||||||
|
parse::{Parse, ParseStream, Result},
|
||||||
|
LitStr, Token,
|
||||||
|
};
|
||||||
|
pub struct StrSlice {
|
||||||
|
pub map: BTreeMap<String, LitStr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ use dioxus_native_core::real_dom::RealDom;
|
||||||
use dioxus_native_core::state::State;
|
use dioxus_native_core::state::State;
|
||||||
use dioxus_native_core_macro::State;
|
use dioxus_native_core_macro::State;
|
||||||
|
|
||||||
#[derive(State, Default, Clone)]
|
#[derive(Default, Clone, State)]
|
||||||
struct Empty {}
|
struct Empty {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
74
packages/native-core-macro/tests/parse.rs
Normal file
74
packages/native-core-macro/tests/parse.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
use dioxus_native_core::node_ref::*;
|
||||||
|
use dioxus_native_core::state::*;
|
||||||
|
use dioxus_native_core_macro::*;
|
||||||
|
|
||||||
|
#[derive(State, Default, Clone)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct Z {
|
||||||
|
// depends on text, the C component of it's parent and a u16 context
|
||||||
|
#[parent_dep_state(c, u16)]
|
||||||
|
d: D,
|
||||||
|
// depends on just attributes and no context
|
||||||
|
#[node_dep_state()]
|
||||||
|
a: A,
|
||||||
|
// depends on the B component of children and i32 context
|
||||||
|
#[child_dep_state(b, i32)]
|
||||||
|
b: B,
|
||||||
|
// depends on the C component of it's parent and a u8 context
|
||||||
|
#[parent_dep_state(c, u8)]
|
||||||
|
c: C,
|
||||||
|
// this will remain uneffected on updates
|
||||||
|
n: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
use dioxus_native_core::state::NodeDepState;
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
struct A;
|
||||||
|
impl NodeDepState for A {
|
||||||
|
type Ctx = ();
|
||||||
|
type DepState = ();
|
||||||
|
const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, false, false, false);
|
||||||
|
fn reduce(&mut self, _: NodeView, _: &Self::DepState, _: &()) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
struct B;
|
||||||
|
impl ChildDepState for B {
|
||||||
|
type Ctx = i32;
|
||||||
|
type DepState = Self;
|
||||||
|
fn reduce<'a>(
|
||||||
|
&mut self,
|
||||||
|
_: NodeView,
|
||||||
|
_: impl Iterator<Item = &'a Self::DepState>,
|
||||||
|
_: &i32,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
Self::DepState: 'a,
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
struct C;
|
||||||
|
impl ParentDepState for C {
|
||||||
|
type Ctx = u8;
|
||||||
|
type DepState = Self;
|
||||||
|
fn reduce(&mut self, _: NodeView, _: Option<&Self::DepState>, _: &u8) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
struct D;
|
||||||
|
impl ParentDepState for D {
|
||||||
|
type Ctx = u16;
|
||||||
|
type DepState = C;
|
||||||
|
const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, false, false, true);
|
||||||
|
fn reduce(&mut self, _: NodeView, _: Option<&Self::DepState>, _: &u16) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
70
packages/native-core-macro/tests/test.rs
Normal file
70
packages/native-core-macro/tests/test.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use dioxus_native_core::node_ref::*;
|
||||||
|
use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
|
||||||
|
use dioxus_native_core_macro::State;
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
|
struct BubbledUpStateTester(Option<String>, Vec<Box<BubbledUpStateTester>>);
|
||||||
|
impl ChildDepState for BubbledUpStateTester {
|
||||||
|
type Ctx = u32;
|
||||||
|
type DepState = Self;
|
||||||
|
const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, true, false, false);
|
||||||
|
fn reduce<'a>(
|
||||||
|
&mut self,
|
||||||
|
node: NodeView,
|
||||||
|
children: impl Iterator<Item = &'a Self::DepState>,
|
||||||
|
ctx: &Self::Ctx,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
Self::DepState: 'a,
|
||||||
|
{
|
||||||
|
assert_eq!(*ctx, 42);
|
||||||
|
*self = BubbledUpStateTester(
|
||||||
|
node.tag().map(|s| s.to_string()),
|
||||||
|
children.into_iter().map(|c| Box::new(c.clone())).collect(),
|
||||||
|
);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
|
struct PushedDownStateTester(Option<String>, Option<Box<PushedDownStateTester>>);
|
||||||
|
impl ParentDepState for PushedDownStateTester {
|
||||||
|
type Ctx = u32;
|
||||||
|
type DepState = Self;
|
||||||
|
const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, true, false, false);
|
||||||
|
fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool {
|
||||||
|
assert_eq!(*ctx, 42);
|
||||||
|
*self = PushedDownStateTester(
|
||||||
|
node.tag().map(|s| s.to_string()),
|
||||||
|
parent.map(|c| Box::new(c.clone())),
|
||||||
|
);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
|
struct NodeStateTester(Option<String>, Vec<(String, String)>);
|
||||||
|
impl NodeDepState for NodeStateTester {
|
||||||
|
type Ctx = u32;
|
||||||
|
type DepState = ();
|
||||||
|
const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, true, false, false);
|
||||||
|
fn reduce(&mut self, node: NodeView, _sibling: &Self::DepState, ctx: &Self::Ctx) -> bool {
|
||||||
|
assert_eq!(*ctx, 42);
|
||||||
|
*self = NodeStateTester(
|
||||||
|
node.tag().map(|s| s.to_string()),
|
||||||
|
node.attributes()
|
||||||
|
.map(|a| (a.name.to_string(), a.value.to_string()))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(State, Clone, Default, Debug)]
|
||||||
|
struct StateTester {
|
||||||
|
#[child_dep_state(bubbled, u32)]
|
||||||
|
bubbled: BubbledUpStateTester,
|
||||||
|
#[parent_dep_state(pushed, u32)]
|
||||||
|
pushed: PushedDownStateTester,
|
||||||
|
#[node_dep_state(NONE, u32)]
|
||||||
|
node: NodeStateTester,
|
||||||
|
}
|
|
@ -3,17 +3,16 @@ use dioxus_core::VNode;
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
use dioxus_core_macro::*;
|
use dioxus_core_macro::*;
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
|
use dioxus_native_core::node_ref::*;
|
||||||
use dioxus_native_core::real_dom::*;
|
use dioxus_native_core::real_dom::*;
|
||||||
use dioxus_native_core::state::{
|
use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
|
||||||
AttributeMask, ChildDepState, NodeDepState, NodeMask, NodeView, ParentDepState, State,
|
|
||||||
};
|
|
||||||
use dioxus_native_core_macro::State;
|
use dioxus_native_core_macro::State;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, State)]
|
#[derive(Debug, Clone, Default, State)]
|
||||||
struct CallCounterState {
|
struct CallCounterState {
|
||||||
#[child_dep_state(ChildDepCallCounter)]
|
#[child_dep_state(child_counter)]
|
||||||
child_counter: ChildDepCallCounter,
|
child_counter: ChildDepCallCounter,
|
||||||
#[parent_dep_state(ParentDepCallCounter)]
|
#[parent_dep_state(parent_counter)]
|
||||||
parent_counter: ParentDepCallCounter,
|
parent_counter: ParentDepCallCounter,
|
||||||
#[node_dep_state()]
|
#[node_dep_state()]
|
||||||
node_counter: NodeDepCallCounter,
|
node_counter: NodeDepCallCounter,
|
||||||
|
@ -60,8 +59,9 @@ impl ParentDepState for ParentDepCallCounter {
|
||||||
struct NodeDepCallCounter(u32);
|
struct NodeDepCallCounter(u32);
|
||||||
impl NodeDepState for NodeDepCallCounter {
|
impl NodeDepState for NodeDepCallCounter {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
|
type DepState = ();
|
||||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
const NODE_MASK: NodeMask = NodeMask::ALL;
|
||||||
fn reduce(&mut self, _node: NodeView, _ctx: &Self::Ctx) -> bool {
|
fn reduce(&mut self, _node: NodeView, _sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
|
||||||
self.0 += 1;
|
self.0 += 1;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -107,22 +107,13 @@ impl ParentDepState for PushedDownStateTester {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(State, Clone, Default, Debug)]
|
|
||||||
struct StateTester {
|
|
||||||
#[child_dep_state(BubbledUpStateTester, u32)]
|
|
||||||
bubbled: BubbledUpStateTester,
|
|
||||||
#[parent_dep_state(PushedDownStateTester, u32)]
|
|
||||||
pushed: PushedDownStateTester,
|
|
||||||
#[node_dep_state(u32)]
|
|
||||||
node: NodeStateTester,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Default)]
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
struct NodeStateTester(Option<String>, Vec<(String, String)>);
|
struct NodeStateTester(Option<String>, Vec<(String, String)>);
|
||||||
impl NodeDepState for NodeStateTester {
|
impl NodeDepState for NodeStateTester {
|
||||||
type Ctx = u32;
|
type Ctx = u32;
|
||||||
|
type DepState = ();
|
||||||
const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, true, false, false);
|
const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, true, false, false);
|
||||||
fn reduce(&mut self, node: NodeView, ctx: &Self::Ctx) -> bool {
|
fn reduce(&mut self, node: NodeView, _sibling: &Self::DepState, ctx: &Self::Ctx) -> bool {
|
||||||
assert_eq!(*ctx, 42);
|
assert_eq!(*ctx, 42);
|
||||||
*self = NodeStateTester(
|
*self = NodeStateTester(
|
||||||
node.tag().map(|s| s.to_string()),
|
node.tag().map(|s| s.to_string()),
|
||||||
|
@ -134,6 +125,16 @@ impl NodeDepState for NodeStateTester {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(State, Clone, Default, Debug)]
|
||||||
|
struct StateTester {
|
||||||
|
#[child_dep_state(bubbled, u32)]
|
||||||
|
bubbled: BubbledUpStateTester,
|
||||||
|
#[parent_dep_state(pushed, u32)]
|
||||||
|
pushed: PushedDownStateTester,
|
||||||
|
#[node_dep_state(NONE, u32)]
|
||||||
|
node: NodeStateTester,
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn state_initial() {
|
fn state_initial() {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -172,7 +173,10 @@ fn state_initial() {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(root_div.state.pushed.0, Some("div".to_string()));
|
assert_eq!(root_div.state.pushed.0, Some("div".to_string()));
|
||||||
assert_eq!(root_div.state.pushed.1, None);
|
assert_eq!(
|
||||||
|
root_div.state.pushed.1,
|
||||||
|
Some(Box::new(PushedDownStateTester(None, None)))
|
||||||
|
);
|
||||||
assert_eq!(root_div.state.node.0, Some("div".to_string()));
|
assert_eq!(root_div.state.node.0, Some("div".to_string()));
|
||||||
assert_eq!(root_div.state.node.1, vec![]);
|
assert_eq!(root_div.state.node.1, vec![]);
|
||||||
|
|
||||||
|
@ -184,7 +188,7 @@ fn state_initial() {
|
||||||
child_p.state.pushed.1,
|
child_p.state.pushed.1,
|
||||||
Some(Box::new(PushedDownStateTester(
|
Some(Box::new(PushedDownStateTester(
|
||||||
Some("div".to_string()),
|
Some("div".to_string()),
|
||||||
None
|
Some(Box::new(PushedDownStateTester(None, None)))
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
assert_eq!(child_p.state.node.0, Some("p".to_string()));
|
assert_eq!(child_p.state.node.0, Some("p".to_string()));
|
||||||
|
@ -201,7 +205,7 @@ fn state_initial() {
|
||||||
child_h1.state.pushed.1,
|
child_h1.state.pushed.1,
|
||||||
Some(Box::new(PushedDownStateTester(
|
Some(Box::new(PushedDownStateTester(
|
||||||
Some("div".to_string()),
|
Some("div".to_string()),
|
||||||
None
|
Some(Box::new(PushedDownStateTester(None, None)))
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
assert_eq!(child_h1.state.node.0, Some("h1".to_string()));
|
assert_eq!(child_h1.state.node.0, Some("h1".to_string()));
|
||||||
|
@ -256,10 +260,6 @@ fn state_reduce_initally_called_minimally() {
|
||||||
let nodes_updated = dom.apply_mutations(vec![mutations]);
|
let nodes_updated = dom.apply_mutations(vec![mutations]);
|
||||||
let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
|
let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
|
||||||
|
|
||||||
dom.traverse_depth_first(|n| {
|
|
||||||
println!("{:#?}", n.state);
|
|
||||||
});
|
|
||||||
|
|
||||||
dom.traverse_depth_first(|n| {
|
dom.traverse_depth_first(|n| {
|
||||||
assert_eq!(n.state.child_counter.0, 1);
|
assert_eq!(n.state.child_counter.0, 1);
|
||||||
assert_eq!(n.state.parent_counter.0, 1);
|
assert_eq!(n.state.parent_counter.0, 1);
|
||||||
|
@ -268,7 +268,7 @@ fn state_reduce_initally_called_minimally() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn state_reduce_down_called_minimally_on_update() {
|
fn state_reduce_parent_called_minimally_on_update() {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn Base(cx: Scope) -> Element {
|
fn Base(cx: Scope) -> Element {
|
||||||
rsx!(cx, div {
|
rsx!(cx, div {
|
||||||
|
@ -329,21 +329,20 @@ fn state_reduce_down_called_minimally_on_update() {
|
||||||
let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
|
let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
|
||||||
|
|
||||||
dom.traverse_depth_first(|n| {
|
dom.traverse_depth_first(|n| {
|
||||||
// println!("{:?}", n.state);
|
|
||||||
assert_eq!(n.state.parent_counter.0, 2);
|
assert_eq!(n.state.parent_counter.0, 2);
|
||||||
});
|
});
|
||||||
// panic!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn state_reduce_up_called_minimally_on_update() {
|
fn state_reduce_child_called_minimally_on_update() {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn Base(cx: Scope) -> Element {
|
fn Base(cx: Scope) -> Element {
|
||||||
rsx!(cx, div {
|
rsx!(cx, div {
|
||||||
width: "100%",
|
|
||||||
div{
|
div{
|
||||||
div{
|
div{
|
||||||
p{}
|
p{
|
||||||
|
width: "100%",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p{
|
p{
|
||||||
"hello"
|
"hello"
|
||||||
|
@ -362,10 +361,11 @@ fn state_reduce_up_called_minimally_on_update() {
|
||||||
|
|
||||||
let mutations = vdom.create_vnodes(rsx! {
|
let mutations = vdom.create_vnodes(rsx! {
|
||||||
div {
|
div {
|
||||||
width: "100%",
|
|
||||||
div{
|
div{
|
||||||
div{
|
div{
|
||||||
p{}
|
p{
|
||||||
|
width: "100%",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p{
|
p{
|
||||||
"hello"
|
"hello"
|
||||||
|
@ -397,6 +397,93 @@ fn state_reduce_up_called_minimally_on_update() {
|
||||||
let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
|
let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
|
||||||
|
|
||||||
dom.traverse_depth_first(|n| {
|
dom.traverse_depth_first(|n| {
|
||||||
|
println!("{:?}", n);
|
||||||
assert_eq!(n.state.child_counter.0, if n.id.0 > 4 { 1 } else { 2 });
|
assert_eq!(n.state.child_counter.0, if n.id.0 > 4 { 1 } else { 2 });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, State)]
|
||||||
|
struct UnorderedDependanciesState {
|
||||||
|
#[node_dep_state(c)]
|
||||||
|
b: BDepCallCounter,
|
||||||
|
#[node_dep_state()]
|
||||||
|
c: CDepCallCounter,
|
||||||
|
#[node_dep_state(b)]
|
||||||
|
a: ADepCallCounter,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq)]
|
||||||
|
struct ADepCallCounter(usize, BDepCallCounter);
|
||||||
|
impl NodeDepState for ADepCallCounter {
|
||||||
|
type Ctx = ();
|
||||||
|
type DepState = BDepCallCounter;
|
||||||
|
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||||
|
fn reduce(&mut self, _node: NodeView, sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
|
||||||
|
self.0 += 1;
|
||||||
|
self.1 = sibling.clone();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq)]
|
||||||
|
struct BDepCallCounter(usize, CDepCallCounter);
|
||||||
|
impl NodeDepState for BDepCallCounter {
|
||||||
|
type Ctx = ();
|
||||||
|
type DepState = CDepCallCounter;
|
||||||
|
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||||
|
fn reduce(&mut self, _node: NodeView, sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
|
||||||
|
self.0 += 1;
|
||||||
|
self.1 = sibling.clone();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq)]
|
||||||
|
struct CDepCallCounter(usize);
|
||||||
|
impl NodeDepState for CDepCallCounter {
|
||||||
|
type Ctx = ();
|
||||||
|
type DepState = ();
|
||||||
|
const NODE_MASK: NodeMask = NodeMask::ALL;
|
||||||
|
fn reduce(&mut self, _node: NodeView, _sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
|
||||||
|
self.0 += 1;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dependancies_order_independant() {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn Base(cx: Scope) -> Element {
|
||||||
|
rsx!(cx, div {
|
||||||
|
width: "100%",
|
||||||
|
p{
|
||||||
|
"hello"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let vdom = VirtualDom::new(Base);
|
||||||
|
|
||||||
|
let mutations = vdom.create_vnodes(rsx! {
|
||||||
|
div {
|
||||||
|
width: "100%",
|
||||||
|
p{
|
||||||
|
"hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut dom: RealDom<UnorderedDependanciesState> = RealDom::new();
|
||||||
|
|
||||||
|
let nodes_updated = dom.apply_mutations(vec![mutations]);
|
||||||
|
let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
|
||||||
|
|
||||||
|
let c = CDepCallCounter(1);
|
||||||
|
let b = BDepCallCounter(1, c.clone());
|
||||||
|
let a = ADepCallCounter(1, b.clone());
|
||||||
|
dom.traverse_depth_first(|n| {
|
||||||
|
assert_eq!(&n.state.a, &a);
|
||||||
|
assert_eq!(&n.state.b, &b);
|
||||||
|
assert_eq!(&n.state.c, &c);
|
||||||
|
});
|
||||||
|
}
|
|
@ -10,7 +10,6 @@ homepage = "https://dioxuslabs.com"
|
||||||
dioxus-core = { path = "../core", version = "^0.2.0" }
|
dioxus-core = { path = "../core", version = "^0.2.0" }
|
||||||
dioxus-html = { path = "../html", version = "^0.2.0" }
|
dioxus-html = { path = "../html", version = "^0.2.0" }
|
||||||
dioxus-core-macro = { path = "../core-macro", version = "^0.2.0" }
|
dioxus-core-macro = { path = "../core-macro", version = "^0.2.0" }
|
||||||
dioxus-native-core-macro = { path = "../native-core-macro", version = "^0.2.0" }
|
|
||||||
|
|
||||||
stretch2 = { git = "https://github.com/DioxusLabs/stretch" }
|
stretch2 = { git = "https://github.com/DioxusLabs/stretch" }
|
||||||
smallvec = "1.6"
|
smallvec = "1.6"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
pub mod layout_attributes;
|
pub mod layout_attributes;
|
||||||
|
pub mod node_ref;
|
||||||
pub mod real_dom;
|
pub mod real_dom;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub use dioxus_native_core_macro;
|
|
||||||
|
|
212
packages/native-core/src/node_ref.rs
Normal file
212
packages/native-core/src/node_ref.rs
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
use dioxus_core::*;
|
||||||
|
|
||||||
|
use crate::state::union_ordered_iter;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NodeView<'a> {
|
||||||
|
inner: &'a VNode<'a>,
|
||||||
|
mask: NodeMask,
|
||||||
|
}
|
||||||
|
impl<'a> NodeView<'a> {
|
||||||
|
pub fn new(mut vnode: &'a VNode<'a>, view: NodeMask, vdom: &'a VirtualDom) -> Self {
|
||||||
|
if let VNode::Component(sc) = vnode {
|
||||||
|
let scope = vdom.get_scope(sc.scope.get().unwrap()).unwrap();
|
||||||
|
vnode = scope.root_node();
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
inner: vnode,
|
||||||
|
mask: view,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> ElementId {
|
||||||
|
self.inner.mounted_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tag(&self) -> Option<&'a str> {
|
||||||
|
self.mask.tag.then(|| self.el().map(|el| el.tag)).flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn namespace(&self) -> Option<&'a str> {
|
||||||
|
self.mask
|
||||||
|
.namespace
|
||||||
|
.then(|| self.el().map(|el| el.namespace).flatten())
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attributes(&self) -> impl Iterator<Item = &Attribute<'a>> {
|
||||||
|
self.el()
|
||||||
|
.map(|el| el.attributes)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.iter()
|
||||||
|
.filter(|a| self.mask.attritutes.contains_attribute(&a.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text(&self) -> Option<&str> {
|
||||||
|
self.mask
|
||||||
|
.text
|
||||||
|
.then(|| self.txt().map(|txt| txt.text))
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn el(&self) -> Option<&'a VElement<'a>> {
|
||||||
|
if let VNode::Element(el) = &self.inner {
|
||||||
|
Some(el)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn txt(&self) -> Option<&'a VText<'a>> {
|
||||||
|
if let VNode::Text(txt) = &self.inner {
|
||||||
|
Some(txt)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
|
pub enum AttributeMask {
|
||||||
|
All,
|
||||||
|
Dynamic(Vec<&'static str>),
|
||||||
|
Static(&'static [&'static str]),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttributeMask {
|
||||||
|
pub const NONE: Self = Self::Static(&[]);
|
||||||
|
|
||||||
|
fn contains_attribute(&self, attr: &'static str) -> bool {
|
||||||
|
match self {
|
||||||
|
AttributeMask::All => true,
|
||||||
|
AttributeMask::Dynamic(l) => l.binary_search(&attr).is_ok(),
|
||||||
|
AttributeMask::Static(l) => l.binary_search(&attr).is_ok(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn single(new: &'static str) -> Self {
|
||||||
|
Self::Dynamic(vec![new])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify(&self) {
|
||||||
|
match &self {
|
||||||
|
AttributeMask::Static(attrs) => debug_assert!(
|
||||||
|
attrs.windows(2).all(|w| w[0] < w[1]),
|
||||||
|
"attritutes must be increasing"
|
||||||
|
),
|
||||||
|
AttributeMask::Dynamic(attrs) => debug_assert!(
|
||||||
|
attrs.windows(2).all(|w| w[0] < w[1]),
|
||||||
|
"attritutes must be increasing"
|
||||||
|
),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn union(&self, other: &Self) -> Self {
|
||||||
|
let new = match (self, other) {
|
||||||
|
(AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic(
|
||||||
|
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
||||||
|
),
|
||||||
|
(AttributeMask::Static(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic(
|
||||||
|
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
||||||
|
),
|
||||||
|
(AttributeMask::Dynamic(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
|
||||||
|
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
||||||
|
),
|
||||||
|
(AttributeMask::Static(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
|
||||||
|
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
||||||
|
),
|
||||||
|
_ => AttributeMask::All,
|
||||||
|
};
|
||||||
|
new.verify();
|
||||||
|
new
|
||||||
|
}
|
||||||
|
|
||||||
|
fn overlaps(&self, other: &Self) -> bool {
|
||||||
|
fn overlaps_iter(
|
||||||
|
mut self_iter: impl Iterator<Item = &'static str>,
|
||||||
|
mut other_iter: impl Iterator<Item = &'static str>,
|
||||||
|
) -> bool {
|
||||||
|
if let Some(mut other_attr) = other_iter.next() {
|
||||||
|
while let Some(self_attr) = self_iter.next() {
|
||||||
|
while other_attr < self_attr {
|
||||||
|
if let Some(attr) = other_iter.next() {
|
||||||
|
other_attr = attr;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if other_attr == self_attr {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
match (self, other) {
|
||||||
|
(AttributeMask::All, AttributeMask::All) => true,
|
||||||
|
(AttributeMask::All, AttributeMask::Dynamic(v)) => !v.is_empty(),
|
||||||
|
(AttributeMask::All, AttributeMask::Static(s)) => !s.is_empty(),
|
||||||
|
(AttributeMask::Dynamic(v), AttributeMask::All) => !v.is_empty(),
|
||||||
|
(AttributeMask::Static(s), AttributeMask::All) => !s.is_empty(),
|
||||||
|
(AttributeMask::Dynamic(v1), AttributeMask::Dynamic(v2)) => {
|
||||||
|
overlaps_iter(v1.iter().copied(), v2.iter().copied())
|
||||||
|
}
|
||||||
|
(AttributeMask::Dynamic(v), AttributeMask::Static(s)) => {
|
||||||
|
overlaps_iter(v.iter().copied(), s.iter().copied())
|
||||||
|
}
|
||||||
|
(AttributeMask::Static(s), AttributeMask::Dynamic(v)) => {
|
||||||
|
overlaps_iter(v.iter().copied(), s.iter().copied())
|
||||||
|
}
|
||||||
|
(AttributeMask::Static(s1), AttributeMask::Static(s2)) => {
|
||||||
|
overlaps_iter(s1.iter().copied(), s2.iter().copied())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AttributeMask {
|
||||||
|
fn default() -> Self {
|
||||||
|
AttributeMask::Static(&[])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Clone, Debug)]
|
||||||
|
pub struct NodeMask {
|
||||||
|
// must be sorted
|
||||||
|
attritutes: AttributeMask,
|
||||||
|
tag: bool,
|
||||||
|
namespace: bool,
|
||||||
|
text: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeMask {
|
||||||
|
pub const NONE: Self = Self::new(AttributeMask::Static(&[]), false, false, false);
|
||||||
|
pub const ALL: Self = Self::new(AttributeMask::All, true, true, true);
|
||||||
|
|
||||||
|
/// attritutes must be sorted!
|
||||||
|
pub const fn new(attritutes: AttributeMask, tag: bool, namespace: bool, text: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
attritutes,
|
||||||
|
tag,
|
||||||
|
namespace,
|
||||||
|
text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn overlaps(&self, other: &Self) -> bool {
|
||||||
|
(self.tag && other.tag)
|
||||||
|
|| (self.namespace && other.namespace)
|
||||||
|
|| self.attritutes.overlaps(&other.attritutes)
|
||||||
|
|| (self.text && other.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn union(&self, other: &Self) -> Self {
|
||||||
|
Self {
|
||||||
|
attritutes: self.attritutes.union(&other.attritutes),
|
||||||
|
tag: self.tag | other.tag,
|
||||||
|
namespace: self.namespace | other.namespace,
|
||||||
|
text: self.text | other.text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,17 @@
|
||||||
use anymap::AnyMap;
|
use anymap::AnyMap;
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
ops::{Index, IndexMut},
|
ops::{Index, IndexMut},
|
||||||
};
|
};
|
||||||
|
|
||||||
use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
|
use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
|
||||||
|
|
||||||
use crate::state::{union_ordered_iter, AttributeMask, NodeMask, State};
|
use crate::state::{union_ordered_iter, State};
|
||||||
|
use crate::{
|
||||||
|
node_ref::{AttributeMask, NodeMask},
|
||||||
|
state::MemberId,
|
||||||
|
};
|
||||||
|
|
||||||
/// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
|
/// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
|
||||||
/// The render state passes from parent to children and or accumulates state from children to parents.
|
/// The render state passes from parent to children and or accumulates state from children to parents.
|
||||||
|
@ -211,7 +214,7 @@ impl<S: State> RealDom<S> {
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
enum StatesToCheck {
|
enum StatesToCheck {
|
||||||
All,
|
All,
|
||||||
Some(Vec<TypeId>),
|
Some(Vec<MemberId>),
|
||||||
}
|
}
|
||||||
impl StatesToCheck {
|
impl StatesToCheck {
|
||||||
fn union(&self, other: &Self) -> Self {
|
fn union(&self, other: &Self) -> Self {
|
||||||
|
@ -294,14 +297,28 @@ impl<S: State> RealDom<S> {
|
||||||
for node_ref in &nodes_updated {
|
for node_ref in &nodes_updated {
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
let node = &mut self[node_ref.id];
|
let node = &mut self[node_ref.id];
|
||||||
let ids = match &node_ref.to_check {
|
let mut ids = match &node_ref.to_check {
|
||||||
StatesToCheck::All => node.state.node_dep_types(&node_ref.node_mask),
|
StatesToCheck::All => node.state.node_dep_types(&node_ref.node_mask),
|
||||||
|
// this should only be triggered from the current node, so all members will need to be checked
|
||||||
StatesToCheck::Some(_) => unreachable!(),
|
StatesToCheck::Some(_) => unreachable!(),
|
||||||
};
|
};
|
||||||
for ty in ids {
|
let mut i = 0;
|
||||||
|
while i < ids.len() {
|
||||||
|
let id = ids[i];
|
||||||
let node = &mut self[node_ref.id];
|
let node = &mut self[node_ref.id];
|
||||||
let vnode = node.element(vdom);
|
let vnode = node.element(vdom);
|
||||||
changed |= node.state.update_node_dep_state(ty, vnode, vdom, &ctx);
|
if let Some(members_effected) =
|
||||||
|
node.state.update_node_dep_state(id, vnode, vdom, &ctx)
|
||||||
|
{
|
||||||
|
debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
|
||||||
|
for m in members_effected.node_dep {
|
||||||
|
if let Err(idx) = ids.binary_search(m) {
|
||||||
|
ids.insert(idx, *m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
if changed {
|
if changed {
|
||||||
to_rerender.insert(node_ref.id);
|
to_rerender.insert(node_ref.id);
|
||||||
|
@ -319,19 +336,30 @@ impl<S: State> RealDom<S> {
|
||||||
} = node_ref;
|
} = node_ref;
|
||||||
let (node, children) = self.get_node_children_mut(id).unwrap();
|
let (node, children) = self.get_node_children_mut(id).unwrap();
|
||||||
let children_state: Vec<_> = children.iter().map(|c| &c.state).collect();
|
let children_state: Vec<_> = children.iter().map(|c| &c.state).collect();
|
||||||
let ids = match to_check {
|
let mut ids = match to_check {
|
||||||
StatesToCheck::All => node.state.child_dep_types(&node_mask),
|
StatesToCheck::All => node.state.child_dep_types(&node_mask),
|
||||||
StatesToCheck::Some(ids) => ids,
|
StatesToCheck::Some(ids) => ids,
|
||||||
};
|
};
|
||||||
let mut changed = Vec::new();
|
let mut changed = Vec::new();
|
||||||
for ty in ids {
|
let mut i = 0;
|
||||||
|
while i < ids.len() {
|
||||||
|
let id = ids[i];
|
||||||
let vnode = node.element(vdom);
|
let vnode = node.element(vdom);
|
||||||
if node
|
if let Some(members_effected) =
|
||||||
.state
|
node.state
|
||||||
.update_child_dep_state(ty, vnode, vdom, &children_state, &ctx)
|
.update_child_dep_state(id, vnode, vdom, &children_state, &ctx)
|
||||||
{
|
{
|
||||||
changed.push(ty);
|
debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
|
||||||
|
for m in members_effected.node_dep {
|
||||||
|
if let Err(idx) = ids.binary_search(m) {
|
||||||
|
ids.insert(idx, *m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for m in members_effected.child_dep {
|
||||||
|
changed.push(*m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
if let Some(parent_id) = node.parent {
|
if let Some(parent_id) = node.parent {
|
||||||
if !changed.is_empty() {
|
if !changed.is_empty() {
|
||||||
|
@ -374,19 +402,32 @@ impl<S: State> RealDom<S> {
|
||||||
to_check,
|
to_check,
|
||||||
} = node_ref;
|
} = node_ref;
|
||||||
let node = &self[id];
|
let node = &self[id];
|
||||||
let ids = match to_check {
|
let mut ids = match to_check {
|
||||||
StatesToCheck::All => node.state.parent_dep_types(&node_mask),
|
StatesToCheck::All => node.state.parent_dep_types(&node_mask),
|
||||||
StatesToCheck::Some(ids) => ids,
|
StatesToCheck::Some(ids) => ids,
|
||||||
};
|
};
|
||||||
let mut changed = Vec::new();
|
let mut changed = Vec::new();
|
||||||
let (node, parent) = self.get_node_parent_mut(id).unwrap();
|
let (node, parent) = self.get_node_parent_mut(id).unwrap();
|
||||||
for ty in ids {
|
let mut i = 0;
|
||||||
|
while i < ids.len() {
|
||||||
|
let id = ids[i];
|
||||||
let vnode = node.element(vdom);
|
let vnode = node.element(vdom);
|
||||||
let parent = parent.as_deref();
|
let parent = parent.as_deref();
|
||||||
let state = &mut node.state;
|
let state = &mut node.state;
|
||||||
if state.update_parent_dep_state(ty, vnode, vdom, parent.map(|n| &n.state), &ctx) {
|
if let Some(members_effected) =
|
||||||
changed.push(ty);
|
state.update_parent_dep_state(id, vnode, vdom, parent.map(|n| &n.state), &ctx)
|
||||||
|
{
|
||||||
|
debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
|
||||||
|
for m in members_effected.node_dep {
|
||||||
|
if let Err(idx) = ids.binary_search(m) {
|
||||||
|
ids.insert(idx, *m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for m in members_effected.parent_dep {
|
||||||
|
changed.push(*m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
to_rerender.insert(id);
|
to_rerender.insert(id);
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use std::{any::TypeId, fmt::Debug};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use anymap::AnyMap;
|
use anymap::AnyMap;
|
||||||
use dioxus_core::{Attribute, ElementId, VElement, VNode, VText, VirtualDom};
|
use dioxus_core::VNode;
|
||||||
|
|
||||||
|
use crate::node_ref::{NodeMask, NodeView};
|
||||||
|
|
||||||
pub(crate) fn union_ordered_iter<T: Ord + Debug>(
|
pub(crate) fn union_ordered_iter<T: Ord + Debug>(
|
||||||
s_iter: impl Iterator<Item = T>,
|
s_iter: impl Iterator<Item = T>,
|
||||||
|
@ -34,215 +36,6 @@ pub(crate) fn union_ordered_iter<T: Ord + Debug>(
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct NodeView<'a> {
|
|
||||||
inner: &'a VNode<'a>,
|
|
||||||
mask: NodeMask,
|
|
||||||
}
|
|
||||||
impl<'a> NodeView<'a> {
|
|
||||||
pub fn new(mut vnode: &'a VNode<'a>, view: NodeMask, vdom: &'a VirtualDom) -> Self {
|
|
||||||
if let VNode::Component(sc) = vnode {
|
|
||||||
let scope = vdom.get_scope(sc.scope.get().unwrap()).unwrap();
|
|
||||||
vnode = scope.root_node();
|
|
||||||
}
|
|
||||||
Self {
|
|
||||||
inner: vnode,
|
|
||||||
mask: view,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn id(&self) -> ElementId {
|
|
||||||
self.inner.mounted_id()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tag(&self) -> Option<&'a str> {
|
|
||||||
self.mask.tag.then(|| self.el().map(|el| el.tag)).flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn namespace(&self) -> Option<&'a str> {
|
|
||||||
self.mask
|
|
||||||
.namespace
|
|
||||||
.then(|| self.el().map(|el| el.namespace).flatten())
|
|
||||||
.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn attributes(&self) -> impl Iterator<Item = &Attribute<'a>> {
|
|
||||||
self.el()
|
|
||||||
.map(|el| el.attributes)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.iter()
|
|
||||||
.filter(|a| self.mask.attritutes.contains_attribute(&a.name))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn text(&self) -> Option<&str> {
|
|
||||||
self.mask
|
|
||||||
.text
|
|
||||||
.then(|| self.txt().map(|txt| txt.text))
|
|
||||||
.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn el(&self) -> Option<&'a VElement<'a>> {
|
|
||||||
if let VNode::Element(el) = &self.inner {
|
|
||||||
Some(el)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn txt(&self) -> Option<&'a VText<'a>> {
|
|
||||||
if let VNode::Text(txt) = &self.inner {
|
|
||||||
Some(txt)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
|
||||||
pub enum AttributeMask {
|
|
||||||
All,
|
|
||||||
Dynamic(Vec<&'static str>),
|
|
||||||
Static(&'static [&'static str]),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AttributeMask {
|
|
||||||
pub const NONE: Self = Self::Static(&[]);
|
|
||||||
|
|
||||||
fn contains_attribute(&self, attr: &'static str) -> bool {
|
|
||||||
match self {
|
|
||||||
AttributeMask::All => true,
|
|
||||||
AttributeMask::Dynamic(l) => l.binary_search(&attr).is_ok(),
|
|
||||||
AttributeMask::Static(l) => l.binary_search(&attr).is_ok(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn single(new: &'static str) -> Self {
|
|
||||||
Self::Dynamic(vec![new])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify(&self) {
|
|
||||||
match &self {
|
|
||||||
AttributeMask::Static(attrs) => debug_assert!(
|
|
||||||
attrs.windows(2).all(|w| w[0] < w[1]),
|
|
||||||
"attritutes must be increasing"
|
|
||||||
),
|
|
||||||
AttributeMask::Dynamic(attrs) => debug_assert!(
|
|
||||||
attrs.windows(2).all(|w| w[0] < w[1]),
|
|
||||||
"attritutes must be increasing"
|
|
||||||
),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn union(&self, other: &Self) -> Self {
|
|
||||||
let new = match (self, other) {
|
|
||||||
(AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic(
|
|
||||||
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
|
||||||
),
|
|
||||||
(AttributeMask::Static(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic(
|
|
||||||
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
|
||||||
),
|
|
||||||
(AttributeMask::Dynamic(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
|
|
||||||
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
|
||||||
),
|
|
||||||
(AttributeMask::Static(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
|
|
||||||
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
|
||||||
),
|
|
||||||
_ => AttributeMask::All,
|
|
||||||
};
|
|
||||||
new.verify();
|
|
||||||
new
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overlaps(&self, other: &Self) -> bool {
|
|
||||||
fn overlaps_iter(
|
|
||||||
mut self_iter: impl Iterator<Item = &'static str>,
|
|
||||||
mut other_iter: impl Iterator<Item = &'static str>,
|
|
||||||
) -> bool {
|
|
||||||
if let Some(mut other_attr) = other_iter.next() {
|
|
||||||
while let Some(self_attr) = self_iter.next() {
|
|
||||||
while other_attr < self_attr {
|
|
||||||
if let Some(attr) = other_iter.next() {
|
|
||||||
other_attr = attr;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if other_attr == self_attr {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
match (self, other) {
|
|
||||||
(AttributeMask::All, AttributeMask::All) => true,
|
|
||||||
(AttributeMask::All, AttributeMask::Dynamic(v)) => !v.is_empty(),
|
|
||||||
(AttributeMask::All, AttributeMask::Static(s)) => !s.is_empty(),
|
|
||||||
(AttributeMask::Dynamic(v), AttributeMask::All) => !v.is_empty(),
|
|
||||||
(AttributeMask::Static(s), AttributeMask::All) => !s.is_empty(),
|
|
||||||
(AttributeMask::Dynamic(v1), AttributeMask::Dynamic(v2)) => {
|
|
||||||
overlaps_iter(v1.iter().copied(), v2.iter().copied())
|
|
||||||
}
|
|
||||||
(AttributeMask::Dynamic(v), AttributeMask::Static(s)) => {
|
|
||||||
overlaps_iter(v.iter().copied(), s.iter().copied())
|
|
||||||
}
|
|
||||||
(AttributeMask::Static(s), AttributeMask::Dynamic(v)) => {
|
|
||||||
overlaps_iter(v.iter().copied(), s.iter().copied())
|
|
||||||
}
|
|
||||||
(AttributeMask::Static(s1), AttributeMask::Static(s2)) => {
|
|
||||||
overlaps_iter(s1.iter().copied(), s2.iter().copied())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AttributeMask {
|
|
||||||
fn default() -> Self {
|
|
||||||
AttributeMask::Static(&[])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Clone, Debug)]
|
|
||||||
pub struct NodeMask {
|
|
||||||
// must be sorted
|
|
||||||
attritutes: AttributeMask,
|
|
||||||
tag: bool,
|
|
||||||
namespace: bool,
|
|
||||||
text: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NodeMask {
|
|
||||||
pub const NONE: Self = Self::new(AttributeMask::Static(&[]), false, false, false);
|
|
||||||
pub const ALL: Self = Self::new(AttributeMask::All, true, true, true);
|
|
||||||
|
|
||||||
/// attritutes must be sorted!
|
|
||||||
pub const fn new(attritutes: AttributeMask, tag: bool, namespace: bool, text: bool) -> Self {
|
|
||||||
Self {
|
|
||||||
attritutes,
|
|
||||||
tag,
|
|
||||||
namespace,
|
|
||||||
text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn overlaps(&self, other: &Self) -> bool {
|
|
||||||
(self.tag && other.tag)
|
|
||||||
|| (self.namespace && other.namespace)
|
|
||||||
|| self.attritutes.overlaps(&other.attritutes)
|
|
||||||
|| (self.text && other.text)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn union(&self, other: &Self) -> Self {
|
|
||||||
Self {
|
|
||||||
attritutes: self.attritutes.union(&other.attritutes),
|
|
||||||
tag: self.tag | other.tag,
|
|
||||||
namespace: self.namespace | other.namespace,
|
|
||||||
text: self.text | other.text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This state is derived from children. For example a node's size could be derived from the size of children.
|
/// This state is derived from children. For example a node's size could be derived from the size of children.
|
||||||
/// Called when the current node's node properties are modified, a child's [BubbledUpState] is modified or a child is removed.
|
/// Called when the current node's node properties are modified, a child's [BubbledUpState] is modified or a child is removed.
|
||||||
/// Called at most once per update.
|
/// Called at most once per update.
|
||||||
|
@ -250,7 +43,8 @@ pub trait ChildDepState {
|
||||||
/// The context is passed to the [PushedDownState::reduce] when it is pushed down.
|
/// The context is passed to the [PushedDownState::reduce] when it is pushed down.
|
||||||
/// This is sometimes nessisary for lifetime purposes.
|
/// This is sometimes nessisary for lifetime purposes.
|
||||||
type Ctx;
|
type Ctx;
|
||||||
type DepState: ChildDepState;
|
/// This must be either a [ChildDepState] or [NodeDepState]
|
||||||
|
type DepState;
|
||||||
const NODE_MASK: NodeMask = NodeMask::NONE;
|
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||||
fn reduce<'a>(
|
fn reduce<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -269,7 +63,8 @@ pub trait ParentDepState {
|
||||||
/// The context is passed to the [PushedDownState::reduce] when it is pushed down.
|
/// The context is passed to the [PushedDownState::reduce] when it is pushed down.
|
||||||
/// This is sometimes nessisary for lifetime purposes.
|
/// This is sometimes nessisary for lifetime purposes.
|
||||||
type Ctx;
|
type Ctx;
|
||||||
type DepState: ParentDepState;
|
/// This must be either a [ParentDepState] or [NodeDepState]
|
||||||
|
type DepState;
|
||||||
const NODE_MASK: NodeMask = NodeMask::NONE;
|
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||||
fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool;
|
fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool;
|
||||||
}
|
}
|
||||||
|
@ -279,40 +74,93 @@ pub trait ParentDepState {
|
||||||
/// Called at most once per update.
|
/// Called at most once per update.
|
||||||
pub trait NodeDepState {
|
pub trait NodeDepState {
|
||||||
type Ctx;
|
type Ctx;
|
||||||
|
type DepState: NodeDepState;
|
||||||
const NODE_MASK: NodeMask = NodeMask::NONE;
|
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||||
fn reduce(&mut self, node: NodeView, ctx: &Self::Ctx) -> bool;
|
fn reduce(&mut self, node: NodeView, sibling: &Self::DepState, ctx: &Self::Ctx) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ChildStatesChanged {
|
||||||
|
pub node_dep: &'static [MemberId],
|
||||||
|
pub child_dep: &'static [MemberId],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ParentStatesChanged {
|
||||||
|
pub node_dep: &'static [MemberId],
|
||||||
|
pub parent_dep: &'static [MemberId],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NodeStatesChanged {
|
||||||
|
pub node_dep: &'static [MemberId],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait State: Default + Clone {
|
pub trait State: Default + Clone {
|
||||||
fn update_node_dep_state<'a>(
|
fn update_node_dep_state<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
ty: TypeId,
|
ty: MemberId,
|
||||||
node: &'a VNode<'a>,
|
node: &'a VNode<'a>,
|
||||||
vdom: &'a dioxus_core::VirtualDom,
|
vdom: &'a dioxus_core::VirtualDom,
|
||||||
ctx: &AnyMap,
|
ctx: &AnyMap,
|
||||||
) -> bool;
|
) -> Option<NodeStatesChanged>;
|
||||||
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
||||||
fn child_dep_types(&self, mask: &NodeMask) -> Vec<TypeId>;
|
fn child_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
|
||||||
|
|
||||||
fn update_parent_dep_state<'a>(
|
fn update_parent_dep_state<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
ty: TypeId,
|
ty: MemberId,
|
||||||
node: &'a VNode<'a>,
|
node: &'a VNode<'a>,
|
||||||
vdom: &'a dioxus_core::VirtualDom,
|
vdom: &'a dioxus_core::VirtualDom,
|
||||||
parent: Option<&Self>,
|
parent: Option<&Self>,
|
||||||
ctx: &AnyMap,
|
ctx: &AnyMap,
|
||||||
) -> bool;
|
) -> Option<ParentStatesChanged>;
|
||||||
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
||||||
fn parent_dep_types(&self, mask: &NodeMask) -> Vec<TypeId>;
|
fn parent_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
|
||||||
|
|
||||||
fn update_child_dep_state<'a>(
|
fn update_child_dep_state<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
ty: TypeId,
|
ty: MemberId,
|
||||||
node: &'a VNode<'a>,
|
node: &'a VNode<'a>,
|
||||||
vdom: &'a dioxus_core::VirtualDom,
|
vdom: &'a dioxus_core::VirtualDom,
|
||||||
children: &[&Self],
|
children: &[&Self],
|
||||||
ctx: &AnyMap,
|
ctx: &AnyMap,
|
||||||
) -> bool;
|
) -> Option<ChildStatesChanged>;
|
||||||
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
||||||
fn node_dep_types(&self, mask: &NodeMask) -> Vec<TypeId>;
|
fn node_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ChildDepState for () {
|
||||||
|
type Ctx = ();
|
||||||
|
type DepState = ();
|
||||||
|
fn reduce<'a>(
|
||||||
|
&mut self,
|
||||||
|
_: NodeView,
|
||||||
|
_: impl Iterator<Item = &'a Self::DepState>,
|
||||||
|
_: &Self::Ctx,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
Self::DepState: 'a,
|
||||||
|
{
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParentDepState for () {
|
||||||
|
type Ctx = ();
|
||||||
|
type DepState = ();
|
||||||
|
fn reduce(&mut self, _: NodeView, _: Option<&Self::DepState>, _: &Self::Ctx) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeDepState for () {
|
||||||
|
type Ctx = ();
|
||||||
|
type DepState = ();
|
||||||
|
fn reduce(&mut self, _: NodeView, _sibling: &Self::DepState, _: &Self::Ctx) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct MemberId(pub usize);
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
use dioxus_native_core::state::*;
|
|
||||||
use dioxus_native_core_macro::*;
|
|
||||||
|
|
||||||
#[derive(State, Default, Clone)]
|
|
||||||
struct Z {
|
|
||||||
// depends on just attributes and no context
|
|
||||||
#[node_dep_state()]
|
|
||||||
x: A,
|
|
||||||
// depends on attributes, the B component of children and i32 context
|
|
||||||
#[child_dep_state(B, i32)]
|
|
||||||
y: B,
|
|
||||||
// depends on attributes, the C component of it's parent and a u8 context
|
|
||||||
#[parent_dep_state(C, u8)]
|
|
||||||
z: C,
|
|
||||||
}
|
|
||||||
|
|
||||||
use dioxus_native_core::state::NodeDepState;
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
struct A;
|
|
||||||
impl NodeDepState for A {
|
|
||||||
type Ctx = ();
|
|
||||||
fn reduce(&mut self, _: NodeView, _: &()) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
struct B;
|
|
||||||
impl ChildDepState for B {
|
|
||||||
type Ctx = i32;
|
|
||||||
type DepState = Self;
|
|
||||||
fn reduce<'a>(
|
|
||||||
&mut self,
|
|
||||||
_: dioxus_native_core::state::NodeView,
|
|
||||||
_: impl Iterator<Item = &'a Self::DepState>,
|
|
||||||
_: &i32,
|
|
||||||
) -> bool
|
|
||||||
where
|
|
||||||
Self::DepState: 'a,
|
|
||||||
{
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
struct C;
|
|
||||||
impl ParentDepState for C {
|
|
||||||
type Ctx = u8;
|
|
||||||
type DepState = Self;
|
|
||||||
fn reduce(
|
|
||||||
&mut self,
|
|
||||||
_: dioxus_native_core::state::NodeView,
|
|
||||||
_: Option<&Self::DepState>,
|
|
||||||
_: &u8,
|
|
||||||
) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,6 +16,7 @@ license = "MIT/Apache-2.0"
|
||||||
dioxus-core = { path = "../core", version = "^0.2.0" }
|
dioxus-core = { path = "../core", version = "^0.2.0" }
|
||||||
dioxus-html = { path = "../html", version = "^0.2.0" }
|
dioxus-html = { path = "../html", version = "^0.2.0" }
|
||||||
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
|
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
|
||||||
|
dioxus-native-core-macro = { path = "../native-core-macro", version = "^0.2.0" }
|
||||||
|
|
||||||
tui = "0.17.0"
|
tui = "0.17.0"
|
||||||
crossterm = "0.23.0"
|
crossterm = "0.23.0"
|
||||||
|
|
|
@ -2,9 +2,10 @@ use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use dioxus_core::*;
|
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::layout_attributes::apply_layout_attributes;
|
||||||
use dioxus_native_core::state::{AttributeMask, ChildDepState, NodeMask, NodeView};
|
use dioxus_native_core::node_ref::{AttributeMask, NodeMask, NodeView};
|
||||||
|
use dioxus_native_core::state::ChildDepState;
|
||||||
|
use dioxus_native_core_macro::sorted_str_slice;
|
||||||
use stretch2::prelude::*;
|
use stretch2::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
|
|
@ -7,7 +7,8 @@ use crossterm::{
|
||||||
};
|
};
|
||||||
use dioxus_core::exports::futures_channel::mpsc::unbounded;
|
use dioxus_core::exports::futures_channel::mpsc::unbounded;
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
use dioxus_native_core::{dioxus_native_core_macro::State, real_dom::RealDom, state::*};
|
use dioxus_native_core::{real_dom::RealDom, state::*};
|
||||||
|
use dioxus_native_core_macro::State;
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::mpsc::{UnboundedReceiver, UnboundedSender},
|
channel::mpsc::{UnboundedReceiver, UnboundedSender},
|
||||||
pin_mut, StreamExt,
|
pin_mut, StreamExt,
|
||||||
|
@ -36,10 +37,10 @@ type Node = dioxus_native_core::real_dom::Node<NodeState>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, State, Default)]
|
#[derive(Debug, Clone, State, Default)]
|
||||||
struct NodeState {
|
struct NodeState {
|
||||||
#[child_dep_state(StretchLayout, RefCell<Stretch>)]
|
#[child_dep_state(layout, RefCell<Stretch>)]
|
||||||
layout: StretchLayout,
|
layout: StretchLayout,
|
||||||
// depends on attributes, the C component of it's parent and a u8 context
|
// depends on attributes, the C component of it's parent and a u8 context
|
||||||
#[parent_dep_state(StyleModifier)]
|
#[parent_dep_state(style)]
|
||||||
style: StyleModifier,
|
style: StyleModifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,10 @@
|
||||||
use dioxus_core::Attribute;
|
use dioxus_core::Attribute;
|
||||||
use dioxus_native_core::{
|
use dioxus_native_core::{
|
||||||
layout_attributes::{parse_value, UnitSystem},
|
layout_attributes::{parse_value, UnitSystem},
|
||||||
state::{AttributeMask, NodeMask, NodeView, ParentDepState},
|
node_ref::{AttributeMask, NodeMask, NodeView},
|
||||||
|
state::ParentDepState,
|
||||||
};
|
};
|
||||||
|
use dioxus_native_core_macro::sorted_str_slice;
|
||||||
|
|
||||||
use crate::style::{RinkColor, RinkStyle};
|
use crate::style::{RinkColor, RinkStyle};
|
||||||
|
|
||||||
|
@ -47,7 +49,8 @@ impl ParentDepState for StyleModifier {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
type DepState = Self;
|
type DepState = Self;
|
||||||
// todo: seperate each attribute into it's own class
|
// todo: seperate each attribute into it's own class
|
||||||
const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, true, true, false);
|
const NODE_MASK: NodeMask =
|
||||||
|
NodeMask::new(AttributeMask::Static(SORTED_STYLE_ATTRS), true, true, false);
|
||||||
|
|
||||||
fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, _: &Self::Ctx) -> bool {
|
fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, _: &Self::Ctx) -> bool {
|
||||||
let mut new = StyleModifier::default();
|
let mut new = StyleModifier::default();
|
||||||
|
@ -586,3 +589,211 @@ fn apply_text(name: &str, value: &str, style: &mut StyleModifier) {
|
||||||
fn apply_transition(_name: &str, _value: &str, _style: &mut StyleModifier) {
|
fn apply_transition(_name: &str, _value: &str, _style: &mut StyleModifier) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SORTED_STYLE_ATTRS: &'static [&'static str] = &sorted_str_slice!([
|
||||||
|
"animation",
|
||||||
|
"animation-delay",
|
||||||
|
"animation-direction",
|
||||||
|
"animation-duration",
|
||||||
|
"animation-fill-mode",
|
||||||
|
"animation-iteration-count",
|
||||||
|
"animation-name",
|
||||||
|
"animation-play-state",
|
||||||
|
"animation-timing-function",
|
||||||
|
"backface-visibility",
|
||||||
|
"background",
|
||||||
|
"background-attachment",
|
||||||
|
"background-clip",
|
||||||
|
"background-color",
|
||||||
|
"background-image",
|
||||||
|
"background-origin",
|
||||||
|
"background-position",
|
||||||
|
"background-repeat",
|
||||||
|
"background-size",
|
||||||
|
"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",
|
||||||
|
"color",
|
||||||
|
"columns",
|
||||||
|
"content",
|
||||||
|
"counter-increment",
|
||||||
|
"counter-reset",
|
||||||
|
"cursor",
|
||||||
|
"empty-cells",
|
||||||
|
"float",
|
||||||
|
"font",
|
||||||
|
"font-family",
|
||||||
|
"font-size",
|
||||||
|
"font-size-adjust",
|
||||||
|
"font-stretch",
|
||||||
|
"font-style",
|
||||||
|
"font-variant",
|
||||||
|
"font-weight",
|
||||||
|
"letter-spacing",
|
||||||
|
"line-height",
|
||||||
|
"list-style",
|
||||||
|
"list-style-image",
|
||||||
|
"list-style-position",
|
||||||
|
"list-style-type",
|
||||||
|
"opacity",
|
||||||
|
"order",
|
||||||
|
"outline",
|
||||||
|
"outline-color",
|
||||||
|
"outline-offset",
|
||||||
|
"outline-style",
|
||||||
|
"outline-width",
|
||||||
|
"page-break-after",
|
||||||
|
"page-break-before",
|
||||||
|
"page-break-inside",
|
||||||
|
"perspective",
|
||||||
|
"perspective-origin",
|
||||||
|
"pointer-events",
|
||||||
|
"quotes",
|
||||||
|
"resize",
|
||||||
|
"tab-size",
|
||||||
|
"table-layout",
|
||||||
|
"text-align",
|
||||||
|
"text-align-last",
|
||||||
|
"text-decoration",
|
||||||
|
"text-decoration-color",
|
||||||
|
"text-decoration-line",
|
||||||
|
"text-decoration-style",
|
||||||
|
"text-indent",
|
||||||
|
"text-justify",
|
||||||
|
"text-overflow",
|
||||||
|
"text-shadow",
|
||||||
|
"text-transform",
|
||||||
|
"transition",
|
||||||
|
"transition-delay",
|
||||||
|
"transition-duration",
|
||||||
|
"transition-property",
|
||||||
|
"transition-timing-function",
|
||||||
|
"visibility",
|
||||||
|
"white-space",
|
||||||
|
"background-color",
|
||||||
|
"background",
|
||||||
|
"background-attachment",
|
||||||
|
"background-clip",
|
||||||
|
"background-image",
|
||||||
|
"background-origin",
|
||||||
|
"background-position",
|
||||||
|
"background-repeat",
|
||||||
|
"background-size",
|
||||||
|
"dotted",
|
||||||
|
"dashed",
|
||||||
|
"solid",
|
||||||
|
"double",
|
||||||
|
"groove",
|
||||||
|
"ridge",
|
||||||
|
"inset",
|
||||||
|
"outset",
|
||||||
|
"none",
|
||||||
|
"hidden",
|
||||||
|
"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",
|
||||||
|
" ",
|
||||||
|
"animation",
|
||||||
|
"animation-delay",
|
||||||
|
"animation-direction",
|
||||||
|
"animation-duration",
|
||||||
|
"animation-fill-mode",
|
||||||
|
"animation-itera ",
|
||||||
|
"animation-name",
|
||||||
|
"animation-play-state",
|
||||||
|
"animation-timing-function",
|
||||||
|
"font",
|
||||||
|
"font-family",
|
||||||
|
"font-size",
|
||||||
|
"font-size-adjust",
|
||||||
|
"font-stretch",
|
||||||
|
"font-style",
|
||||||
|
"italic",
|
||||||
|
"oblique",
|
||||||
|
"font-variant",
|
||||||
|
"font-weight",
|
||||||
|
"bold",
|
||||||
|
"normal",
|
||||||
|
"text-align",
|
||||||
|
"text-align-last",
|
||||||
|
"text-decoration",
|
||||||
|
"text-decoration-line",
|
||||||
|
"line-through",
|
||||||
|
"underline",
|
||||||
|
"text-decoration-color",
|
||||||
|
"text-decoration-style",
|
||||||
|
"text-indent",
|
||||||
|
"text-justify",
|
||||||
|
"text-overflow",
|
||||||
|
"text-shadow",
|
||||||
|
"text-transform"
|
||||||
|
]);
|
||||||
|
|
Loading…
Reference in a new issue