remove native-core-macro
This commit is contained in:
1 parent
fb0c6c1b62
commit
4738352955
13 files changed
+118
-1081
No files matched your search
@@ -17,7 +17,6 @@ members = [
|
|||||||
"packages/rsx",
|
"packages/rsx",
|
||||||
"packages/tui",
|
"packages/tui",
|
||||||
"packages/native-core",
|
"packages/native-core",
|
||||||
"packages/native-core-macro",
|
|
||||||
"packages/rsx-rosetta",
|
"packages/rsx-rosetta",
|
||||||
"packages/signals",
|
"packages/signals",
|
||||||
"docs/guide",
|
"docs/guide",
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
/target
|
|
||||||
Cargo.lock
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "dioxus-native-core-macro"
|
|
||||||
version = "0.2.0"
|
|
||||||
edition = "2021"
|
|
||||||
description = "Build natively rendered apps with Dioxus"
|
|
||||||
license = "MIT/Apache-2.0"
|
|
||||||
repository = "https://github.com/DioxusLabs/dioxus/"
|
|
||||||
homepage = "https://dioxuslabs.com"
|
|
||||||
documentation = "https://dioxuslabs.com"
|
|
||||||
keywords = ["dom", "ui", "gui", "react"]
|
|
||||||
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
[dependencies]
|
|
||||||
syn = { version = "1.0.11", features = ["extra-traits"] }
|
|
||||||
quote = "1.0"
|
|
||||||
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
|
|
||||||
dioxus = { path = "../dioxus" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
smallvec = "1.6"
|
|
||||||
rustc-hash = "1.1.0"
|
|
||||||
anymap = "0.12.1"
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# Dioxus Native Core Macro
|
|
||||||
|
|
||||||
[![Crates.io][crates-badge]][crates-url]
|
|
||||||
[![MIT licensed][mit-badge]][mit-url]
|
|
||||||
[![Build Status][actions-badge]][actions-url]
|
|
||||||
[![Discord chat][discord-badge]][discord-url]
|
|
||||||
|
|
||||||
[crates-badge]: https://img.shields.io/crates/v/dioxus-native-core-macro.svg
|
|
||||||
[crates-url]: https://crates.io/crates/dioxus-native-core-macro
|
|
||||||
|
|
||||||
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
|
|
||||||
[mit-url]: https://github.com/dioxuslabs/dioxus/blob/master/LICENSE
|
|
||||||
|
|
||||||
[actions-badge]: https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg
|
|
||||||
[actions-url]: https://github.com/dioxuslabs/dioxus/actions?query=workflow%3ACI+branch%3Amaster
|
|
||||||
|
|
||||||
[discord-badge]: https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square
|
|
||||||
[discord-url]: https://discord.gg/XgGxMSkvUM
|
|
||||||
|
|
||||||
[Website](https://dioxuslabs.com) |
|
|
||||||
[Guides](https://dioxuslabs.com/guide/) |
|
|
||||||
[API Docs](https://docs.rs/dioxus-native-core-macro/latest/dioxus_native_core_macro) |
|
|
||||||
[Chat](https://discord.gg/XgGxMSkvUM)
|
|
||||||
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
`dioxus-native-core-macro` provides a handful of macros used by native-core for native renderers like TUI, Blitz, and Freya to derive their state.
|
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
- Report issues on our [issue tracker](https://github.com/dioxuslabs/dioxus/issues).
|
|
||||||
- Join the discord and ask questions!
|
|
||||||
|
|
||||||
## License
|
|
||||||
This project is licensed under the [MIT license].
|
|
||||||
|
|
||||||
[mit license]: https://github.com/DioxusLabs/dioxus/blob/master/LICENSE-MIT
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
|
||||||
for inclusion in Dioxus by you shall be licensed as MIT without any additional
|
|
||||||
terms or conditions.
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
extern crate proc_macro;
|
|
||||||
|
|
||||||
mod sorted_slice;
|
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
use sorted_slice::StrSlice;
|
|
||||||
use syn::{self, parse_macro_input};
|
|
||||||
|
|
||||||
/// Sorts a slice of string literals at compile time.
|
|
||||||
#[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's the state from any members that implement the Pass trait
|
|
||||||
#[proc_macro_derive(State, attributes(skip, skip_clone))]
|
|
||||||
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 custom_type = ast
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.find(|a| a.path.is_ident("state"))
|
|
||||||
.and_then(|attr| {
|
|
||||||
// parse custom_type = "MyType"
|
|
||||||
let assignment = attr.parse_args::<syn::Expr>().unwrap();
|
|
||||||
if let syn::Expr::Assign(assign) = assignment {
|
|
||||||
let (left, right) = (&*assign.left, &*assign.right);
|
|
||||||
if let syn::Expr::Path(e) = left {
|
|
||||||
let path = &e.path;
|
|
||||||
if let Some(ident) = path.get_ident() {
|
|
||||||
if ident == "custom_value" {
|
|
||||||
return match right {
|
|
||||||
syn::Expr::Path(e) => {
|
|
||||||
let path = &e.path;
|
|
||||||
Some(quote! {#path})
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.unwrap_or(quote! {()});
|
|
||||||
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 structs"),
|
|
||||||
}
|
|
||||||
.iter()
|
|
||||||
.collect(),
|
|
||||||
_ => unimplemented!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let types = fields
|
|
||||||
.iter()
|
|
||||||
.filter(|field| !field.attrs.iter().any(|attr| attr.path.is_ident("skip")))
|
|
||||||
.map(|field| &field.ty);
|
|
||||||
|
|
||||||
let gen = quote! {
|
|
||||||
impl dioxus_native_core::State<#custom_type> for #type_name {
|
|
||||||
fn create_passes() -> Box<[dioxus_native_core::TypeErasedPass<Self>]> {
|
|
||||||
Box::new([
|
|
||||||
#(
|
|
||||||
<#types as dioxus_native_core::Pass>::to_type_erased()
|
|
||||||
),*
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
gen.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derive's the state from any elements that have a node_dep_state, child_dep_state, parent_dep_state, or state attribute.
|
|
||||||
#[proc_macro_derive(AnyMapLike)]
|
|
||||||
pub fn anymap_like_macro_derive(input: TokenStream) -> TokenStream {
|
|
||||||
let ast = syn::parse(input).unwrap();
|
|
||||||
impl_anymap_like_derive_macro(&ast)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn impl_anymap_like_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 structs"),
|
|
||||||
}
|
|
||||||
.iter()
|
|
||||||
.collect(),
|
|
||||||
_ => unimplemented!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let names: Vec<_> = fields
|
|
||||||
.iter()
|
|
||||||
.map(|field| field.ident.as_ref().unwrap())
|
|
||||||
.collect();
|
|
||||||
let types: Vec<_> = fields.iter().map(|field| &field.ty).collect();
|
|
||||||
|
|
||||||
let gen = quote! {
|
|
||||||
impl dioxus_native_core::AnyMapLike for #type_name {
|
|
||||||
fn get<T: std::any::Any>(&self) -> Option<&T> {
|
|
||||||
#(
|
|
||||||
if std::any::TypeId::of::<T>() == std::any::TypeId::of::<#types>() {
|
|
||||||
return unsafe { Some(&*(&self.#names as *const _ as *const T)) }
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_mut<T: std::any::Any>(&mut self) -> Option<&mut T> {
|
|
||||||
#(
|
|
||||||
if std::any::TypeId::of::<T>() == std::any::TypeId::of::<#types>() {
|
|
||||||
return unsafe { Some(&mut *(&mut self.#names as *mut _ as *mut T)) }
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
gen.into()
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
use dioxus::prelude::*;
|
|
||||||
use dioxus_native_core::node_ref::*;
|
|
||||||
use dioxus_native_core::real_dom::*;
|
|
||||||
use dioxus_native_core::state::{
|
|
||||||
ChildDepState, ElementBorrowable, NodeDepState, ParentDepState, State,
|
|
||||||
};
|
|
||||||
use dioxus_native_core::tree::*;
|
|
||||||
use dioxus_native_core::SendAnyMap;
|
|
||||||
use dioxus_native_core_macro::State;
|
|
||||||
|
|
||||||
macro_rules! dep {
|
|
||||||
( child( $name:ty, $dep:ty ) ) => {
|
|
||||||
impl ChildDepState for $name {
|
|
||||||
type Ctx = ();
|
|
||||||
type DepState = $dep;
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
|
||||||
fn reduce<'a>(
|
|
||||||
&mut self,
|
|
||||||
_: NodeView,
|
|
||||||
_: impl Iterator<Item = <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
|
|
||||||
_: &Self::Ctx,
|
|
||||||
) -> bool
|
|
||||||
where
|
|
||||||
Self::DepState: 'a,
|
|
||||||
{
|
|
||||||
self.0 += 1;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
( parent( $name:ty, $dep:ty ) ) => {
|
|
||||||
impl ParentDepState for $name {
|
|
||||||
type Ctx = ();
|
|
||||||
type DepState = $dep;
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
|
||||||
fn reduce(
|
|
||||||
&mut self,
|
|
||||||
_: NodeView,
|
|
||||||
_: Option<<Self::DepState as ElementBorrowable>::ElementBorrowed<'_>>,
|
|
||||||
_: &Self::Ctx,
|
|
||||||
) -> bool {
|
|
||||||
self.0 += 1;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
( node( $name:ty, $dep:ty ) ) => {
|
|
||||||
impl NodeDepState for $name {
|
|
||||||
type Ctx = ();
|
|
||||||
type DepState = $dep;
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
|
||||||
fn reduce(
|
|
||||||
&mut self,
|
|
||||||
_: NodeView,
|
|
||||||
_: <Self::DepState as ElementBorrowable>::ElementBorrowed<'_>,
|
|
||||||
_: &Self::Ctx,
|
|
||||||
) -> bool {
|
|
||||||
self.0 += 1;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! test_state{
|
|
||||||
( $s:ty, child: ( $( $child:ident ),* ), node: ( $( $node:ident ),* ), parent: ( $( $parent:ident ),* ) ) => {
|
|
||||||
#[test]
|
|
||||||
fn state_reduce_initally_called_minimally() {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn Base(cx: Scope) -> Element {
|
|
||||||
render!{
|
|
||||||
div {
|
|
||||||
div{
|
|
||||||
div{
|
|
||||||
p{}
|
|
||||||
}
|
|
||||||
p{
|
|
||||||
"hello"
|
|
||||||
}
|
|
||||||
div{
|
|
||||||
h1{}
|
|
||||||
}
|
|
||||||
p{
|
|
||||||
"world"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut vdom = VirtualDom::new(Base);
|
|
||||||
|
|
||||||
let mutations = vdom.rebuild();
|
|
||||||
|
|
||||||
let mut dom: RealDom<$s> = RealDom::new();
|
|
||||||
|
|
||||||
let (nodes_updated, _) = dom.apply_mutations(mutations);
|
|
||||||
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
|
|
||||||
|
|
||||||
dom.traverse_depth_first(|n| {
|
|
||||||
$(
|
|
||||||
assert_eq!(n.state.$child.0, 1);
|
|
||||||
)*
|
|
||||||
$(
|
|
||||||
assert_eq!(n.state.$node.0, 1);
|
|
||||||
)*
|
|
||||||
$(
|
|
||||||
assert_eq!(n.state.$parent.0, 1);
|
|
||||||
)*
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod node_depends_on_child_and_parent {
|
|
||||||
use super::*;
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Node(i32);
|
|
||||||
dep!(node(Node, (Child, Parent)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Child(i32);
|
|
||||||
dep!(child(Child, (Child,)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Parent(i32);
|
|
||||||
dep!(parent(Parent, (Parent,)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, State)]
|
|
||||||
struct StateTester {
|
|
||||||
#[node_dep_state((child, parent))]
|
|
||||||
node: Node,
|
|
||||||
#[child_dep_state(child)]
|
|
||||||
child: Child,
|
|
||||||
#[parent_dep_state(parent)]
|
|
||||||
parent: Parent,
|
|
||||||
}
|
|
||||||
|
|
||||||
test_state!(StateTester, child: (child), node: (node), parent: (parent));
|
|
||||||
}
|
|
||||||
|
|
||||||
mod child_depends_on_node_that_depends_on_parent {
|
|
||||||
use super::*;
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Node(i32);
|
|
||||||
dep!(node(Node, (Parent,)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Child(i32);
|
|
||||||
dep!(child(Child, (Node,)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Parent(i32);
|
|
||||||
dep!(parent(Parent, (Parent,)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, State)]
|
|
||||||
struct StateTester {
|
|
||||||
#[node_dep_state(parent)]
|
|
||||||
node: Node,
|
|
||||||
#[child_dep_state(node)]
|
|
||||||
child: Child,
|
|
||||||
#[parent_dep_state(parent)]
|
|
||||||
parent: Parent,
|
|
||||||
}
|
|
||||||
|
|
||||||
test_state!(StateTester, child: (child), node: (node), parent: (parent));
|
|
||||||
}
|
|
||||||
|
|
||||||
mod parent_depends_on_node_that_depends_on_child {
|
|
||||||
use super::*;
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Node(i32);
|
|
||||||
dep!(node(Node, (Child,)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Child(i32);
|
|
||||||
dep!(child(Child, (Child,)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Parent(i32);
|
|
||||||
dep!(parent(Parent, (Node,)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, State)]
|
|
||||||
struct StateTester {
|
|
||||||
#[node_dep_state(child)]
|
|
||||||
node: Node,
|
|
||||||
#[child_dep_state(child)]
|
|
||||||
child: Child,
|
|
||||||
#[parent_dep_state(node)]
|
|
||||||
parent: Parent,
|
|
||||||
}
|
|
||||||
|
|
||||||
test_state!(StateTester, child: (child), node: (node), parent: (parent));
|
|
||||||
}
|
|
||||||
|
|
||||||
mod node_depends_on_other_node_state {
|
|
||||||
use super::*;
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Node1(i32);
|
|
||||||
dep!(node(Node1, (Node2,)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Node2(i32);
|
|
||||||
dep!(node(Node2, ()));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, State)]
|
|
||||||
struct StateTester {
|
|
||||||
#[node_dep_state((node2))]
|
|
||||||
node1: Node1,
|
|
||||||
#[node_dep_state()]
|
|
||||||
node2: Node2,
|
|
||||||
}
|
|
||||||
|
|
||||||
test_state!(StateTester, child: (), node: (node1, node2), parent: ());
|
|
||||||
}
|
|
||||||
|
|
||||||
mod node_child_and_parent_state_depends_on_self {
|
|
||||||
use super::*;
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Node(i32);
|
|
||||||
dep!(node(Node, ()));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Child(i32);
|
|
||||||
dep!(child(Child, (Child,)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
struct Parent(i32);
|
|
||||||
dep!(parent(Parent, (Parent,)));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, State)]
|
|
||||||
struct StateTester {
|
|
||||||
#[node_dep_state()]
|
|
||||||
node: Node,
|
|
||||||
#[child_dep_state(child)]
|
|
||||||
child: Child,
|
|
||||||
#[parent_dep_state(parent)]
|
|
||||||
parent: Parent,
|
|
||||||
}
|
|
||||||
|
|
||||||
test_state!(StateTester, child: (child), node: (node), parent: (parent));
|
|
||||||
}
|
|
||||||
@@ -1,421 +0,0 @@
|
|||||||
use dioxus::prelude::*;
|
|
||||||
use dioxus_native_core::real_dom::*;
|
|
||||||
use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
|
|
||||||
use dioxus_native_core::tree::TreeView;
|
|
||||||
use dioxus_native_core::{node_ref::*, NodeId, SendAnyMap};
|
|
||||||
use dioxus_native_core_macro::State;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, State)]
|
|
||||||
struct CallCounterState {
|
|
||||||
#[child_dep_state(child_counter)]
|
|
||||||
child_counter: ChildDepCallCounter,
|
|
||||||
#[parent_dep_state(parent_counter)]
|
|
||||||
parent_counter: ParentDepCallCounter,
|
|
||||||
#[node_dep_state()]
|
|
||||||
node_counter: NodeDepCallCounter,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
struct ChildDepCallCounter(u32);
|
|
||||||
impl ChildDepState for ChildDepCallCounter {
|
|
||||||
type Ctx = ();
|
|
||||||
type DepState = (Self,);
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
|
||||||
fn reduce<'a>(
|
|
||||||
&mut self,
|
|
||||||
_: NodeView,
|
|
||||||
_: impl Iterator<Item = (&'a Self,)>,
|
|
||||||
_: &Self::Ctx,
|
|
||||||
) -> bool
|
|
||||||
where
|
|
||||||
Self::DepState: 'a,
|
|
||||||
{
|
|
||||||
self.0 += 1;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
struct ParentDepCallCounter(u32);
|
|
||||||
impl ParentDepState for ParentDepCallCounter {
|
|
||||||
type Ctx = ();
|
|
||||||
type DepState = (Self,);
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
|
||||||
fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
|
|
||||||
self.0 += 1;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
struct NodeDepCallCounter(u32);
|
|
||||||
impl NodeDepState for NodeDepCallCounter {
|
|
||||||
type Ctx = ();
|
|
||||||
type DepState = ();
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
|
||||||
fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool {
|
|
||||||
self.0 += 1;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::vec_box)]
|
|
||||||
#[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().with_tag();
|
|
||||||
fn reduce<'a>(
|
|
||||||
&mut self,
|
|
||||||
node: NodeView,
|
|
||||||
children: impl Iterator<Item = (&'a Self,)>,
|
|
||||||
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().with_tag();
|
|
||||||
fn reduce(&mut self, node: NodeView, parent: Option<(&Self,)>, 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_with_attrs(AttributeMask::All).with_tag();
|
|
||||||
fn reduce(&mut self, node: NodeView, _sibling: (), ctx: &Self::Ctx) -> bool {
|
|
||||||
assert_eq!(*ctx, 42);
|
|
||||||
*self = NodeStateTester(
|
|
||||||
node.tag().map(|s| s.to_string()),
|
|
||||||
node.attributes()
|
|
||||||
.map(|iter| {
|
|
||||||
iter.map(|a| {
|
|
||||||
(
|
|
||||||
a.attribute.name.to_string(),
|
|
||||||
a.value.as_text().unwrap().to_string(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default(),
|
|
||||||
);
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn state_initial() {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn Base(cx: Scope) -> Element {
|
|
||||||
render! {
|
|
||||||
div {
|
|
||||||
p{
|
|
||||||
color: "red"
|
|
||||||
}
|
|
||||||
h1{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut vdom = VirtualDom::new(Base);
|
|
||||||
|
|
||||||
let mutations = vdom.rebuild();
|
|
||||||
|
|
||||||
let mut dom: RealDom<StateTester> = RealDom::new();
|
|
||||||
|
|
||||||
let (nodes_updated, _) = dom.apply_mutations(mutations);
|
|
||||||
let mut ctx = SendAnyMap::new();
|
|
||||||
ctx.insert(42u32);
|
|
||||||
let _to_rerender = dom.update_state(nodes_updated, ctx);
|
|
||||||
|
|
||||||
let root_div_id = dom.children_ids(NodeId(0)).unwrap()[0];
|
|
||||||
let root_div = &dom.get(root_div_id).unwrap();
|
|
||||||
assert_eq!(root_div.state.bubbled.0, Some("div".to_string()));
|
|
||||||
assert_eq!(
|
|
||||||
root_div.state.bubbled.1,
|
|
||||||
vec![
|
|
||||||
Box::new(BubbledUpStateTester(Some("p".to_string()), Vec::new())),
|
|
||||||
Box::new(BubbledUpStateTester(Some("h1".to_string()), Vec::new()))
|
|
||||||
]
|
|
||||||
);
|
|
||||||
assert_eq!(root_div.state.pushed.0, Some("div".to_string()));
|
|
||||||
assert_eq!(
|
|
||||||
root_div.state.pushed.1,
|
|
||||||
Some(Box::new(PushedDownStateTester(
|
|
||||||
Some("Root".to_string()),
|
|
||||||
None
|
|
||||||
)))
|
|
||||||
);
|
|
||||||
assert_eq!(root_div.state.node.0, Some("div".to_string()));
|
|
||||||
assert_eq!(root_div.state.node.1, vec![]);
|
|
||||||
|
|
||||||
let child_p_id = dom.children_ids(root_div_id).unwrap()[0];
|
|
||||||
let child_p = &dom[child_p_id];
|
|
||||||
assert_eq!(child_p.state.bubbled.0, Some("p".to_string()));
|
|
||||||
assert_eq!(child_p.state.bubbled.1, Vec::new());
|
|
||||||
assert_eq!(child_p.state.pushed.0, Some("p".to_string()));
|
|
||||||
assert_eq!(
|
|
||||||
child_p.state.pushed.1,
|
|
||||||
Some(Box::new(PushedDownStateTester(
|
|
||||||
Some("div".to_string()),
|
|
||||||
Some(Box::new(PushedDownStateTester(
|
|
||||||
Some("Root".to_string()),
|
|
||||||
None
|
|
||||||
)))
|
|
||||||
)))
|
|
||||||
);
|
|
||||||
assert_eq!(child_p.state.node.0, Some("p".to_string()));
|
|
||||||
assert_eq!(
|
|
||||||
child_p.state.node.1,
|
|
||||||
vec![("color".to_string(), "red".to_string())]
|
|
||||||
);
|
|
||||||
|
|
||||||
let child_h1_id = dom.children_ids(root_div_id).unwrap()[1];
|
|
||||||
let child_h1 = &dom[child_h1_id];
|
|
||||||
assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string()));
|
|
||||||
assert_eq!(child_h1.state.bubbled.1, Vec::new());
|
|
||||||
assert_eq!(child_h1.state.pushed.0, Some("h1".to_string()));
|
|
||||||
assert_eq!(
|
|
||||||
child_h1.state.pushed.1,
|
|
||||||
Some(Box::new(PushedDownStateTester(
|
|
||||||
Some("div".to_string()),
|
|
||||||
Some(Box::new(PushedDownStateTester(
|
|
||||||
Some("Root".to_string()),
|
|
||||||
None
|
|
||||||
)))
|
|
||||||
)))
|
|
||||||
);
|
|
||||||
assert_eq!(child_h1.state.node.0, Some("h1".to_string()));
|
|
||||||
assert_eq!(child_h1.state.node.1, vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn state_reduce_parent_called_minimally_on_update() {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn Base(cx: Scope) -> Element {
|
|
||||||
let width = if cx.generation() == 0 { "100%" } else { "99%" };
|
|
||||||
cx.render(rsx! {
|
|
||||||
div {
|
|
||||||
width: "{width}",
|
|
||||||
div{
|
|
||||||
div{
|
|
||||||
p{}
|
|
||||||
}
|
|
||||||
p{
|
|
||||||
"hello"
|
|
||||||
}
|
|
||||||
div{
|
|
||||||
h1{}
|
|
||||||
}
|
|
||||||
p{
|
|
||||||
"world"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut vdom = VirtualDom::new(Base);
|
|
||||||
|
|
||||||
let mut dom: RealDom<CallCounterState> = RealDom::new();
|
|
||||||
|
|
||||||
let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild());
|
|
||||||
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
|
|
||||||
vdom.mark_dirty(ScopeId(0));
|
|
||||||
let (nodes_updated, _) = dom.apply_mutations(vdom.render_immediate());
|
|
||||||
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
|
|
||||||
|
|
||||||
let mut is_root = true;
|
|
||||||
dom.traverse_depth_first(|n| {
|
|
||||||
if is_root {
|
|
||||||
is_root = false;
|
|
||||||
assert_eq!(n.state.parent_counter.0, 1);
|
|
||||||
} else {
|
|
||||||
assert_eq!(n.state.parent_counter.0, 2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn state_reduce_child_called_minimally_on_update() {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn Base(cx: Scope) -> Element {
|
|
||||||
let width = if cx.generation() == 0 { "100%" } else { "99%" };
|
|
||||||
cx.render(rsx! {
|
|
||||||
// updated: 2
|
|
||||||
div {
|
|
||||||
// updated: 2
|
|
||||||
div{
|
|
||||||
// updated: 2
|
|
||||||
div{
|
|
||||||
// updated: 2
|
|
||||||
p{
|
|
||||||
width: "{width}",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// updated: 1
|
|
||||||
p{
|
|
||||||
// updated: 1
|
|
||||||
"hello"
|
|
||||||
}
|
|
||||||
// updated: 1
|
|
||||||
div{
|
|
||||||
// updated: 1
|
|
||||||
h1{}
|
|
||||||
}
|
|
||||||
// updated: 1
|
|
||||||
p{
|
|
||||||
// updated: 1
|
|
||||||
"world"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut vdom = VirtualDom::new(Base);
|
|
||||||
|
|
||||||
let mut dom: RealDom<CallCounterState> = RealDom::new();
|
|
||||||
|
|
||||||
let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild());
|
|
||||||
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
|
|
||||||
vdom.mark_dirty(ScopeId(0));
|
|
||||||
let (nodes_updated, _) = dom.apply_mutations(vdom.render_immediate());
|
|
||||||
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new());
|
|
||||||
|
|
||||||
let mut traverse_count = 0;
|
|
||||||
dom.traverse_depth_first(|n| {
|
|
||||||
assert_eq!(n.state.child_counter.0, {
|
|
||||||
if traverse_count > 4 {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
2
|
|
||||||
}
|
|
||||||
});
|
|
||||||
traverse_count += 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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,): (&BDepCallCounter,),
|
|
||||||
_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,): (&CDepCallCounter,),
|
|
||||||
_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: (), _ctx: &Self::Ctx) -> bool {
|
|
||||||
self.0 += 1;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dependancies_order_independant() {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn Base(cx: Scope) -> Element {
|
|
||||||
render!(div {
|
|
||||||
width: "100%",
|
|
||||||
p{
|
|
||||||
"hello"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut vdom = VirtualDom::new(Base);
|
|
||||||
|
|
||||||
let mut dom: RealDom<UnorderedDependanciesState> = RealDom::new();
|
|
||||||
|
|
||||||
let mutations = vdom.rebuild();
|
|
||||||
let (nodes_updated, _) = dom.apply_mutations(mutations);
|
|
||||||
let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -31,5 +31,4 @@ lightningcss = "1.0.0-alpha.39"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
dioxus = { path = "../dioxus", version = "^0.3.0" }
|
dioxus = { path = "../dioxus", version = "^0.3.0" }
|
||||||
dioxus-native-core-macro = { path = "../native-core-macro" }
|
|
||||||
tokio = { version = "*", features = ["full"] }
|
tokio = { version = "*", features = ["full"] }
|
||||||
@@ -1,47 +1,11 @@
|
|||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
use dioxus_core::ElementId;
|
use dioxus_core::ElementId;
|
||||||
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
node::{ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeView},
|
node::{ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeView},
|
||||||
NodeId,
|
NodeId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Join two sorted iterators
|
|
||||||
pub(crate) fn union_ordered_iter<'a>(
|
|
||||||
s_iter: impl Iterator<Item = &'a str>,
|
|
||||||
o_iter: impl Iterator<Item = &'a str>,
|
|
||||||
new_len_guess: usize,
|
|
||||||
) -> Vec<String> {
|
|
||||||
let mut s_peekable = s_iter.peekable();
|
|
||||||
let mut o_peekable = o_iter.peekable();
|
|
||||||
let mut v = Vec::with_capacity(new_len_guess);
|
|
||||||
while let Some(s_i) = s_peekable.peek() {
|
|
||||||
while let Some(o_i) = o_peekable.peek() {
|
|
||||||
match o_i.cmp(s_i) {
|
|
||||||
Ordering::Greater => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ordering::Less => {
|
|
||||||
v.push(o_peekable.next().unwrap().to_string());
|
|
||||||
}
|
|
||||||
Ordering::Equal => {
|
|
||||||
o_peekable.next();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v.push(s_peekable.next().unwrap().to_string());
|
|
||||||
}
|
|
||||||
for o_i in o_peekable {
|
|
||||||
v.push(o_i.to_string());
|
|
||||||
}
|
|
||||||
for w in v.windows(2) {
|
|
||||||
debug_assert!(w[1] > w[0]);
|
|
||||||
}
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A view into a [VNode] with limited access.
|
/// A view into a [VNode] with limited access.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NodeView<'a, V: FromAnyValue = ()> {
|
pub struct NodeView<'a, V: FromAnyValue = ()> {
|
||||||
@@ -138,124 +102,50 @@ impl<'a, V: FromAnyValue> NodeView<'a, V> {
|
|||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub enum AttributeMask {
|
pub enum AttributeMask {
|
||||||
All,
|
All,
|
||||||
/// A list of attribute names that are visible, this list must be sorted
|
Some(FxHashSet<Box<str>>),
|
||||||
Dynamic(Vec<String>),
|
|
||||||
/// A list of attribute names that are visible, this list must be sorted
|
|
||||||
Static(&'static [&'static str]),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttributeMask {
|
impl AttributeMask {
|
||||||
/// A empty attribute mask
|
|
||||||
pub const NONE: Self = Self::Static(&[]);
|
|
||||||
|
|
||||||
fn contains_attribute(&self, attr: &str) -> bool {
|
fn contains_attribute(&self, attr: &str) -> bool {
|
||||||
match self {
|
match self {
|
||||||
AttributeMask::All => true,
|
AttributeMask::All => true,
|
||||||
AttributeMask::Dynamic(l) => l.binary_search_by_key(&attr, |s| s.as_str()).is_ok(),
|
AttributeMask::Some(attrs) => attrs.contains(attr),
|
||||||
AttributeMask::Static(l) => l.binary_search(&attr).is_ok(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new dynamic attribute mask with a single attribute
|
/// Create a new dynamic attribute mask with a single attribute
|
||||||
pub fn single(new: &str) -> Self {
|
pub fn single(new: &str) -> Self {
|
||||||
Self::Dynamic(vec![new.to_string()])
|
let mut set = FxHashSet::default();
|
||||||
}
|
set.insert(new.into());
|
||||||
|
Self::Some(set)
|
||||||
/// Ensure the attribute list is sorted.
|
|
||||||
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"
|
|
||||||
),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combine two attribute masks
|
/// Combine two attribute masks
|
||||||
pub fn union(&self, other: &Self) -> Self {
|
pub fn union(&self, other: &Self) -> Self {
|
||||||
let new = match (self, other) {
|
match (self, other) {
|
||||||
(AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => {
|
(AttributeMask::Some(s), AttributeMask::Some(o)) => {
|
||||||
AttributeMask::Dynamic(union_ordered_iter(
|
AttributeMask::Some(s.intersection(o).cloned().collect())
|
||||||
s.iter().map(|s| s.as_str()),
|
|
||||||
o.iter().map(|s| s.as_str()),
|
|
||||||
s.len() + o.len(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
(AttributeMask::Static(s), AttributeMask::Dynamic(o)) => {
|
|
||||||
AttributeMask::Dynamic(union_ordered_iter(
|
|
||||||
s.iter().copied(),
|
|
||||||
o.iter().map(|s| s.as_str()),
|
|
||||||
s.len() + o.len(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
(AttributeMask::Dynamic(s), AttributeMask::Static(o)) => {
|
|
||||||
AttributeMask::Dynamic(union_ordered_iter(
|
|
||||||
s.iter().map(|s| s.as_str()),
|
|
||||||
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,
|
_ => AttributeMask::All,
|
||||||
};
|
}
|
||||||
new.verify();
|
|
||||||
new
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if two attribute masks overlap
|
/// Check if two attribute masks overlap
|
||||||
fn overlaps(&self, other: &Self) -> bool {
|
fn overlaps(&self, other: &Self) -> bool {
|
||||||
fn overlaps_iter<'a>(
|
|
||||||
self_iter: impl Iterator<Item = &'a str>,
|
|
||||||
mut other_iter: impl Iterator<Item = &'a str>,
|
|
||||||
) -> bool {
|
|
||||||
if let Some(mut other_attr) = other_iter.next() {
|
|
||||||
for self_attr in self_iter {
|
|
||||||
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) {
|
match (self, other) {
|
||||||
(AttributeMask::All, AttributeMask::All) => true,
|
(AttributeMask::All, AttributeMask::Some(attrs)) => !attrs.is_empty(),
|
||||||
(AttributeMask::All, AttributeMask::Dynamic(v)) => !v.is_empty(),
|
(AttributeMask::Some(attrs), AttributeMask::All) => !attrs.is_empty(),
|
||||||
(AttributeMask::All, AttributeMask::Static(s)) => !s.is_empty(),
|
(AttributeMask::Some(attrs1), AttributeMask::Some(attrs2)) => {
|
||||||
(AttributeMask::Dynamic(v), AttributeMask::All) => !v.is_empty(),
|
!attrs1.is_disjoint(attrs2)
|
||||||
(AttributeMask::Static(s), AttributeMask::All) => !s.is_empty(),
|
|
||||||
(AttributeMask::Dynamic(v1), AttributeMask::Dynamic(v2)) => {
|
|
||||||
overlaps_iter(v1.iter().map(|s| s.as_str()), v2.iter().map(|s| s.as_str()))
|
|
||||||
}
|
|
||||||
(AttributeMask::Dynamic(v), AttributeMask::Static(s)) => {
|
|
||||||
overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied())
|
|
||||||
}
|
|
||||||
(AttributeMask::Static(s), AttributeMask::Dynamic(v)) => {
|
|
||||||
overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied())
|
|
||||||
}
|
|
||||||
(AttributeMask::Static(s1), AttributeMask::Static(s2)) => {
|
|
||||||
overlaps_iter(s1.iter().copied(), s2.iter().copied())
|
|
||||||
}
|
}
|
||||||
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AttributeMask {
|
impl Default for AttributeMask {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
AttributeMask::Static(&[])
|
AttributeMask::Some(FxHashSet::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,33 +160,33 @@ pub struct NodeMask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NodeMask {
|
impl NodeMask {
|
||||||
/// A node mask with no parts visible.
|
|
||||||
pub const NONE: Self = Self::new();
|
|
||||||
/// A node mask with every part visible.
|
|
||||||
pub const ALL: Self = Self::new_with_attrs(AttributeMask::All)
|
|
||||||
.with_text()
|
|
||||||
.with_element()
|
|
||||||
.with_listeners();
|
|
||||||
|
|
||||||
pub fn from_node(node: &NodeType) -> Self {
|
pub fn from_node(node: &NodeType) -> Self {
|
||||||
match node {
|
match node {
|
||||||
NodeType::Text { .. } => Self::new().with_text(),
|
NodeType::Text { .. } => NodeMaskBuilder::new().with_text().build(),
|
||||||
NodeType::Element(ElementNode {
|
NodeType::Element(ElementNode {
|
||||||
tag: _,
|
tag: _,
|
||||||
namespace: _,
|
namespace: _,
|
||||||
attributes,
|
attributes,
|
||||||
listeners,
|
listeners,
|
||||||
}) => {
|
}) => Self {
|
||||||
let mut mask = Self::new_with_attrs(AttributeMask::Dynamic(
|
attritutes: AttributeMask::Some(
|
||||||
attributes.keys().map(|key| key.name.clone()).collect(),
|
attributes
|
||||||
))
|
.keys()
|
||||||
.with_element();
|
.map(|key| key.name.as_str().into())
|
||||||
if !listeners.is_empty() {
|
.collect(),
|
||||||
mask = mask.with_listeners();
|
),
|
||||||
}
|
namespace: true,
|
||||||
mask
|
tag: true,
|
||||||
}
|
text: false,
|
||||||
NodeType::Placeholder => Self::new().with_element(),
|
listeners: !listeners.is_empty(),
|
||||||
|
},
|
||||||
|
NodeType::Placeholder => Self {
|
||||||
|
attritutes: AttributeMask::default(),
|
||||||
|
tag: true,
|
||||||
|
namespace: true,
|
||||||
|
text: false,
|
||||||
|
listeners: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,8 +210,65 @@ impl NodeMask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allow the mask to view the given attributes
|
||||||
|
pub fn add_attributes(&mut self, attributes: AttributeMask) {
|
||||||
|
self.attritutes = self.attritutes.union(&attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the mask to view the tag
|
||||||
|
pub fn set_tag(&mut self) {
|
||||||
|
self.tag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the mask to view the namespace
|
||||||
|
pub fn set_namespace(&mut self) {
|
||||||
|
self.namespace = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the mask to view the text
|
||||||
|
pub fn set_text(&mut self) {
|
||||||
|
self.text = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the mask to view the listeners
|
||||||
|
pub fn set_listeners(&mut self) {
|
||||||
|
self.listeners = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
|
pub enum AttributeMaskBuilder {
|
||||||
|
All,
|
||||||
|
Some(&'static [&'static str]),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AttributeMaskBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
AttributeMaskBuilder::Some(&[])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A mask that limits what parts of a node a dependency can see.
|
||||||
|
#[derive(Default, PartialEq, Eq, Clone, Debug)]
|
||||||
|
pub struct NodeMaskBuilder {
|
||||||
|
attritutes: AttributeMaskBuilder,
|
||||||
|
tag: bool,
|
||||||
|
namespace: bool,
|
||||||
|
text: bool,
|
||||||
|
listeners: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeMaskBuilder {
|
||||||
|
/// A node mask with no parts visible.
|
||||||
|
pub const NONE: Self = Self::new();
|
||||||
|
/// A node mask with every part visible.
|
||||||
|
pub const ALL: Self = Self::new_with_attrs(AttributeMaskBuilder::All)
|
||||||
|
.with_text()
|
||||||
|
.with_element()
|
||||||
|
.with_listeners();
|
||||||
|
|
||||||
/// Create a new node mask with the given attributes
|
/// Create a new node mask with the given attributes
|
||||||
pub const fn new_with_attrs(attritutes: AttributeMask) -> Self {
|
pub const fn new_with_attrs(attritutes: AttributeMaskBuilder) -> Self {
|
||||||
Self {
|
Self {
|
||||||
attritutes,
|
attritutes,
|
||||||
tag: false,
|
tag: false,
|
||||||
@@ -333,7 +280,7 @@ impl NodeMask {
|
|||||||
|
|
||||||
/// Create a empty node mask
|
/// Create a empty node mask
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self::new_with_attrs(AttributeMask::NONE)
|
Self::new_with_attrs(AttributeMaskBuilder::Some(&[]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allow the mask to view the tag
|
/// Allow the mask to view the tag
|
||||||
@@ -342,22 +289,12 @@ impl NodeMask {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the mask to view the tag
|
|
||||||
pub fn set_tag(&mut self) {
|
|
||||||
self.tag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allow the mask to view the namespace
|
/// Allow the mask to view the namespace
|
||||||
pub const fn with_namespace(mut self) -> Self {
|
pub const fn with_namespace(mut self) -> Self {
|
||||||
self.namespace = true;
|
self.namespace = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the mask to view the namespace
|
|
||||||
pub fn set_namespace(&mut self) {
|
|
||||||
self.namespace = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allow the mask to view the namespace and tag
|
/// Allow the mask to view the namespace and tag
|
||||||
pub const fn with_element(self) -> Self {
|
pub const fn with_element(self) -> Self {
|
||||||
self.with_namespace().with_tag()
|
self.with_namespace().with_tag()
|
||||||
@@ -369,24 +306,25 @@ impl NodeMask {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the mask to view the text
|
|
||||||
pub fn set_text(&mut self) {
|
|
||||||
self.text = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allow the mask to view the listeners
|
/// Allow the mask to view the listeners
|
||||||
pub const fn with_listeners(mut self) -> Self {
|
pub const fn with_listeners(mut self) -> Self {
|
||||||
self.listeners = true;
|
self.listeners = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the mask to view the listeners
|
/// Build the mask
|
||||||
pub fn set_listeners(&mut self) {
|
pub fn build(self) -> NodeMask {
|
||||||
self.listeners = true;
|
NodeMask {
|
||||||
}
|
attritutes: match self.attritutes {
|
||||||
|
AttributeMaskBuilder::All => AttributeMask::All,
|
||||||
/// Allow the mask to view the given attributes
|
AttributeMaskBuilder::Some(attrs) => {
|
||||||
pub fn add_attributes(&mut self, attributes: AttributeMask) {
|
AttributeMask::Some(attrs.iter().map(|s| (*s).into()).collect())
|
||||||
self.attritutes = self.attritutes.union(&attributes);
|
}
|
||||||
|
},
|
||||||
|
tag: self.tag,
|
||||||
|
namespace: self.namespace,
|
||||||
|
text: self.text,
|
||||||
|
listeners: self.listeners,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ use std::marker::PhantomData;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::node::{FromAnyValue, NodeData};
|
use crate::node::{FromAnyValue, NodeData};
|
||||||
use crate::node_ref::NodeView;
|
use crate::node_ref::{NodeMaskBuilder, NodeView};
|
||||||
use crate::real_dom::RealDom;
|
use crate::real_dom::RealDom;
|
||||||
use crate::tree::TreeStateView;
|
use crate::tree::TreeStateView;
|
||||||
use crate::{FxDashMap, FxDashSet, SendAnyMap};
|
use crate::{FxDashMap, FxDashSet, SendAnyMap};
|
||||||
@@ -116,7 +116,7 @@ pub trait Pass<V: FromAnyValue + Send = ()>: Any {
|
|||||||
type ChildDependencies: Dependancy;
|
type ChildDependencies: Dependancy;
|
||||||
/// This is a tuple of (T: Any, ..)
|
/// This is a tuple of (T: Any, ..)
|
||||||
type NodeDependencies: Dependancy;
|
type NodeDependencies: Dependancy;
|
||||||
const NODE_MASK: NodeMask;
|
const NODE_MASK: NodeMaskBuilder;
|
||||||
|
|
||||||
fn pass<'a>(
|
fn pass<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -158,13 +158,14 @@ pub trait Pass<V: FromAnyValue + Send = ()>: Any {
|
|||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
Self::validate();
|
Self::validate();
|
||||||
|
let node_mask = Self::NODE_MASK.build();
|
||||||
TypeErasedPass {
|
TypeErasedPass {
|
||||||
this_type_id: TypeId::of::<Self>(),
|
this_type_id: TypeId::of::<Self>(),
|
||||||
combined_dependancy_type_ids: Self::all_dependanices().iter().copied().collect(),
|
combined_dependancy_type_ids: Self::all_dependanices().iter().copied().collect(),
|
||||||
parent_dependant: !Self::parent_type_ids().is_empty(),
|
parent_dependant: !Self::parent_type_ids().is_empty(),
|
||||||
child_dependant: !Self::child_type_ids().is_empty(),
|
child_dependant: !Self::child_type_ids().is_empty(),
|
||||||
dependants: FxHashSet::default(),
|
dependants: FxHashSet::default(),
|
||||||
mask: Self::NODE_MASK,
|
mask: node_mask,
|
||||||
pass_direction: Self::pass_direction(),
|
pass_direction: Self::pass_direction(),
|
||||||
pass: Box::new(
|
pass: Box::new(
|
||||||
|node_id: NodeId, tree: &mut TreeStateView, context: &SendAnyMap| {
|
|node_id: NodeId, tree: &mut TreeStateView, context: &SendAnyMap| {
|
||||||
@@ -181,7 +182,7 @@ pub trait Pass<V: FromAnyValue + Send = ()>: Any {
|
|||||||
let myself = unsafe { &mut *node_raw };
|
let myself = unsafe { &mut *node_raw };
|
||||||
|
|
||||||
myself.pass(
|
myself.pass(
|
||||||
NodeView::new(node_data, Self::NODE_MASK),
|
NodeView::new(node_data, Self::NODE_MASK.build()),
|
||||||
node,
|
node,
|
||||||
parent,
|
parent,
|
||||||
children,
|
children,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::collections::VecDeque;
|
|||||||
use crate::node::{
|
use crate::node::{
|
||||||
ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeDiscription, OwnedAttributeValue,
|
ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeDiscription, OwnedAttributeValue,
|
||||||
};
|
};
|
||||||
use crate::node_ref::{AttributeMask, NodeMask};
|
use crate::node_ref::{AttributeMask, NodeMask, NodeMaskBuilder};
|
||||||
use crate::passes::{resolve_passes, DirtyNodeStates, TypeErasedPass};
|
use crate::passes::{resolve_passes, DirtyNodeStates, TypeErasedPass};
|
||||||
use crate::tree::{EntryBuilder, NodeId, Tree};
|
use crate::tree::{EntryBuilder, NodeId, Tree};
|
||||||
use crate::{FxDashSet, SendAnyMap};
|
use crate::{FxDashSet, SendAnyMap};
|
||||||
@@ -64,7 +64,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
|
|
||||||
let mut nodes_updated = FxHashMap::default();
|
let mut nodes_updated = FxHashMap::default();
|
||||||
let root_id = NodeId(0);
|
let root_id = NodeId(0);
|
||||||
nodes_updated.insert(root_id, NodeMask::ALL);
|
nodes_updated.insert(root_id, NodeMaskBuilder::ALL.build());
|
||||||
|
|
||||||
RealDom {
|
RealDom {
|
||||||
tree,
|
tree,
|
||||||
@@ -389,7 +389,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
|||||||
pub fn get_mut(&mut self, id: NodeId) -> Option<NodeMut<'_, V>> {
|
pub fn get_mut(&mut self, id: NodeId) -> Option<NodeMut<'_, V>> {
|
||||||
self.tree.contains(id).then(|| NodeMut {
|
self.tree.contains(id).then(|| NodeMut {
|
||||||
id,
|
id,
|
||||||
dirty: NodeMask::new(),
|
dirty: NodeMask::default(),
|
||||||
dom: self,
|
dom: self,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -616,7 +616,7 @@ impl<'a, V: FromAnyValue + Send> NodeMut<'a, V> {
|
|||||||
|
|
||||||
pub fn set_type(&mut self, new: NodeType<V>) {
|
pub fn set_type(&mut self, new: NodeType<V>) {
|
||||||
self.node_data_mut().node_type = new;
|
self.node_data_mut().node_type = new;
|
||||||
self.dirty = NodeMask::ALL;
|
self.dirty = NodeMaskBuilder::ALL.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<T: Any>(&self) -> Option<&T> {
|
pub fn read<T: Any>(&self) -> Option<&T> {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ dioxus = { path = "../dioxus", version = "^0.3.0" }
|
|||||||
dioxus-core = { path = "../core", version = "^0.3.0" }
|
dioxus-core = { path = "../core", version = "^0.3.0" }
|
||||||
dioxus-html = { path = "../html", version = "^0.3.0" }
|
dioxus-html = { path = "../html", version = "^0.3.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"
|
||||||
|
|||||||
Reference in new issue
Block a user