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/tui",
|
||||
"packages/native-core",
|
||||
"packages/native-core-macro",
|
||||
"packages/rsx-rosetta",
|
||||
"packages/signals",
|
||||
"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]
|
||||
rand = "0.8.5"
|
||||
dioxus = { path = "../dioxus", version = "^0.3.0" }
|
||||
dioxus-native-core-macro = { path = "../native-core-macro" }
|
||||
tokio = { version = "*", features = ["full"] }
|
||||
@@ -1,47 +1,11 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use dioxus_core::ElementId;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
node::{ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeView},
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
pub struct NodeView<'a, V: FromAnyValue = ()> {
|
||||
@@ -138,124 +102,50 @@ impl<'a, V: FromAnyValue> NodeView<'a, V> {
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum AttributeMask {
|
||||
All,
|
||||
/// A list of attribute names that are visible, this list must be sorted
|
||||
Dynamic(Vec<String>),
|
||||
/// A list of attribute names that are visible, this list must be sorted
|
||||
Static(&'static [&'static str]),
|
||||
Some(FxHashSet<Box<str>>),
|
||||
}
|
||||
|
||||
impl AttributeMask {
|
||||
/// A empty attribute mask
|
||||
pub const NONE: Self = Self::Static(&[]);
|
||||
|
||||
fn contains_attribute(&self, attr: &str) -> bool {
|
||||
match self {
|
||||
AttributeMask::All => true,
|
||||
AttributeMask::Dynamic(l) => l.binary_search_by_key(&attr, |s| s.as_str()).is_ok(),
|
||||
AttributeMask::Static(l) => l.binary_search(&attr).is_ok(),
|
||||
AttributeMask::Some(attrs) => attrs.contains(attr),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new dynamic attribute mask with a single attribute
|
||||
pub fn single(new: &str) -> Self {
|
||||
Self::Dynamic(vec![new.to_string()])
|
||||
}
|
||||
|
||||
/// 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"
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
let mut set = FxHashSet::default();
|
||||
set.insert(new.into());
|
||||
Self::Some(set)
|
||||
}
|
||||
|
||||
/// Combine two attribute masks
|
||||
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().map(|s| s.as_str()),
|
||||
o.iter().map(|s| s.as_str()),
|
||||
s.len() + o.len(),
|
||||
))
|
||||
match (self, other) {
|
||||
(AttributeMask::Some(s), AttributeMask::Some(o)) => {
|
||||
AttributeMask::Some(s.intersection(o).cloned().collect())
|
||||
}
|
||||
(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,
|
||||
};
|
||||
new.verify();
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if two attribute masks overlap
|
||||
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) {
|
||||
(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().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())
|
||||
(AttributeMask::All, AttributeMask::Some(attrs)) => !attrs.is_empty(),
|
||||
(AttributeMask::Some(attrs), AttributeMask::All) => !attrs.is_empty(),
|
||||
(AttributeMask::Some(attrs1), AttributeMask::Some(attrs2)) => {
|
||||
!attrs1.is_disjoint(attrs2)
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AttributeMask {
|
||||
fn default() -> Self {
|
||||
AttributeMask::Static(&[])
|
||||
AttributeMask::Some(FxHashSet::default())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,33 +160,33 @@ pub struct 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 {
|
||||
match node {
|
||||
NodeType::Text { .. } => Self::new().with_text(),
|
||||
NodeType::Text { .. } => NodeMaskBuilder::new().with_text().build(),
|
||||
NodeType::Element(ElementNode {
|
||||
tag: _,
|
||||
namespace: _,
|
||||
attributes,
|
||||
listeners,
|
||||
}) => {
|
||||
let mut mask = Self::new_with_attrs(AttributeMask::Dynamic(
|
||||
attributes.keys().map(|key| key.name.clone()).collect(),
|
||||
))
|
||||
.with_element();
|
||||
if !listeners.is_empty() {
|
||||
mask = mask.with_listeners();
|
||||
}
|
||||
mask
|
||||
}
|
||||
NodeType::Placeholder => Self::new().with_element(),
|
||||
}) => Self {
|
||||
attritutes: AttributeMask::Some(
|
||||
attributes
|
||||
.keys()
|
||||
.map(|key| key.name.as_str().into())
|
||||
.collect(),
|
||||
),
|
||||
namespace: true,
|
||||
tag: true,
|
||||
text: false,
|
||||
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
|
||||
pub const fn new_with_attrs(attritutes: AttributeMask) -> Self {
|
||||
pub const fn new_with_attrs(attritutes: AttributeMaskBuilder) -> Self {
|
||||
Self {
|
||||
attritutes,
|
||||
tag: false,
|
||||
@@ -333,7 +280,7 @@ impl NodeMask {
|
||||
|
||||
/// Create a empty node mask
|
||||
pub const fn new() -> Self {
|
||||
Self::new_with_attrs(AttributeMask::NONE)
|
||||
Self::new_with_attrs(AttributeMaskBuilder::Some(&[]))
|
||||
}
|
||||
|
||||
/// Allow the mask to view the tag
|
||||
@@ -342,22 +289,12 @@ impl NodeMask {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the mask to view the tag
|
||||
pub fn set_tag(&mut self) {
|
||||
self.tag = true;
|
||||
}
|
||||
|
||||
/// Allow the mask to view the namespace
|
||||
pub const fn with_namespace(mut self) -> Self {
|
||||
self.namespace = true;
|
||||
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
|
||||
pub const fn with_element(self) -> Self {
|
||||
self.with_namespace().with_tag()
|
||||
@@ -369,24 +306,25 @@ impl NodeMask {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the mask to view the text
|
||||
pub fn set_text(&mut self) {
|
||||
self.text = true;
|
||||
}
|
||||
|
||||
/// Allow the mask to view the listeners
|
||||
pub const fn with_listeners(mut self) -> Self {
|
||||
self.listeners = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the mask to view the listeners
|
||||
pub fn set_listeners(&mut self) {
|
||||
self.listeners = true;
|
||||
}
|
||||
|
||||
/// Allow the mask to view the given attributes
|
||||
pub fn add_attributes(&mut self, attributes: AttributeMask) {
|
||||
self.attritutes = self.attritutes.union(&attributes);
|
||||
/// Build the mask
|
||||
pub fn build(self) -> NodeMask {
|
||||
NodeMask {
|
||||
attritutes: match self.attritutes {
|
||||
AttributeMaskBuilder::All => AttributeMask::All,
|
||||
AttributeMaskBuilder::Some(attrs) => {
|
||||
AttributeMask::Some(attrs.iter().map(|s| (*s).into()).collect())
|
||||
}
|
||||
},
|
||||
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 crate::node::{FromAnyValue, NodeData};
|
||||
use crate::node_ref::NodeView;
|
||||
use crate::node_ref::{NodeMaskBuilder, NodeView};
|
||||
use crate::real_dom::RealDom;
|
||||
use crate::tree::TreeStateView;
|
||||
use crate::{FxDashMap, FxDashSet, SendAnyMap};
|
||||
@@ -116,7 +116,7 @@ pub trait Pass<V: FromAnyValue + Send = ()>: Any {
|
||||
type ChildDependencies: Dependancy;
|
||||
/// This is a tuple of (T: Any, ..)
|
||||
type NodeDependencies: Dependancy;
|
||||
const NODE_MASK: NodeMask;
|
||||
const NODE_MASK: NodeMaskBuilder;
|
||||
|
||||
fn pass<'a>(
|
||||
&mut self,
|
||||
@@ -158,13 +158,14 @@ pub trait Pass<V: FromAnyValue + Send = ()>: Any {
|
||||
Self: Sized,
|
||||
{
|
||||
Self::validate();
|
||||
let node_mask = Self::NODE_MASK.build();
|
||||
TypeErasedPass {
|
||||
this_type_id: TypeId::of::<Self>(),
|
||||
combined_dependancy_type_ids: Self::all_dependanices().iter().copied().collect(),
|
||||
parent_dependant: !Self::parent_type_ids().is_empty(),
|
||||
child_dependant: !Self::child_type_ids().is_empty(),
|
||||
dependants: FxHashSet::default(),
|
||||
mask: Self::NODE_MASK,
|
||||
mask: node_mask,
|
||||
pass_direction: Self::pass_direction(),
|
||||
pass: Box::new(
|
||||
|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 };
|
||||
|
||||
myself.pass(
|
||||
NodeView::new(node_data, Self::NODE_MASK),
|
||||
NodeView::new(node_data, Self::NODE_MASK.build()),
|
||||
node,
|
||||
parent,
|
||||
children,
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::collections::VecDeque;
|
||||
use crate::node::{
|
||||
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::tree::{EntryBuilder, NodeId, Tree};
|
||||
use crate::{FxDashSet, SendAnyMap};
|
||||
@@ -64,7 +64,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
||||
|
||||
let mut nodes_updated = FxHashMap::default();
|
||||
let root_id = NodeId(0);
|
||||
nodes_updated.insert(root_id, NodeMask::ALL);
|
||||
nodes_updated.insert(root_id, NodeMaskBuilder::ALL.build());
|
||||
|
||||
RealDom {
|
||||
tree,
|
||||
@@ -389,7 +389,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
||||
pub fn get_mut(&mut self, id: NodeId) -> Option<NodeMut<'_, V>> {
|
||||
self.tree.contains(id).then(|| NodeMut {
|
||||
id,
|
||||
dirty: NodeMask::new(),
|
||||
dirty: NodeMask::default(),
|
||||
dom: self,
|
||||
})
|
||||
}
|
||||
@@ -616,7 +616,7 @@ impl<'a, V: FromAnyValue + Send> NodeMut<'a, V> {
|
||||
|
||||
pub fn set_type(&mut self, new: NodeType<V>) {
|
||||
self.node_data_mut().node_type = new;
|
||||
self.dirty = NodeMask::ALL;
|
||||
self.dirty = NodeMaskBuilder::ALL.build();
|
||||
}
|
||||
|
||||
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-html = { path = "../html", version = "^0.3.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"
|
||||
crossterm = "0.23.0"
|
||||
|
||||
Reference in new issue
Block a user