fix rendering
This commit is contained in:
1 parent
628638b9af
commit
3b06059834
11 files changed
+509
-65
No files matched your search
+56
-18
@@ -10,50 +10,64 @@ fn tui_update(c: &mut Criterion) {
|
|||||||
let mut group = c.benchmark_group("Update boxes");
|
let mut group = c.benchmark_group("Update boxes");
|
||||||
|
|
||||||
// We can also use loops to define multiple benchmarks, even over multiple dimensions.
|
// We can also use loops to define multiple benchmarks, even over multiple dimensions.
|
||||||
for size in 1..=6 {
|
for size in 1..=8u32 {
|
||||||
let parameter_string = format!("{}", 5 * size);
|
let parameter_string = format!("{}", (3 * size).pow(2));
|
||||||
group.bench_with_input(
|
group.bench_with_input(
|
||||||
BenchmarkId::new("size", parameter_string),
|
BenchmarkId::new("size", parameter_string),
|
||||||
&size,
|
&size,
|
||||||
|b, size| {
|
|b, size| {
|
||||||
b.iter(|| match size {
|
b.iter(|| match size {
|
||||||
1 => dioxus::tui::launch_cfg(
|
1 => dioxus::tui::launch_cfg(
|
||||||
app5,
|
app3,
|
||||||
Config {
|
Config {
|
||||||
headless: true,
|
headless: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
2 => dioxus::tui::launch_cfg(
|
2 => dioxus::tui::launch_cfg(
|
||||||
app10,
|
app6,
|
||||||
Config {
|
Config {
|
||||||
headless: true,
|
headless: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
3 => dioxus::tui::launch_cfg(
|
3 => dioxus::tui::launch_cfg(
|
||||||
app15,
|
app9,
|
||||||
Config {
|
Config {
|
||||||
headless: true,
|
headless: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
4 => dioxus::tui::launch_cfg(
|
4 => dioxus::tui::launch_cfg(
|
||||||
app20,
|
app12,
|
||||||
Config {
|
Config {
|
||||||
headless: true,
|
headless: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
5 => dioxus::tui::launch_cfg(
|
5 => dioxus::tui::launch_cfg(
|
||||||
app25,
|
app15,
|
||||||
Config {
|
Config {
|
||||||
headless: true,
|
headless: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
6 => dioxus::tui::launch_cfg(
|
6 => dioxus::tui::launch_cfg(
|
||||||
app30,
|
app18,
|
||||||
|
Config {
|
||||||
|
headless: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
7 => dioxus::tui::launch_cfg(
|
||||||
|
app21,
|
||||||
|
Config {
|
||||||
|
headless: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
8 => dioxus::tui::launch_cfg(
|
||||||
|
app24,
|
||||||
Config {
|
Config {
|
||||||
headless: true,
|
headless: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -157,25 +171,49 @@ fn Grid(cx: Scope<GridProps>) -> Element {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app5(cx: Scope) -> Element {
|
fn app3(cx: Scope) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div{
|
div{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
Grid{
|
Grid{
|
||||||
size: 5,
|
size: 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app10(cx: Scope) -> Element {
|
fn app6(cx: Scope) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div{
|
div{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
Grid{
|
Grid{
|
||||||
size: 10,
|
size: 6,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app9(cx: Scope) -> Element {
|
||||||
|
cx.render(rsx! {
|
||||||
|
div{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
Grid{
|
||||||
|
size: 9,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app12(cx: Scope) -> Element {
|
||||||
|
cx.render(rsx! {
|
||||||
|
div{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
Grid{
|
||||||
|
size: 12,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -193,37 +231,37 @@ fn app15(cx: Scope) -> Element {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app20(cx: Scope) -> Element {
|
fn app18(cx: Scope) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div{
|
div{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
Grid{
|
Grid{
|
||||||
size: 20,
|
size: 18,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app25(cx: Scope) -> Element {
|
fn app21(cx: Scope) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div{
|
div{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
Grid{
|
Grid{
|
||||||
size: 25,
|
size: 21,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app30(cx: Scope) -> Element {
|
fn app24(cx: Scope) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div{
|
div{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
Grid{
|
Grid{
|
||||||
size: 30,
|
size: 24,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,12 +4,35 @@ use proc_macro::TokenStream;
|
|||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::{
|
use syn::{
|
||||||
self,
|
self,
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream, Result},
|
||||||
punctuated::Punctuated,
|
punctuated::Punctuated,
|
||||||
token::Paren,
|
token::Paren,
|
||||||
Field, Ident, Token, Type, TypeTuple,
|
Field, Ident, LitStr, Token, Type, TypeTuple,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// struct StrSlice {
|
||||||
|
// init: LitStr,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl Parse for StrSlice {
|
||||||
|
// fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
// input.parse::<Token![[]>()?;
|
||||||
|
// let str: LitStr = input.parse()?;
|
||||||
|
// let name: Ident = input.parse()?;
|
||||||
|
// input.parse::<Token![,]>()?;
|
||||||
|
// input.parse::<Token![]]>()?;
|
||||||
|
// Ok(LazyStatic {
|
||||||
|
// visibility,
|
||||||
|
// name,
|
||||||
|
// ty,
|
||||||
|
// init,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[proc_macro]
|
||||||
|
// pub fn sorted_str_slice(input: TokenStream) -> TokenStream {}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum DepKind {
|
enum DepKind {
|
||||||
NodeDepState,
|
NodeDepState,
|
||||||
@@ -105,7 +128,7 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
|
|
||||||
let gen = quote! {
|
let gen = quote! {
|
||||||
impl State for #type_name{
|
impl State for #type_name{
|
||||||
fn update_node_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, ctx: &anymap::AnyMap) -> bool{
|
fn update_node_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, ctx: &anymap::AnyMap) -> bool{
|
||||||
use dioxus_native_core::state::NodeDepState as _;
|
use dioxus_native_core::state::NodeDepState as _;
|
||||||
// println!("called update_node_dep_state with ty: {:?}", ty);
|
// println!("called update_node_dep_state with ty: {:?}", ty);
|
||||||
if false {
|
if false {
|
||||||
@@ -117,7 +140,7 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_parent_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, parent: Option<&Self>, ctx: &anymap::AnyMap) -> bool{
|
fn update_parent_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, parent: Option<&Self>, ctx: &anymap::AnyMap) -> bool{
|
||||||
use dioxus_native_core::state::ParentDepState as _;
|
use dioxus_native_core::state::ParentDepState as _;
|
||||||
// println!("called update_parent_dep_state with ty: {:?}", ty);
|
// println!("called update_parent_dep_state with ty: {:?}", ty);
|
||||||
if false {
|
if false {
|
||||||
@@ -129,7 +152,7 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_child_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, children: &[&Self], ctx: &anymap::AnyMap) -> bool{
|
fn update_child_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, children: &[&Self], ctx: &anymap::AnyMap) -> bool{
|
||||||
use dioxus_native_core::state::ChildDepState as _;
|
use dioxus_native_core::state::ChildDepState as _;
|
||||||
// println!("called update_child_dep_state with ty: {:?}", ty);
|
// println!("called update_child_dep_state with ty: {:?}", ty);
|
||||||
if false {
|
if false {
|
||||||
@@ -205,7 +228,7 @@ struct DepTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for DepTypes {
|
impl Parse for DepTypes {
|
||||||
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
let dep_ty = input.parse().ok();
|
let dep_ty = input.parse().ok();
|
||||||
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();
|
||||||
@@ -221,7 +244,7 @@ struct NodeDepTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for NodeDepTypes {
|
impl Parse for NodeDepTypes {
|
||||||
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
let ctx_ty = input.parse().ok();
|
let ctx_ty = input.parse().ok();
|
||||||
Ok(Self { ctx_ty })
|
Ok(Self { ctx_ty })
|
||||||
}
|
}
|
||||||
@@ -307,7 +330,7 @@ impl<'a> StateMember<'a> {
|
|||||||
quote! {&()}
|
quote! {&()}
|
||||||
};
|
};
|
||||||
let ty = &self.mem.ty;
|
let ty = &self.mem.ty;
|
||||||
let node_view = quote!(NodeView::new(node, #ty::NODE_MASK));
|
let node_view = quote!(NodeView::new(node, #ty::NODE_MASK, vdom));
|
||||||
if let Some(dep_ident) = &self.dep_mem.map(|m| &m.ident) {
|
if let Some(dep_ident) = &self.dep_mem.map(|m| &m.ident) {
|
||||||
match self.dep_kind {
|
match self.dep_kind {
|
||||||
DepKind::NodeDepState => {
|
DepKind::NodeDepState => {
|
||||||
|
|||||||
@@ -0,0 +1,368 @@
|
|||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::{quote, ToTokens};
|
||||||
|
use syn::{
|
||||||
|
self,
|
||||||
|
parse::{Parse, ParseStream, Result},
|
||||||
|
punctuated::Punctuated,
|
||||||
|
token::Paren,
|
||||||
|
Field, Ident, LitStr, Token, Type, TypeTuple,
|
||||||
|
};
|
||||||
|
|
||||||
|
// struct StrSlice {
|
||||||
|
// init: LitStr,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl Parse for StrSlice {
|
||||||
|
// fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
// input.parse::<Token![[]>()?;
|
||||||
|
// let str: LitStr = input.parse()?;
|
||||||
|
// let name: Ident = input.parse()?;
|
||||||
|
// input.parse::<Token![,]>()?;
|
||||||
|
// input.parse::<Token![]]>()?;
|
||||||
|
// Ok(LazyStatic {
|
||||||
|
// visibility,
|
||||||
|
// name,
|
||||||
|
// ty,
|
||||||
|
// init,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[proc_macro]
|
||||||
|
// pub fn sorted_str_slice(input: TokenStream) -> TokenStream {}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum DepKind {
|
||||||
|
NodeDepState,
|
||||||
|
ChildDepState,
|
||||||
|
ParentDepState,
|
||||||
|
}
|
||||||
|
|
||||||
|
// macro that streams data from the State for any attributes that end with _
|
||||||
|
#[proc_macro_derive(State, attributes(node_dep_state, child_dep_state, parent_dep_state))]
|
||||||
|
pub fn state_macro_derive(input: TokenStream) -> TokenStream {
|
||||||
|
let ast = syn::parse(input).unwrap();
|
||||||
|
impl_derive_macro(&ast)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
|
let type_name = &ast.ident;
|
||||||
|
let fields: Vec<_> = match &ast.data {
|
||||||
|
syn::Data::Struct(data) => match &data.fields {
|
||||||
|
syn::Fields::Named(e) => &e.named,
|
||||||
|
syn::Fields::Unnamed(_) => todo!("unnamed fields"),
|
||||||
|
syn::Fields::Unit => todo!("unit fields"),
|
||||||
|
}
|
||||||
|
.iter()
|
||||||
|
.collect(),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
let strct = Struct::parse(&fields);
|
||||||
|
let state_strct = StateStruct::parse(&fields, &strct);
|
||||||
|
|
||||||
|
let node_dep_state_fields = quote::__private::TokenStream::from_iter(
|
||||||
|
state_strct
|
||||||
|
.state_members
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.dep_kind == DepKind::NodeDepState)
|
||||||
|
.map(|f| {
|
||||||
|
let ty_id = &f.type_id();
|
||||||
|
let reduce = &f.reduce_self();
|
||||||
|
quote! {
|
||||||
|
else if ty == #ty_id {
|
||||||
|
#reduce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
let child_dep_state_fields = quote::__private::TokenStream::from_iter(
|
||||||
|
state_strct
|
||||||
|
.state_members
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.dep_kind == DepKind::ChildDepState)
|
||||||
|
.map(|f| {
|
||||||
|
let ty_id = &f.type_id();
|
||||||
|
let reduce = &f.reduce_self();
|
||||||
|
quote! {
|
||||||
|
else if ty == #ty_id {
|
||||||
|
#reduce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
let parent_dep_state_fields = quote::__private::TokenStream::from_iter(
|
||||||
|
state_strct
|
||||||
|
.state_members
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.dep_kind == DepKind::ParentDepState)
|
||||||
|
.map(|f| {
|
||||||
|
let ty_id = &f.type_id();
|
||||||
|
let reduce = &f.reduce_self();
|
||||||
|
quote! {
|
||||||
|
else if ty == #ty_id {
|
||||||
|
#reduce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let node_types = state_strct
|
||||||
|
.state_members
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.dep_kind == DepKind::NodeDepState)
|
||||||
|
.map(|f| &f.mem.ty);
|
||||||
|
let child_types = state_strct
|
||||||
|
.state_members
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.dep_kind == DepKind::ChildDepState)
|
||||||
|
.map(|f| &f.mem.ty);
|
||||||
|
let parent_types = state_strct
|
||||||
|
.state_members
|
||||||
|
.iter()
|
||||||
|
.filter(|f| f.dep_kind == DepKind::ParentDepState)
|
||||||
|
.map(|f| &f.mem.ty);
|
||||||
|
|
||||||
|
let type_name_str = type_name.to_string();
|
||||||
|
|
||||||
|
let gen = quote! {
|
||||||
|
impl State for #type_name{
|
||||||
|
fn update_node_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, ctx: &anymap::AnyMap) -> bool{
|
||||||
|
use dioxus_native_core::state::NodeDepState as _;
|
||||||
|
// println!("called update_node_dep_state with ty: {:?}", ty);
|
||||||
|
if false {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
#node_dep_state_fields
|
||||||
|
else{
|
||||||
|
panic!("{:?} not in {}", ty, #type_name_str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_parent_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, parent: Option<&Self>, ctx: &anymap::AnyMap) -> bool{
|
||||||
|
use dioxus_native_core::state::ParentDepState as _;
|
||||||
|
// println!("called update_parent_dep_state with ty: {:?}", ty);
|
||||||
|
if false {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
#parent_dep_state_fields
|
||||||
|
else{
|
||||||
|
panic!("{:?} not in {}", ty, #type_name_str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_child_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, children: &[&Self], ctx: &anymap::AnyMap) -> bool{
|
||||||
|
use dioxus_native_core::state::ChildDepState as _;
|
||||||
|
// println!("called update_child_dep_state with ty: {:?}", ty);
|
||||||
|
if false {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
#child_dep_state_fields
|
||||||
|
else{
|
||||||
|
panic!("{:?} not in {}", ty, #type_name_str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn child_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec<std::any::TypeId>{
|
||||||
|
let mut dep_types = Vec::new();
|
||||||
|
#(if #child_types::NODE_MASK.overlaps(mask) {
|
||||||
|
dep_types.push(std::any::TypeId::of::<#child_types>());
|
||||||
|
})*
|
||||||
|
dep_types
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec<std::any::TypeId>{
|
||||||
|
let mut dep_types = Vec::new();
|
||||||
|
#(if #parent_types::NODE_MASK.overlaps(mask) {
|
||||||
|
dep_types.push(std::any::TypeId::of::<#parent_types>());
|
||||||
|
})*
|
||||||
|
dep_types
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec<std::any::TypeId>{
|
||||||
|
let mut dep_types = Vec::new();
|
||||||
|
#(if #node_types::NODE_MASK.overlaps(mask) {
|
||||||
|
dep_types.push(std::any::TypeId::of::<#node_types>());
|
||||||
|
})*
|
||||||
|
dep_types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
gen.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Struct {
|
||||||
|
members: Vec<Member>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Struct {
|
||||||
|
fn parse(fields: &[&Field]) -> Self {
|
||||||
|
let members = fields.iter().filter_map(|f| Member::parse(f)).collect();
|
||||||
|
Self { members }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StateStruct<'a> {
|
||||||
|
state_members: Vec<StateMember<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> StateStruct<'a> {
|
||||||
|
fn parse(fields: &[&'a Field], strct: &'a Struct) -> Self {
|
||||||
|
let state_members = strct
|
||||||
|
.members
|
||||||
|
.iter()
|
||||||
|
.zip(fields.iter())
|
||||||
|
.filter_map(|(m, f)| StateMember::parse(f, m, &strct))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// todo: sort members
|
||||||
|
|
||||||
|
Self { state_members }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DepTypes {
|
||||||
|
ctx_ty: Option<Type>,
|
||||||
|
dep_ty: Option<Type>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for DepTypes {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let dep_ty = input.parse().ok();
|
||||||
|
let comma: Option<Token![,]> = input.parse().ok();
|
||||||
|
let ctx_ty = input.parse().ok();
|
||||||
|
Ok(Self {
|
||||||
|
ctx_ty: comma.and(ctx_ty),
|
||||||
|
dep_ty,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NodeDepTypes {
|
||||||
|
ctx_ty: Option<Type>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for NodeDepTypes {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let ctx_ty = input.parse().ok();
|
||||||
|
Ok(Self { ctx_ty })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NodeDepTypes> for DepTypes {
|
||||||
|
fn from(node_dep_types: NodeDepTypes) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx_ty: node_dep_types.ctx_ty,
|
||||||
|
dep_ty: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Member {
|
||||||
|
ty: Type,
|
||||||
|
ident: Ident,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Member {
|
||||||
|
fn parse(field: &Field) -> Option<Self> {
|
||||||
|
Some(Self {
|
||||||
|
ty: field.ty.clone(),
|
||||||
|
ident: field.ident.as_ref()?.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StateMember<'a> {
|
||||||
|
mem: &'a Member,
|
||||||
|
dep_kind: DepKind,
|
||||||
|
dep_mem: Option<&'a Member>,
|
||||||
|
ctx_ty: Option<Type>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> StateMember<'a> {
|
||||||
|
fn parse(field: &Field, mem: &'a Member, parent: &'a Struct) -> Option<StateMember<'a>> {
|
||||||
|
field.attrs.iter().find_map(|a| {
|
||||||
|
let dep_kind = a
|
||||||
|
.path
|
||||||
|
.get_ident()
|
||||||
|
.map(|i| match i.to_string().as_str() {
|
||||||
|
"node_dep_state" => Some(DepKind::NodeDepState),
|
||||||
|
"child_dep_state" => Some(DepKind::ChildDepState),
|
||||||
|
"parent_dep_state" => Some(DepKind::ParentDepState),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.flatten()?;
|
||||||
|
let deps: DepTypes = match dep_kind {
|
||||||
|
DepKind::NodeDepState => a.parse_args::<NodeDepTypes>().ok()?.into(),
|
||||||
|
_ => a.parse_args().ok()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
mem,
|
||||||
|
dep_kind,
|
||||||
|
dep_mem: deps
|
||||||
|
.dep_ty
|
||||||
|
.map(|ty| parent.members.iter().find(|m| m.ty == ty))
|
||||||
|
.flatten(),
|
||||||
|
ctx_ty: deps.ctx_ty,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduce_self(&self) -> quote::__private::TokenStream {
|
||||||
|
let ident = &self.mem.ident;
|
||||||
|
let get_ctx = if let Some(ctx_ty) = &self.ctx_ty {
|
||||||
|
if ctx_ty
|
||||||
|
== &Type::Tuple(TypeTuple {
|
||||||
|
paren_token: Paren {
|
||||||
|
span: quote::__private::Span::call_site(),
|
||||||
|
},
|
||||||
|
elems: Punctuated::new(),
|
||||||
|
})
|
||||||
|
{
|
||||||
|
quote! {&()}
|
||||||
|
} else {
|
||||||
|
let msg = ctx_ty.to_token_stream().to_string() + " not found in context";
|
||||||
|
quote! {ctx.get().expect(#msg)}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {&()}
|
||||||
|
};
|
||||||
|
let ty = &self.mem.ty;
|
||||||
|
let node_view = quote!(NodeView::new(node, #ty::NODE_MASK, vdom));
|
||||||
|
if let Some(dep_ident) = &self.dep_mem.map(|m| &m.ident) {
|
||||||
|
match self.dep_kind {
|
||||||
|
DepKind::NodeDepState => {
|
||||||
|
quote!(self.#ident.reduce(#node_view, #get_ctx))
|
||||||
|
}
|
||||||
|
DepKind::ChildDepState => {
|
||||||
|
quote!(self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident), #get_ctx))
|
||||||
|
}
|
||||||
|
DepKind::ParentDepState => {
|
||||||
|
quote!(self.#ident.reduce(#node_view, parent.as_ref().map(|p| &p.#dep_ident), #get_ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match self.dep_kind {
|
||||||
|
DepKind::NodeDepState => {
|
||||||
|
quote!(self.#ident.reduce(#node_view, #get_ctx))
|
||||||
|
}
|
||||||
|
DepKind::ChildDepState => {
|
||||||
|
quote!(self.#ident.reduce(#node_view, &(), #get_ctx))
|
||||||
|
}
|
||||||
|
DepKind::ParentDepState => {
|
||||||
|
quote!(self.#ident.reduce(#node_view, Some(&()), #get_ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_id(&self) -> quote::__private::TokenStream {
|
||||||
|
let ty = &self.mem.ty;
|
||||||
|
quote!({
|
||||||
|
let type_id = std::any::TypeId::of::<#ty>();
|
||||||
|
type_id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,8 @@
|
|||||||
- [ ] pub aspect_ratio: Number,
|
- [ ] pub aspect_ratio: Number,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// align-content,align-items,align-self,animation,animation-delay,animation-direction,animation-duration,animation-fill-mode,animation-iteration-count,animation-name,animation-play-state,animation-timing-function,backface-visibility,border,border-bottom,border-bottom-color,border-bottom-left-radius,border-bottom-right-radius,border-bottom-style,border-bottom-width,border-collapse,border-color,border-image,border-image-outset,border-image-repeat,border-image-slice,border-image-source,border-image-width,border-left,border-left-color,border-left-style,border-left-width,border-radius,border-right,border-right-color,border-right-style,border-right-width,border-spacing,border-style,border-top,border-top-color,border-top-left-radius,border-top-right-radius,border-top-style,border-top-width,border-width,bottom,box-shadow,box-sizing,caption-side,clear,clip,column-count,column-fill,column-gap,column-rule,column-rule-color,column-rule-style,column-rule-width,column-span,column-width,columns,content,counter-increment,counter-reset,cursor,direction,ltr,rtl,display,empty-cells,flex,flex-basis,flex-direction,flex-flow,flex-grow,flex-shrink,flex-wrap,float,height,justify-content,flex-start,flex-end,center,space-between,space-around,space-evenly,left,letter-spacing,line-height,list-style,list-style-image,list-style-position,list-style-type,margin,margin-bottom,margin-left,margin-right,margin-top,max-height,max-width,min-height,min-width,opacity,order,outline,outline-color,outline-offset,outline-style,outline-width,overflow,overflow-x,overflow-y,padding,padding-bottom,padding-left,padding-right,padding-top,page-break-after,page-break-before,page-break-inside,perspective,perspective-origin,position,static,relative,fixed,absolute,sticky,pointer-events,quotes,resize,right,tab-size,table-layout,top,transform,transform-origin,transform-style,transition,transition-delay,transition-duration,transition-property,transition-timing-function,vertical-align,visibility,white-space,width,word-break,word-spacing,word-wrap,z-index
|
||||||
|
|
||||||
use stretch2::{prelude::*, style::PositionType};
|
use stretch2::{prelude::*, style::PositionType};
|
||||||
|
|
||||||
/// applies the entire html namespace defined in dioxus-html
|
/// applies the entire html namespace defined in dioxus-html
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ impl<S: State> RealDom<S> {
|
|||||||
for ty in ids {
|
for ty in ids {
|
||||||
let node = &mut self[node_ref.id];
|
let node = &mut self[node_ref.id];
|
||||||
let vnode = node.element(vdom);
|
let vnode = node.element(vdom);
|
||||||
changed |= node.state.update_node_dep_state(ty, vnode, &ctx);
|
changed |= node.state.update_node_dep_state(ty, vnode, vdom, &ctx);
|
||||||
}
|
}
|
||||||
if changed {
|
if changed {
|
||||||
to_rerender.insert(node_ref.id);
|
to_rerender.insert(node_ref.id);
|
||||||
@@ -328,7 +328,7 @@ impl<S: State> RealDom<S> {
|
|||||||
let vnode = node.element(vdom);
|
let vnode = node.element(vdom);
|
||||||
if node
|
if node
|
||||||
.state
|
.state
|
||||||
.update_child_dep_state(ty, vnode, &children_state, &ctx)
|
.update_child_dep_state(ty, vnode, vdom, &children_state, &ctx)
|
||||||
{
|
{
|
||||||
changed.push(ty);
|
changed.push(ty);
|
||||||
}
|
}
|
||||||
@@ -387,6 +387,7 @@ impl<S: State> RealDom<S> {
|
|||||||
if state.update_parent_dep_state(
|
if state.update_parent_dep_state(
|
||||||
ty,
|
ty,
|
||||||
vnode,
|
vnode,
|
||||||
|
vdom,
|
||||||
parent.filter(|n| n.id.0 != 0).map(|n| &n.state),
|
parent.filter(|n| n.id.0 != 0).map(|n| &n.state),
|
||||||
&ctx,
|
&ctx,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{any::TypeId, fmt::Debug};
|
use std::{any::TypeId, fmt::Debug};
|
||||||
|
|
||||||
use anymap::AnyMap;
|
use anymap::AnyMap;
|
||||||
use dioxus_core::{Attribute, ElementId, VElement, VNode, VText};
|
use dioxus_core::{Attribute, ElementId, VElement, VNode, VText, VirtualDom};
|
||||||
|
|
||||||
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>,
|
||||||
@@ -40,7 +40,11 @@ pub struct NodeView<'a> {
|
|||||||
mask: NodeMask,
|
mask: NodeMask,
|
||||||
}
|
}
|
||||||
impl<'a> NodeView<'a> {
|
impl<'a> NodeView<'a> {
|
||||||
pub fn new(vnode: &'a VNode<'a>, view: NodeMask) -> Self {
|
pub fn new(mut vnode: &'a VNode<'a>, view: NodeMask, vdom: &'a VirtualDom) -> Self {
|
||||||
|
if let VNode::Component(sc) = vnode {
|
||||||
|
let scope = vdom.get_scope(sc.scope.get().unwrap()).unwrap();
|
||||||
|
vnode = scope.root_node();
|
||||||
|
}
|
||||||
Self {
|
Self {
|
||||||
inner: vnode,
|
inner: vnode,
|
||||||
mask: view,
|
mask: view,
|
||||||
@@ -284,6 +288,7 @@ pub trait State: Default + Clone {
|
|||||||
&'a mut self,
|
&'a mut self,
|
||||||
ty: TypeId,
|
ty: TypeId,
|
||||||
node: &'a VNode<'a>,
|
node: &'a VNode<'a>,
|
||||||
|
vdom: &'a dioxus_core::VirtualDom,
|
||||||
ctx: &AnyMap,
|
ctx: &AnyMap,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
||||||
@@ -293,6 +298,7 @@ pub trait State: Default + Clone {
|
|||||||
&'a mut self,
|
&'a mut self,
|
||||||
ty: TypeId,
|
ty: TypeId,
|
||||||
node: &'a VNode<'a>,
|
node: &'a VNode<'a>,
|
||||||
|
vdom: &'a dioxus_core::VirtualDom,
|
||||||
parent: Option<&Self>,
|
parent: Option<&Self>,
|
||||||
ctx: &AnyMap,
|
ctx: &AnyMap,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
@@ -303,6 +309,7 @@ pub trait State: Default + Clone {
|
|||||||
&'a mut self,
|
&'a mut self,
|
||||||
ty: TypeId,
|
ty: TypeId,
|
||||||
node: &'a VNode<'a>,
|
node: &'a VNode<'a>,
|
||||||
|
vdom: &'a dioxus_core::VirtualDom,
|
||||||
children: &[&Self],
|
children: &[&Self],
|
||||||
ctx: &AnyMap,
|
ctx: &AnyMap,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
|
|||||||
@@ -14,12 +14,6 @@ struct Z {
|
|||||||
z: C,
|
z: C,
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct Z {
|
|
||||||
// x: A,
|
|
||||||
// y: B,
|
|
||||||
// z: C,
|
|
||||||
// }
|
|
||||||
|
|
||||||
use dioxus_native_core::state::NodeDepState;
|
use dioxus_native_core::state::NodeDepState;
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
|
|||||||
+18
-12
@@ -28,11 +28,13 @@ impl ChildDepState for StretchLayout {
|
|||||||
where
|
where
|
||||||
Self::DepState: 'a,
|
Self::DepState: 'a,
|
||||||
{
|
{
|
||||||
|
let mut changed = false;
|
||||||
let mut stretch = ctx.borrow_mut();
|
let mut stretch = ctx.borrow_mut();
|
||||||
|
let mut style = Style::default();
|
||||||
if let Some(text) = node.text() {
|
if let Some(text) = node.text() {
|
||||||
let char_len = text.chars().count();
|
let char_len = text.chars().count();
|
||||||
|
|
||||||
let style = Style {
|
style = Style {
|
||||||
size: Size {
|
size: Size {
|
||||||
// characters are 1 point tall
|
// characters are 1 point tall
|
||||||
height: Dimension::Points(1.0),
|
height: Dimension::Points(1.0),
|
||||||
@@ -42,7 +44,6 @@ impl ChildDepState for StretchLayout {
|
|||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(n) = self.node {
|
if let Some(n) = self.node {
|
||||||
if self.style != style {
|
if self.style != style {
|
||||||
stretch.set_style(n, style).unwrap();
|
stretch.set_style(n, style).unwrap();
|
||||||
@@ -50,12 +51,8 @@ impl ChildDepState for StretchLayout {
|
|||||||
} else {
|
} else {
|
||||||
self.node = Some(stretch.new_node(style, &[]).unwrap());
|
self.node = Some(stretch.new_node(style, &[]).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.style = style;
|
|
||||||
} else {
|
} else {
|
||||||
// gather up all the styles from the attribute list
|
// gather up all the styles from the attribute list
|
||||||
let mut style = Style::default();
|
|
||||||
|
|
||||||
for &Attribute { name, value, .. } in node.attributes() {
|
for &Attribute { name, value, .. } in node.attributes() {
|
||||||
apply_layout_attributes(name, value, &mut style);
|
apply_layout_attributes(name, value, &mut style);
|
||||||
}
|
}
|
||||||
@@ -73,18 +70,27 @@ impl ChildDepState for StretchLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(n) = self.node {
|
if let Some(n) = self.node {
|
||||||
if stretch.children(n).unwrap() != child_layout {
|
|
||||||
stretch.set_children(n, &child_layout).unwrap();
|
|
||||||
}
|
|
||||||
if self.style != style {
|
if self.style != style {
|
||||||
stretch.set_style(n, style).unwrap();
|
stretch.set_style(n, style).unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.node = Some(stretch.new_node(style, &child_layout).unwrap());
|
self.node = Some(stretch.new_node(style, &[]).unwrap());
|
||||||
}
|
}
|
||||||
|
if let Some(n) = self.node {
|
||||||
|
if self.style != style {
|
||||||
|
stretch.set_style(n, style).unwrap();
|
||||||
|
}
|
||||||
|
if stretch.children(n).unwrap() != child_layout {
|
||||||
|
stretch.set_children(n, &child_layout).unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.node = Some(stretch.new_node(style, &[]).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.style != style {
|
||||||
|
changed = true;
|
||||||
self.style = style;
|
self.style = style;
|
||||||
}
|
}
|
||||||
true
|
changed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,8 +148,7 @@ fn render_vdom(
|
|||||||
fn resize(dims: Rect, stretch: &mut Stretch, rdom: &Dom) {
|
fn resize(dims: Rect, stretch: &mut Stretch, rdom: &Dom) {
|
||||||
let width = dims.width;
|
let width = dims.width;
|
||||||
let height = dims.height;
|
let height = dims.height;
|
||||||
let root_id = rdom.root_id();
|
let root_node = rdom[0].state.layout.node.unwrap();
|
||||||
let root_node = rdom[root_id].state.layout.node.unwrap();
|
|
||||||
|
|
||||||
stretch
|
stretch
|
||||||
.compute_layout(
|
.compute_layout(
|
||||||
@@ -165,7 +164,7 @@ fn render_vdom(
|
|||||||
terminal.draw(|frame| {
|
terminal.draw(|frame| {
|
||||||
// size is guaranteed to not change when rendering
|
// size is guaranteed to not change when rendering
|
||||||
resize(frame.size(), &mut stretch.borrow_mut(), &rdom);
|
resize(frame.size(), &mut stretch.borrow_mut(), &rdom);
|
||||||
let root = &rdom[rdom.root_id()];
|
let root = &rdom[0];
|
||||||
render::render_vnode(frame, &stretch.borrow(), &rdom, &root, cfg);
|
render::render_vnode(frame, &stretch.borrow(), &rdom, &root, cfg);
|
||||||
})?;
|
})?;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ pub(crate) fn render_vnode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let Layout { location, size, .. } = layout.layout(node.state.layout.node.unwrap()).unwrap();
|
let Layout { location, size, .. } = layout.layout(node.state.layout.node.unwrap()).unwrap();
|
||||||
|
// println!("rendering {node:?} {location:?} {size:?}");
|
||||||
|
|
||||||
let Point { x, y } = location;
|
let Point { x, y } = location;
|
||||||
let Size { width, height } = size;
|
let Size { width, height } = size;
|
||||||
|
|||||||
@@ -50,22 +50,22 @@ impl ParentDepState for StyleModifier {
|
|||||||
const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, true, true, false);
|
const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, true, true, false);
|
||||||
|
|
||||||
fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, _: &Self::Ctx) -> bool {
|
fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, _: &Self::Ctx) -> bool {
|
||||||
*self = StyleModifier::default();
|
let mut new = StyleModifier::default();
|
||||||
if parent.is_some() {
|
if parent.is_some() {
|
||||||
self.style.fg = None;
|
new.style.fg = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle text modifier elements
|
// handle text modifier elements
|
||||||
if node.namespace().is_none() {
|
if node.namespace().is_none() {
|
||||||
if let Some(tag) = node.tag() {
|
if let Some(tag) = node.tag() {
|
||||||
match tag {
|
match tag {
|
||||||
"b" => apply_style_attributes("font-weight", "bold", self),
|
"b" => apply_style_attributes("font-weight", "bold", &mut new),
|
||||||
"strong" => apply_style_attributes("font-weight", "bold", self),
|
"strong" => apply_style_attributes("font-weight", "bold", &mut new),
|
||||||
"u" => apply_style_attributes("text-decoration", "underline", self),
|
"u" => apply_style_attributes("text-decoration", "underline", &mut new),
|
||||||
"ins" => apply_style_attributes("text-decoration", "underline", self),
|
"ins" => apply_style_attributes("text-decoration", "underline", &mut new),
|
||||||
"del" => apply_style_attributes("text-decoration", "line-through", self),
|
"del" => apply_style_attributes("text-decoration", "line-through", &mut new),
|
||||||
"i" => apply_style_attributes("font-style", "italic", self),
|
"i" => apply_style_attributes("font-style", "italic", &mut new),
|
||||||
"em" => apply_style_attributes("font-style", "italic", self),
|
"em" => apply_style_attributes("font-style", "italic", &mut new),
|
||||||
"mark" => {
|
"mark" => {
|
||||||
apply_style_attributes("background-color", "rgba(241, 231, 64, 50%)", self)
|
apply_style_attributes("background-color", "rgba(241, 231, 64, 50%)", self)
|
||||||
}
|
}
|
||||||
@@ -76,16 +76,21 @@ impl ParentDepState for StyleModifier {
|
|||||||
|
|
||||||
// gather up all the styles from the attribute list
|
// gather up all the styles from the attribute list
|
||||||
for &Attribute { name, value, .. } in node.attributes() {
|
for &Attribute { name, value, .. } in node.attributes() {
|
||||||
apply_style_attributes(name, value, self);
|
apply_style_attributes(name, value, &mut new);
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep the text styling from the parent element
|
// keep the text styling from the parent element
|
||||||
if let Some(parent) = parent {
|
if let Some(parent) = parent {
|
||||||
let mut new_style = self.style.merge(parent.style);
|
let mut new_style = new.style.merge(parent.style);
|
||||||
new_style.bg = self.style.bg;
|
new_style.bg = new.style.bg;
|
||||||
self.style = new_style;
|
new.style = new_style;
|
||||||
|
}
|
||||||
|
if &mut new != self {
|
||||||
|
*self = new;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user