Merge branch 'upstream' into simplify-native-core
This commit is contained in:
commit
5aa3587ea9
92 files changed
+2090
-1094
No files matched your search
@@ -7,7 +7,7 @@ repository = "https://github.com/DioxusLabs/dioxus/"
|
||||
homepage = "https://dioxuslabs.com"
|
||||
description = "Build natively rendered apps with Dioxus"
|
||||
documentation = "https://dioxuslabs.com"
|
||||
keywords = ["dom", "ui", "gui", "react", "wasm"]
|
||||
keywords = ["dom", "ui", "gui", "react"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
@@ -30,3 +30,5 @@ lightningcss = "1.0.0-alpha.39"
|
||||
[dev-dependencies]
|
||||
rand = "0.8.5"
|
||||
dioxus = { path = "../dioxus", version = "^0.3.0" }
|
||||
dioxus-native-core-macro = { path = "../native-core-macro" }
|
||||
tokio = { version = "*", features = ["full"] }
|
||||
@@ -19,15 +19,15 @@
|
||||
|
||||
[Website](https://dioxuslabs.com) |
|
||||
[Guides](https://dioxuslabs.com/guide/) |
|
||||
[API Docs](https://docs.rs/dioxus-native-core/latest/dioxus-native-core) |
|
||||
[API Docs](https://docs.rs/dioxus-native-core/latest/dioxus_native_core) |
|
||||
[Chat](https://discord.gg/XgGxMSkvUM)
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
`dioxus-native-core` provides a number of helpful utilities for lazily resolving computed values of the Dioxus VirtualDom to be used in conjunction with a native rendering engine.
|
||||
`dioxus-native-core` provides several helpful utilities for lazily resolving computed values of the Dioxus VirtualDom to be used in conjunction with a native rendering engine.
|
||||
|
||||
The main "value-add" of this crate over implementing your own native tree is that this tree is incrementally recomputed using the Dioxus VirtualDom's edit stream. Only parts of the tree that rely on each other will be redrawn - all else will be ignored.
|
||||
The main "value-add" of this crate over implementing your native tree is that this tree is incrementally recomputed using the Dioxus VirtualDom's edit stream. Only parts of the tree that rely on each other will be redrawn - all else will be ignored.
|
||||
|
||||
|
||||
## Contributing
|
||||
@@ -41,5 +41,5 @@ This project is licensed under the [MIT license].
|
||||
[mit license]: https://github.com/DioxusLabs/dioxus/blob/master/LICENSE-MIT
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Dioxus by you, shall be licensed as MIT, without any additional
|
||||
for inclusion in Dioxus by you shall be licensed as MIT without any additional
|
||||
terms or conditions.
|
||||
@@ -295,47 +295,31 @@ pub struct DirtyNodes {
|
||||
}
|
||||
|
||||
impl DirtyNodes {
|
||||
pub fn insert(&mut self, depth: u16, node_id: NodeId) {
|
||||
self.map
|
||||
.entry(depth)
|
||||
.or_insert_with(FxHashSet::default)
|
||||
.insert(node_id);
|
||||
}
|
||||
|
||||
fn pop_front(&mut self) -> Option<NodeId> {
|
||||
let (&depth, values) = self.map.iter_mut().next()?;
|
||||
let key = *values.iter().next()?;
|
||||
let node_id = values.take(&key)?;
|
||||
if values.is_empty() {
|
||||
self.map.remove(&depth);
|
||||
fn add_node(&mut self, node_id: NodeId) {
|
||||
let node_id = node_id.0;
|
||||
let index = node_id / 64;
|
||||
let bit = node_id % 64;
|
||||
let encoded = 1 << bit;
|
||||
if let Some(passes) = self.passes_dirty.get_mut(index) {
|
||||
*passes |= encoded;
|
||||
} else {
|
||||
self.passes_dirty.resize(index + 1, 0);
|
||||
self.passes_dirty[index] |= encoded;
|
||||
}
|
||||
Some(node_id)
|
||||
}
|
||||
|
||||
fn pop_back(&mut self) -> Option<NodeId> {
|
||||
let (&depth, values) = self.map.iter_mut().rev().next()?;
|
||||
let key = *values.iter().next()?;
|
||||
let node_id = values.take(&key)?;
|
||||
if values.is_empty() {
|
||||
self.map.remove(&depth);
|
||||
}
|
||||
Some(node_id)
|
||||
fn is_empty(&self) -> bool {
|
||||
self.passes_dirty.iter().all(|dirty| *dirty == 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dirty_nodes() {
|
||||
let mut dirty_nodes = DirtyNodes::default();
|
||||
|
||||
dirty_nodes.insert(1, NodeId(1));
|
||||
dirty_nodes.insert(0, NodeId(0));
|
||||
dirty_nodes.insert(2, NodeId(3));
|
||||
dirty_nodes.insert(1, NodeId(2));
|
||||
|
||||
assert_eq!(dirty_nodes.pop_front(), Some(NodeId(0)));
|
||||
assert!(matches!(dirty_nodes.pop_front(), Some(NodeId(1 | 2))));
|
||||
assert!(matches!(dirty_nodes.pop_front(), Some(NodeId(1 | 2))));
|
||||
assert_eq!(dirty_nodes.pop_front(), Some(NodeId(3)));
|
||||
fn pop(&mut self) -> Option<NodeId> {
|
||||
let index = self.passes_dirty.iter().position(|dirty| *dirty != 0)?;
|
||||
let passes = self.passes_dirty[index];
|
||||
let node_id = passes.trailing_zeros();
|
||||
let encoded = 1 << node_id;
|
||||
self.passes_dirty[index] &= !encoded;
|
||||
Some(NodeId((index * 64) + node_id as usize))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -362,6 +346,29 @@ impl DirtyNodeStates {
|
||||
dirty_nodes.insert(tree.height(*node_id).unwrap(), *node_id);
|
||||
}
|
||||
}
|
||||
if values.is_empty() {
|
||||
self.dirty.remove(&height);
|
||||
}
|
||||
|
||||
Some((height, node_id))
|
||||
}
|
||||
|
||||
fn pop_back(&mut self, pass_id: PassId) -> Option<(u16, NodeId)> {
|
||||
let (&height, values) = self
|
||||
.dirty
|
||||
.iter_mut()
|
||||
.rev()
|
||||
.find(|(_, values)| values.contains_key(&pass_id))?;
|
||||
let dirty = values.get_mut(&pass_id)?;
|
||||
let node_id = dirty.pop()?;
|
||||
if dirty.is_empty() {
|
||||
values.remove(&pass_id);
|
||||
}
|
||||
if values.is_empty() {
|
||||
self.dirty.remove(&height);
|
||||
}
|
||||
|
||||
Some((height, node_id))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_native_core::{
|
||||
node_ref::{AttributeMask, NodeView},
|
||||
real_dom::RealDom,
|
||||
state::{ParentDepState, State},
|
||||
NodeMask, SendAnyMap,
|
||||
};
|
||||
use dioxus_native_core_macro::{sorted_str_slice, State};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct BlablaState {}
|
||||
|
||||
/// Font style are inherited by default if not specified otherwise by some of the supported attributes.
|
||||
impl ParentDepState for BlablaState {
|
||||
type Ctx = ();
|
||||
type DepState = (Self,);
|
||||
|
||||
const NODE_MASK: NodeMask =
|
||||
NodeMask::new_with_attrs(AttributeMask::Static(&sorted_str_slice!(["blabla",])));
|
||||
|
||||
fn reduce<'a>(
|
||||
&mut self,
|
||||
_node: NodeView,
|
||||
_parent: Option<(&'a Self,)>,
|
||||
_ctx: &Self::Ctx,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, State, Default, Debug)]
|
||||
pub struct NodeState {
|
||||
#[parent_dep_state(blabla)]
|
||||
blabla: BlablaState,
|
||||
}
|
||||
|
||||
mod dioxus_elements {
|
||||
macro_rules! builder_constructors {
|
||||
(
|
||||
$(
|
||||
$(#[$attr:meta])*
|
||||
$name:ident {
|
||||
$(
|
||||
$(#[$attr_method:meta])*
|
||||
$fil:ident: $vil:ident,
|
||||
)*
|
||||
};
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
$(#[$attr])*
|
||||
pub struct $name;
|
||||
|
||||
impl $name {
|
||||
pub const TAG_NAME: &'static str = stringify!($name);
|
||||
pub const NAME_SPACE: Option<&'static str> = None;
|
||||
|
||||
$(
|
||||
pub const $fil: (&'static str, Option<&'static str>, bool) = (stringify!($fil), None, false);
|
||||
)*
|
||||
}
|
||||
|
||||
impl GlobalAttributes for $name {}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GlobalAttributes {}
|
||||
|
||||
pub trait SvgAttributes {}
|
||||
|
||||
builder_constructors! {
|
||||
blabla {
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_core_is_okay() {
|
||||
use std::time::Duration;
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let colors = use_state(cx, || vec!["green", "blue", "red"]);
|
||||
let padding = use_state(cx, || 10);
|
||||
|
||||
use_effect(cx, colors, |colors| async move {
|
||||
sleep(Duration::from_millis(1000)).await;
|
||||
colors.with_mut(|colors| colors.reverse());
|
||||
});
|
||||
|
||||
use_effect(cx, padding, |padding| async move {
|
||||
sleep(Duration::from_millis(10)).await;
|
||||
padding.with_mut(|padding| {
|
||||
if *padding < 65 {
|
||||
*padding += 1;
|
||||
} else {
|
||||
*padding = 5;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let _big = colors[0];
|
||||
let _mid = colors[1];
|
||||
let _small = colors[2];
|
||||
|
||||
cx.render(rsx! {
|
||||
blabla {}
|
||||
})
|
||||
}
|
||||
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_time()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
rt.block_on(async {
|
||||
let rdom = Arc::new(Mutex::new(RealDom::<NodeState>::new()));
|
||||
let mut dom = VirtualDom::new(app);
|
||||
|
||||
let muts = dom.rebuild();
|
||||
let (to_update, _diff) = rdom.lock().unwrap().apply_mutations(muts);
|
||||
|
||||
let ctx = SendAnyMap::new();
|
||||
rdom.lock().unwrap().update_state(to_update, ctx);
|
||||
|
||||
for _ in 0..10 {
|
||||
dom.wait_for_work().await;
|
||||
|
||||
let mutations = dom.render_immediate();
|
||||
let (to_update, _diff) = rdom.lock().unwrap().apply_mutations(mutations);
|
||||
|
||||
let ctx = SendAnyMap::new();
|
||||
rdom.lock().unwrap().update_state(to_update, ctx);
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user