Merge branch 'master' of github.com:DioxusLabs/dioxus
This commit is contained in:
commit
2b9888627b
22 files changed
+994
-1113
No files matched your search
@@ -243,11 +243,11 @@ You've probably noticed that many elements in the `rsx!` macros support on-hover
|
|||||||
|
|
||||||
# Native Core
|
# Native Core
|
||||||
|
|
||||||
Renderers take a lot of work. If you are creating a renderer in rust, native core provides some utilites to implement a renderer. It provides an abstraction over DomEdits and handles layout for you.
|
If you are creating a renderer in rust, native core provides some utilites to implement a renderer. It provides an abstraction over DomEdits and handles layout for you.
|
||||||
|
|
||||||
## RealDom
|
## RealDom
|
||||||
|
|
||||||
The `RealDom` is a higher level abstraction over updating the Dom. It updates with `DomEdits` and provides a way to lazily update the state of nodes based on what attributes change.
|
The `RealDom` is a higher level abstraction over updating the Dom. It updates with `DomEdits` and provides a way to incrementally update the state of nodes based on what attributes change.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -267,43 +267,52 @@ cx.render(rsx!{
|
|||||||
|
|
||||||
In this tree the color depends on the parent's color. The size depends on the childrens size, the current text, and a text size. The border depends on only the current node.
|
In this tree the color depends on the parent's color. The size depends on the childrens size, the current text, and a text size. The border depends on only the current node.
|
||||||
|
|
||||||
```mermaid
|
In the following diagram arrows represent dataflow:
|
||||||
flowchart TB
|
|
||||||
subgraph context
|
|
||||||
text_width(text width)
|
|
||||||
end
|
|
||||||
subgraph div
|
|
||||||
state1(state)-->color1(color)
|
|
||||||
state1(state)-->border1(border)
|
|
||||||
border1-.->text_width
|
|
||||||
linkStyle 2 stroke:#5555ff,stroke-width:4px;
|
|
||||||
state1(state)-->layout_width1(layout width)
|
|
||||||
end
|
|
||||||
subgraph p
|
|
||||||
state2(state)-->color2(color)
|
|
||||||
color2-.->color1(color)
|
|
||||||
linkStyle 5 stroke:#0000ff,stroke-width:4px;
|
|
||||||
state2(state)-->border2(border)
|
|
||||||
border2-.->text_width
|
|
||||||
linkStyle 7 stroke:#5555ff,stroke-width:4px;
|
|
||||||
state2(state)-->layout_width2(layout width)
|
|
||||||
layout_width1-.->layout_width2
|
|
||||||
linkStyle 9 stroke:#aaaaff,stroke-width:4px;
|
|
||||||
end
|
|
||||||
subgraph hello world
|
|
||||||
state3(state)-->color3(color)
|
|
||||||
color3-.->color2(color)
|
|
||||||
linkStyle 11 stroke:#0000ff,stroke-width:4px;
|
|
||||||
state3(state)-->border3(border)
|
|
||||||
border3-.->text_width
|
|
||||||
linkStyle 13 stroke:#5555ff,stroke-width:4px;
|
|
||||||
state3(state)-->layout_width3(layout width)
|
|
||||||
layout_width2-.->layout_width3
|
|
||||||
linkStyle 15 stroke:#aaaaff,stroke-width:4px;
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
To help in building a Dom, native core provides four traits: State, ChildDepState, ParentDepState, and NodeDepState and a RealDom struct.
|
[](https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNqdVNFqgzAU_RXJXizUUZPJmIM-jO0LukdhpCbO0JhIGteW0n9fNK1Oa0brfUnu9VxyzzkXjyCVhIIYZFzu0hwr7X2-JcIzsa3W3wqXuZdKoele22oddfa1Y0Tnfn31muvMfqeCDNq3GmvaNROmaKqZFO1DPTRhP8MOd1fTWYNDvzlmQbBMJZcq9JtjNgY1mLVUhBqQPQeojl3wGCw5PsjqnIe-zXqEL8GZ2Kz0gVMPmoeU3ND4IcuiaLGY2zRouuKncv_qGKv3VodpJe0JVU6QCQ5kgqMyWQVr8hbk4hm1PBcmsuwmnrCVH94rP7xN_ucp8sOB_EPSfz9drYVrkpc_AmH8_yTjJueUc-ntpOJkgt2os9tKjcYlt-DLUiD3UsB2KZCLcwjv3Aq33-g2v0M0xXA0MBy5DUdXi-gcJZriuLmAOSioKjAj5ld8rMsJ0DktaAJicyVYbRKQiJPBVSUx438QpqUCcYb5ls4BrrRcHUTaFizqnWGzR8W5evoFI-bJdw)
|
||||||
|
|
||||||
|
[//]: # "%% mermaid flow chart"
|
||||||
|
[//]: # "flowchart TB"
|
||||||
|
[//]: # " subgraph context"
|
||||||
|
[//]: # " text_width(text width)"
|
||||||
|
[//]: # " end"
|
||||||
|
[//]: # " subgraph state"
|
||||||
|
[//]: # " direction TB"
|
||||||
|
[//]: # " subgraph div state"
|
||||||
|
[//]: # " direction TB"
|
||||||
|
[//]: # " state1(state)-->color1(color)"
|
||||||
|
[//]: # " state1-->border1(border)"
|
||||||
|
[//]: # " text_width-.->layout_width1(layout width)"
|
||||||
|
[//]: # " linkStyle 2 stroke:#ff5500,stroke-width:4px;"
|
||||||
|
[//]: # " state1-->layout_width1"
|
||||||
|
[//]: # " end"
|
||||||
|
[//]: # " subgraph p state"
|
||||||
|
[//]: # " direction TB"
|
||||||
|
[//]: # " state2(state)-->color2(color)"
|
||||||
|
[//]: # " color1-.->color2"
|
||||||
|
[//]: # " linkStyle 5 stroke:#0000ff,stroke-width:4px;"
|
||||||
|
[//]: # " state2-->border2(border)"
|
||||||
|
[//]: # " text_width-.->layout_width2(layout width)"
|
||||||
|
[//]: # " linkStyle 7 stroke:#ff5500,stroke-width:4px;"
|
||||||
|
[//]: # " state2-->layout_width2"
|
||||||
|
[//]: # " layout_width2-.->layout_width1"
|
||||||
|
[//]: # " linkStyle 9 stroke:#00aa00,stroke-width:4px;"
|
||||||
|
[//]: # " end"
|
||||||
|
[//]: # " subgraph hello world state"
|
||||||
|
[//]: # " direction TB"
|
||||||
|
[//]: # " state3(state)-->border3(border)"
|
||||||
|
[//]: # " state3-->color3(color)"
|
||||||
|
[//]: # " color2-.->color3"
|
||||||
|
[//]: # " linkStyle 12 stroke:#0000ff,stroke-width:4px;"
|
||||||
|
[//]: # " text_width-.->layout_width3(layout width)"
|
||||||
|
[//]: # " linkStyle 13 stroke:#ff5500,stroke-width:4px;"
|
||||||
|
[//]: # " state3-->layout_width3"
|
||||||
|
[//]: # " layout_width3-.->layout_width2"
|
||||||
|
[//]: # " linkStyle 15 stroke:#00aa00,stroke-width:4px;"
|
||||||
|
[//]: # " end"
|
||||||
|
[//]: # " end"
|
||||||
|
|
||||||
|
To help in building a Dom, native core provides four traits: State, ChildDepState, ParentDepState, and NodeDepState and a RealDom struct. The ChildDepState, ParentDepState, and NodeDepState provide a way to discribe how some information in a node relates to that of its relatives. By providing how to build a single node from its relations, native-core will derive a way to update the state of all nodes for you with ```#[derive(State)]```. Once you have a state you can provide it as a generic to RealDom. RealDom provides all of the methods to interact and update your new dom.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use dioxus_native_core::node_ref::*;
|
use dioxus_native_core::node_ref::*;
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ mod util {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr_to_single_string(expr: &syn::Expr) -> Option<String> {
|
pub fn expr_to_single_string(expr: &syn::Expr) -> Option<String> {
|
||||||
if let syn::Expr::Path(path) = &*expr {
|
if let syn::Expr::Path(path) = expr {
|
||||||
path_to_single_string(&path.path)
|
path_to_single_string(&path.path)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -779,13 +779,12 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
// NOTE: both auto_into and strip_option affect `arg_type` and `arg_expr`, but the order of
|
// NOTE: both auto_into and strip_option affect `arg_type` and `arg_expr`, but the order of
|
||||||
// nesting is different so we have to do this little dance.
|
// nesting is different so we have to do this little dance.
|
||||||
let arg_type = if field.builder_attr.strip_option {
|
let arg_type = if field.builder_attr.strip_option {
|
||||||
let internal_type = field.type_from_inside_option(false).ok_or_else(|| {
|
field.type_from_inside_option(false).ok_or_else(|| {
|
||||||
Error::new_spanned(
|
Error::new_spanned(
|
||||||
&field_type,
|
&field_type,
|
||||||
"can't `strip_option` - field is not `Option<...>`",
|
"can't `strip_option` - field is not `Option<...>`",
|
||||||
)
|
)
|
||||||
})?;
|
})?
|
||||||
internal_type
|
|
||||||
} else {
|
} else {
|
||||||
field_type
|
field_type
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ serde = "1.0.136"
|
|||||||
serde_json = "1.0.79"
|
serde_json = "1.0.79"
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.30"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
wry = { version = "0.16.0" }
|
wry = { version = "0.19.0" }
|
||||||
futures-channel = "0.3.21"
|
futures-channel = "0.3.21"
|
||||||
tokio = { version = "1.16.1", features = [
|
tokio = { version = "1.16.1", features = [
|
||||||
"sync",
|
"sync",
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ use std::sync::Arc;
|
|||||||
use dioxus_core::{ElementId, EventPriority, UserEvent};
|
use dioxus_core::{ElementId, EventPriority, UserEvent};
|
||||||
use dioxus_html::event_bubbles;
|
use dioxus_html::event_bubbles;
|
||||||
use dioxus_html::on::*;
|
use dioxus_html::on::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub(crate) struct IpcMessage {
|
pub(crate) struct IpcMessage {
|
||||||
method: String,
|
method: String,
|
||||||
params: serde_json::Value,
|
params: serde_json::Value,
|
||||||
@@ -33,7 +34,7 @@ pub(crate) fn parse_ipc_message(payload: &str) -> Option<IpcMessage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
struct ImEvent {
|
struct ImEvent {
|
||||||
event: String,
|
event: String,
|
||||||
mounted_dom_id: u64,
|
mounted_dom_id: u64,
|
||||||
|
|||||||
@@ -213,13 +213,6 @@ pub fn launch_with_props<P: 'static + Send>(
|
|||||||
} => match event {
|
} => match event {
|
||||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||||
WindowEvent::Destroyed { .. } => desktop.close_window(window_id, control_flow),
|
WindowEvent::Destroyed { .. } => desktop.close_window(window_id, control_flow),
|
||||||
|
|
||||||
WindowEvent::Resized(_) | WindowEvent::Moved(_) => {
|
|
||||||
if let Some(view) = desktop.webviews.get_mut(&window_id) {
|
|
||||||
let _ = view.resize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -937,7 +937,7 @@ pub mod on {
|
|||||||
feature = "serialize",
|
feature = "serialize",
|
||||||
derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)
|
derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)
|
||||||
)]
|
)]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum KeyCode {
|
pub enum KeyCode {
|
||||||
// That key has no keycode, = 0
|
// That key has no keycode, = 0
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ extern crate proc_macro;
|
|||||||
|
|
||||||
mod sorted_slice;
|
mod sorted_slice;
|
||||||
|
|
||||||
use dioxus_native_core::state::MemberId;
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::format_ident;
|
use quote::{quote, ToTokens};
|
||||||
use quote::{quote, ToTokens, __private::Span};
|
|
||||||
use sorted_slice::StrSlice;
|
use sorted_slice::StrSlice;
|
||||||
|
use syn::parenthesized;
|
||||||
|
use syn::parse::ParseBuffer;
|
||||||
|
use syn::punctuated::Punctuated;
|
||||||
use syn::{
|
use syn::{
|
||||||
self,
|
self,
|
||||||
parse::{Parse, ParseStream, Result},
|
parse::{Parse, ParseStream, Result},
|
||||||
@@ -51,228 +52,99 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
let strct = Struct::new(type_name.clone(), &fields);
|
let strct = Struct::new(type_name.clone(), &fields);
|
||||||
match StateStruct::parse(&fields, &strct) {
|
match StateStruct::parse(&fields, &strct) {
|
||||||
Ok(state_strct) => {
|
Ok(state_strct) => {
|
||||||
let node_dep_state_fields = state_strct
|
let members: Vec<_> = state_strct
|
||||||
.state_members
|
.state_members
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|f| f.dep_kind == DepKind::Node)
|
.map(|m| &m.mem.ident)
|
||||||
.map(|f| f.reduce_self());
|
|
||||||
let child_dep_state_fields = state_strct
|
|
||||||
.state_members
|
|
||||||
.iter()
|
|
||||||
.filter(|f| f.dep_kind == DepKind::Child)
|
|
||||||
.map(|f| f.reduce_self());
|
|
||||||
let parent_dep_state_fields = state_strct
|
|
||||||
.state_members
|
|
||||||
.iter()
|
|
||||||
.filter(|f| f.dep_kind == DepKind::Parent)
|
|
||||||
.map(|f| f.reduce_self());
|
|
||||||
|
|
||||||
let node_iter = state_strct
|
|
||||||
.state_members
|
|
||||||
.iter()
|
|
||||||
.filter(|m| m.dep_kind == DepKind::Node);
|
|
||||||
let node_ids = node_iter.clone().map(|m| m.member_id.0);
|
|
||||||
let node_ids_clone = node_ids.clone();
|
|
||||||
let node_types = node_iter.map(|f| &f.mem.ty);
|
|
||||||
|
|
||||||
let child_iter = state_strct
|
|
||||||
.state_members
|
|
||||||
.iter()
|
|
||||||
.filter(|m| m.dep_kind == DepKind::Child);
|
|
||||||
let child_ids = child_iter.clone().map(|m| m.member_id.0);
|
|
||||||
let child_ids_clone = child_ids.clone();
|
|
||||||
let child_types = child_iter.map(|f| &f.mem.ty);
|
|
||||||
|
|
||||||
let parent_iter = state_strct
|
|
||||||
.state_members
|
|
||||||
.iter()
|
|
||||||
.filter(|m| m.dep_kind == DepKind::Parent);
|
|
||||||
let parent_ids = parent_iter.clone().map(|m| m.member_id.0);
|
|
||||||
let parent_ids_clone = parent_ids.clone();
|
|
||||||
let parent_types = parent_iter.map(|f| &f.mem.ty);
|
|
||||||
|
|
||||||
let type_name_str = type_name.to_string();
|
|
||||||
|
|
||||||
let child_states = &state_strct.child_states;
|
|
||||||
|
|
||||||
let member_size = state_strct.state_members.len();
|
|
||||||
|
|
||||||
let child_state_ty = child_states.iter().map(|m| &m.ty);
|
|
||||||
let child_state_idents: Vec<_> = child_states.iter().map(|m| &m.ident).collect();
|
|
||||||
let sum_const_declarations = child_state_ty.clone().enumerate().map(|(i, ty)| {
|
|
||||||
let ident = format_ident!("__{}_SUM_{}", i, type_name.to_string());
|
|
||||||
let ident_minus = format_ident!("__{}_SUM_{}_minus", i, type_name.to_string());
|
|
||||||
if i == 0 {
|
|
||||||
quote!(const #ident_minus: usize = #member_size + #ty::SIZE - 1;
|
|
||||||
const #ident: usize = #member_size + #ty::SIZE;)
|
|
||||||
} else {
|
|
||||||
let prev_ident = format_ident!("__{}_SUM_{}", i - 1, type_name.to_string());
|
|
||||||
quote!(const #ident_minus: usize = #prev_ident + #ty::SIZE - 1;
|
|
||||||
const #ident: usize = #prev_ident + #ty::SIZE;)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let sum_idents: Vec<_> = std::iter::once(quote!(#member_size))
|
|
||||||
.chain((0..child_states.len()).map(|i| {
|
|
||||||
let ident = format_ident!("__{}_SUM_{}", i, type_name.to_string());
|
|
||||||
quote!(#ident)
|
|
||||||
}))
|
|
||||||
.collect();
|
.collect();
|
||||||
|
let member_types = state_strct.state_members.iter().map(|m| &m.mem.ty);
|
||||||
|
let resolve_members = state_strct
|
||||||
|
.state_members
|
||||||
|
.iter()
|
||||||
|
.map(|m| state_strct.resolve(m));
|
||||||
|
|
||||||
let child_state_ranges: Vec<_> = (0..child_state_ty.len())
|
let child_types = state_strct.child_states.iter().map(|s| &s.ty);
|
||||||
.map(|i| {
|
let child_members = state_strct.child_states.iter().map(|s| &s.ident);
|
||||||
let current = format_ident!("__{}_SUM_{}_minus", i, type_name.to_string());
|
|
||||||
let previous = if i == 0 {
|
|
||||||
quote!(#member_size)
|
|
||||||
} else {
|
|
||||||
let ident = format_ident!("__{}_SUM_{}", i - 1, type_name.to_string());
|
|
||||||
quote!(#ident)
|
|
||||||
};
|
|
||||||
quote!(#previous..=#current)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let gen = quote! {
|
let gen = quote! {
|
||||||
#(
|
impl State for #type_name {
|
||||||
#sum_const_declarations
|
fn update<'a, T: dioxus_native_core::traversable::Traversable<Node = Self, Id = dioxus_core::ElementId>>(
|
||||||
)*
|
dirty: &[(dioxus_core::ElementId, dioxus_native_core::node_ref::NodeMask)],
|
||||||
impl State for #type_name{
|
state_tree: &'a mut T,
|
||||||
const SIZE: usize = #member_size #( + #child_state_ty::SIZE)*;
|
|
||||||
|
|
||||||
fn update_node_dep_state<'a>(
|
|
||||||
&'a mut self,
|
|
||||||
ty: dioxus_native_core::state::MemberId,
|
|
||||||
node: &'a dioxus_core::VNode<'a>,
|
|
||||||
vdom: &'a dioxus_core::VirtualDom,
|
vdom: &'a dioxus_core::VirtualDom,
|
||||||
ctx: &anymap::AnyMap,
|
ctx: &anymap::AnyMap,
|
||||||
) -> Option<dioxus_native_core::state::NodeStatesChanged>{
|
) -> fxhash::FxHashSet<dioxus_core::ElementId>{
|
||||||
use dioxus_native_core::state::NodeDepState as _;
|
#[derive(Eq, PartialEq)]
|
||||||
use dioxus_native_core::state::State as _;
|
struct HeightOrdering {
|
||||||
match ty.0{
|
height: u16,
|
||||||
#(
|
id: dioxus_core::ElementId,
|
||||||
#node_ids => #node_dep_state_fields,
|
|
||||||
)*
|
|
||||||
#(
|
|
||||||
#child_state_ranges => {
|
|
||||||
self.#child_state_idents.update_node_dep_state(
|
|
||||||
ty - #sum_idents,
|
|
||||||
node,
|
|
||||||
vdom,
|
|
||||||
ctx,
|
|
||||||
).map(|mut changed|{
|
|
||||||
for id in &mut changed.node_dep{
|
|
||||||
*id += #sum_idents;
|
|
||||||
}
|
|
||||||
changed
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
_ => panic!("{:?} not in {}", ty, #type_name_str),
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn update_parent_dep_state<'a>(
|
impl HeightOrdering {
|
||||||
&'a mut self,
|
fn new(height: u16, id: dioxus_core::ElementId) -> Self {
|
||||||
ty: dioxus_native_core::state::MemberId,
|
HeightOrdering {
|
||||||
node: &'a dioxus_core::VNode<'a>,
|
height,
|
||||||
vdom: &'a dioxus_core::VirtualDom,
|
id,
|
||||||
parent: Option<&Self>,
|
|
||||||
ctx: &anymap::AnyMap,
|
|
||||||
) -> Option<dioxus_native_core::state::ParentStatesChanged>{
|
|
||||||
use dioxus_native_core::state::ParentDepState as _;
|
|
||||||
match ty.0{
|
|
||||||
#(
|
|
||||||
#parent_ids => #parent_dep_state_fields,
|
|
||||||
)*
|
|
||||||
#(
|
|
||||||
#child_state_ranges => {
|
|
||||||
self.#child_state_idents.update_parent_dep_state(
|
|
||||||
ty - #sum_idents,
|
|
||||||
node,
|
|
||||||
vdom,
|
|
||||||
parent.map(|p| &p.#child_state_idents),
|
|
||||||
ctx,
|
|
||||||
).map(|mut changed|{
|
|
||||||
for id in &mut changed.node_dep{
|
|
||||||
*id += #sum_idents;
|
|
||||||
}
|
|
||||||
for id in &mut changed.parent_dep{
|
|
||||||
*id += #sum_idents;
|
|
||||||
}
|
|
||||||
changed
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
)*
|
}
|
||||||
_ => panic!("{:?} not in {}", ty, #type_name_str),
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn update_child_dep_state<'a>(
|
impl Ord for HeightOrdering {
|
||||||
&'a mut self,
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
ty: dioxus_native_core::state::MemberId,
|
self.height.cmp(&other.height).then(self.id.0.cmp(&other.id.0))
|
||||||
node: &'a dioxus_core::VNode<'a>,
|
}
|
||||||
vdom: &'a dioxus_core::VirtualDom,
|
|
||||||
children: &[&Self],
|
|
||||||
ctx: &anymap::AnyMap,
|
|
||||||
) -> Option<dioxus_native_core::state::ChildStatesChanged>{
|
|
||||||
use dioxus_native_core::state::ChildDepState as _;
|
|
||||||
match ty.0{
|
|
||||||
#(
|
|
||||||
#child_ids => #child_dep_state_fields,
|
|
||||||
)*
|
|
||||||
#(
|
|
||||||
#child_state_ranges => {
|
|
||||||
self.#child_state_idents.update_child_dep_state(
|
|
||||||
ty - #sum_idents,
|
|
||||||
node,
|
|
||||||
vdom,
|
|
||||||
&children.iter().map(|p| &p.#child_state_idents).collect::<Vec<_>>(),
|
|
||||||
ctx,
|
|
||||||
).map(|mut changed|{
|
|
||||||
for id in &mut changed.node_dep{
|
|
||||||
*id += #sum_idents;
|
|
||||||
}
|
|
||||||
for id in &mut changed.child_dep{
|
|
||||||
*id += #sum_idents;
|
|
||||||
}
|
|
||||||
changed
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
_ => panic!("{:?} not in {}", ty, #type_name_str),
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn child_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
|
impl PartialOrd for HeightOrdering {
|
||||||
let mut dep_types = Vec::new();
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
#(if #child_types::NODE_MASK.overlaps(mask) {
|
Some(self.cmp(&other))
|
||||||
dep_types.push(dioxus_native_core::state::MemberId(#child_ids_clone));
|
}
|
||||||
})*
|
}
|
||||||
#(
|
|
||||||
dep_types.extend(self.#child_state_idents.child_dep_types(mask).into_iter().map(|id| id + #sum_idents));
|
|
||||||
)*
|
|
||||||
dep_types
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parent_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
|
struct MembersDirty {
|
||||||
let mut dep_types = Vec::new();
|
#(#members: bool, )*
|
||||||
#(if #parent_types::NODE_MASK.overlaps(mask) {
|
}
|
||||||
dep_types.push(dioxus_native_core::state::MemberId(#parent_ids_clone));
|
|
||||||
})*
|
impl MembersDirty {
|
||||||
#(
|
fn new() -> Self {
|
||||||
dep_types.extend(self.#child_state_idents.parent_dep_types(mask).into_iter().map(|id| id + #sum_idents));
|
Self {#(#members: false),*}
|
||||||
)*
|
}
|
||||||
dep_types
|
|
||||||
}
|
fn any(&self) -> bool {
|
||||||
|
#(self.#members || )* false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dirty_elements = fxhash::FxHashSet::default();
|
||||||
|
// the states of any elements that are dirty
|
||||||
|
let mut states = fxhash::FxHashMap::default();
|
||||||
|
|
||||||
|
for (id, mask) in dirty {
|
||||||
|
let members_dirty = MembersDirty {
|
||||||
|
#(#members: #member_types::NODE_MASK.overlaps(mask),)*
|
||||||
|
};
|
||||||
|
if members_dirty.any(){
|
||||||
|
states.insert(*id, members_dirty);
|
||||||
|
}
|
||||||
|
dirty_elements.insert(*id);
|
||||||
|
}
|
||||||
|
|
||||||
fn node_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
|
|
||||||
let mut dep_types = Vec::new();
|
|
||||||
#(if #node_types::NODE_MASK.overlaps(mask) {
|
|
||||||
dep_types.push(dioxus_native_core::state::MemberId(#node_ids_clone));
|
|
||||||
})*
|
|
||||||
#(
|
#(
|
||||||
dep_types.extend(self.#child_state_idents.node_dep_types(mask).into_iter().map(|id| id + #sum_idents));
|
#resolve_members;
|
||||||
)*
|
)*
|
||||||
dep_types
|
|
||||||
|
#(
|
||||||
|
dirty_elements.extend(
|
||||||
|
<#child_types as dioxus_native_core::state::State>::update(
|
||||||
|
dirty,
|
||||||
|
&mut state_tree.map(|n| &n.#child_members, |n| &mut n.#child_members),
|
||||||
|
vdom,
|
||||||
|
ctx,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
)*
|
||||||
|
|
||||||
|
dirty_elements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -282,6 +154,12 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Depenadants<'a> {
|
||||||
|
node: Vec<&'a Member>,
|
||||||
|
child: Vec<&'a Member>,
|
||||||
|
parent: Vec<&'a Member>,
|
||||||
|
}
|
||||||
|
|
||||||
struct Struct {
|
struct Struct {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
members: Vec<Member>,
|
members: Vec<Member>,
|
||||||
@@ -302,7 +180,7 @@ struct StateStruct<'a> {
|
|||||||
impl<'a> StateStruct<'a> {
|
impl<'a> StateStruct<'a> {
|
||||||
fn parse(fields: &[&'a Field], strct: &'a Struct) -> Result<Self> {
|
fn parse(fields: &[&'a Field], strct: &'a Struct) -> Result<Self> {
|
||||||
let mut parse_err = Ok(());
|
let mut parse_err = Ok(());
|
||||||
let state_members = strct
|
let mut unordered_state_members: Vec<_> = strct
|
||||||
.members
|
.members
|
||||||
.iter()
|
.iter()
|
||||||
.zip(fields.iter())
|
.zip(fields.iter())
|
||||||
@@ -312,7 +190,44 @@ impl<'a> StateStruct<'a> {
|
|||||||
parse_err = Err(err);
|
parse_err = Err(err);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.collect();
|
||||||
|
parse_err?;
|
||||||
|
|
||||||
|
let mut state_members = Vec::new();
|
||||||
|
while !unordered_state_members.is_empty() {
|
||||||
|
let mut resolved = false;
|
||||||
|
for i in 0..unordered_state_members.len() {
|
||||||
|
let mem = &mut unordered_state_members[i];
|
||||||
|
if mem.dep_mems.iter().all(|(dep, resolved)| {
|
||||||
|
*resolved || (*dep == mem.mem && mem.dep_kind != DepKind::Node)
|
||||||
|
}) {
|
||||||
|
let mem = unordered_state_members.remove(i);
|
||||||
|
// mark any dependancy that depends on this member as resolved
|
||||||
|
for member in unordered_state_members.iter_mut() {
|
||||||
|
for (dep, resolved) in &mut member.dep_mems {
|
||||||
|
*resolved |= *dep == mem.mem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state_members.push(mem);
|
||||||
|
resolved = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !resolved {
|
||||||
|
return Err(Error::new(
|
||||||
|
strct.name.span(),
|
||||||
|
format!(
|
||||||
|
"{} has circular dependacy in {:?}",
|
||||||
|
strct.name,
|
||||||
|
unordered_state_members
|
||||||
|
.iter()
|
||||||
|
.map(|m| format!("{}", &m.mem.ident))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let child_states = strct
|
let child_states = strct
|
||||||
.members
|
.members
|
||||||
@@ -328,177 +243,234 @@ impl<'a> StateStruct<'a> {
|
|||||||
})
|
})
|
||||||
.map(|(m, _)| m);
|
.map(|(m, _)| m);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct DepNode<'a> {
|
|
||||||
state_mem: StateMember<'a>,
|
|
||||||
depandants: Vec<DepNode<'a>>,
|
|
||||||
}
|
|
||||||
impl<'a> DepNode<'a> {
|
|
||||||
fn new(state_mem: StateMember<'a>) -> Self {
|
|
||||||
Self {
|
|
||||||
state_mem,
|
|
||||||
depandants: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// flattens the node in pre order
|
|
||||||
fn flatten(self) -> Vec<StateMember<'a>> {
|
|
||||||
let DepNode {
|
|
||||||
state_mem,
|
|
||||||
depandants,
|
|
||||||
} = self;
|
|
||||||
let mut flat = vec![state_mem];
|
|
||||||
for d in depandants {
|
|
||||||
flat.append(&mut d.flatten());
|
|
||||||
}
|
|
||||||
flat
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_ids(&mut self, current_id: &mut usize) {
|
|
||||||
self.state_mem.member_id = dioxus_native_core::state::MemberId(*current_id);
|
|
||||||
// if the node depends on itself, we need to add the dependency seperately
|
|
||||||
if let Some(dep) = self.state_mem.dep_mem {
|
|
||||||
if dep == self.state_mem.mem {
|
|
||||||
self.state_mem
|
|
||||||
.dependants
|
|
||||||
.push((MemberId(*current_id), self.state_mem.dep_kind.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*current_id += 1;
|
|
||||||
for d in &mut self.depandants {
|
|
||||||
self.state_mem
|
|
||||||
.dependants
|
|
||||||
.push((MemberId(*current_id), d.state_mem.dep_kind.clone()));
|
|
||||||
d.set_ids(current_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains_member(&self, member: &Member) -> bool {
|
|
||||||
if self.state_mem.mem == member {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
self.depandants.iter().any(|d| d.contains_member(member))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if there are any mixed child/parent dependancies
|
|
||||||
fn check(&self) -> Option<Error> {
|
|
||||||
self.kind().err()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kind(&self) -> Result<&DepKind> {
|
|
||||||
fn reduce_kind<'a>(dk1: &'a DepKind, dk2: &'a DepKind) -> Result<&'a DepKind> {
|
|
||||||
match (dk1, dk2) {
|
|
||||||
(DepKind::Child, DepKind::Parent) | (DepKind::Parent, DepKind::Child) => {
|
|
||||||
Err(Error::new(
|
|
||||||
Span::call_site(),
|
|
||||||
"There is a ChildDepState that depends on a ParentDepState",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
// node dep state takes the lowest priority
|
|
||||||
(DepKind::Node, important) | (important, DepKind::Node) => Ok(important),
|
|
||||||
// they are the same
|
|
||||||
(fst, _) => Ok(fst),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reduce_kind(
|
|
||||||
self.depandants
|
|
||||||
.iter()
|
|
||||||
.try_fold(&DepKind::Node, |dk1, dk2| reduce_kind(dk1, dk2.kind()?))?,
|
|
||||||
&self.state_mem.dep_kind,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_dependant(&mut self, other: DepNode<'a>) -> bool {
|
|
||||||
let dep = other.state_mem.dep_mem.unwrap();
|
|
||||||
if self.contains_member(dep) {
|
|
||||||
if self.state_mem.mem == dep {
|
|
||||||
self.depandants.push(other);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
self.depandants
|
|
||||||
.iter_mut()
|
|
||||||
.find(|d| d.contains_member(dep))
|
|
||||||
.unwrap()
|
|
||||||
.insert_dependant(other)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// members need to be sorted so that members are updated after the members they depend on
|
// members need to be sorted so that members are updated after the members they depend on
|
||||||
let mut roots: Vec<DepNode> = vec![];
|
Ok(Self {
|
||||||
for m in state_members {
|
state_members,
|
||||||
if let Some(dep) = m.dep_mem {
|
child_states: child_states.collect(),
|
||||||
let root_depends_on = roots
|
})
|
||||||
.iter()
|
}
|
||||||
.filter_map(|m| m.state_mem.dep_mem)
|
|
||||||
.any(|d| m.mem == d);
|
|
||||||
|
|
||||||
if let Some(r) = roots.iter_mut().find(|r| r.contains_member(dep)) {
|
fn get_depenadants(&self, mem: &Member) -> Depenadants {
|
||||||
let new = DepNode::new(m);
|
let mut dependants = Depenadants {
|
||||||
if root_depends_on {
|
node: Vec::new(),
|
||||||
return Err(Error::new(
|
child: Vec::new(),
|
||||||
new.state_mem.mem.ident.span(),
|
parent: Vec::new(),
|
||||||
format!("{} has a circular dependancy", new.state_mem.mem.ident),
|
};
|
||||||
));
|
for member in &self.state_members {
|
||||||
|
for (dep, _) in &member.dep_mems {
|
||||||
|
if *dep == mem {
|
||||||
|
match member.dep_kind {
|
||||||
|
DepKind::Node => dependants.node.push(member.mem),
|
||||||
|
DepKind::Child => dependants.child.push(member.mem),
|
||||||
|
DepKind::Parent => dependants.parent.push(member.mem),
|
||||||
}
|
}
|
||||||
// return Err(Error::new(new.state_mem.mem.ident.span(), "stuff"));
|
|
||||||
r.insert_dependant(new);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut new = DepNode::new(m);
|
|
||||||
let mut i = 0;
|
|
||||||
while i < roots.len() {
|
|
||||||
if roots[i].state_mem.dep_mem == Some(new.state_mem.mem) {
|
|
||||||
let child = roots.remove(i);
|
|
||||||
new.insert_dependant(child);
|
|
||||||
} else {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
roots.push(new);
|
|
||||||
}
|
}
|
||||||
parse_err?;
|
dependants
|
||||||
let mut current_id = 0;
|
}
|
||||||
for r in &mut roots {
|
|
||||||
r.set_ids(&mut current_id);
|
|
||||||
}
|
|
||||||
if let Some(err) = roots.iter().find_map(DepNode::check) {
|
|
||||||
Err(err)
|
|
||||||
} else {
|
|
||||||
let state_members: Vec<_> = roots
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|r| r.flatten().into_iter())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(Self {
|
fn update_dependants(&self, mem: &Member) -> impl ToTokens {
|
||||||
state_members,
|
let dep = self.get_depenadants(mem);
|
||||||
child_states: child_states.collect(),
|
let update_child_dependants = if dep.child.is_empty() {
|
||||||
})
|
quote!()
|
||||||
|
} else {
|
||||||
|
let insert = dep.child.iter().map(|d|{
|
||||||
|
if *d == mem {
|
||||||
|
quote! {
|
||||||
|
let seeking = HeightOrdering::new(state_tree.height(parent_id).unwrap(), parent_id);
|
||||||
|
if let Err(idx) = resolution_order
|
||||||
|
.binary_search_by(|ordering| ordering.cmp(&seeking).reverse()){
|
||||||
|
resolution_order.insert(
|
||||||
|
idx,
|
||||||
|
seeking,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let update: Vec<_> = dep
|
||||||
|
.child
|
||||||
|
.iter()
|
||||||
|
.map(|d| {
|
||||||
|
let ident = &d.ident;
|
||||||
|
quote! {
|
||||||
|
dirty.#ident = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
quote! {
|
||||||
|
if let Some(parent_id) = state_tree.parent(id) {
|
||||||
|
#(#insert)*
|
||||||
|
if let Some(dirty) = states.get_mut(&parent_id) {
|
||||||
|
#(#update)*
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let mut dirty = MembersDirty::new();
|
||||||
|
#(#update)*
|
||||||
|
states.insert(parent_id, dirty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let node_dependants: Vec<_> = dep.node.iter().map(|d| &d.ident).collect();
|
||||||
|
let update_node_dependants = quote! {#(members_dirty.#node_dependants = true;)*};
|
||||||
|
let update_parent_dependants = if dep.parent.is_empty() {
|
||||||
|
quote!()
|
||||||
|
} else {
|
||||||
|
let insert = dep.parent.iter().map(|d| {
|
||||||
|
if *d == mem {
|
||||||
|
quote! {
|
||||||
|
let seeking = HeightOrdering::new(state_tree.height(*child_id).unwrap(), *child_id);
|
||||||
|
if let Err(idx) = resolution_order
|
||||||
|
.binary_search(&seeking){
|
||||||
|
resolution_order.insert(
|
||||||
|
idx,
|
||||||
|
seeking,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let update: Vec<_> = dep
|
||||||
|
.parent
|
||||||
|
.iter()
|
||||||
|
.map(|d| {
|
||||||
|
let ident = &d.ident;
|
||||||
|
quote! {
|
||||||
|
dirty.#ident = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
quote! {
|
||||||
|
for child_id in state_tree.children(id) {
|
||||||
|
#(#insert)*
|
||||||
|
if let Some(dirty) = states.get_mut(&child_id) {
|
||||||
|
#(#update)*
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let mut dirty = MembersDirty::new();
|
||||||
|
#(#update)*
|
||||||
|
states.insert(*child_id, dirty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#update_node_dependants
|
||||||
|
#update_child_dependants
|
||||||
|
#update_parent_dependants
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(&self, mem: &StateMember) -> impl ToTokens {
|
||||||
|
let reduce_member = mem.reduce_self();
|
||||||
|
let update_dependant = self.update_dependants(mem.mem);
|
||||||
|
let member = &mem.mem.ident;
|
||||||
|
|
||||||
|
match mem.dep_kind {
|
||||||
|
DepKind::Parent => {
|
||||||
|
quote! {
|
||||||
|
// resolve parent dependant state
|
||||||
|
let mut resolution_order = states.keys().copied().map(|id| HeightOrdering::new(state_tree.height(id).unwrap(), id)).collect::<Vec<_>>();
|
||||||
|
resolution_order.sort();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < resolution_order.len(){
|
||||||
|
let id = resolution_order[i].id;
|
||||||
|
let vnode = vdom.get_element(id).unwrap();
|
||||||
|
let members_dirty = states.get_mut(&id).unwrap();
|
||||||
|
let (current_state, parent) = state_tree.get_node_parent_mut(id);
|
||||||
|
let current_state = current_state.unwrap();
|
||||||
|
if members_dirty.#member && #reduce_member {
|
||||||
|
dirty_elements.insert(id);
|
||||||
|
#update_dependant
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DepKind::Child => {
|
||||||
|
quote! {
|
||||||
|
// resolve child dependant state
|
||||||
|
let mut resolution_order = states.keys().copied().map(|id| HeightOrdering::new(state_tree.height(id).unwrap(), id)).collect::<Vec<_>>();
|
||||||
|
resolution_order.sort_by(|height_ordering1, height_ordering2| {
|
||||||
|
height_ordering1.cmp(&height_ordering2).reverse()
|
||||||
|
});
|
||||||
|
let mut i = 0;
|
||||||
|
while i < resolution_order.len(){
|
||||||
|
let id = resolution_order[i].id;
|
||||||
|
let vnode = vdom.get_element(id).unwrap();
|
||||||
|
let members_dirty = states.get_mut(&id).unwrap();
|
||||||
|
let (current_state, children) = state_tree.get_node_children_mut(id);
|
||||||
|
let current_state = current_state.unwrap();
|
||||||
|
if members_dirty.#member && #reduce_member {
|
||||||
|
dirty_elements.insert(id);
|
||||||
|
#update_dependant
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DepKind::Node => {
|
||||||
|
quote! {
|
||||||
|
// resolve node dependant state
|
||||||
|
let mut resolution_order = states.keys().copied().collect::<Vec<_>>();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < resolution_order.len(){
|
||||||
|
let id = resolution_order[i];
|
||||||
|
let vnode = vdom.get_element(id).unwrap();
|
||||||
|
let members_dirty = states.get_mut(&id).unwrap();
|
||||||
|
let current_state = state_tree.get_mut(id).unwrap();
|
||||||
|
if members_dirty.#member && #reduce_member {
|
||||||
|
dirty_elements.insert(id);
|
||||||
|
#update_dependant
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_parenthesized(input: ParseStream) -> Result<ParseBuffer> {
|
||||||
|
let inside;
|
||||||
|
parenthesized!(inside in input);
|
||||||
|
Ok(inside)
|
||||||
|
}
|
||||||
|
|
||||||
struct Dependancy {
|
struct Dependancy {
|
||||||
ctx_ty: Option<Type>,
|
ctx_ty: Option<Type>,
|
||||||
dep: Option<Ident>,
|
deps: Vec<Ident>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for Dependancy {
|
impl Parse for Dependancy {
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
let dep = input
|
let deps: Option<Punctuated<Ident, Token![,]>> = {
|
||||||
.parse()
|
try_parenthesized(input)
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|i: &Ident| format!("{}", i) != "NONE");
|
.and_then(|inside| inside.parse_terminated(Ident::parse).ok())
|
||||||
|
};
|
||||||
|
let deps: Vec<_> = deps
|
||||||
|
.map(|deps| deps.into_iter().collect())
|
||||||
|
.or_else(|| {
|
||||||
|
input
|
||||||
|
.parse::<Ident>()
|
||||||
|
.ok()
|
||||||
|
.filter(|i: &Ident| format!("{}", i) != "NONE")
|
||||||
|
.map(|i| vec![i])
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
let comma: Option<Token![,]> = input.parse().ok();
|
let comma: Option<Token![,]> = input.parse().ok();
|
||||||
let ctx_ty = input.parse().ok();
|
let ctx_ty = input.parse().ok();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ctx_ty: comma.and(ctx_ty),
|
ctx_ty: comma.and(ctx_ty),
|
||||||
dep,
|
deps,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -522,11 +494,9 @@ impl Member {
|
|||||||
struct StateMember<'a> {
|
struct StateMember<'a> {
|
||||||
mem: &'a Member,
|
mem: &'a Member,
|
||||||
dep_kind: DepKind,
|
dep_kind: DepKind,
|
||||||
dep_mem: Option<&'a Member>,
|
// the depenancy and if it is satified
|
||||||
|
dep_mems: Vec<(&'a Member, bool)>,
|
||||||
ctx_ty: Option<Type>,
|
ctx_ty: Option<Type>,
|
||||||
dependants: Vec<(dioxus_native_core::state::MemberId, DepKind)>,
|
|
||||||
// This is just the index of the final order of the struct it is used to communicate which parts need updated and what order to update them in.
|
|
||||||
member_id: dioxus_native_core::state::MemberId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StateMember<'a> {
|
impl<'a> StateMember<'a> {
|
||||||
@@ -548,26 +518,26 @@ impl<'a> StateMember<'a> {
|
|||||||
})?;
|
})?;
|
||||||
match a.parse_args::<Dependancy>() {
|
match a.parse_args::<Dependancy>() {
|
||||||
Ok(dependancy) => {
|
Ok(dependancy) => {
|
||||||
let dep_mem = if let Some(name) = &dependancy.dep {
|
let dep_mems = dependancy
|
||||||
if let Some(found) = parent.members.iter().find(|m| &m.ident == name) {
|
.deps
|
||||||
Some(found)
|
.iter()
|
||||||
} else {
|
.filter_map(|name| {
|
||||||
err = Err(Error::new(
|
if let Some(found) = parent.members.iter().find(|m| &m.ident == name) {
|
||||||
name.span(),
|
Some((found, false))
|
||||||
format!("{} not found in {}", name, parent.name),
|
} else {
|
||||||
));
|
err = Err(Error::new(
|
||||||
None
|
name.span(),
|
||||||
}
|
format!("{} not found in {}", name, parent.name),
|
||||||
} else {
|
));
|
||||||
None
|
None
|
||||||
};
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
Some(Self {
|
Some(Self {
|
||||||
mem,
|
mem,
|
||||||
dep_kind,
|
dep_kind,
|
||||||
dep_mem,
|
dep_mems,
|
||||||
ctx_ty: dependancy.ctx_ty,
|
ctx_ty: dependancy.ctx_ty,
|
||||||
dependants: Vec::new(),
|
|
||||||
member_id: dioxus_native_core::state::MemberId(0),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -592,111 +562,26 @@ impl<'a> StateMember<'a> {
|
|||||||
} else {
|
} else {
|
||||||
quote! {&()}
|
quote! {&()}
|
||||||
};
|
};
|
||||||
let states_changed = {
|
|
||||||
let child_dep = self
|
|
||||||
.dependants
|
|
||||||
.iter()
|
|
||||||
.filter(|(_, kind)| kind == &DepKind::Child)
|
|
||||||
.map(|(id, _)| id.0);
|
|
||||||
let parent_dep = self
|
|
||||||
.dependants
|
|
||||||
.iter()
|
|
||||||
.filter(|(_, kind)| kind == &DepKind::Parent)
|
|
||||||
.map(|(id, _)| id.0);
|
|
||||||
let node_dep = self
|
|
||||||
.dependants
|
|
||||||
.iter()
|
|
||||||
.filter(|(_, kind)| kind == &DepKind::Node)
|
|
||||||
.map(|(id, _)| id.0);
|
|
||||||
match self.dep_kind {
|
|
||||||
DepKind::Node => {
|
|
||||||
quote! {
|
|
||||||
dioxus_native_core::state::NodeStatesChanged{
|
|
||||||
node_dep: vec![#(dioxus_native_core::state::MemberId(#node_dep), )*],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DepKind::Child => {
|
|
||||||
quote! {
|
|
||||||
dioxus_native_core::state::ChildStatesChanged{
|
|
||||||
node_dep: vec![#(dioxus_native_core::state::MemberId(#node_dep), )*],
|
|
||||||
child_dep: vec![#(dioxus_native_core::state::MemberId(#child_dep), )*],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DepKind::Parent => {
|
|
||||||
quote! {
|
|
||||||
dioxus_native_core::state::ParentStatesChanged{
|
|
||||||
node_dep: vec![#(dioxus_native_core::state::MemberId(#node_dep), )*],
|
|
||||||
parent_dep: vec![#(dioxus_native_core::state::MemberId(#parent_dep), )*],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let ty = &self.mem.ty;
|
let ty = &self.mem.ty;
|
||||||
let node_view =
|
let node_view =
|
||||||
quote!(dioxus_native_core::node_ref::NodeView::new(node, #ty::NODE_MASK, vdom));
|
quote!(dioxus_native_core::node_ref::NodeView::new(vnode, #ty::NODE_MASK, vdom));
|
||||||
if let Some(dep_ident) = &self.dep_mem.map(|m| &m.ident) {
|
let dep_idents = self.dep_mems.iter().map(|m| &m.0.ident);
|
||||||
match self.dep_kind {
|
match self.dep_kind {
|
||||||
DepKind::Node => {
|
DepKind::Node => {
|
||||||
quote!({
|
quote!({
|
||||||
if self.#ident.reduce(#node_view, &self.#dep_ident, #get_ctx){
|
current_state.#ident.reduce(#node_view, (#(¤t_state.#dep_idents,)*), #get_ctx)
|
||||||
Some(#states_changed)
|
})
|
||||||
} else{
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
DepKind::Child => {
|
|
||||||
quote!({
|
|
||||||
if self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident), #get_ctx){
|
|
||||||
Some(#states_changed)
|
|
||||||
} else{
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
DepKind::Parent => {
|
|
||||||
quote!({
|
|
||||||
if self.#ident.reduce(#node_view, parent.as_ref().map(|p| &p.#dep_ident), #get_ctx){
|
|
||||||
Some(#states_changed)
|
|
||||||
} else{
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
DepKind::Child => {
|
||||||
match self.dep_kind {
|
quote!({
|
||||||
DepKind::Node => {
|
current_state.#ident.reduce(#node_view, children.iter().map(|c| (#(&c.#dep_idents)*)), #get_ctx)
|
||||||
quote!({
|
})
|
||||||
if self.#ident.reduce(#node_view, &(), #get_ctx){
|
}
|
||||||
Some(#states_changed)
|
DepKind::Parent => {
|
||||||
} else{
|
quote!({
|
||||||
None
|
current_state.#ident.reduce(#node_view, parent.as_ref().map(|p| (#(&p.#dep_idents)*)), #get_ctx)
|
||||||
}
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
DepKind::Child => {
|
|
||||||
quote!({
|
|
||||||
if self.#ident.reduce(#node_view, std::iter::empty(), #get_ctx){
|
|
||||||
Some(#states_changed)
|
|
||||||
} else{
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
DepKind::Parent => {
|
|
||||||
quote!({
|
|
||||||
if self.#ident.reduce(#node_view, Some(&()), #get_ctx){
|
|
||||||
Some(#states_changed)
|
|
||||||
} else{
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
use anymap::AnyMap;
|
||||||
|
use dioxus::core as dioxus_core;
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_native_core::node_ref::*;
|
||||||
|
use dioxus_native_core::real_dom::*;
|
||||||
|
use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
|
||||||
|
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 = &'a Self::DepState>,
|
||||||
|
_: &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>,
|
||||||
|
_: &Self::Ctx,
|
||||||
|
) -> bool {
|
||||||
|
self.0 += 1;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
( node( $name:ty, ($($l:lifetime),*), $dep:ty ) ) => {
|
||||||
|
impl<$($l),*> NodeDepState<$dep> for $name {
|
||||||
|
type Ctx = ();
|
||||||
|
const NODE_MASK: NodeMask = NodeMask::ALL;
|
||||||
|
fn reduce(
|
||||||
|
&mut self,
|
||||||
|
_: NodeView,
|
||||||
|
_: $dep,
|
||||||
|
_: &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 {
|
||||||
|
rsx!(cx, div {
|
||||||
|
div{
|
||||||
|
div{
|
||||||
|
p{}
|
||||||
|
}
|
||||||
|
p{
|
||||||
|
"hello"
|
||||||
|
}
|
||||||
|
div{
|
||||||
|
h1{}
|
||||||
|
}
|
||||||
|
p{
|
||||||
|
"world"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let vdom = VirtualDom::new(Base);
|
||||||
|
|
||||||
|
let mutations = vdom.create_vnodes(rsx! {
|
||||||
|
div {
|
||||||
|
div{
|
||||||
|
div{
|
||||||
|
p{}
|
||||||
|
}
|
||||||
|
p{
|
||||||
|
"hello"
|
||||||
|
}
|
||||||
|
div{
|
||||||
|
h1{}
|
||||||
|
}
|
||||||
|
p{
|
||||||
|
"world"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut dom: RealDom<$s> = RealDom::new();
|
||||||
|
|
||||||
|
let nodes_updated = dom.apply_mutations(vec![mutations]);
|
||||||
|
let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::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, ('a, 'b), (&'a Child, &'b 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, ('a), (&'a 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, ('a), (&'a 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, ('a), (&'a 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));
|
||||||
|
}
|
||||||
@@ -51,8 +51,8 @@ fn remove_node() {
|
|||||||
|
|
||||||
assert_eq!(dom.size(), 2);
|
assert_eq!(dom.size(), 2);
|
||||||
assert!(&dom.contains_node(&VNode::Element(&root_div)));
|
assert!(&dom.contains_node(&VNode::Element(&root_div)));
|
||||||
assert_eq!(dom[1].height, 1);
|
assert_eq!(dom[ElementId(1)].height, 1);
|
||||||
assert_eq!(dom[2].height, 2);
|
assert_eq!(dom[ElementId(2)].height, 2);
|
||||||
|
|
||||||
let vdom = VirtualDom::new(Base);
|
let vdom = VirtualDom::new(Base);
|
||||||
let mutations = vdom.diff_lazynodes(
|
let mutations = vdom.diff_lazynodes(
|
||||||
@@ -80,7 +80,7 @@ fn remove_node() {
|
|||||||
|
|
||||||
assert_eq!(dom.size(), 1);
|
assert_eq!(dom.size(), 1);
|
||||||
assert!(&dom.contains_node(&VNode::Element(&new_root_div)));
|
assert!(&dom.contains_node(&VNode::Element(&new_root_div)));
|
||||||
assert_eq!(dom[1].height, 1);
|
assert_eq!(dom[ElementId(1)].height, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -113,7 +113,7 @@ fn add_node() {
|
|||||||
|
|
||||||
assert_eq!(dom.size(), 1);
|
assert_eq!(dom.size(), 1);
|
||||||
assert!(&dom.contains_node(&VNode::Element(&root_div)));
|
assert!(&dom.contains_node(&VNode::Element(&root_div)));
|
||||||
assert_eq!(dom[1].height, 1);
|
assert_eq!(dom[ElementId(1)].height, 1);
|
||||||
|
|
||||||
let vdom = VirtualDom::new(Base);
|
let vdom = VirtualDom::new(Base);
|
||||||
let mutations = vdom.diff_lazynodes(
|
let mutations = vdom.diff_lazynodes(
|
||||||
@@ -152,6 +152,6 @@ fn add_node() {
|
|||||||
|
|
||||||
assert_eq!(dom.size(), 2);
|
assert_eq!(dom.size(), 2);
|
||||||
assert!(&dom.contains_node(&VNode::Element(&new_root_div)));
|
assert!(&dom.contains_node(&VNode::Element(&new_root_div)));
|
||||||
assert_eq!(dom[1].height, 1);
|
assert_eq!(dom[ElementId(1)].height, 1);
|
||||||
assert_eq!(dom[2].height, 2);
|
assert_eq!(dom[ElementId(2)].height, 2);
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ fn initial_build_simple() {
|
|||||||
};
|
};
|
||||||
assert_eq!(dom.size(), 1);
|
assert_eq!(dom.size(), 1);
|
||||||
assert!(&dom.contains_node(&VNode::Element(&root_div)));
|
assert!(&dom.contains_node(&VNode::Element(&root_div)));
|
||||||
assert_eq!(dom[1].height, 1);
|
assert_eq!(dom[ElementId(1)].height, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -119,10 +119,10 @@ fn initial_build_with_children() {
|
|||||||
};
|
};
|
||||||
assert_eq!(dom.size(), 6);
|
assert_eq!(dom.size(), 6);
|
||||||
assert!(&dom.contains_node(&VNode::Element(&root_div)));
|
assert!(&dom.contains_node(&VNode::Element(&root_div)));
|
||||||
assert_eq!(dom[1].height, 1);
|
assert_eq!(dom[ElementId(1)].height, 1);
|
||||||
assert_eq!(dom[2].height, 2);
|
assert_eq!(dom[ElementId(2)].height, 2);
|
||||||
assert_eq!(dom[3].height, 3);
|
assert_eq!(dom[ElementId(3)].height, 3);
|
||||||
assert_eq!(dom[4].height, 3);
|
assert_eq!(dom[ElementId(4)].height, 3);
|
||||||
assert_eq!(dom[5].height, 4);
|
assert_eq!(dom[ElementId(5)].height, 4);
|
||||||
assert_eq!(dom[6].height, 3);
|
assert_eq!(dom[ElementId(6)].height, 3);
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
use anymap::AnyMap;
|
use anymap::AnyMap;
|
||||||
use dioxus::core as dioxus_core;
|
use dioxus::core as dioxus_core;
|
||||||
|
use dioxus::core::ElementId;
|
||||||
use dioxus::core::{AttributeValue, DomEdit, Mutations};
|
use dioxus::core::{AttributeValue, DomEdit, Mutations};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_native_core::node_ref::*;
|
use dioxus_native_core::node_ref::*;
|
||||||
@@ -49,13 +50,14 @@ impl ChildDepState for ChildDepCallCounter {
|
|||||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
const NODE_MASK: NodeMask = NodeMask::ALL;
|
||||||
fn reduce<'a>(
|
fn reduce<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: NodeView,
|
node: NodeView,
|
||||||
_: impl Iterator<Item = &'a Self::DepState>,
|
_: impl Iterator<Item = &'a Self::DepState>,
|
||||||
_: &Self::Ctx,
|
_: &Self::Ctx,
|
||||||
) -> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
Self::DepState: 'a,
|
Self::DepState: 'a,
|
||||||
{
|
{
|
||||||
|
println!("{self:?} {:?}: {} {:?}", node.tag(), node.id(), node.text());
|
||||||
self.0 += 1;
|
self.0 += 1;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -80,11 +82,10 @@ impl ParentDepState for ParentDepCallCounter {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
struct NodeDepCallCounter(u32);
|
struct NodeDepCallCounter(u32);
|
||||||
impl NodeDepState for NodeDepCallCounter {
|
impl NodeDepState<()> for NodeDepCallCounter {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
type DepState = ();
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
const NODE_MASK: NodeMask = NodeMask::ALL;
|
||||||
fn reduce(&mut self, _node: NodeView, _sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
|
fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool {
|
||||||
self.0 += 1;
|
self.0 += 1;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -133,11 +134,10 @@ impl ParentDepState for PushedDownStateTester {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Default)]
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
struct NodeStateTester(Option<String>, Vec<(String, String)>);
|
struct NodeStateTester(Option<String>, Vec<(String, String)>);
|
||||||
impl NodeDepState for NodeStateTester {
|
impl NodeDepState<()> for NodeStateTester {
|
||||||
type Ctx = u32;
|
type Ctx = u32;
|
||||||
type DepState = ();
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::All).with_tag();
|
const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::All).with_tag();
|
||||||
fn reduce(&mut self, node: NodeView, _sibling: &Self::DepState, ctx: &Self::Ctx) -> bool {
|
fn reduce(&mut self, node: NodeView, _sibling: (), ctx: &Self::Ctx) -> bool {
|
||||||
assert_eq!(*ctx, 42);
|
assert_eq!(*ctx, 42);
|
||||||
*self = NodeStateTester(
|
*self = NodeStateTester(
|
||||||
node.tag().map(|s| s.to_string()),
|
node.tag().map(|s| s.to_string()),
|
||||||
@@ -187,7 +187,7 @@ fn state_initial() {
|
|||||||
ctx.insert(42u32);
|
ctx.insert(42u32);
|
||||||
let _to_rerender = dom.update_state(&vdom, nodes_updated, ctx);
|
let _to_rerender = dom.update_state(&vdom, nodes_updated, ctx);
|
||||||
|
|
||||||
let root_div = &dom[1];
|
let root_div = &dom[ElementId(1)];
|
||||||
assert_eq!(root_div.state.bubbled.0, Some("div".to_string()));
|
assert_eq!(root_div.state.bubbled.0, Some("div".to_string()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
root_div.state.bubbled.1,
|
root_div.state.bubbled.1,
|
||||||
@@ -204,7 +204,7 @@ fn state_initial() {
|
|||||||
assert_eq!(root_div.state.node.0, Some("div".to_string()));
|
assert_eq!(root_div.state.node.0, Some("div".to_string()));
|
||||||
assert_eq!(root_div.state.node.1, vec![]);
|
assert_eq!(root_div.state.node.1, vec![]);
|
||||||
|
|
||||||
let child_p = &dom[2];
|
let child_p = &dom[ElementId(2)];
|
||||||
assert_eq!(child_p.state.bubbled.0, Some("p".to_string()));
|
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.bubbled.1, Vec::new());
|
||||||
assert_eq!(child_p.state.pushed.0, Some("p".to_string()));
|
assert_eq!(child_p.state.pushed.0, Some("p".to_string()));
|
||||||
@@ -221,7 +221,7 @@ fn state_initial() {
|
|||||||
vec![("color".to_string(), "red".to_string())]
|
vec![("color".to_string(), "red".to_string())]
|
||||||
);
|
);
|
||||||
|
|
||||||
let child_h1 = &dom[3];
|
let child_h1 = &dom[ElementId(3)];
|
||||||
assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string()));
|
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.bubbled.1, Vec::new());
|
||||||
assert_eq!(child_h1.state.pushed.0, Some("h1".to_string()));
|
assert_eq!(child_h1.state.pushed.0, Some("h1".to_string()));
|
||||||
@@ -236,64 +236,6 @@ fn state_initial() {
|
|||||||
assert_eq!(child_h1.state.node.1, vec![]);
|
assert_eq!(child_h1.state.node.1, vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn state_reduce_initally_called_minimally() {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn Base(cx: Scope) -> Element {
|
|
||||||
rsx!(cx, div {
|
|
||||||
div{
|
|
||||||
div{
|
|
||||||
p{}
|
|
||||||
}
|
|
||||||
p{
|
|
||||||
"hello"
|
|
||||||
}
|
|
||||||
div{
|
|
||||||
h1{}
|
|
||||||
}
|
|
||||||
p{
|
|
||||||
"world"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let vdom = VirtualDom::new(Base);
|
|
||||||
|
|
||||||
let mutations = vdom.create_vnodes(rsx! {
|
|
||||||
div {
|
|
||||||
div{
|
|
||||||
div{
|
|
||||||
p{}
|
|
||||||
}
|
|
||||||
p{
|
|
||||||
"hello"
|
|
||||||
}
|
|
||||||
div{
|
|
||||||
h1{}
|
|
||||||
}
|
|
||||||
p{
|
|
||||||
"world"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut dom: RealDom<CallCounterState> = RealDom::new();
|
|
||||||
|
|
||||||
let nodes_updated = dom.apply_mutations(vec![mutations]);
|
|
||||||
let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
|
|
||||||
|
|
||||||
dom.traverse_depth_first(|n| {
|
|
||||||
assert_eq!(n.state.part1.child_counter.0, 1);
|
|
||||||
assert_eq!(n.state.child_counter.0, 1);
|
|
||||||
assert_eq!(n.state.part2.parent_counter.0, 1);
|
|
||||||
assert_eq!(n.state.parent_counter.0, 1);
|
|
||||||
assert_eq!(n.state.part3.node_counter.0, 1);
|
|
||||||
assert_eq!(n.state.node_counter.0, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn state_reduce_parent_called_minimally_on_update() {
|
fn state_reduce_parent_called_minimally_on_update() {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
@@ -446,11 +388,15 @@ struct UnorderedDependanciesState {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
#[derive(Debug, Clone, Default, PartialEq)]
|
||||||
struct ADepCallCounter(usize, BDepCallCounter);
|
struct ADepCallCounter(usize, BDepCallCounter);
|
||||||
impl NodeDepState for ADepCallCounter {
|
impl<'a> NodeDepState<(&'a BDepCallCounter,)> for ADepCallCounter {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
type DepState = BDepCallCounter;
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::NONE;
|
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||||
fn reduce(&mut self, _node: NodeView, sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
|
fn reduce(
|
||||||
|
&mut self,
|
||||||
|
_node: NodeView,
|
||||||
|
(sibling,): (&'a BDepCallCounter,),
|
||||||
|
_ctx: &Self::Ctx,
|
||||||
|
) -> bool {
|
||||||
self.0 += 1;
|
self.0 += 1;
|
||||||
self.1 = sibling.clone();
|
self.1 = sibling.clone();
|
||||||
true
|
true
|
||||||
@@ -459,11 +405,15 @@ impl NodeDepState for ADepCallCounter {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
#[derive(Debug, Clone, Default, PartialEq)]
|
||||||
struct BDepCallCounter(usize, CDepCallCounter);
|
struct BDepCallCounter(usize, CDepCallCounter);
|
||||||
impl NodeDepState for BDepCallCounter {
|
impl<'a> NodeDepState<(&'a CDepCallCounter,)> for BDepCallCounter {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
type DepState = CDepCallCounter;
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::NONE;
|
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||||
fn reduce(&mut self, _node: NodeView, sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
|
fn reduce(
|
||||||
|
&mut self,
|
||||||
|
_node: NodeView,
|
||||||
|
(sibling,): (&'a CDepCallCounter,),
|
||||||
|
_ctx: &Self::Ctx,
|
||||||
|
) -> bool {
|
||||||
self.0 += 1;
|
self.0 += 1;
|
||||||
self.1 = sibling.clone();
|
self.1 = sibling.clone();
|
||||||
true
|
true
|
||||||
@@ -472,11 +422,10 @@ impl NodeDepState for BDepCallCounter {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
#[derive(Debug, Clone, Default, PartialEq)]
|
||||||
struct CDepCallCounter(usize);
|
struct CDepCallCounter(usize);
|
||||||
impl NodeDepState for CDepCallCounter {
|
impl NodeDepState<()> for CDepCallCounter {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
type DepState = ();
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
const NODE_MASK: NodeMask = NodeMask::ALL;
|
||||||
fn reduce(&mut self, _node: NodeView, _sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
|
fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool {
|
||||||
self.0 += 1;
|
self.0 += 1;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,6 @@ pub mod layout_attributes;
|
|||||||
pub mod node_ref;
|
pub mod node_ref;
|
||||||
pub mod real_dom;
|
pub mod real_dom;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod traversable;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
@@ -70,7 +70,7 @@ impl<'a> NodeView<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub enum AttributeMask {
|
pub enum AttributeMask {
|
||||||
All,
|
All,
|
||||||
Dynamic(Vec<&'static str>),
|
Dynamic(Vec<&'static str>),
|
||||||
@@ -175,7 +175,7 @@ impl Default for AttributeMask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Clone, Debug)]
|
#[derive(Default, PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct NodeMask {
|
pub struct NodeMask {
|
||||||
// must be sorted
|
// must be sorted
|
||||||
attritutes: AttributeMask,
|
attritutes: AttributeMask,
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
use anymap::AnyMap;
|
use anymap::AnyMap;
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use std::{
|
use std::ops::{Index, IndexMut};
|
||||||
collections::VecDeque,
|
|
||||||
ops::{Index, IndexMut},
|
|
||||||
};
|
|
||||||
|
|
||||||
use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
|
use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
|
||||||
|
|
||||||
use crate::state::{union_ordered_iter, State};
|
use crate::node_ref::{AttributeMask, NodeMask};
|
||||||
use crate::{
|
use crate::state::State;
|
||||||
node_ref::{AttributeMask, NodeMask},
|
use crate::traversable::Traversable;
|
||||||
state::MemberId,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
|
/// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers.
|
||||||
/// The render state passes from parent to children and or accumulates state from children to parents.
|
/// The render state passes from parent to children and or accumulates state from children to parents.
|
||||||
@@ -20,7 +15,7 @@ use crate::{
|
|||||||
pub struct RealDom<S: State> {
|
pub struct RealDom<S: State> {
|
||||||
root: usize,
|
root: usize,
|
||||||
nodes: Vec<Option<Node<S>>>,
|
nodes: Vec<Option<Node<S>>>,
|
||||||
nodes_listening: FxHashMap<&'static str, FxHashSet<usize>>,
|
nodes_listening: FxHashMap<&'static str, FxHashSet<ElementId>>,
|
||||||
node_stack: smallvec::SmallVec<[usize; 10]>,
|
node_stack: smallvec::SmallVec<[usize; 10]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +46,7 @@ impl<S: State> RealDom<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the dom, up and down state and return a set of nodes that were updated pass this to update_state.
|
/// Updates the dom, up and down state and return a set of nodes that were updated pass this to update_state.
|
||||||
pub fn apply_mutations(&mut self, mutations_vec: Vec<Mutations>) -> Vec<(usize, NodeMask)> {
|
pub fn apply_mutations(&mut self, mutations_vec: Vec<Mutations>) -> Vec<(ElementId, NodeMask)> {
|
||||||
let mut nodes_updated = Vec::new();
|
let mut nodes_updated = Vec::new();
|
||||||
for mutations in mutations_vec {
|
for mutations in mutations_vec {
|
||||||
for e in mutations.edits {
|
for e in mutations.edits {
|
||||||
@@ -72,40 +67,44 @@ impl<S: State> RealDom<S> {
|
|||||||
.drain(self.node_stack.len() - many as usize..)
|
.drain(self.node_stack.len() - many as usize..)
|
||||||
.collect();
|
.collect();
|
||||||
for ns in drained {
|
for ns in drained {
|
||||||
self.link_child(ns, target).unwrap();
|
let id = ElementId(ns);
|
||||||
nodes_updated.push((ns, NodeMask::ALL));
|
self.link_child(id, ElementId(target)).unwrap();
|
||||||
|
nodes_updated.push((id, NodeMask::ALL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ReplaceWith { root, m } => {
|
ReplaceWith { root, m } => {
|
||||||
let root = self.remove(root as usize).unwrap();
|
let root = self.remove(ElementId(root as usize)).unwrap();
|
||||||
let target = root.parent.unwrap().0;
|
let target = root.parent.unwrap().0;
|
||||||
let drained: Vec<_> = self.node_stack.drain(0..m as usize).collect();
|
let drained: Vec<_> = self.node_stack.drain(0..m as usize).collect();
|
||||||
for ns in drained {
|
for ns in drained {
|
||||||
nodes_updated.push((ns, NodeMask::ALL));
|
let id = ElementId(ns);
|
||||||
self.link_child(ns, target).unwrap();
|
nodes_updated.push((id, NodeMask::ALL));
|
||||||
|
self.link_child(id, ElementId(target)).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InsertAfter { root, n } => {
|
InsertAfter { root, n } => {
|
||||||
let target = self[root as usize].parent.unwrap().0;
|
let target = self[ElementId(root as usize)].parent.unwrap().0;
|
||||||
let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
|
let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
|
||||||
for ns in drained {
|
for ns in drained {
|
||||||
nodes_updated.push((ns, NodeMask::ALL));
|
let id = ElementId(ns);
|
||||||
self.link_child(ns, target).unwrap();
|
nodes_updated.push((id, NodeMask::ALL));
|
||||||
|
self.link_child(id, ElementId(target)).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InsertBefore { root, n } => {
|
InsertBefore { root, n } => {
|
||||||
let target = self[root as usize].parent.unwrap().0;
|
let target = self[ElementId(root as usize)].parent.unwrap().0;
|
||||||
let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
|
let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
|
||||||
for ns in drained {
|
for ns in drained {
|
||||||
nodes_updated.push((ns, NodeMask::ALL));
|
let id = ElementId(ns);
|
||||||
self.link_child(ns, target).unwrap();
|
nodes_updated.push((id, NodeMask::ALL));
|
||||||
|
self.link_child(id, ElementId(target)).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Remove { root } => {
|
Remove { root } => {
|
||||||
if let Some(parent) = self[root as usize].parent {
|
if let Some(parent) = self[ElementId(root as usize)].parent {
|
||||||
nodes_updated.push((parent.0, NodeMask::NONE));
|
nodes_updated.push((parent, NodeMask::NONE));
|
||||||
}
|
}
|
||||||
self.remove(root as usize).unwrap();
|
self.remove(ElementId(root as usize)).unwrap();
|
||||||
}
|
}
|
||||||
CreateTextNode { root, text } => {
|
CreateTextNode { root, text } => {
|
||||||
let n = Node::new(
|
let n = Node::new(
|
||||||
@@ -152,26 +151,29 @@ impl<S: State> RealDom<S> {
|
|||||||
scope: _,
|
scope: _,
|
||||||
root,
|
root,
|
||||||
} => {
|
} => {
|
||||||
nodes_updated.push((root as usize, NodeMask::new().with_listeners()));
|
let id = ElementId(root as usize);
|
||||||
|
nodes_updated.push((id, NodeMask::new().with_listeners()));
|
||||||
if let Some(v) = self.nodes_listening.get_mut(event_name) {
|
if let Some(v) = self.nodes_listening.get_mut(event_name) {
|
||||||
v.insert(root as usize);
|
v.insert(id);
|
||||||
} else {
|
} else {
|
||||||
let mut hs = FxHashSet::default();
|
let mut hs = FxHashSet::default();
|
||||||
hs.insert(root as usize);
|
hs.insert(id);
|
||||||
self.nodes_listening.insert(event_name, hs);
|
self.nodes_listening.insert(event_name, hs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RemoveEventListener { root, event } => {
|
RemoveEventListener { root, event } => {
|
||||||
nodes_updated.push((root as usize, NodeMask::new().with_listeners()));
|
let id = ElementId(root as usize);
|
||||||
|
nodes_updated.push((id, NodeMask::new().with_listeners()));
|
||||||
let v = self.nodes_listening.get_mut(event).unwrap();
|
let v = self.nodes_listening.get_mut(event).unwrap();
|
||||||
v.remove(&(root as usize));
|
v.remove(&id);
|
||||||
}
|
}
|
||||||
SetText {
|
SetText {
|
||||||
root,
|
root,
|
||||||
text: new_text,
|
text: new_text,
|
||||||
} => {
|
} => {
|
||||||
let target = &mut self[root as usize];
|
let id = ElementId(root as usize);
|
||||||
nodes_updated.push((root as usize, NodeMask::new().with_text()));
|
let target = &mut self[id];
|
||||||
|
nodes_updated.push((id, NodeMask::new().with_text()));
|
||||||
match &mut target.node_type {
|
match &mut target.node_type {
|
||||||
NodeType::Text { text } => {
|
NodeType::Text { text } => {
|
||||||
*text = new_text.to_string();
|
*text = new_text.to_string();
|
||||||
@@ -180,18 +182,16 @@ impl<S: State> RealDom<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetAttribute { root, field, .. } => {
|
SetAttribute { root, field, .. } => {
|
||||||
nodes_updated.push((
|
let id = ElementId(root as usize);
|
||||||
root as usize,
|
nodes_updated
|
||||||
NodeMask::new_with_attrs(AttributeMask::single(field)),
|
.push((id, NodeMask::new_with_attrs(AttributeMask::single(field))));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
RemoveAttribute {
|
RemoveAttribute {
|
||||||
root, name: field, ..
|
root, name: field, ..
|
||||||
} => {
|
} => {
|
||||||
nodes_updated.push((
|
let id = ElementId(root as usize);
|
||||||
root as usize,
|
nodes_updated
|
||||||
NodeMask::new_with_attrs(AttributeMask::single(field)),
|
.push((id, NodeMask::new_with_attrs(AttributeMask::single(field))));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
PopRoot {} => {
|
PopRoot {} => {
|
||||||
self.node_stack.pop();
|
self.node_stack.pop();
|
||||||
@@ -203,309 +203,60 @@ impl<S: State> RealDom<S> {
|
|||||||
nodes_updated
|
nodes_updated
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Seperated from apply_mutations because Mutations require a mutable reference to the VirtualDom.
|
|
||||||
pub fn update_state(
|
pub fn update_state(
|
||||||
&mut self,
|
&mut self,
|
||||||
vdom: &VirtualDom,
|
vdom: &VirtualDom,
|
||||||
nodes_updated: Vec<(usize, NodeMask)>,
|
nodes_updated: Vec<(ElementId, NodeMask)>,
|
||||||
ctx: AnyMap,
|
ctx: AnyMap,
|
||||||
) -> Option<FxHashSet<usize>> {
|
) -> FxHashSet<ElementId> {
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
S::update(
|
||||||
enum StatesToCheck {
|
&nodes_updated,
|
||||||
All,
|
&mut self.map(|n| &n.state, |n| &mut n.state),
|
||||||
Some(Vec<MemberId>),
|
vdom,
|
||||||
}
|
&ctx,
|
||||||
impl StatesToCheck {
|
)
|
||||||
fn union(&self, other: &Self) -> Self {
|
|
||||||
match (self, other) {
|
|
||||||
(Self::Some(s), Self::Some(o)) => Self::Some(union_ordered_iter(
|
|
||||||
s.iter().copied(),
|
|
||||||
o.iter().copied(),
|
|
||||||
s.len() + o.len(),
|
|
||||||
)),
|
|
||||||
_ => Self::All,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct NodeRef {
|
|
||||||
id: usize,
|
|
||||||
height: u16,
|
|
||||||
node_mask: NodeMask,
|
|
||||||
to_check: StatesToCheck,
|
|
||||||
}
|
|
||||||
impl NodeRef {
|
|
||||||
fn union_with(&mut self, other: &Self) {
|
|
||||||
self.node_mask = self.node_mask.union(&other.node_mask);
|
|
||||||
self.to_check = self.to_check.union(&other.to_check);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Eq for NodeRef {}
|
|
||||||
impl PartialEq for NodeRef {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.id == other.id && self.height == other.height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Ord for NodeRef {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
self.partial_cmp(other).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl PartialOrd for NodeRef {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
// Sort nodes first by height, then if the height is the same id.
|
|
||||||
// The order of the id does not matter it just helps with binary search later
|
|
||||||
Some(self.height.cmp(&other.height).then(self.id.cmp(&other.id)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut to_rerender = FxHashSet::default();
|
|
||||||
to_rerender.extend(nodes_updated.iter().map(|(id, _)| id));
|
|
||||||
let mut nodes_updated: Vec<_> = nodes_updated
|
|
||||||
.into_iter()
|
|
||||||
.map(|(id, mask)| NodeRef {
|
|
||||||
id,
|
|
||||||
height: self[id].height,
|
|
||||||
node_mask: mask,
|
|
||||||
to_check: StatesToCheck::All,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
nodes_updated.sort();
|
|
||||||
|
|
||||||
// Combine mutations that affect the same node.
|
|
||||||
let mut last_node: Option<NodeRef> = None;
|
|
||||||
let mut new_nodes_updated = VecDeque::new();
|
|
||||||
for current in nodes_updated.into_iter() {
|
|
||||||
if let Some(node) = &mut last_node {
|
|
||||||
if *node == current {
|
|
||||||
node.union_with(¤t);
|
|
||||||
} else {
|
|
||||||
new_nodes_updated.push_back(last_node.replace(current).unwrap());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
last_node = Some(current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(node) = last_node {
|
|
||||||
new_nodes_updated.push_back(node);
|
|
||||||
}
|
|
||||||
let nodes_updated = new_nodes_updated;
|
|
||||||
|
|
||||||
// update the state that only depends on nodes. The order does not matter.
|
|
||||||
for node_ref in &nodes_updated {
|
|
||||||
let mut changed = false;
|
|
||||||
let node = &mut self[node_ref.id];
|
|
||||||
let mut ids = match &node_ref.to_check {
|
|
||||||
StatesToCheck::All => node.state.node_dep_types(&node_ref.node_mask),
|
|
||||||
// this should only be triggered from the current node, so all members will need to be checked
|
|
||||||
StatesToCheck::Some(_) => unreachable!(),
|
|
||||||
};
|
|
||||||
let mut i = 0;
|
|
||||||
while i < ids.len() {
|
|
||||||
let id = ids[i];
|
|
||||||
let node = &mut self[node_ref.id];
|
|
||||||
let vnode = node.element(vdom);
|
|
||||||
if let Some(members_effected) =
|
|
||||||
node.state.update_node_dep_state(id, vnode, vdom, &ctx)
|
|
||||||
{
|
|
||||||
debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
|
|
||||||
for m in &members_effected.node_dep {
|
|
||||||
if let Err(idx) = ids.binary_search(m) {
|
|
||||||
ids.insert(idx, *m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
if changed {
|
|
||||||
to_rerender.insert(node_ref.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bubble up state. To avoid calling reduce more times than nessisary start from the bottom and go up.
|
|
||||||
let mut to_bubble = nodes_updated.clone();
|
|
||||||
while let Some(node_ref) = to_bubble.pop_back() {
|
|
||||||
let NodeRef {
|
|
||||||
id,
|
|
||||||
height,
|
|
||||||
node_mask,
|
|
||||||
to_check,
|
|
||||||
} = node_ref;
|
|
||||||
let (node, children) = self.get_node_children_mut(id).unwrap();
|
|
||||||
let children_state: Vec<_> = children.iter().map(|c| &c.state).collect();
|
|
||||||
let mut ids = match to_check {
|
|
||||||
StatesToCheck::All => node.state.child_dep_types(&node_mask),
|
|
||||||
StatesToCheck::Some(ids) => ids,
|
|
||||||
};
|
|
||||||
let mut changed = Vec::new();
|
|
||||||
let mut i = 0;
|
|
||||||
while i < ids.len() {
|
|
||||||
let id = ids[i];
|
|
||||||
let vnode = node.element(vdom);
|
|
||||||
if let Some(members_effected) =
|
|
||||||
node.state
|
|
||||||
.update_child_dep_state(id, vnode, vdom, &children_state, &ctx)
|
|
||||||
{
|
|
||||||
debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
|
|
||||||
for m in members_effected.node_dep {
|
|
||||||
if let Err(idx) = ids.binary_search(&m) {
|
|
||||||
ids.insert(idx, m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for m in members_effected.child_dep {
|
|
||||||
changed.push(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
if let Some(parent_id) = node.parent {
|
|
||||||
if !changed.is_empty() {
|
|
||||||
to_rerender.insert(id);
|
|
||||||
let i = to_bubble.partition_point(
|
|
||||||
|NodeRef {
|
|
||||||
id: other_id,
|
|
||||||
height: h,
|
|
||||||
..
|
|
||||||
}| {
|
|
||||||
*h < height - 1 || (*h == height - 1 && *other_id < parent_id.0)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
// make sure the parent is not already queued
|
|
||||||
if i < to_bubble.len() && to_bubble[i].id == parent_id.0 {
|
|
||||||
to_bubble[i].to_check =
|
|
||||||
to_bubble[i].to_check.union(&StatesToCheck::Some(changed));
|
|
||||||
} else {
|
|
||||||
to_bubble.insert(
|
|
||||||
i,
|
|
||||||
NodeRef {
|
|
||||||
id: parent_id.0,
|
|
||||||
height: height - 1,
|
|
||||||
node_mask: NodeMask::NONE,
|
|
||||||
to_check: StatesToCheck::Some(changed),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// push down state. To avoid calling reduce more times than nessisary start from the top and go down.
|
|
||||||
let mut to_push = nodes_updated;
|
|
||||||
while let Some(node_ref) = to_push.pop_front() {
|
|
||||||
let NodeRef {
|
|
||||||
id,
|
|
||||||
height,
|
|
||||||
node_mask,
|
|
||||||
to_check,
|
|
||||||
} = node_ref;
|
|
||||||
let node = &self[id];
|
|
||||||
let mut ids = match to_check {
|
|
||||||
StatesToCheck::All => node.state.parent_dep_types(&node_mask),
|
|
||||||
StatesToCheck::Some(ids) => ids,
|
|
||||||
};
|
|
||||||
let mut changed = Vec::new();
|
|
||||||
let (node, parent) = self.get_node_parent_mut(id).unwrap();
|
|
||||||
let mut i = 0;
|
|
||||||
while i < ids.len() {
|
|
||||||
let id = ids[i];
|
|
||||||
let vnode = node.element(vdom);
|
|
||||||
let parent = parent.as_deref();
|
|
||||||
let state = &mut node.state;
|
|
||||||
if let Some(members_effected) =
|
|
||||||
state.update_parent_dep_state(id, vnode, vdom, parent.map(|n| &n.state), &ctx)
|
|
||||||
{
|
|
||||||
debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
|
|
||||||
for m in members_effected.node_dep {
|
|
||||||
if let Err(idx) = ids.binary_search(&m) {
|
|
||||||
ids.insert(idx, m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for m in members_effected.parent_dep {
|
|
||||||
changed.push(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
to_rerender.insert(id);
|
|
||||||
if !changed.is_empty() {
|
|
||||||
let node = &self[id];
|
|
||||||
if let NodeType::Element { children, .. } = &node.node_type {
|
|
||||||
for c in children {
|
|
||||||
let i = to_push.partition_point(
|
|
||||||
|NodeRef {
|
|
||||||
id: other_id,
|
|
||||||
height: h,
|
|
||||||
..
|
|
||||||
}| {
|
|
||||||
*h < height + 1 || (*h == height + 1 && *other_id < c.0)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if i < to_push.len() && to_push[i].id == c.0 {
|
|
||||||
to_push[i].to_check = to_push[i]
|
|
||||||
.to_check
|
|
||||||
.union(&StatesToCheck::Some(changed.clone()));
|
|
||||||
} else {
|
|
||||||
to_push.insert(
|
|
||||||
i,
|
|
||||||
NodeRef {
|
|
||||||
id: c.0,
|
|
||||||
height: height + 1,
|
|
||||||
node_mask: NodeMask::NONE,
|
|
||||||
to_check: StatesToCheck::Some(changed.clone()),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(to_rerender)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_child(&mut self, child_id: usize, parent_id: usize) -> Option<()> {
|
fn link_child(&mut self, child_id: ElementId, parent_id: ElementId) -> Option<()> {
|
||||||
debug_assert_ne!(child_id, parent_id);
|
debug_assert_ne!(child_id, parent_id);
|
||||||
let parent = &mut self[parent_id];
|
let parent = &mut self[parent_id];
|
||||||
parent.add_child(ElementId(child_id));
|
parent.add_child(child_id);
|
||||||
let parent_height = parent.height + 1;
|
let parent_height = parent.height + 1;
|
||||||
self[child_id].set_parent(ElementId(parent_id));
|
self[child_id].set_parent(parent_id);
|
||||||
self.increase_height(child_id, parent_height);
|
self.increase_height(child_id, parent_height);
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increase_height(&mut self, id: usize, amount: u16) {
|
fn increase_height(&mut self, id: ElementId, amount: u16) {
|
||||||
let n = &mut self[id];
|
let n = &mut self[id];
|
||||||
n.height += amount;
|
n.height += amount;
|
||||||
if let NodeType::Element { children, .. } = &n.node_type {
|
if let NodeType::Element { children, .. } = &n.node_type {
|
||||||
for c in children.clone() {
|
for c in children.clone() {
|
||||||
self.increase_height(c.0, amount);
|
self.increase_height(c, amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove a node and it's children from the dom.
|
// remove a node and it's children from the dom.
|
||||||
fn remove(&mut self, id: usize) -> Option<Node<S>> {
|
fn remove(&mut self, id: ElementId) -> Option<Node<S>> {
|
||||||
// We do not need to remove the node from the parent's children list for children.
|
// We do not need to remove the node from the parent's children list for children.
|
||||||
fn inner<S: State>(dom: &mut RealDom<S>, id: usize) -> Option<Node<S>> {
|
fn inner<S: State>(dom: &mut RealDom<S>, id: ElementId) -> Option<Node<S>> {
|
||||||
let mut node = dom.nodes[id as usize].take()?;
|
let mut node = dom.nodes[id.0].take()?;
|
||||||
if let NodeType::Element { children, .. } = &mut node.node_type {
|
if let NodeType::Element { children, .. } = &mut node.node_type {
|
||||||
for c in children {
|
for c in children {
|
||||||
inner(dom, c.0)?;
|
inner(dom, *c)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(node)
|
Some(node)
|
||||||
}
|
}
|
||||||
let mut node = self.nodes[id as usize].take()?;
|
let mut node = self.nodes[id.0].take()?;
|
||||||
if let Some(parent) = node.parent {
|
if let Some(parent) = node.parent {
|
||||||
let parent = &mut self[parent];
|
let parent = &mut self[parent];
|
||||||
parent.remove_child(ElementId(id));
|
parent.remove_child(id);
|
||||||
}
|
}
|
||||||
if let NodeType::Element { children, .. } = &mut node.node_type {
|
if let NodeType::Element { children, .. } = &mut node.node_type {
|
||||||
for c in children {
|
for c in children {
|
||||||
inner(self, c.0)?;
|
inner(self, *c)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(node)
|
Some(node)
|
||||||
@@ -521,62 +272,6 @@ impl<S: State> RealDom<S> {
|
|||||||
self.nodes[id] = Some(node);
|
self.nodes[id] = Some(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, id: usize) -> Option<&Node<S>> {
|
|
||||||
self.nodes.get(id)?.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut(&mut self, id: usize) -> Option<&mut Node<S>> {
|
|
||||||
self.nodes.get_mut(id)?.as_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is safe because no node will have itself as a child
|
|
||||||
pub fn get_node_children_mut(
|
|
||||||
&mut self,
|
|
||||||
id: usize,
|
|
||||||
) -> Option<(&mut Node<S>, Vec<&mut Node<S>>)> {
|
|
||||||
let ptr = self.nodes.as_mut_ptr();
|
|
||||||
unsafe {
|
|
||||||
if id >= self.nodes.len() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let node = &mut *ptr.add(id);
|
|
||||||
if let Some(node) = node.as_mut() {
|
|
||||||
let children = match &node.node_type {
|
|
||||||
NodeType::Element { children, .. } => children
|
|
||||||
.iter()
|
|
||||||
.map(|id| (&mut *ptr.add(id.0)).as_mut().unwrap())
|
|
||||||
.collect(),
|
|
||||||
_ => Vec::new(),
|
|
||||||
};
|
|
||||||
Some((node, children))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is safe because no node will have itself as a parent
|
|
||||||
pub fn get_node_parent_mut(
|
|
||||||
&mut self,
|
|
||||||
id: usize,
|
|
||||||
) -> Option<(&mut Node<S>, Option<&mut Node<S>>)> {
|
|
||||||
let ptr = self.nodes.as_mut_ptr();
|
|
||||||
unsafe {
|
|
||||||
let node = &mut *ptr.add(id);
|
|
||||||
if id >= self.nodes.len() {
|
|
||||||
None
|
|
||||||
} else if let Some(node) = node.as_mut() {
|
|
||||||
let parent = node
|
|
||||||
.parent
|
|
||||||
.map(|id| (&mut *ptr.add(id.0)).as_mut().unwrap());
|
|
||||||
Some((node, parent))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node<S>> {
|
pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node<S>> {
|
||||||
if let Some(nodes) = self.nodes_listening.get(event) {
|
if let Some(nodes) = self.nodes_listening.get(event) {
|
||||||
let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
|
let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
|
||||||
@@ -658,7 +353,7 @@ impl<S: State> RealDom<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let NodeType::Element { children, .. } = &self[self.root].node_type {
|
if let NodeType::Element { children, .. } = &self[ElementId(self.root)].node_type {
|
||||||
for c in children {
|
for c in children {
|
||||||
inner(self, *c, &mut f);
|
inner(self, *c, &mut f);
|
||||||
}
|
}
|
||||||
@@ -677,7 +372,7 @@ impl<S: State> RealDom<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let root = self.root;
|
let root = self.root;
|
||||||
if let NodeType::Element { children, .. } = &mut self[root].node_type {
|
if let NodeType::Element { children, .. } = &mut self[ElementId(root)].node_type {
|
||||||
for c in children.clone() {
|
for c in children.clone() {
|
||||||
inner(self, c, &mut f);
|
inner(self, c, &mut f);
|
||||||
}
|
}
|
||||||
@@ -685,30 +380,17 @@ impl<S: State> RealDom<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: State> Index<usize> for RealDom<S> {
|
|
||||||
type Output = Node<S>;
|
|
||||||
|
|
||||||
fn index(&self, idx: usize) -> &Self::Output {
|
|
||||||
self.get(idx).expect("Node does not exist")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: State> Index<ElementId> for RealDom<S> {
|
impl<S: State> Index<ElementId> for RealDom<S> {
|
||||||
type Output = Node<S>;
|
type Output = Node<S>;
|
||||||
|
|
||||||
fn index(&self, idx: ElementId) -> &Self::Output {
|
fn index(&self, idx: ElementId) -> &Self::Output {
|
||||||
&self[idx.0]
|
self.get(idx).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: State> IndexMut<usize> for RealDom<S> {
|
|
||||||
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
|
|
||||||
self.get_mut(idx).expect("Node does not exist")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<S: State> IndexMut<ElementId> for RealDom<S> {
|
impl<S: State> IndexMut<ElementId> for RealDom<S> {
|
||||||
fn index_mut(&mut self, idx: ElementId) -> &mut Self::Output {
|
fn index_mut(&mut self, idx: ElementId) -> &mut Self::Output {
|
||||||
&mut self[idx.0]
|
self.get_mut(idx).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -772,3 +454,35 @@ impl<S: State> Node<S> {
|
|||||||
self.parent = Some(parent);
|
self.parent = Some(parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: State> Traversable for RealDom<T> {
|
||||||
|
type Id = ElementId;
|
||||||
|
type Node = Node<T>;
|
||||||
|
|
||||||
|
fn height(&self, id: Self::Id) -> Option<u16> {
|
||||||
|
Some(<Self as Traversable>::get(self, id)?.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, id: Self::Id) -> Option<&Self::Node> {
|
||||||
|
self.nodes.get(id.0)?.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node> {
|
||||||
|
self.nodes.get_mut(id.0)?.as_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn children(&self, id: Self::Id) -> &[Self::Id] {
|
||||||
|
if let Some(node) = <Self as Traversable>::get(self, id) {
|
||||||
|
match &node.node_type {
|
||||||
|
NodeType::Element { children, .. } => children,
|
||||||
|
_ => &[],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
&[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent(&self, id: Self::Id) -> Option<Self::Id> {
|
||||||
|
<Self as Traversable>::get(self, id).and_then(|n| n.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
use std::{
|
use std::{cmp::Ordering, fmt::Debug};
|
||||||
cmp::Ordering,
|
|
||||||
fmt::Debug,
|
|
||||||
ops::{Add, AddAssign, Sub, SubAssign},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anymap::AnyMap;
|
use anymap::AnyMap;
|
||||||
use dioxus_core::VNode;
|
use dioxus_core::ElementId;
|
||||||
|
use fxhash::FxHashSet;
|
||||||
|
|
||||||
use crate::node_ref::{NodeMask, NodeView};
|
use crate::node_ref::{NodeMask, NodeView};
|
||||||
|
use crate::traversable::Traversable;
|
||||||
|
|
||||||
pub(crate) fn union_ordered_iter<T: Ord + Debug>(
|
pub(crate) fn union_ordered_iter<T: Ord + Debug>(
|
||||||
s_iter: impl Iterator<Item = T>,
|
s_iter: impl Iterator<Item = T>,
|
||||||
@@ -50,7 +48,7 @@ pub trait ChildDepState {
|
|||||||
/// The context is passed to the [ChildDepState::reduce] when it is pushed down.
|
/// The context is passed to the [ChildDepState::reduce] when it is pushed down.
|
||||||
/// This is sometimes nessisary for lifetime purposes.
|
/// This is sometimes nessisary for lifetime purposes.
|
||||||
type Ctx;
|
type Ctx;
|
||||||
/// This must be either a [ChildDepState] or [NodeDepState]
|
/// This must be either a [ChildDepState], or [NodeDepState]
|
||||||
type DepState;
|
type DepState;
|
||||||
const NODE_MASK: NodeMask = NodeMask::NONE;
|
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||||
fn reduce<'a>(
|
fn reduce<'a>(
|
||||||
@@ -73,70 +71,77 @@ pub trait ParentDepState {
|
|||||||
/// This must be either a [ParentDepState] or [NodeDepState]
|
/// This must be either a [ParentDepState] or [NodeDepState]
|
||||||
type DepState;
|
type DepState;
|
||||||
const NODE_MASK: NodeMask = NodeMask::NONE;
|
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||||
fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool;
|
fn reduce<'a>(
|
||||||
|
&mut self,
|
||||||
|
node: NodeView,
|
||||||
|
parent: Option<&'a Self::DepState>,
|
||||||
|
ctx: &Self::Ctx,
|
||||||
|
) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This state that is upadated lazily. For example any propertys that do not effect other parts of the dom like bg-color.
|
/// This state that is upadated lazily. For example any propertys that do not effect other parts of the dom like bg-color.
|
||||||
/// Called when the current node's node properties are modified or a sibling's [NodeDepState] is modified.
|
/// Called when the current node's node properties are modified or a sibling's [NodeDepState] is modified.
|
||||||
/// Called at most once per update.
|
/// Called at most once per update.
|
||||||
pub trait NodeDepState {
|
/// NodeDepState is the only state that can accept multiple dependancies, but only from the current node.
|
||||||
|
/// ```rust
|
||||||
|
/// impl<'a, 'b> NodeDepState<(&'a TextWrap, &'b ChildLayout)> for Layout {
|
||||||
|
/// type Ctx = LayoutCache;
|
||||||
|
/// const NODE_MASK: NodeMask =
|
||||||
|
/// NodeMask::new_with_attrs(AttributeMask::Static(&sorted_str_slice!([
|
||||||
|
/// "width", "height"
|
||||||
|
/// ])))
|
||||||
|
/// .with_text();
|
||||||
|
/// fn reduce<'a>(
|
||||||
|
/// &mut self,
|
||||||
|
/// node: NodeView,
|
||||||
|
/// siblings: (&'a TextWrap, &'b ChildLayout),
|
||||||
|
/// ctx: &Self::Ctx,
|
||||||
|
/// ) -> bool {
|
||||||
|
/// let old = self.clone();
|
||||||
|
/// let (text_wrap, child_layout) = siblings;
|
||||||
|
/// if TextWrap::Wrap == text_wrap {
|
||||||
|
/// if let Some(text) = node.text() {
|
||||||
|
/// let lines = text_wrap.get_lines(text);
|
||||||
|
/// self.width = lines.max_by(|l| l.len());
|
||||||
|
/// self.height = lines.len();
|
||||||
|
/// return old != self;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// let mut width = child_layout.width;
|
||||||
|
/// let mut height = child_layout.width;
|
||||||
|
/// for attr in node.attributes() {
|
||||||
|
/// match attr.name {
|
||||||
|
/// "width" => {
|
||||||
|
/// width = attr.value.as_text().unwrap().parse().unwrap();
|
||||||
|
/// }
|
||||||
|
/// "height" => {
|
||||||
|
/// height = attr.value.as_text().unwrap().parse().unwrap();
|
||||||
|
/// }
|
||||||
|
/// _ => unreachable!(),
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// self.width = width;
|
||||||
|
/// self.height = height;
|
||||||
|
/// old != self
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// The generic argument (Depstate) must be a tuple containing any number of borrowed elments that are either a [ChildDepState], [ParentDepState] or [NodeDepState].
|
||||||
|
pub trait NodeDepState<DepState> {
|
||||||
type Ctx;
|
type Ctx;
|
||||||
type DepState: NodeDepState;
|
|
||||||
const NODE_MASK: NodeMask = NodeMask::NONE;
|
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||||
fn reduce(&mut self, node: NodeView, sibling: &Self::DepState, ctx: &Self::Ctx) -> bool;
|
fn reduce(&mut self, node: NodeView, siblings: DepState, ctx: &Self::Ctx) -> bool;
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ChildStatesChanged {
|
|
||||||
pub node_dep: Vec<MemberId>,
|
|
||||||
pub child_dep: Vec<MemberId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ParentStatesChanged {
|
|
||||||
pub node_dep: Vec<MemberId>,
|
|
||||||
pub parent_dep: Vec<MemberId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct NodeStatesChanged {
|
|
||||||
pub node_dep: Vec<MemberId>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom].
|
||||||
pub trait State: Default + Clone {
|
pub trait State: Default + Clone {
|
||||||
const SIZE: usize;
|
#[doc(hidden)]
|
||||||
|
fn update<'a, T: Traversable<Node = Self, Id = ElementId>>(
|
||||||
fn update_node_dep_state<'a>(
|
dirty: &[(ElementId, NodeMask)],
|
||||||
&'a mut self,
|
state_tree: &'a mut T,
|
||||||
ty: MemberId,
|
|
||||||
node: &'a VNode<'a>,
|
|
||||||
vdom: &'a dioxus_core::VirtualDom,
|
vdom: &'a dioxus_core::VirtualDom,
|
||||||
ctx: &AnyMap,
|
ctx: &AnyMap,
|
||||||
) -> Option<NodeStatesChanged>;
|
) -> FxHashSet<ElementId>;
|
||||||
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
|
||||||
fn child_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
|
|
||||||
|
|
||||||
fn update_parent_dep_state<'a>(
|
|
||||||
&'a mut self,
|
|
||||||
ty: MemberId,
|
|
||||||
node: &'a VNode<'a>,
|
|
||||||
vdom: &'a dioxus_core::VirtualDom,
|
|
||||||
parent: Option<&Self>,
|
|
||||||
ctx: &AnyMap,
|
|
||||||
) -> Option<ParentStatesChanged>;
|
|
||||||
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
|
||||||
fn parent_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
|
|
||||||
|
|
||||||
fn update_child_dep_state<'a>(
|
|
||||||
&'a mut self,
|
|
||||||
ty: MemberId,
|
|
||||||
node: &'a VNode<'a>,
|
|
||||||
vdom: &'a dioxus_core::VirtualDom,
|
|
||||||
children: &[&Self],
|
|
||||||
ctx: &AnyMap,
|
|
||||||
) -> Option<ChildStatesChanged>;
|
|
||||||
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
|
||||||
fn node_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: once GATs land we can model multable dependencies
|
// Todo: once GATs land we can model multable dependencies
|
||||||
@@ -159,44 +164,14 @@ impl ChildDepState for () {
|
|||||||
impl ParentDepState for () {
|
impl ParentDepState for () {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
type DepState = ();
|
type DepState = ();
|
||||||
fn reduce(&mut self, _: NodeView, _: Option<&Self::DepState>, _: &Self::Ctx) -> bool {
|
fn reduce<'a>(&mut self, _: NodeView, _: Option<&'a Self::DepState>, _: &Self::Ctx) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeDepState for () {
|
impl NodeDepState<()> for () {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
type DepState = ();
|
fn reduce(&mut self, _: NodeView, _sibling: (), _: &Self::Ctx) -> bool {
|
||||||
fn reduce(&mut self, _: NodeView, _sibling: &Self::DepState, _: &Self::Ctx) -> bool {
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct MemberId(pub usize);
|
|
||||||
|
|
||||||
impl Sub<usize> for MemberId {
|
|
||||||
type Output = MemberId;
|
|
||||||
fn sub(self, rhs: usize) -> Self::Output {
|
|
||||||
MemberId(self.0 - rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<usize> for MemberId {
|
|
||||||
type Output = MemberId;
|
|
||||||
fn add(self, rhs: usize) -> Self::Output {
|
|
||||||
MemberId(self.0 + rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SubAssign<usize> for MemberId {
|
|
||||||
fn sub_assign(&mut self, rhs: usize) {
|
|
||||||
*self = *self - rhs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign<usize> for MemberId {
|
|
||||||
fn add_assign(&mut self, rhs: usize) {
|
|
||||||
*self = *self + rhs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
pub trait Traversable {
|
||||||
|
type Id: Copy;
|
||||||
|
type Node;
|
||||||
|
|
||||||
|
fn height(&self, id: Self::Id) -> Option<u16>;
|
||||||
|
|
||||||
|
fn get(&self, id: Self::Id) -> Option<&Self::Node>;
|
||||||
|
fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node>;
|
||||||
|
|
||||||
|
fn children(&self, node: Self::Id) -> &[Self::Id];
|
||||||
|
fn parent(&self, node: Self::Id) -> Option<Self::Id>;
|
||||||
|
|
||||||
|
fn map<N, F: Fn(&Self::Node) -> &N, FMut: Fn(&mut Self::Node) -> &mut N>(
|
||||||
|
&mut self,
|
||||||
|
f: F,
|
||||||
|
f_mut: FMut,
|
||||||
|
) -> Map<Self, N, F, FMut>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Map {
|
||||||
|
tree: self,
|
||||||
|
f,
|
||||||
|
f_mut,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is safe because no node will have itself as it's parent
|
||||||
|
fn get_node_parent_mut(
|
||||||
|
&mut self,
|
||||||
|
id: Self::Id,
|
||||||
|
) -> (Option<&mut Self::Node>, Option<&mut Self::Node>) {
|
||||||
|
let node = self.get_mut(id).map(|n| n as *mut _);
|
||||||
|
let parent = self
|
||||||
|
.parent(id)
|
||||||
|
.and_then(|n| self.get_mut(n))
|
||||||
|
.map(|n| n as *mut _);
|
||||||
|
unsafe { (node.map(|n| &mut *n), parent.map(|n| &mut *n)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is safe because no node will have itself as a child
|
||||||
|
fn get_node_children_mut(
|
||||||
|
&mut self,
|
||||||
|
id: Self::Id,
|
||||||
|
) -> (Option<&mut Self::Node>, Vec<&mut Self::Node>) {
|
||||||
|
let node = self.get_mut(id).map(|n| n as *mut _);
|
||||||
|
let mut children = Vec::new();
|
||||||
|
let children_indexes = self.children(id).to_vec();
|
||||||
|
for id in children_indexes {
|
||||||
|
if let Some(n) = self.get_mut(id) {
|
||||||
|
children.push(unsafe { &mut *(n as *mut _) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe { (node.map(|n| &mut *n), children) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Map<
|
||||||
|
'a,
|
||||||
|
T: Traversable,
|
||||||
|
N,
|
||||||
|
F: Fn(&<T as Traversable>::Node) -> &N,
|
||||||
|
FMut: Fn(&mut <T as Traversable>::Node) -> &mut N,
|
||||||
|
> {
|
||||||
|
f: F,
|
||||||
|
f_mut: FMut,
|
||||||
|
tree: &'a mut T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
'a,
|
||||||
|
T: Traversable,
|
||||||
|
N,
|
||||||
|
F: Fn(&<T as Traversable>::Node) -> &N,
|
||||||
|
FMut: Fn(&mut <T as Traversable>::Node) -> &mut N,
|
||||||
|
> Traversable for Map<'a, T, N, F, FMut>
|
||||||
|
{
|
||||||
|
type Id = <T as Traversable>::Id;
|
||||||
|
type Node = N;
|
||||||
|
|
||||||
|
fn height(&self, id: Self::Id) -> Option<u16> {
|
||||||
|
self.tree.height(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, id: Self::Id) -> Option<&Self::Node> {
|
||||||
|
self.tree.get(id).map(&self.f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node> {
|
||||||
|
self.tree.get_mut(id).map(&self.f_mut)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn children(&self, id: Self::Id) -> &[Self::Id] {
|
||||||
|
self.tree.children(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent(&self, id: Self::Id) -> Option<Self::Id> {
|
||||||
|
self.tree.parent(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,13 +62,12 @@ pub(crate) struct Focus {
|
|||||||
pub level: FocusLevel,
|
pub level: FocusLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeDepState for Focus {
|
impl NodeDepState<()> for Focus {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
type DepState = ();
|
|
||||||
const NODE_MASK: NodeMask =
|
const NODE_MASK: NodeMask =
|
||||||
NodeMask::new_with_attrs(AttributeMask::Static(FOCUS_ATTRIBUTES)).with_listeners();
|
NodeMask::new_with_attrs(AttributeMask::Static(FOCUS_ATTRIBUTES)).with_listeners();
|
||||||
|
|
||||||
fn reduce(&mut self, node: NodeView<'_>, _sibling: &Self::DepState, _: &Self::Ctx) -> bool {
|
fn reduce(&mut self, node: NodeView<'_>, _sibling: (), _: &Self::Ctx) -> bool {
|
||||||
let new = Focus {
|
let new = Focus {
|
||||||
level: if let Some(a) = node.attributes().find(|a| a.name == "tabindex") {
|
level: if let Some(a) = node.attributes().find(|a| a.name == "tabindex") {
|
||||||
if let Some(index) = a
|
if let Some(index) = a
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ impl InnerInputState {
|
|||||||
let mut parent = node.parent;
|
let mut parent = node.parent;
|
||||||
while let Some(parent_id) = parent {
|
while let Some(parent_id) = parent {
|
||||||
will_bubble.insert(parent_id);
|
will_bubble.insert(parent_id);
|
||||||
parent = dom[parent_id.0].parent;
|
parent = dom[parent_id].parent;
|
||||||
}
|
}
|
||||||
resolved_events.push(UserEvent {
|
resolved_events.push(UserEvent {
|
||||||
scope_id: None,
|
scope_id: None,
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
|
|||||||
let to_update = rdom.apply_mutations(vec![mutations]);
|
let to_update = rdom.apply_mutations(vec![mutations]);
|
||||||
let mut any_map = AnyMap::new();
|
let mut any_map = AnyMap::new();
|
||||||
any_map.insert(taffy.clone());
|
any_map.insert(taffy.clone());
|
||||||
let _to_rerender = rdom.update_state(&dom, to_update, any_map).unwrap();
|
let _to_rerender = rdom.update_state(&dom, to_update, any_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
render_vdom(
|
render_vdom(
|
||||||
@@ -133,7 +133,8 @@ fn render_vdom(
|
|||||||
terminal.clear().unwrap();
|
terminal.clear().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut to_rerender: fxhash::FxHashSet<usize> = vec![0].into_iter().collect();
|
let mut to_rerender: fxhash::FxHashSet<ElementId> =
|
||||||
|
vec![ElementId(0)].into_iter().collect();
|
||||||
let mut updated = true;
|
let mut updated = true;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -153,7 +154,7 @@ fn render_vdom(
|
|||||||
fn resize(dims: Rect, taffy: &mut Taffy, rdom: &Dom) {
|
fn resize(dims: Rect, taffy: &mut Taffy, rdom: &Dom) {
|
||||||
let width = dims.width;
|
let width = dims.width;
|
||||||
let height = dims.height;
|
let height = dims.height;
|
||||||
let root_node = rdom[0].state.layout.node.unwrap();
|
let root_node = rdom[ElementId(0)].state.layout.node.unwrap();
|
||||||
|
|
||||||
taffy
|
taffy
|
||||||
.compute_layout(
|
.compute_layout(
|
||||||
@@ -170,7 +171,7 @@ fn render_vdom(
|
|||||||
let rdom = rdom.borrow();
|
let rdom = rdom.borrow();
|
||||||
// size is guaranteed to not change when rendering
|
// size is guaranteed to not change when rendering
|
||||||
resize(frame.size(), &mut taffy.borrow_mut(), &rdom);
|
resize(frame.size(), &mut taffy.borrow_mut(), &rdom);
|
||||||
let root = &rdom[0];
|
let root = &rdom[ElementId(0)];
|
||||||
render::render_vnode(
|
render::render_vnode(
|
||||||
frame,
|
frame,
|
||||||
&taffy.borrow(),
|
&taffy.borrow(),
|
||||||
@@ -249,7 +250,7 @@ fn render_vdom(
|
|||||||
// update the style and layout
|
// update the style and layout
|
||||||
let mut any_map = AnyMap::new();
|
let mut any_map = AnyMap::new();
|
||||||
any_map.insert(taffy.clone());
|
any_map.insert(taffy.clone());
|
||||||
to_rerender = rdom.update_state(vdom, to_update, any_map).unwrap();
|
to_rerender = rdom.update_state(vdom, to_update, any_map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,11 +45,9 @@ impl Default for PreventDefault {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeDepState for PreventDefault {
|
impl NodeDepState<()> for PreventDefault {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
|
|
||||||
type DepState = ();
|
|
||||||
|
|
||||||
const NODE_MASK: dioxus_native_core::node_ref::NodeMask =
|
const NODE_MASK: dioxus_native_core::node_ref::NodeMask =
|
||||||
dioxus_native_core::node_ref::NodeMask::new_with_attrs(
|
dioxus_native_core::node_ref::NodeMask::new_with_attrs(
|
||||||
dioxus_native_core::node_ref::AttributeMask::Static(&sorted_str_slice!([
|
dioxus_native_core::node_ref::AttributeMask::Static(&sorted_str_slice!([
|
||||||
@@ -60,7 +58,7 @@ impl NodeDepState for PreventDefault {
|
|||||||
fn reduce(
|
fn reduce(
|
||||||
&mut self,
|
&mut self,
|
||||||
node: dioxus_native_core::node_ref::NodeView,
|
node: dioxus_native_core::node_ref::NodeView,
|
||||||
_sibling: &Self::DepState,
|
_sibling: (),
|
||||||
_ctx: &Self::Ctx,
|
_ctx: &Self::Ctx,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let new = match node
|
let new = match node
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use crate::Dom;
|
|||||||
/// Allows querying the layout of nodes after rendering. It will only provide a correct value after a node is rendered.
|
/// Allows querying the layout of nodes after rendering. It will only provide a correct value after a node is rendered.
|
||||||
/// Provided as a root context for all tui applictions.
|
/// Provided as a root context for all tui applictions.
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```rust
|
/// ```rust, ignore
|
||||||
/// use dioxus::prelude::*;
|
/// use dioxus::prelude::*;
|
||||||
/// use dioxus::tui::query::Query;
|
/// use dioxus::tui::query::Query;
|
||||||
/// use dioxus::tui::Size;
|
/// use dioxus::tui::Size;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ pub(crate) fn render_vnode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for c in children {
|
for c in children {
|
||||||
render_vnode(frame, layout, rdom, &rdom[c.0], cfg, location);
|
render_vnode(frame, layout, rdom, &rdom[*c], cfg, location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeType::Placeholder => unreachable!(),
|
NodeType::Placeholder => unreachable!(),
|
||||||
|
|||||||
Reference in new issue
Block a user