wip: move things into a "shared" object
This commit is contained in:
1 parent
22f894e6b9
commit
f644d7c441
19 files changed
+542
-698
No files matched your search
@@ -109,20 +109,29 @@ impl Parse for Element<AS_HTML> {
|
||||
let name_str = name.to_string();
|
||||
stream.parse::<Token![=]>()?;
|
||||
if name_str.starts_with("on") {
|
||||
todo!()
|
||||
let inner;
|
||||
syn::braced!(inner in stream);
|
||||
let toks = inner.parse::<Expr>()?;
|
||||
let ty = AttrType::EventTokens(toks);
|
||||
listeners.push(ElementAttr {
|
||||
element_name: el_name.clone(),
|
||||
name,
|
||||
value: ty,
|
||||
namespace: None,
|
||||
})
|
||||
} else {
|
||||
match name_str.as_str() {
|
||||
"style" => todo!(),
|
||||
"key" => todo!(),
|
||||
"classes" => todo!(),
|
||||
"namespace" => todo!(),
|
||||
"ref" => todo!(),
|
||||
_ => {
|
||||
"style" => {}
|
||||
"key" => {}
|
||||
"classes" | "namespace" | "ref" | _ => {
|
||||
let ty = if stream.peek(LitStr) {
|
||||
let rawtext = stream.parse::<LitStr>().unwrap();
|
||||
AttrType::BumpText(rawtext)
|
||||
} else {
|
||||
let toks = stream.parse::<Expr>()?;
|
||||
// like JSX, we expect raw expressions
|
||||
let inner;
|
||||
syn::braced!(inner in stream);
|
||||
let toks = inner.parse::<Expr>()?;
|
||||
AttrType::FieldTokens(toks)
|
||||
};
|
||||
attributes.push(ElementAttr {
|
||||
@@ -134,13 +143,6 @@ impl Parse for Element<AS_HTML> {
|
||||
}
|
||||
}
|
||||
};
|
||||
// if stream.peek(LitStr) {
|
||||
|
||||
// } else {
|
||||
// }
|
||||
// if name_str.starts_with("on") {}
|
||||
|
||||
// attributes.push(stream.parse()?);
|
||||
}
|
||||
stream.parse::<Token![>]>()?;
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"rust-analyzer.inlayHints.enable": false
|
||||
"rust-analyzer.inlayHints.enable": true
|
||||
}
|
||||
+111
-37
@@ -1,55 +1,99 @@
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::{cell::UnsafeCell, rc::Rc};
|
||||
|
||||
use crate::heuristics::*;
|
||||
use crate::innerlude::*;
|
||||
use futures_util::stream::FuturesUnordered;
|
||||
use slotmap::SlotMap;
|
||||
|
||||
slotmap::new_key_type! {
|
||||
// A dedicated key type for the all the scopes
|
||||
pub struct ScopeId;
|
||||
}
|
||||
|
||||
slotmap::new_key_type! {
|
||||
// A dedicated key type for every real element that the virtualdom creates.
|
||||
//
|
||||
// This is a slotmap key because we expect the "mirror" realdom to also maintain a slotmap mapping
|
||||
// of virtual element to real element.
|
||||
pub struct ElementId;
|
||||
}
|
||||
|
||||
impl ElementId {
|
||||
pub fn as_u64(self) -> u64 {
|
||||
self.0.as_ffi()
|
||||
}
|
||||
}
|
||||
|
||||
type Shared<T> = Rc<RefCell<T>>;
|
||||
|
||||
/// These are resources shared among all the components and the virtualdom itself
|
||||
#[derive(Clone)]
|
||||
pub struct SharedArena {
|
||||
pub components: Rc<UnsafeCell<ScopeMap>>,
|
||||
}
|
||||
pub type ScopeMap = SlotMap<ScopeId, Scope>;
|
||||
pub struct SharedResources {
|
||||
pub components: Rc<UnsafeCell<SlotMap<ScopeId, Scope>>>,
|
||||
|
||||
enum MutStatus {
|
||||
Immut,
|
||||
Mut,
|
||||
pub event_queue: Shared<Vec<HeightMarker>>,
|
||||
|
||||
pub events: Shared<Vec<EventTrigger>>,
|
||||
|
||||
pub(crate) heuristics: Shared<HeuristicsEngine>,
|
||||
|
||||
pub(crate) tasks: Shared<FuturesUnordered<FiberTask>>,
|
||||
|
||||
/// We use a SlotSet to keep track of the keys that are currently being used.
|
||||
/// However, we don't store any specific data since the "mirror"
|
||||
pub raw_elements: Shared<SlotMap<ElementId, ()>>,
|
||||
|
||||
pub task_setter: Rc<dyn Fn(ScopeId)>,
|
||||
}
|
||||
|
||||
impl SharedArena {
|
||||
pub fn new(arena: ScopeMap) -> Self {
|
||||
let components = Rc::new(UnsafeCell::new(arena));
|
||||
SharedArena { components }
|
||||
impl SharedResources {
|
||||
pub fn new() -> Self {
|
||||
// preallocate 1000 elements and 20 scopes to avoid dynamic allocation
|
||||
let components = Rc::new(UnsafeCell::new(
|
||||
SlotMap::<ScopeId, Scope>::with_capacity_and_key(20),
|
||||
));
|
||||
let raw_elements = SlotMap::<ElementId, ()>::with_capacity_and_key(1000);
|
||||
|
||||
let event_queue = Rc::new(RefCell::new(Vec::new()));
|
||||
let tasks = Vec::new();
|
||||
let heuristics = HeuristicsEngine::new();
|
||||
|
||||
let queue = event_queue.clone();
|
||||
let _components = components.clone();
|
||||
let task_setter = Rc::new(move |idx| {
|
||||
let comps = unsafe { &*_components.get() };
|
||||
if let Some(scope) = comps.get(idx) {
|
||||
queue.borrow_mut().push(HeightMarker {
|
||||
height: scope.height,
|
||||
idx,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
event_queue,
|
||||
components,
|
||||
tasks: Rc::new(RefCell::new(FuturesUnordered::new())),
|
||||
events: Rc::new(RefCell::new(tasks)),
|
||||
heuristics: Rc::new(RefCell::new(heuristics)),
|
||||
raw_elements: Rc::new(RefCell::new(raw_elements)),
|
||||
task_setter,
|
||||
}
|
||||
}
|
||||
|
||||
/// THIS METHOD IS CURRENTLY UNSAFE
|
||||
/// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
|
||||
pub fn get(&self, idx: ScopeId) -> Option<&Scope> {
|
||||
let inner = unsafe { &*self.components.get() };
|
||||
/// this is unsafe because the caller needs to track which other scopes it's already using
|
||||
pub unsafe fn get_scope(&self, idx: ScopeId) -> Option<&Scope> {
|
||||
let inner = &*self.components.get();
|
||||
inner.get(idx)
|
||||
}
|
||||
|
||||
/// THIS METHOD IS CURRENTLY UNSAFE
|
||||
/// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
|
||||
pub fn get_mut(&self, idx: ScopeId) -> Option<&mut Scope> {
|
||||
let inner = unsafe { &mut *self.components.get() };
|
||||
/// this is unsafe because the caller needs to track which other scopes it's already using
|
||||
pub unsafe fn get_sope_mut(&self, idx: ScopeId) -> Option<&mut Scope> {
|
||||
let inner = &mut *self.components.get();
|
||||
inner.get_mut(idx)
|
||||
}
|
||||
|
||||
fn inner(&self) -> &ScopeMap {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn inner_mut(&mut self) -> &mut ScopeMap {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// THIS METHOD IS CURRENTLY UNSAFE
|
||||
/// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
|
||||
pub fn with<T>(&self, f: impl FnOnce(&mut ScopeMap) -> T) -> Result<T> {
|
||||
let inner = unsafe { &mut *self.components.get() };
|
||||
Ok(f(inner))
|
||||
// todo!()
|
||||
}
|
||||
|
||||
pub fn with_scope<'b, O: 'static>(
|
||||
&'b self,
|
||||
_id: ScopeId,
|
||||
@@ -75,7 +119,37 @@ impl SharedArena {
|
||||
.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||
}
|
||||
|
||||
unsafe fn inner_unchecked<'s>() -> &'s mut ScopeMap {
|
||||
todo!()
|
||||
pub fn reserve_node(&self) -> ElementId {
|
||||
self.raw_elements.borrow_mut().insert(())
|
||||
}
|
||||
|
||||
/// return the id, freeing the space of the original node
|
||||
pub fn collect_garbage(&self, id: ElementId) {}
|
||||
|
||||
pub fn borrow_queue(&self) -> RefMut<Vec<HeightMarker>> {
|
||||
self.event_queue.borrow_mut()
|
||||
}
|
||||
|
||||
pub fn insert_scope_with_key(&self, f: impl FnOnce(ScopeId) -> Scope) -> ScopeId {
|
||||
let g = unsafe { &mut *self.components.get() };
|
||||
g.insert_with_key(f)
|
||||
}
|
||||
|
||||
pub fn schedule_update(&self) -> Rc<dyn Fn(ScopeId)> {
|
||||
self.task_setter.clone()
|
||||
}
|
||||
|
||||
pub fn submit_task(&self, task: FiberTask) -> TaskHandle {
|
||||
self.tasks.borrow_mut().push(task);
|
||||
TaskHandle {}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TaskHandle {}
|
||||
|
||||
impl TaskHandle {
|
||||
pub fn toggle(&self) {}
|
||||
pub fn start(&self) {}
|
||||
pub fn stop(&self) {}
|
||||
pub fn restart(&self) {}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::innerlude::*;
|
||||
use bumpalo::Bump;
|
||||
use std::cell::Cell;
|
||||
|
||||
use std::cell::RefCell;
|
||||
pub struct ActiveFrame {
|
||||
// We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
|
||||
pub generation: RefCell<usize>,
|
||||
pub generation: Cell<usize>,
|
||||
|
||||
// The double-buffering situation that we will use
|
||||
pub frames: [BumpFrame; 2],
|
||||
@@ -13,78 +13,100 @@ pub struct ActiveFrame {
|
||||
pub struct BumpFrame {
|
||||
pub bump: Bump,
|
||||
pub head_node: VNode<'static>,
|
||||
|
||||
#[cfg(test)]
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
impl ActiveFrame {
|
||||
pub fn new() -> Self {
|
||||
Self::from_frames(
|
||||
BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: NodeFactory::static_text(""),
|
||||
},
|
||||
BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: NodeFactory::static_text(""),
|
||||
},
|
||||
)
|
||||
}
|
||||
let frame_a = BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: NodeFactory::unstable_place_holder(),
|
||||
|
||||
pub fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
|
||||
#[cfg(test)]
|
||||
name: "old",
|
||||
};
|
||||
let frame_b = BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: NodeFactory::unstable_place_holder(),
|
||||
|
||||
#[cfg(test)]
|
||||
name: "new",
|
||||
};
|
||||
Self {
|
||||
generation: 0.into(),
|
||||
frames: [a, b],
|
||||
frames: [frame_a, frame_b],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cur_frame(&self) -> &BumpFrame {
|
||||
match *self.generation.borrow() & 1 == 0 {
|
||||
pub fn prev_frame(&self) -> &BumpFrame {
|
||||
match self.generation.get() & 1 == 0 {
|
||||
true => &self.frames[0],
|
||||
false => &self.frames[1],
|
||||
}
|
||||
}
|
||||
pub fn cur_frame_mut(&mut self) -> &mut BumpFrame {
|
||||
match *self.generation.borrow() & 1 == 0 {
|
||||
|
||||
pub fn prev_frame_mut(&mut self) -> &mut BumpFrame {
|
||||
match self.generation.get() & 1 == 0 {
|
||||
true => &mut self.frames[0],
|
||||
false => &mut self.frames[1],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cur_frame(&self) -> &BumpFrame {
|
||||
match self.generation.get() & 1 == 1 {
|
||||
true => &self.frames[0],
|
||||
false => &self.frames[1],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cur_frame_mut(&mut self) -> &mut BumpFrame {
|
||||
match self.generation.get() & 1 == 1 {
|
||||
true => &mut self.frames[0],
|
||||
false => &mut self.frames[1],
|
||||
}
|
||||
}
|
||||
/// Give out our self-referential item with our own borrowed lifetime
|
||||
pub fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
|
||||
let raw_node = match *self.generation.borrow() & 1 == 0 {
|
||||
true => &self.frames[0],
|
||||
false => &self.frames[1],
|
||||
};
|
||||
|
||||
// Give out our self-referential item with our own borrowed lifetime
|
||||
unsafe {
|
||||
let unsafe_head = &raw_node.head_node;
|
||||
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
|
||||
safe_node
|
||||
}
|
||||
let cur_head = &self.cur_frame().head_node;
|
||||
unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
|
||||
}
|
||||
|
||||
/// Give out our self-referential item with our own borrowed lifetime
|
||||
pub fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
|
||||
let raw_node = match *self.generation.borrow() & 1 != 0 {
|
||||
true => &self.frames[0],
|
||||
false => &self.frames[1],
|
||||
};
|
||||
|
||||
// Give out our self-referential item with our own borrowed lifetime
|
||||
unsafe {
|
||||
let unsafe_head = &raw_node.head_node;
|
||||
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
|
||||
safe_node
|
||||
}
|
||||
}
|
||||
|
||||
pub fn old_frame_mut(&mut self) -> &mut BumpFrame {
|
||||
match *self.generation.borrow() & 1 == 0 {
|
||||
true => &mut self.frames[1],
|
||||
false => &mut self.frames[0],
|
||||
}
|
||||
let cur_head = &self.prev_frame().head_node;
|
||||
unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
|
||||
}
|
||||
|
||||
pub fn cycle_frame(&mut self) {
|
||||
*self.generation.borrow_mut() += 1;
|
||||
self.generation.set(self.generation.get() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//! These tests are bad. I don't have a good way of properly testing the ActiveFrame stuff
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_bump_frame() {
|
||||
let mut frames = ActiveFrame::new();
|
||||
|
||||
// just cycle a few times and make sure we get the right frames out
|
||||
for _ in 0..5 {
|
||||
let old = frames.prev_frame();
|
||||
let new = frames.cur_frame();
|
||||
assert_eq!(old.name, "old");
|
||||
assert_eq!(new.name, "new");
|
||||
frames.cycle_frame();
|
||||
|
||||
let old = frames.prev_frame();
|
||||
let new = frames.cur_frame();
|
||||
assert_eq!(old.name, "new");
|
||||
assert_eq!(new.name, "old");
|
||||
frames.cycle_frame();
|
||||
}
|
||||
assert_eq!(frames.generation.get(), 10);
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/// This iterator iterates through a list of virtual children and only returns real children (Elements or Text).
|
||||
///
|
||||
/// This iterator is useful when it's important to load the next real root onto the top of the stack for operations like
|
||||
/// "InsertBefore".
|
||||
struct RealChildIterator<'a> {
|
||||
scopes: &'a SharedArena,
|
||||
|
||||
// Heuristcally we should never bleed into 5 completely nested fragments/components
|
||||
// Smallvec lets us stack allocate our little stack machine so the vast majority of cases are sane
|
||||
stack: smallvec::SmallVec<[(u16, &'a VNode<'a>); 5]>,
|
||||
}
|
||||
|
||||
impl<'a> RealChildIterator<'a> {
|
||||
fn new(starter: &'a VNode<'a>, scopes: &'a SharedArena) -> Self {
|
||||
Self {
|
||||
scopes,
|
||||
stack: smallvec::smallvec![(0, starter)],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for RealChildIterator<'a> {
|
||||
type Item = &'a VNode<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<&'a VNode<'a>> {
|
||||
let mut should_pop = false;
|
||||
let mut returned_node = None;
|
||||
let mut should_push = None;
|
||||
|
||||
while returned_node.is_none() {
|
||||
if let Some((count, node)) = self.stack.last_mut() {
|
||||
match node {
|
||||
// We can only exit our looping when we get "real" nodes
|
||||
// This includes fragments and components when they're empty (have a single root)
|
||||
VNode::Element(_) | VNode::Text(_) => {
|
||||
// We've recursed INTO an element/text
|
||||
// We need to recurse *out* of it and move forward to the next
|
||||
should_pop = true;
|
||||
returned_node = Some(&**node);
|
||||
}
|
||||
|
||||
// If we get a fragment we push the next child
|
||||
VNode::Fragment(frag) => {
|
||||
let subcount = *count as usize;
|
||||
|
||||
if frag.children.len() == 0 {
|
||||
should_pop = true;
|
||||
returned_node = Some(&**node);
|
||||
}
|
||||
|
||||
if subcount >= frag.children.len() {
|
||||
should_pop = true;
|
||||
} else {
|
||||
should_push = Some(&frag.children[subcount]);
|
||||
}
|
||||
}
|
||||
|
||||
// Immediately abort suspended nodes - can't do anything with them yet
|
||||
// VNode::Suspended => should_pop = true,
|
||||
VNode::Suspended { real } => todo!(),
|
||||
|
||||
// For components, we load their root and push them onto the stack
|
||||
VNode::Component(sc) => {
|
||||
let scope = self.scopes.try_get(sc.ass_scope.get().unwrap()).unwrap();
|
||||
|
||||
// Simply swap the current node on the stack with the root of the component
|
||||
*node = scope.root();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If there's no more items on the stack, we're done!
|
||||
return None;
|
||||
}
|
||||
|
||||
if should_pop {
|
||||
self.stack.pop();
|
||||
if let Some((id, _)) = self.stack.last_mut() {
|
||||
*id += 1;
|
||||
}
|
||||
should_pop = false;
|
||||
}
|
||||
|
||||
if let Some(push) = should_push {
|
||||
self.stack.push((0, push));
|
||||
should_push = None;
|
||||
}
|
||||
}
|
||||
|
||||
returned_node
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,40 @@
|
||||
//! if the type suppports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
|
||||
//! that ensures compile-time required and optional fields on cx.
|
||||
|
||||
use crate::innerlude::{Context, DomTree, LazyNodes, VNode, FC};
|
||||
use crate::innerlude::{Context, DomTree, LazyNodes, FC};
|
||||
|
||||
/// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
|
||||
/// on how to memoize the props and some additional optimizations that can be made. We strongly encourage using the
|
||||
/// derive macro to implement the `Properties` trait automatically as guarantee that your memoization strategy is safe.
|
||||
///
|
||||
/// If your props are 'static, then Dioxus will require that they also be PartialEq for the derived memoize strategy. However,
|
||||
/// if your props borrow data, then the memoization strategy will simply default to "false" and the PartialEq will be ignored.
|
||||
/// This tends to be useful when props borrow something that simply cannot be compared (IE a reference to a closure);
|
||||
///
|
||||
/// By default, the memoization strategy is very conservative, but can be tuned to be more aggressive manually. However,
|
||||
/// this is only safe if the props are 'static - otherwise you might borrow references after-free.
|
||||
///
|
||||
/// We strongly suggest that any changes to memoization be done at the "PartialEq" level for 'static props. Additionally,
|
||||
/// we advise the use of smart pointers in cases where memoization is important.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// For props that are 'static:
|
||||
/// ```rust ignore
|
||||
/// #[derive(Props, PartialEq)]
|
||||
/// struct MyProps {
|
||||
/// data: String
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// For props that borrow:
|
||||
///
|
||||
/// ```rust ignore
|
||||
/// #[derive(Props)]
|
||||
/// struct MyProps<'a >{
|
||||
/// data: &'a str
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Properties: Sized {
|
||||
type Builder;
|
||||
const IS_STATIC: bool = false;
|
||||
@@ -50,6 +82,16 @@ pub fn fc_to_builder<T: Properties>(_: FC<T>) -> T::Builder {
|
||||
///
|
||||
/// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
|
||||
/// Try to avoid nesting fragments if you can. Infinitely nested Fragments *will* cause diffing to crash.
|
||||
///
|
||||
/// This function defines a dedicated `Fragment` component that can be used to create inline fragments in the RSX macro.
|
||||
///
|
||||
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
|
||||
///
|
||||
/// ```rust
|
||||
/// rsx!{
|
||||
/// Fragment { key: "abc" }
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(non_upper_case_globals, non_snake_case)]
|
||||
pub fn Fragment<'a>(cx: Context<'a, ()>) -> DomTree<'a> {
|
||||
cx.render(LazyNodes::new(move |f| f.fragment_from_iter(cx.children())))
|
||||
|
||||
@@ -99,12 +99,21 @@ impl<'src, P> Context<'src, P> {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn children(&self) -> &'src [VNode<'src>] {
|
||||
self.scope.child_nodes
|
||||
self.scope.child_nodes()
|
||||
}
|
||||
|
||||
/// Create a subscription that schedules a future render for the reference component
|
||||
///
|
||||
/// ## Notice: you should prefer using prepare_update and get_scope_id
|
||||
///
|
||||
pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
|
||||
self.scope.event_channel.clone()
|
||||
let cb = self.scope.vdom.schedule_update();
|
||||
let id = self.get_scope_id();
|
||||
Rc::new(move || cb(id))
|
||||
}
|
||||
|
||||
fn prepare_update(&self) -> Rc<dyn Fn(ScopeId)> {
|
||||
self.scope.vdom.schedule_update()
|
||||
}
|
||||
|
||||
pub fn schedule_effect(&self) -> Rc<dyn Fn() + 'static> {
|
||||
@@ -142,7 +151,7 @@ impl<'src, P> Context<'src, P> {
|
||||
let scope_ref = self.scope;
|
||||
let listener_id = &scope_ref.listener_idx;
|
||||
Some(lazy_nodes.into_vnode(NodeFactory {
|
||||
scope_ref,
|
||||
scope: scope_ref,
|
||||
listener_id,
|
||||
}))
|
||||
}
|
||||
@@ -297,7 +306,7 @@ Any function prefixed with "use" should not be called conditionally.
|
||||
} else {
|
||||
match inner.parent_idx {
|
||||
Some(parent_id) => {
|
||||
scope = inner.arena_link.get(parent_id);
|
||||
scope = unsafe { inner.vdom.get_scope(parent_id) };
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
@@ -329,8 +338,7 @@ Any function prefixed with "use" should not be called conditionally.
|
||||
///
|
||||
///
|
||||
pub fn submit_task(&self, task: FiberTask) -> TaskHandle {
|
||||
(self.scope.task_submitter)(task);
|
||||
TaskHandle { _p: PhantomData {} }
|
||||
self.scope.vdom.submit_task(task)
|
||||
}
|
||||
|
||||
/// Awaits the given task, forcing the component to re-render when the value is ready.
|
||||
@@ -341,13 +349,14 @@ Any function prefixed with "use" should not be called conditionally.
|
||||
pub fn use_task<Out, Fut, Init>(
|
||||
self,
|
||||
task_initializer: Init,
|
||||
) -> (TaskHandle<'src>, &'src Option<Out>)
|
||||
) -> (&'src TaskHandle, &'src Option<Out>)
|
||||
where
|
||||
Out: 'static,
|
||||
Fut: Future<Output = Out> + 'static,
|
||||
Init: FnOnce() -> Fut + 'src,
|
||||
{
|
||||
struct TaskHook<T> {
|
||||
handle: TaskHandle,
|
||||
task_dump: Rc<RefCell<Option<T>>>,
|
||||
value: Option<T>,
|
||||
}
|
||||
@@ -360,12 +369,15 @@ Any function prefixed with "use" should not be called conditionally.
|
||||
let task_dump = Rc::new(RefCell::new(None));
|
||||
|
||||
let slot = task_dump.clone();
|
||||
let update = self.schedule_update();
|
||||
|
||||
let updater = self.prepare_update();
|
||||
let update_id = self.get_scope_id();
|
||||
|
||||
let originator = self.scope.our_arena_idx.clone();
|
||||
|
||||
self.submit_task(Box::pin(task_fut.then(move |output| async move {
|
||||
let handle = self.submit_task(Box::pin(task_fut.then(move |output| async move {
|
||||
*slot.as_ref().borrow_mut() = Some(output);
|
||||
update();
|
||||
updater(update_id);
|
||||
EventTrigger {
|
||||
event: VirtualEvent::AsyncEvent { hook_idx },
|
||||
originator,
|
||||
@@ -377,13 +389,14 @@ Any function prefixed with "use" should not be called conditionally.
|
||||
TaskHook {
|
||||
task_dump,
|
||||
value: None,
|
||||
handle,
|
||||
}
|
||||
},
|
||||
|hook| {
|
||||
if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() {
|
||||
hook.value = Some(val);
|
||||
}
|
||||
(TaskHandle { _p: PhantomData }, &hook.value)
|
||||
(&hook.handle, &hook.value)
|
||||
},
|
||||
|_| {},
|
||||
)
|
||||
@@ -393,7 +406,7 @@ Any function prefixed with "use" should not be called conditionally.
|
||||
pub(crate) struct SuspenseHook {
|
||||
pub value: Rc<RefCell<Option<Box<dyn Any>>>>,
|
||||
pub callback: SuspendedCallback,
|
||||
pub dom_node_id: Rc<Cell<RealDomNode>>,
|
||||
pub dom_node_id: Rc<Cell<Option<ElementId>>>,
|
||||
}
|
||||
type SuspendedCallback = Box<dyn for<'a> Fn(SuspendedContext<'a>) -> DomTree<'a>>;
|
||||
|
||||
@@ -422,7 +435,7 @@ impl<'src, P> Context<'src, P> {
|
||||
move |hook_idx| {
|
||||
let value = Rc::new(RefCell::new(None));
|
||||
|
||||
let dom_node_id = Rc::new(RealDomNode::empty_cell());
|
||||
let dom_node_id = Rc::new(empty_cell());
|
||||
let domnode = dom_node_id.clone();
|
||||
|
||||
let slot = value.clone();
|
||||
@@ -438,7 +451,7 @@ impl<'src, P> Context<'src, P> {
|
||||
None => {
|
||||
//
|
||||
Some(VNode {
|
||||
dom_id: RealDomNode::empty_cell(),
|
||||
dom_id: empty_cell(),
|
||||
key: None,
|
||||
kind: VNodeKind::Suspended {
|
||||
node: domnode.clone(),
|
||||
@@ -495,20 +508,8 @@ impl<'src> SuspendedContext<'src> {
|
||||
let scope_ref = self.inner.scope;
|
||||
let listener_id = &scope_ref.listener_idx;
|
||||
Some(lazy_nodes.into_vnode(NodeFactory {
|
||||
scope_ref,
|
||||
scope: scope_ref,
|
||||
listener_id,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TaskHandle<'src> {
|
||||
_p: PhantomData<&'src ()>,
|
||||
}
|
||||
|
||||
impl<'src> TaskHandle<'src> {
|
||||
pub fn toggle(&self) {}
|
||||
pub fn start(&self) {}
|
||||
pub fn stop(&self) {}
|
||||
pub fn restart(&self) {}
|
||||
}
|
||||
+91
-75
@@ -15,7 +15,7 @@
|
||||
//! whose keys can be converted to u64 on FFI boundaries.
|
||||
//!
|
||||
//! When new nodes are created through `render`, they won't know which real node they correspond to. During diffing, we
|
||||
//! always make sure to copy over the ID. If we don't do this properly, the realdomnode will be populated incorrectly and
|
||||
//! always make sure to copy over the ID. If we don't do this properly, the ElementId will be populated incorrectly and
|
||||
//! brick the user's page.
|
||||
//!
|
||||
//! ## Subtree Memoization
|
||||
@@ -48,11 +48,11 @@
|
||||
//! More info on how to improve this diffing algorithm:
|
||||
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
||||
|
||||
use crate::{arena::SharedArena, innerlude::*, tasks::TaskQueue};
|
||||
use fxhash::FxHashSet;
|
||||
use crate::{arena::SharedResources, innerlude::*};
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use std::any::Any;
|
||||
use std::{any::Any, borrow::Borrow};
|
||||
|
||||
/// The accompanying "real dom" exposes an imperative API for controlling the UI layout
|
||||
///
|
||||
@@ -66,21 +66,35 @@ use std::any::Any;
|
||||
/// any modifications that follow. This technique enables the diffing algorithm to avoid directly handling or storing any
|
||||
/// target-specific Node type as well as easily serializing the edits to be sent over a network or IPC connection.
|
||||
pub trait RealDom<'a> {
|
||||
fn request_available_node(&mut self) -> RealDomNode;
|
||||
fn raw_node_as_any(&self) -> &mut dyn Any;
|
||||
}
|
||||
|
||||
pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> {
|
||||
pub dom: &'real mut Dom,
|
||||
pub real_dom: &'real mut Dom,
|
||||
pub vdom: &'bump SharedResources,
|
||||
pub edits: DomEditor<'real, 'bump>,
|
||||
pub components: &'bump SharedArena,
|
||||
pub task_queue: &'bump TaskQueue,
|
||||
|
||||
pub cur_idxs: SmallVec<[ScopeId; 5]>,
|
||||
pub diffed: FxHashSet<ScopeId>,
|
||||
pub event_queue: EventQueue,
|
||||
pub seen_nodes: FxHashSet<ScopeId>,
|
||||
}
|
||||
|
||||
impl<'r, 'b, D: RealDom<'b>> DiffMachine<'r, 'b, D> {
|
||||
pub fn get_scope_mut(&mut self, id: &ScopeId) -> Option<&'b mut Scope> {
|
||||
// ensure we haven't seen this scope before
|
||||
// if we have, then we're trying to alias it, which is not allowed
|
||||
debug_assert!(!self.seen_nodes.contains(id));
|
||||
let compon = unsafe { &mut *self.vdom.components.get() };
|
||||
compon.get_mut(*id)
|
||||
}
|
||||
pub fn get_scope(&mut self, id: &ScopeId) -> Option<&'b Scope> {
|
||||
// ensure we haven't seen this scope before
|
||||
// if we have, then we're trying to alias it, which is not allowed
|
||||
let compon = unsafe { &*self.vdom.components.get() };
|
||||
compon.get(*id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'real, 'bump, Dom> DiffMachine<'real, 'bump, Dom>
|
||||
where
|
||||
Dom: RealDom<'bump>,
|
||||
@@ -88,18 +102,14 @@ where
|
||||
pub fn new(
|
||||
edits: &'real mut Vec<DomEdit<'bump>>,
|
||||
dom: &'real mut Dom,
|
||||
components: &'bump SharedArena,
|
||||
cur_idx: ScopeId,
|
||||
event_queue: EventQueue,
|
||||
task_queue: &'bump TaskQueue,
|
||||
shared: &'bump SharedResources,
|
||||
) -> Self {
|
||||
Self {
|
||||
real_dom: dom,
|
||||
edits: DomEditor::new(edits),
|
||||
components,
|
||||
dom,
|
||||
cur_idxs: smallvec![cur_idx],
|
||||
event_queue,
|
||||
task_queue,
|
||||
vdom: shared,
|
||||
diffed: FxHashSet::default(),
|
||||
seen_nodes: FxHashSet::default(),
|
||||
}
|
||||
@@ -112,20 +122,24 @@ where
|
||||
//
|
||||
// each function call assumes the stack is fresh (empty).
|
||||
pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
|
||||
let root = old_node
|
||||
.dom_id
|
||||
.get()
|
||||
.expect("Should not be diffing old nodes that were never assigned");
|
||||
|
||||
match (&old_node.kind, &new_node.kind) {
|
||||
// Handle the "sane" cases first.
|
||||
// The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment".
|
||||
// So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable.
|
||||
(VNodeKind::Text(old), VNodeKind::Text(new)) => {
|
||||
let root = old_node.dom_id.get();
|
||||
if old.text != new.text {
|
||||
self.edits.push(root);
|
||||
self.edits.push_root(root);
|
||||
log::debug!("Text has changed {}, {}", old.text, new.text);
|
||||
self.edits.set_text(new.text);
|
||||
self.edits.pop();
|
||||
}
|
||||
|
||||
new_node.dom_id.set(root);
|
||||
new_node.dom_id.set(Some(root));
|
||||
}
|
||||
|
||||
(VNodeKind::Element(old), VNodeKind::Element(new)) => {
|
||||
@@ -133,20 +147,19 @@ where
|
||||
// This is an optimization React makes due to how users structure their code
|
||||
//
|
||||
// In Dioxus, this is less likely to occur unless through a fragment
|
||||
let root = old_node.dom_id.get();
|
||||
if new.tag_name != old.tag_name || new.namespace != old.namespace {
|
||||
self.edits.push(root);
|
||||
self.edits.push_root(root);
|
||||
let meta = self.create(new_node);
|
||||
self.edits.replace_with(meta.added_to_stack);
|
||||
self.edits.pop();
|
||||
return;
|
||||
}
|
||||
|
||||
new_node.dom_id.set(root);
|
||||
new_node.dom_id.set(Some(root));
|
||||
|
||||
// push it just in case
|
||||
// TODO: remove this - it clogs up things and is inefficient
|
||||
self.edits.push(root);
|
||||
self.edits.push_root(root);
|
||||
self.diff_listeners(old.listeners, new.listeners);
|
||||
self.diff_attr(old.attributes, new.attributes, new.namespace);
|
||||
self.diff_children(old.children, new.children);
|
||||
@@ -160,11 +173,11 @@ where
|
||||
self.cur_idxs.push(old.ass_scope.get().unwrap());
|
||||
|
||||
// Make sure the new component vnode is referencing the right scope id
|
||||
let scope_id = old.ass_scope.get();
|
||||
new.ass_scope.set(scope_id);
|
||||
let scope_addr = old.ass_scope.get().unwrap();
|
||||
new.ass_scope.set(Some(scope_addr));
|
||||
|
||||
// make sure the component's caller function is up to date
|
||||
let scope = self.components.get_mut(scope_id.unwrap()).unwrap();
|
||||
let scope = self.get_scope_mut(&scope_addr).unwrap();
|
||||
|
||||
scope.caller = new.caller.clone();
|
||||
|
||||
@@ -185,22 +198,22 @@ where
|
||||
}
|
||||
self.cur_idxs.pop();
|
||||
|
||||
self.seen_nodes.insert(scope_id.unwrap());
|
||||
self.seen_nodes.insert(scope_addr);
|
||||
} else {
|
||||
// this seems to be a fairy common code path that we could
|
||||
let mut old_iter = RealChildIterator::new(old_node, &self.components);
|
||||
let mut old_iter = RealChildIterator::new(old_node, &self.vdom);
|
||||
let first = old_iter
|
||||
.next()
|
||||
.expect("Components should generate a placeholder root");
|
||||
|
||||
// remove any leftovers
|
||||
for to_remove in old_iter {
|
||||
self.edits.push(to_remove);
|
||||
self.edits.push_root(to_remove);
|
||||
self.edits.remove();
|
||||
}
|
||||
|
||||
// seems like we could combine this into a single instruction....
|
||||
self.edits.push(first);
|
||||
self.edits.push_root(first);
|
||||
let meta = self.create(new_node);
|
||||
self.edits.replace_with(meta.added_to_stack);
|
||||
self.edits.pop();
|
||||
@@ -229,8 +242,12 @@ where
|
||||
}
|
||||
|
||||
// The strategy here is to pick the first possible node from the previous set and use that as our replace with root
|
||||
//
|
||||
// We also walk the "real node" list to make sure all latent roots are claened up
|
||||
// This covers the case any time a fragment or component shows up with pretty much anything else
|
||||
//
|
||||
// This likely isn't the fastest way to go about replacing one node with a virtual node, but the "insane" cases
|
||||
// are pretty rare. IE replacing a list (component or fragment) with a single node.
|
||||
(
|
||||
VNodeKind::Component(_)
|
||||
| VNodeKind::Fragment(_)
|
||||
@@ -242,12 +259,12 @@ where
|
||||
| VNodeKind::Element(_),
|
||||
) => {
|
||||
// Choose the node to use as the placeholder for replacewith
|
||||
let back_node = match old_node.kind {
|
||||
let back_node_id = match old_node.kind {
|
||||
// We special case these two types to avoid allocating the small-vecs
|
||||
VNodeKind::Element(_) | VNodeKind::Text(_) => old_node.dom_id.get(),
|
||||
VNodeKind::Element(_) | VNodeKind::Text(_) => root,
|
||||
|
||||
_ => {
|
||||
let mut old_iter = RealChildIterator::new(old_node, &self.components);
|
||||
let mut old_iter = RealChildIterator::new(old_node, &self.vdom);
|
||||
|
||||
let back_node = old_iter
|
||||
.next()
|
||||
@@ -255,7 +272,7 @@ where
|
||||
|
||||
// remove any leftovers
|
||||
for to_remove in old_iter {
|
||||
self.edits.push(to_remove);
|
||||
self.edits.push_root(to_remove);
|
||||
self.edits.remove();
|
||||
}
|
||||
|
||||
@@ -264,7 +281,7 @@ where
|
||||
};
|
||||
|
||||
// replace the placeholder or first node with the nodes generated from the "new"
|
||||
self.edits.push(back_node);
|
||||
self.edits.push_root(back_node_id);
|
||||
let meta = self.create(new_node);
|
||||
self.edits.replace_with(meta.added_to_stack);
|
||||
|
||||
@@ -314,9 +331,10 @@ where
|
||||
log::warn!("Creating node! ... {:#?}", node);
|
||||
match &node.kind {
|
||||
VNodeKind::Text(text) => {
|
||||
let real_id = self.dom.request_available_node();
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edits.create_text_node(text.text, real_id);
|
||||
node.dom_id.set(real_id);
|
||||
node.dom_id.set(Some(real_id));
|
||||
|
||||
CreateMeta::new(text.is_static, 1)
|
||||
}
|
||||
VNodeKind::Element(el) => {
|
||||
@@ -338,18 +356,18 @@ where
|
||||
static_listeners: _,
|
||||
} = el;
|
||||
|
||||
let real_id = self.dom.request_available_node();
|
||||
let real_id = self.vdom.reserve_node();
|
||||
if let Some(namespace) = namespace {
|
||||
self.edits
|
||||
.create_element(tag_name, Some(namespace), real_id)
|
||||
} else {
|
||||
self.edits.create_element(tag_name, None, real_id)
|
||||
};
|
||||
node.dom_id.set(real_id);
|
||||
node.dom_id.set(Some(real_id));
|
||||
|
||||
listeners.iter().enumerate().for_each(|(idx, listener)| {
|
||||
log::info!("setting listener id to {:#?}", real_id);
|
||||
listener.mounted_node.set(real_id);
|
||||
listener.mounted_node.set(Some(real_id));
|
||||
self.edits
|
||||
.new_event_listener(listener.event, listener.scope, idx, real_id);
|
||||
|
||||
@@ -405,25 +423,18 @@ where
|
||||
let parent_idx = self.cur_idxs.last().unwrap().clone();
|
||||
|
||||
// Insert a new scope into our component list
|
||||
let new_idx = self
|
||||
.components
|
||||
.with(|components| {
|
||||
components.insert_with_key(|new_idx| {
|
||||
let parent_scope = self.components.get(parent_idx).unwrap();
|
||||
let height = parent_scope.height + 1;
|
||||
Scope::new(
|
||||
caller,
|
||||
new_idx,
|
||||
Some(parent_idx),
|
||||
height,
|
||||
self.event_queue.new_channel(height, new_idx),
|
||||
self.components.clone(),
|
||||
vcomponent.children,
|
||||
self.task_queue.new_submitter(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
|
||||
let parent_scope = self.get_scope(&parent_idx).unwrap();
|
||||
let height = parent_scope.height + 1;
|
||||
Scope::new(
|
||||
caller,
|
||||
new_idx,
|
||||
Some(parent_idx),
|
||||
height,
|
||||
vcomponent.children,
|
||||
self.vdom.clone(),
|
||||
)
|
||||
});
|
||||
|
||||
// This code is supposed to insert the new idx into the parent's descendent list, but it doesn't really work.
|
||||
// This is mostly used for cleanup - to remove old scopes when components are destroyed.
|
||||
@@ -437,7 +448,7 @@ where
|
||||
// .insert(idx);
|
||||
|
||||
// TODO: abstract this unsafe into the arena abstraction
|
||||
let inner: &'bump mut _ = unsafe { &mut *self.components.components.get() };
|
||||
let inner: &'bump mut _ = unsafe { &mut *self.vdom.components.get() };
|
||||
let new_component = inner.get_mut(new_idx).unwrap();
|
||||
|
||||
// Actually initialize the caller's slot with the right address
|
||||
@@ -477,10 +488,10 @@ where
|
||||
}
|
||||
|
||||
VNodeKind::Suspended { node: real_node } => {
|
||||
let id = self.dom.request_available_node();
|
||||
let id = self.vdom.reserve_node();
|
||||
self.edits.create_placeholder(id);
|
||||
node.dom_id.set(id);
|
||||
real_node.set(id);
|
||||
node.dom_id.set(Some(id));
|
||||
real_node.set(Some(id));
|
||||
CreateMeta::new(false, 1)
|
||||
}
|
||||
}
|
||||
@@ -500,7 +511,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
||||
while let Some(scope_id) = scopes_to_explore.pop() {
|
||||
// If we're planning on deleting this node, then we don't need to both rendering it
|
||||
self.seen_nodes.insert(scope_id);
|
||||
let scope = self.components.get(scope_id).unwrap();
|
||||
let scope = self.get_scope(&scope_id).unwrap();
|
||||
for child in scope.descendents.borrow().iter() {
|
||||
// Add this node to be explored
|
||||
scopes_to_explore.push(child.clone());
|
||||
@@ -513,7 +524,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
||||
// Delete all scopes that we found as part of this subtree
|
||||
for node in nodes_to_delete {
|
||||
log::debug!("Removing scope {:#?}", node);
|
||||
let _scope = self.components.try_remove(node).unwrap();
|
||||
let _scope = self.vdom.try_remove(node).unwrap();
|
||||
// do anything we need to do to delete the scope
|
||||
// I think we need to run the destructors on the hooks
|
||||
// TODO
|
||||
@@ -527,7 +538,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
||||
// [... node]
|
||||
//
|
||||
// The change list stack is left unchanged.
|
||||
fn diff_listeners(&mut self, old: &[Listener<'_>], new: &[Listener<'_>]) {
|
||||
fn diff_listeners(&mut self, old: &[&mut Listener<'_>], new: &[&mut Listener<'_>]) {
|
||||
if !old.is_empty() || !new.is_empty() {
|
||||
// self.edits.commit_traversal();
|
||||
}
|
||||
@@ -547,7 +558,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
||||
new_l.mounted_node.set(old_l.mounted_node.get());
|
||||
// if new_l.id != old_l.id {
|
||||
// self.edits.remove_event_listener(event_type);
|
||||
// // TODO! we need to mess with events and assign them by RealDomNode
|
||||
// // TODO! we need to mess with events and assign them by ElementId
|
||||
// // self.edits
|
||||
// // .update_event_listener(event_type, new_l.scope, new_l.id)
|
||||
// }
|
||||
@@ -1270,7 +1281,7 @@ enum KeyedPrefixResult {
|
||||
/// This iterator is useful when it's important to load the next real root onto the top of the stack for operations like
|
||||
/// "InsertBefore".
|
||||
struct RealChildIterator<'a> {
|
||||
scopes: &'a SharedArena,
|
||||
scopes: &'a SharedResources,
|
||||
|
||||
// Heuristcally we should never bleed into 4 completely nested fragments/components
|
||||
// Smallvec lets us stack allocate our little stack machine so the vast majority of cases are sane
|
||||
@@ -1279,7 +1290,7 @@ struct RealChildIterator<'a> {
|
||||
}
|
||||
|
||||
impl<'a> RealChildIterator<'a> {
|
||||
fn new(starter: &'a VNode<'a>, scopes: &'a SharedArena) -> Self {
|
||||
fn new(starter: &'a VNode<'a>, scopes: &'a SharedResources) -> Self {
|
||||
Self {
|
||||
scopes,
|
||||
stack: smallvec::smallvec![(0, starter)],
|
||||
@@ -1288,9 +1299,9 @@ impl<'a> RealChildIterator<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Iterator for RealChildIterator<'a> {
|
||||
type Item = RealDomNode;
|
||||
type Item = ElementId;
|
||||
|
||||
fn next(&mut self) -> Option<RealDomNode> {
|
||||
fn next(&mut self) -> Option<ElementId> {
|
||||
let mut should_pop = false;
|
||||
let mut returned_node = None;
|
||||
let mut should_push = None;
|
||||
@@ -1304,7 +1315,7 @@ impl<'a> Iterator for RealChildIterator<'a> {
|
||||
// We've recursed INTO an element/text
|
||||
// We need to recurse *out* of it and move forward to the next
|
||||
should_pop = true;
|
||||
returned_node = Some(node.dom_id.get());
|
||||
returned_node = node.dom_id.get();
|
||||
}
|
||||
|
||||
// If we get a fragment we push the next child
|
||||
@@ -1313,7 +1324,7 @@ impl<'a> Iterator for RealChildIterator<'a> {
|
||||
|
||||
if frag.children.len() == 0 {
|
||||
should_pop = true;
|
||||
returned_node = Some(node.dom_id.get());
|
||||
returned_node = node.dom_id.get();
|
||||
}
|
||||
|
||||
if subcount >= frag.children.len() {
|
||||
@@ -1325,11 +1336,16 @@ impl<'a> Iterator for RealChildIterator<'a> {
|
||||
|
||||
// Immediately abort suspended nodes - can't do anything with them yet
|
||||
// VNodeKind::Suspended => should_pop = true,
|
||||
VNodeKind::Suspended { .. } => todo!(),
|
||||
VNodeKind::Suspended { node, .. } => {
|
||||
todo!()
|
||||
// *node = node.as_ref().borrow().get().expect("msg");
|
||||
}
|
||||
|
||||
// For components, we load their root and push them onto the stack
|
||||
VNodeKind::Component(sc) => {
|
||||
let scope = self.scopes.get(sc.ass_scope.get().unwrap()).unwrap();
|
||||
let scope =
|
||||
unsafe { self.scopes.get_scope(sc.ass_scope.get().unwrap()) }.unwrap();
|
||||
// let scope = self.scopes.get(sc.ass_scope.get().unwrap()).unwrap();
|
||||
|
||||
// Simply swap the current node on the stack with the root of the component
|
||||
*node = scope.root();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//!
|
||||
//!
|
||||
|
||||
use crate::{innerlude::ScopeId, RealDomNode};
|
||||
use crate::{innerlude::ScopeId, ElementId};
|
||||
|
||||
/// The `DomEditor` provides an imperative interface for the Diffing algorithm to plan out its changes.
|
||||
///
|
||||
@@ -24,7 +24,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
|
||||
}
|
||||
|
||||
// Navigation
|
||||
pub(crate) fn push(&mut self, root: RealDomNode) {
|
||||
pub(crate) fn push_root(&mut self, root: ElementId) {
|
||||
let id = root.as_u64();
|
||||
self.edits.push(PushRoot { id });
|
||||
}
|
||||
@@ -61,7 +61,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
|
||||
|
||||
// Create
|
||||
#[inline]
|
||||
pub(crate) fn create_text_node(&mut self, text: &'bump str, id: RealDomNode) {
|
||||
pub(crate) fn create_text_node(&mut self, text: &'bump str, id: ElementId) {
|
||||
let id = id.as_u64();
|
||||
self.edits.push(CreateTextNode { text, id });
|
||||
}
|
||||
@@ -70,7 +70,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
|
||||
&mut self,
|
||||
tag: &'static str,
|
||||
ns: Option<&'static str>,
|
||||
id: RealDomNode,
|
||||
id: ElementId,
|
||||
) {
|
||||
let id = id.as_u64();
|
||||
match ns {
|
||||
@@ -80,7 +80,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
|
||||
}
|
||||
|
||||
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
|
||||
pub(crate) fn create_placeholder(&mut self, id: RealDomNode) {
|
||||
pub(crate) fn create_placeholder(&mut self, id: ElementId) {
|
||||
let id = id.as_u64();
|
||||
self.edits.push(CreatePlaceholder { id });
|
||||
}
|
||||
@@ -91,7 +91,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
|
||||
event: &'static str,
|
||||
scope: ScopeId,
|
||||
element_id: usize,
|
||||
realnode: RealDomNode,
|
||||
realnode: ElementId,
|
||||
) {
|
||||
self.edits.push(NewEventListener {
|
||||
scope,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
|
||||
use crate::innerlude::{RealDomNode, ScopeId};
|
||||
use crate::innerlude::{ElementId, HeightMarker, ScopeId};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventTrigger {
|
||||
@@ -14,7 +14,7 @@ pub struct EventTrigger {
|
||||
pub originator: ScopeId,
|
||||
|
||||
/// The optional real node associated with the trigger
|
||||
pub real_node_id: Option<RealDomNode>,
|
||||
pub real_node_id: Option<ElementId>,
|
||||
|
||||
/// The type of event
|
||||
pub event: VirtualEvent,
|
||||
@@ -72,7 +72,7 @@ impl EventTrigger {
|
||||
pub fn new(
|
||||
event: VirtualEvent,
|
||||
scope: ScopeId,
|
||||
mounted_dom_id: Option<RealDomNode>,
|
||||
mounted_dom_id: Option<ElementId>,
|
||||
priority: EventPriority,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -105,6 +105,9 @@ pub enum VirtualEvent {
|
||||
|
||||
// image event has conflicting method types
|
||||
// ImageEvent(event_data::ImageEvent),
|
||||
SetStateEvent {
|
||||
height: HeightMarker,
|
||||
},
|
||||
|
||||
// Whenever a task is ready (complete) Dioxus produces this "AsyncEvent"
|
||||
//
|
||||
@@ -117,10 +120,8 @@ pub enum VirtualEvent {
|
||||
// These are more intrusive than the rest
|
||||
SuspenseEvent {
|
||||
hook_idx: usize,
|
||||
domnode: Rc<Cell<RealDomNode>>,
|
||||
domnode: Rc<Cell<Option<ElementId>>>,
|
||||
},
|
||||
|
||||
OtherEvent,
|
||||
}
|
||||
|
||||
pub mod on {
|
||||
@@ -138,7 +139,7 @@ pub mod on {
|
||||
|
||||
use crate::{
|
||||
innerlude::NodeFactory,
|
||||
innerlude::{Attribute, Listener, RealDomNode, VNode},
|
||||
innerlude::{Attribute, ElementId, Listener, VNode},
|
||||
};
|
||||
use std::cell::Cell;
|
||||
|
||||
@@ -180,8 +181,8 @@ pub mod on {
|
||||
let bump = &c.bump();
|
||||
Listener {
|
||||
event: stringify!($name),
|
||||
mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
|
||||
scope: c.scope_ref.our_arena_idx,
|
||||
mounted_node: Cell::new(None),
|
||||
scope: c.scope.our_arena_idx,
|
||||
callback: bump.alloc(move |evt: VirtualEvent| match evt {
|
||||
VirtualEvent::$wrapper(event) => callback(event),
|
||||
_ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!")
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use fxhash::FxHashMap;
|
||||
|
||||
use crate::FC;
|
||||
|
||||
pub(crate) struct HeuristicsEngine {
|
||||
heuristics: HashMap<FcSlot, Heuristic>,
|
||||
/// Provides heuristics to the "SharedResources" object for improving allocation performance.
|
||||
///
|
||||
/// This heueristic engine records the memory footprint of bump arenas and hook lists for each component. These records are
|
||||
/// then used later on to optimize the initial allocation for future components. This helps save large allocations later on
|
||||
/// that would slow down the diffing and initializion process.
|
||||
///
|
||||
///
|
||||
pub struct HeuristicsEngine {
|
||||
heuristics: FxHashMap<FcSlot, Heuristic>,
|
||||
}
|
||||
|
||||
pub(crate) type FcSlot = *const ();
|
||||
pub type FcSlot = *const ();
|
||||
|
||||
pub(crate) struct Heuristic {
|
||||
pub struct Heuristic {
|
||||
hooks: u32,
|
||||
bump_size: u64,
|
||||
}
|
||||
@@ -16,7 +25,7 @@ pub(crate) struct Heuristic {
|
||||
impl HeuristicsEngine {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
heuristics: HashMap::new(),
|
||||
heuristics: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
//!
|
||||
|
||||
pub use crate::innerlude::{
|
||||
format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, EventTrigger, LazyNodes,
|
||||
NodeFactory, Properties, RealDom, RealDomNode, ScopeId, VNode, VNodeKind, VirtualDom,
|
||||
format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, ElementId, EventTrigger,
|
||||
LazyNodes, NodeFactory, Properties, RealDom, ScopeId, VNode, VNodeKind, VirtualDom,
|
||||
VirtualEvent, FC,
|
||||
};
|
||||
|
||||
@@ -38,7 +38,6 @@ pub(crate) mod innerlude {
|
||||
pub use crate::hooklist::*;
|
||||
pub use crate::nodes::*;
|
||||
pub use crate::scope::*;
|
||||
pub use crate::tasks::*;
|
||||
pub use crate::util::*;
|
||||
pub use crate::virtual_dom::*;
|
||||
|
||||
@@ -66,6 +65,5 @@ pub mod hooklist;
|
||||
pub mod nodes;
|
||||
pub mod scope;
|
||||
pub mod signals;
|
||||
pub mod tasks;
|
||||
pub mod util;
|
||||
pub mod virtual_dom;
|
||||
+41
-26
@@ -5,7 +5,7 @@
|
||||
//! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
|
||||
use crate::{
|
||||
events::VirtualEvent,
|
||||
innerlude::{Context, DomTree, Properties, RealDomNode, Scope, ScopeId, FC},
|
||||
innerlude::{empty_cell, Context, DomTree, ElementId, Properties, Scope, ScopeId, FC},
|
||||
};
|
||||
use std::{
|
||||
cell::Cell,
|
||||
@@ -16,7 +16,12 @@ use std::{
|
||||
|
||||
pub struct VNode<'src> {
|
||||
pub kind: VNodeKind<'src>,
|
||||
pub(crate) dom_id: Cell<RealDomNode>,
|
||||
|
||||
///
|
||||
/// ElementId supports NonZero32 and Cell is zero cost, so the size of this field is unaffected
|
||||
///
|
||||
///
|
||||
pub(crate) dom_id: Cell<Option<ElementId>>,
|
||||
pub(crate) key: Option<&'src str>,
|
||||
}
|
||||
impl VNode<'_> {
|
||||
@@ -39,7 +44,7 @@ pub enum VNodeKind<'src> {
|
||||
|
||||
Component(&'src VComponent<'src>),
|
||||
|
||||
Suspended { node: Rc<Cell<RealDomNode>> },
|
||||
Suspended { node: Rc<Cell<Option<ElementId>>> },
|
||||
}
|
||||
|
||||
pub struct VText<'src> {
|
||||
@@ -50,7 +55,6 @@ pub struct VText<'src> {
|
||||
pub struct VFragment<'src> {
|
||||
pub children: &'src [VNode<'src>],
|
||||
pub is_static: bool,
|
||||
pub is_error: bool,
|
||||
}
|
||||
|
||||
pub trait DioxusElement {
|
||||
@@ -72,7 +76,7 @@ pub struct VElement<'a> {
|
||||
pub namespace: Option<&'static str>,
|
||||
|
||||
pub static_listeners: bool,
|
||||
pub listeners: &'a [Listener<'a>],
|
||||
pub listeners: &'a [&'a mut Listener<'a>],
|
||||
|
||||
pub static_attrs: bool,
|
||||
pub attributes: &'a [Attribute<'a>],
|
||||
@@ -105,9 +109,9 @@ pub struct Listener<'bump> {
|
||||
|
||||
pub scope: ScopeId,
|
||||
|
||||
pub mounted_node: &'bump mut Cell<RealDomNode>,
|
||||
pub mounted_node: Cell<Option<ElementId>>,
|
||||
|
||||
pub(crate) callback: &'bump dyn FnMut(VirtualEvent),
|
||||
pub(crate) callback: &'bump mut dyn FnMut(VirtualEvent),
|
||||
}
|
||||
|
||||
/// Virtual Components for custom user-defined components
|
||||
@@ -136,20 +140,31 @@ pub struct VComponent<'src> {
|
||||
/// This struct adds metadata to the final VNode about listeners, attributes, and children
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct NodeFactory<'a> {
|
||||
pub scope_ref: &'a Scope,
|
||||
pub scope: &'a Scope,
|
||||
pub listener_id: &'a Cell<usize>,
|
||||
}
|
||||
|
||||
impl<'a> NodeFactory<'a> {
|
||||
#[inline]
|
||||
pub fn bump(&self) -> &'a bumpalo::Bump {
|
||||
&self.scope_ref.cur_frame().bump
|
||||
&self.scope.cur_frame().bump
|
||||
}
|
||||
|
||||
pub fn unstable_place_holder() -> VNode<'static> {
|
||||
VNode {
|
||||
dom_id: empty_cell(),
|
||||
key: None,
|
||||
kind: VNodeKind::Text(VText {
|
||||
text: "",
|
||||
is_static: true,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Used in a place or two to make it easier to build vnodes from dummy text
|
||||
pub fn static_text(text: &'static str) -> VNode {
|
||||
pub fn static_text(&self, text: &'static str) -> VNode<'a> {
|
||||
VNode {
|
||||
dom_id: RealDomNode::empty_cell(),
|
||||
dom_id: empty_cell(),
|
||||
key: None,
|
||||
kind: VNodeKind::Text(VText {
|
||||
text,
|
||||
@@ -178,7 +193,7 @@ impl<'a> NodeFactory<'a> {
|
||||
pub fn text(&self, args: Arguments) -> VNode<'a> {
|
||||
let (text, is_static) = self.raw_text(args);
|
||||
VNode {
|
||||
dom_id: RealDomNode::empty_cell(),
|
||||
dom_id: empty_cell(),
|
||||
key: None,
|
||||
kind: VNodeKind::Text(VText { text, is_static }),
|
||||
}
|
||||
@@ -187,7 +202,7 @@ impl<'a> NodeFactory<'a> {
|
||||
pub fn element(
|
||||
&self,
|
||||
el: impl DioxusElement,
|
||||
listeners: &'a mut [Listener<'a>],
|
||||
listeners: &'a mut [&'a mut Listener<'a>],
|
||||
attributes: &'a [Attribute<'a>],
|
||||
children: &'a [VNode<'a>],
|
||||
key: Option<&'a str>,
|
||||
@@ -206,7 +221,7 @@ impl<'a> NodeFactory<'a> {
|
||||
&self,
|
||||
tag: &'static str,
|
||||
namespace: Option<&'static str>,
|
||||
listeners: &'a mut [Listener],
|
||||
listeners: &'a mut [&'a mut Listener<'a>],
|
||||
attributes: &'a [Attribute],
|
||||
children: &'a [VNode<'a>],
|
||||
key: Option<&'a str>,
|
||||
@@ -215,15 +230,17 @@ impl<'a> NodeFactory<'a> {
|
||||
// TODO: this code shouldn't necessarily be here of all places
|
||||
// It would make more sense to do this in diffing
|
||||
|
||||
let mut queue = self.scope_ref.listeners.borrow_mut();
|
||||
let mut queue = self.scope.listeners.borrow_mut();
|
||||
for listener in listeners.iter_mut() {
|
||||
let mounted = listener.mounted_node as *mut _;
|
||||
let callback = listener.callback as *const _ as *mut _;
|
||||
queue.push((mounted, callback))
|
||||
let raw_listener = &mut **listener;
|
||||
|
||||
let long_listener: &mut Listener<'static> =
|
||||
unsafe { std::mem::transmute(raw_listener) };
|
||||
queue.push(long_listener as *mut _)
|
||||
}
|
||||
|
||||
VNode {
|
||||
dom_id: RealDomNode::empty_cell(),
|
||||
dom_id: empty_cell(),
|
||||
key,
|
||||
kind: VNodeKind::Element(self.bump().alloc(VElement {
|
||||
tag_name: tag,
|
||||
@@ -242,10 +259,10 @@ impl<'a> NodeFactory<'a> {
|
||||
|
||||
pub fn suspended() -> VNode<'static> {
|
||||
VNode {
|
||||
dom_id: RealDomNode::empty_cell(),
|
||||
dom_id: empty_cell(),
|
||||
key: None,
|
||||
kind: VNodeKind::Suspended {
|
||||
node: Rc::new(RealDomNode::empty_cell()),
|
||||
node: Rc::new(empty_cell()),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -322,7 +339,7 @@ impl<'a> NodeFactory<'a> {
|
||||
|
||||
VNode {
|
||||
key,
|
||||
dom_id: Cell::new(RealDomNode::empty()),
|
||||
dom_id: empty_cell(),
|
||||
kind: VNodeKind::Component(self.bump().alloc_with(|| VComponent {
|
||||
user_fc,
|
||||
comparator,
|
||||
@@ -383,12 +400,11 @@ To help you identify where this error is coming from, we've generated a backtrac
|
||||
}
|
||||
}
|
||||
VNode {
|
||||
dom_id: RealDomNode::empty_cell(),
|
||||
dom_id: empty_cell(),
|
||||
key: None,
|
||||
kind: VNodeKind::Fragment(VFragment {
|
||||
children: nodes.into_bump_slice(),
|
||||
is_static: false,
|
||||
is_error: false,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -444,7 +460,6 @@ impl<'a> IntoVNode<'a> for &VNode<'a> {
|
||||
VNodeKind::Fragment(fragment) => VNodeKind::Fragment(VFragment {
|
||||
children: fragment.children,
|
||||
is_static: fragment.is_static,
|
||||
is_error: false,
|
||||
}),
|
||||
VNodeKind::Component(component) => VNodeKind::Component(component),
|
||||
|
||||
@@ -535,7 +550,7 @@ impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
|
||||
|
||||
impl IntoVNode<'_> for &'static str {
|
||||
fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||
NodeFactory::static_text(self)
|
||||
cx.static_text(self)
|
||||
}
|
||||
}
|
||||
impl IntoVNode<'_> for Arguments<'_> {
|
||||
|
||||
+31
-54
@@ -18,40 +18,30 @@ use std::{
|
||||
/// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
|
||||
/// usecase they might have.
|
||||
pub struct Scope {
|
||||
// Book-keeping about the arena
|
||||
// Book-keeping about our spot in the arena
|
||||
pub(crate) parent_idx: Option<ScopeId>,
|
||||
pub(crate) descendents: RefCell<HashSet<ScopeId>>,
|
||||
pub(crate) our_arena_idx: ScopeId,
|
||||
pub(crate) height: u32,
|
||||
pub(crate) descendents: RefCell<HashSet<ScopeId>>,
|
||||
|
||||
// Nodes
|
||||
// an internal, highly efficient storage of vnodes
|
||||
pub(crate) frames: ActiveFrame,
|
||||
pub(crate) child_nodes: &'static [VNode<'static>],
|
||||
pub(crate) caller: Rc<WrappedCaller>,
|
||||
pub(crate) child_nodes: &'static [VNode<'static>],
|
||||
|
||||
// Listeners
|
||||
pub(crate) listeners: RefCell<Vec<(*mut Cell<RealDomNode>, *mut dyn FnMut(VirtualEvent))>>,
|
||||
pub(crate) listeners: RefCell<Vec<*mut Listener<'static>>>,
|
||||
pub(crate) listener_idx: Cell<usize>,
|
||||
|
||||
// State
|
||||
pub(crate) hooks: HookList,
|
||||
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
|
||||
|
||||
// Events
|
||||
pub(crate) event_channel: Rc<dyn Fn() + 'static>,
|
||||
|
||||
// Tasks
|
||||
pub(crate) task_submitter: TaskSubmitter,
|
||||
|
||||
// A reference to the list of components.
|
||||
// This lets us traverse the component list whenever we need to access our parent or children.
|
||||
pub(crate) arena_link: SharedArena,
|
||||
// A reference to the resources shared by all the comonents
|
||||
pub(crate) vdom: SharedResources,
|
||||
}
|
||||
|
||||
// The type of the channel function
|
||||
type EventChannel = Rc<dyn Fn()>;
|
||||
|
||||
// The type of closure that wraps calling components
|
||||
pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> DomTree<'b>;
|
||||
|
||||
@@ -68,13 +58,16 @@ impl Scope {
|
||||
// Therefore, their lifetimes are connected exclusively to the virtual dom
|
||||
pub fn new<'creator_node>(
|
||||
caller: Rc<WrappedCaller>,
|
||||
|
||||
arena_idx: ScopeId,
|
||||
|
||||
parent: Option<ScopeId>,
|
||||
|
||||
height: u32,
|
||||
event_channel: EventChannel,
|
||||
arena_link: SharedArena,
|
||||
|
||||
child_nodes: &'creator_node [VNode<'creator_node>],
|
||||
task_submitter: TaskSubmitter,
|
||||
|
||||
vdom: SharedResources,
|
||||
) -> Self {
|
||||
let child_nodes = unsafe { std::mem::transmute(child_nodes) };
|
||||
Self {
|
||||
@@ -83,9 +76,7 @@ impl Scope {
|
||||
parent_idx: parent,
|
||||
our_arena_idx: arena_idx,
|
||||
height,
|
||||
event_channel,
|
||||
arena_link,
|
||||
task_submitter,
|
||||
vdom,
|
||||
listener_idx: Default::default(),
|
||||
frames: ActiveFrame::new(),
|
||||
hooks: Default::default(),
|
||||
@@ -113,7 +104,7 @@ impl Scope {
|
||||
// Remove all the outdated listeners
|
||||
|
||||
// This is a very dangerous operation
|
||||
let next_frame = self.frames.old_frame_mut();
|
||||
let next_frame = self.frames.prev_frame_mut();
|
||||
next_frame.bump.reset();
|
||||
|
||||
self.listeners.borrow_mut().clear();
|
||||
@@ -132,7 +123,7 @@ impl Scope {
|
||||
}
|
||||
Some(new_head) => {
|
||||
// the user's component succeeded. We can safely cycle to the next frame
|
||||
self.frames.old_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
|
||||
self.frames.prev_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
|
||||
self.frames.cycle_frame();
|
||||
Ok(())
|
||||
}
|
||||
@@ -162,33 +153,27 @@ impl Scope {
|
||||
|
||||
let listners = self.listeners.borrow_mut();
|
||||
|
||||
let raw_listener = listners.iter().find(|(domptr, _)| {
|
||||
let search = unsafe { &**domptr };
|
||||
let search_id = search.get();
|
||||
let raw_listener = listners.iter().find(|lis| {
|
||||
let search = unsafe { &mut ***lis };
|
||||
let search_id = search.mounted_node.get();
|
||||
log::info!("searching listener {:#?}", search_id);
|
||||
match real_node_id {
|
||||
Some(e) => search_id == e,
|
||||
None => false,
|
||||
|
||||
match (real_node_id, search_id) {
|
||||
(Some(e), Some(search_id)) => search_id == e,
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
|
||||
match raw_listener {
|
||||
Some((_node, listener)) => unsafe {
|
||||
// TODO: Don'tdo a linear scan! Do a hashmap lookup! It'll be faster!
|
||||
let listener_fn = &mut **listener;
|
||||
listener_fn(event);
|
||||
},
|
||||
None => todo!(),
|
||||
if let Some(raw_listener) = raw_listener {
|
||||
let listener = unsafe { &mut **raw_listener };
|
||||
(listener.callback)(event);
|
||||
} else {
|
||||
log::warn!("An event was triggered but there was no listener to handle it");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn submit_task(&self, task: FiberTask) {
|
||||
log::debug!("Task submitted into scope");
|
||||
(self.task_submitter)(task);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn next_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
||||
self.frames.current_head_node()
|
||||
@@ -207,18 +192,10 @@ impl Scope {
|
||||
/// Get the root VNode of this component
|
||||
#[inline]
|
||||
pub fn root<'a>(&'a self) -> &'a VNode<'a> {
|
||||
&self.frames.cur_frame().head_node
|
||||
&self.frames.current_head_node()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn errored_fragment() -> VNode<'static> {
|
||||
VNode {
|
||||
dom_id: RealDomNode::empty_cell(),
|
||||
key: None,
|
||||
kind: VNodeKind::Fragment(VFragment {
|
||||
children: &[],
|
||||
is_static: false,
|
||||
is_error: true,
|
||||
}),
|
||||
#[inline]
|
||||
pub fn child_nodes<'a>(&'a self) -> &'a [VNode<'a>] {
|
||||
unsafe { std::mem::transmute(self.child_nodes) }
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
//! The TaskQueue serves as a centralized async store for all tasks in Dioxus.
|
||||
//! When a component renders, it may submit an async task to the queue.
|
||||
//!
|
||||
//! Then the task complete, it is emitted from the virtual dom in the event loop, which is then fed back into the virtualdom
|
||||
//! as an event trigger.
|
||||
//!
|
||||
//! When a component is scheduled to re-render, the awaing task must be dumped from the queue.
|
||||
//!
|
||||
//! This is all pretty unsafe stuff.
|
||||
//! The major invariant here is that tasks that enter the queue may be invalidated during transitions.
|
||||
|
||||
use std::{
|
||||
cell::Cell,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use futures_util::{stream::FuturesUnordered, Future, Stream, StreamExt};
|
||||
use slotmap::{DefaultKey, SlotMap};
|
||||
|
||||
use crate::innerlude::{EventTrigger, FiberTask, ScopeId};
|
||||
|
||||
pub type TaskSubmitter = Arc<dyn Fn(FiberTask)>;
|
||||
|
||||
pub struct TaskQueue {
|
||||
slots: Arc<RwLock<FuturesUnordered<FiberTask>>>,
|
||||
// slots: Arc<RwLock<SlotMap<DefaultKey, DTask>>>,
|
||||
submitter: TaskSubmitter,
|
||||
}
|
||||
|
||||
impl TaskQueue {
|
||||
pub fn new() -> Self {
|
||||
let slots = Arc::new(RwLock::new(FuturesUnordered::new()));
|
||||
let slots2 = slots.clone();
|
||||
|
||||
let submitter = Arc::new(move |task| {
|
||||
let mut slots = slots2.write().unwrap();
|
||||
log::debug!("Task submitted into global task queue");
|
||||
slots.push(task);
|
||||
});
|
||||
Self { slots, submitter }
|
||||
}
|
||||
|
||||
pub fn new_submitter(&self) -> TaskSubmitter {
|
||||
self.submitter.clone()
|
||||
}
|
||||
|
||||
pub fn submit_task(&mut self, task: FiberTask) {
|
||||
self.slots.write().unwrap().push(task);
|
||||
// TaskHandle { key }
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.slots.read().unwrap().is_empty()
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.slots.read().unwrap().len()
|
||||
}
|
||||
|
||||
pub async fn next(&mut self) -> Option<EventTrigger> {
|
||||
let mut slots = self.slots.write().unwrap();
|
||||
slots.next().await
|
||||
}
|
||||
}
|
||||
|
||||
// impl Stream for TaskQueue {
|
||||
// type Item = EventTrigger;
|
||||
|
||||
// /// We can never be finished polling
|
||||
// fn poll_next(
|
||||
// self: Pin<&mut Self>,
|
||||
// cx: &mut std::task::Context<'_>,
|
||||
// ) -> std::task::Poll<Option<Self::Item>> {
|
||||
// // let yield_every = self.len();
|
||||
// // let mut polled = 0;
|
||||
|
||||
// let mut slots = self.slots.write().unwrap();
|
||||
// for (_key, slot) in slots.iter_mut() {
|
||||
// if slot.dead.get() {
|
||||
// continue;
|
||||
// }
|
||||
// let r = slot.fut;
|
||||
// // let fut = unsafe { &mut *r };
|
||||
// // use futures::{future::Future, poll, FutureExt};
|
||||
|
||||
// let f2 = fut.as_mut();
|
||||
// let w = cx.waker();
|
||||
// let mut cx = Context::from_waker(&w);
|
||||
|
||||
// // Pin::new_unchecked(pointer)
|
||||
// // use std::future::Future;
|
||||
// match f2.poll(&mut cx) {
|
||||
// Poll::Ready(_) => {
|
||||
// let trigger = EventTrigger::new_from_task(slot.originator);
|
||||
// slot.dead.set(true);
|
||||
// return Poll::Ready(Some(trigger));
|
||||
// }
|
||||
// Poll::Pending => continue,
|
||||
// }
|
||||
// }
|
||||
|
||||
// // we tried polling every active task.
|
||||
// // give up and relinquish controlto the parent
|
||||
|
||||
// // We have polled a large number of futures in a row without yielding.
|
||||
// // To ensure we do not starve other tasks waiting on the executor,
|
||||
// // we yield here, but immediately wake ourselves up to continue.
|
||||
// // cx.waker().wake_by_ref();
|
||||
// return Poll::Pending;
|
||||
// }
|
||||
// }
|
||||
|
||||
pub struct TaskHandle {
|
||||
key: DefaultKey,
|
||||
}
|
||||
|
||||
pub struct DTask {
|
||||
fut: FiberTask,
|
||||
originator: ScopeId,
|
||||
dead: Cell<bool>,
|
||||
}
|
||||
impl DTask {
|
||||
pub fn new(fut: FiberTask, originator: ScopeId) -> Self {
|
||||
Self {
|
||||
fut,
|
||||
originator,
|
||||
dead: Cell::new(false),
|
||||
}
|
||||
}
|
||||
pub fn debug_new(fut: FiberTask) -> Self {
|
||||
let originator = ScopeId::default();
|
||||
Self {
|
||||
fut,
|
||||
originator,
|
||||
dead: Cell::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,11 @@
|
||||
use std::{
|
||||
cell::{Cell, RefCell, RefMut},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use futures_util::StreamExt;
|
||||
use slotmap::{DefaultKey, Key, KeyData};
|
||||
use std::cell::Cell;
|
||||
|
||||
use crate::innerlude::*;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Default)]
|
||||
pub struct EventQueue {
|
||||
pub queue: Rc<RefCell<Vec<HeightMarker>>>,
|
||||
}
|
||||
|
||||
impl EventQueue {
|
||||
pub fn new_channel(&self, height: u32, idx: ScopeId) -> Rc<dyn Fn()> {
|
||||
let inner = self.clone();
|
||||
let marker = HeightMarker { height, idx };
|
||||
Rc::new(move || {
|
||||
log::debug!("channel updated {:#?}", marker);
|
||||
inner.queue.as_ref().borrow_mut().push(marker)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sort_unstable(&self) {
|
||||
self.queue.borrow_mut().sort_unstable()
|
||||
}
|
||||
|
||||
pub fn borrow_mut(&self) -> RefMut<Vec<HeightMarker>> {
|
||||
self.queue.borrow_mut()
|
||||
}
|
||||
// create a cell with a "none" value
|
||||
#[inline]
|
||||
pub fn empty_cell() -> Cell<Option<ElementId>> {
|
||||
Cell::new(None as Option<ElementId>)
|
||||
}
|
||||
|
||||
/// A helper type that lets scopes be ordered by their height
|
||||
@@ -51,41 +27,10 @@ impl PartialOrd for HeightMarker {
|
||||
}
|
||||
}
|
||||
|
||||
/// The `RealDomNode` is an ID handle that corresponds to a foreign DOM node.
|
||||
///
|
||||
/// "u64" was chosen for two reasons
|
||||
/// - 0 cost hashing
|
||||
/// - use with slotmap and other versioned slot arenas
|
||||
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct RealDomNode(pub u64);
|
||||
impl RealDomNode {
|
||||
#[inline]
|
||||
pub fn empty() -> Self {
|
||||
Self(u64::MIN)
|
||||
}
|
||||
#[inline]
|
||||
pub fn empty_cell() -> Cell<Self> {
|
||||
Cell::new(Self::empty())
|
||||
}
|
||||
#[inline]
|
||||
pub fn from_u64(id: u64) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_u64(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugDom {
|
||||
counter: u64,
|
||||
}
|
||||
pub struct DebugDom {}
|
||||
impl DebugDom {
|
||||
pub fn new() -> Self {
|
||||
Self { counter: 0 }
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,9 +38,4 @@ impl<'a> RealDom<'a> for DebugDom {
|
||||
fn raw_node_as_any(&self) -> &mut dyn std::any::Any {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn request_available_node(&mut self) -> RealDomNode {
|
||||
self.counter += 1;
|
||||
RealDomNode::from_u64(self.counter)
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,7 @@
|
||||
//! This module includes just the barebones for a complete VirtualDOM API.
|
||||
//! Additional functionality is defined in the respective files.
|
||||
|
||||
use crate::tasks::TaskQueue;
|
||||
use crate::{arena::SharedArena, innerlude::*};
|
||||
use crate::{arena::SharedResources, innerlude::*};
|
||||
|
||||
use slotmap::DefaultKey;
|
||||
use slotmap::SlotMap;
|
||||
@@ -30,10 +29,6 @@ use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
use std::pin::Pin;
|
||||
|
||||
slotmap::new_key_type! {
|
||||
pub struct ScopeId;
|
||||
}
|
||||
|
||||
/// An integrated virtual node system that progresses events and diffs UI trees.
|
||||
/// Differences are converted into patches which a renderer can use to draw the UI.
|
||||
///
|
||||
@@ -49,7 +44,7 @@ pub struct VirtualDom {
|
||||
///
|
||||
/// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
|
||||
/// and rusts's guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
|
||||
pub components: SharedArena,
|
||||
pub shared: SharedResources,
|
||||
|
||||
/// The index of the root component
|
||||
/// Should always be the first (gen=0, id=0)
|
||||
@@ -57,21 +52,12 @@ pub struct VirtualDom {
|
||||
|
||||
pub triggers: RefCell<Vec<EventTrigger>>,
|
||||
|
||||
/// All components dump their updates into a queue to be processed
|
||||
pub event_queue: EventQueue,
|
||||
|
||||
pub tasks: TaskQueue,
|
||||
|
||||
heuristics: HeuristicsEngine,
|
||||
|
||||
root_props: std::pin::Pin<Box<dyn std::any::Any>>,
|
||||
|
||||
/// Type of the original props. This is stored as TypeId so VirtualDom does not need to be generic.
|
||||
///
|
||||
/// Whenver props need to be updated, an Error will be thrown if the new props do not
|
||||
/// match the props used to create the VirtualDom.
|
||||
// for managing the props that were used to create the dom
|
||||
#[doc(hidden)]
|
||||
_root_prop_type: std::any::TypeId,
|
||||
|
||||
#[doc(hidden)]
|
||||
_root_props: std::pin::Pin<Box<dyn std::any::Any>>,
|
||||
}
|
||||
|
||||
// ======================================
|
||||
@@ -142,37 +128,22 @@ impl VirtualDom {
|
||||
/// let dom = VirtualDom::new(Example);
|
||||
/// ```
|
||||
pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
|
||||
let components = SharedArena::new(SlotMap::<ScopeId, Scope>::with_key());
|
||||
let components = SharedResources::new();
|
||||
|
||||
let root_props: Pin<Box<dyn Any>> = Box::pin(root_props);
|
||||
let props_ptr = root_props.as_ref().downcast_ref::<P>().unwrap() as *const P;
|
||||
|
||||
// Build a funnel for hooks to send their updates into. The `use_hook` method will call into the update funnel.
|
||||
let event_queue = EventQueue::default();
|
||||
let _event_queue = event_queue.clone();
|
||||
|
||||
let link = components.clone();
|
||||
|
||||
let tasks = TaskQueue::new();
|
||||
let submitter = tasks.new_submitter();
|
||||
|
||||
let base_scope = components
|
||||
.with(|arena| {
|
||||
arena.insert_with_key(move |myidx| {
|
||||
let event_channel = _event_queue.new_channel(0, myidx);
|
||||
let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
|
||||
Scope::new(caller, myidx, None, 0, event_channel, link, &[], submitter)
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
let base_scope = components.insert_scope_with_key(move |myidx| {
|
||||
let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
|
||||
Scope::new(caller, myidx, None, 0, &[], link)
|
||||
});
|
||||
|
||||
Self {
|
||||
base_scope,
|
||||
event_queue,
|
||||
components,
|
||||
root_props,
|
||||
tasks,
|
||||
heuristics: HeuristicsEngine::new(),
|
||||
_root_props: root_props,
|
||||
shared: components,
|
||||
triggers: Default::default(),
|
||||
_root_prop_type: TypeId::of::<P>(),
|
||||
}
|
||||
@@ -209,29 +180,28 @@ impl VirtualDom {
|
||||
|
||||
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
|
||||
///
|
||||
/// Currently this doesn't do what we want it to do
|
||||
///
|
||||
/// The diff machine expects the RealDom's stack to be the root of the application
|
||||
pub fn rebuild<'s, Dom: RealDom<'s>>(
|
||||
&'s mut self,
|
||||
realdom: &mut Dom,
|
||||
edits: &mut Vec<DomEdit<'s>>,
|
||||
) -> Result<()> {
|
||||
let mut diff_machine = DiffMachine::new(
|
||||
edits,
|
||||
realdom,
|
||||
&self.components,
|
||||
self.base_scope,
|
||||
self.event_queue.clone(),
|
||||
&self.tasks,
|
||||
);
|
||||
let mut diff_machine = DiffMachine::new(edits, realdom, self.base_scope, &self.shared);
|
||||
|
||||
let cur_component = self.components.get_mut(self.base_scope).unwrap();
|
||||
let cur_component = diff_machine
|
||||
.get_scope_mut(&self.base_scope)
|
||||
.expect("The base scope should never be moved");
|
||||
|
||||
// We run the component. If it succeeds, then we can diff it and add the changes to the dom.
|
||||
if cur_component.run_scope().is_ok() {
|
||||
let meta = diff_machine.create(cur_component.next_frame());
|
||||
diff_machine.edits.append_children(meta.added_to_stack);
|
||||
} else {
|
||||
// todo: should this be a hard error?
|
||||
log::warn!(
|
||||
"Component failed to run succesfully during rebuild.
|
||||
This does not result in a failed rebuild, but indicates a logic failure within your app."
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -242,10 +212,9 @@ impl VirtualDom {
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn queue_event(&self, trigger: EventTrigger) -> Result<()> {
|
||||
pub fn queue_event(&self, trigger: EventTrigger) {
|
||||
let mut triggers = self.triggers.borrow_mut();
|
||||
triggers.push(trigger);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
|
||||
@@ -298,26 +267,18 @@ impl VirtualDom {
|
||||
) -> Result<()> {
|
||||
let trigger = self.triggers.borrow_mut().pop().expect("failed");
|
||||
|
||||
let mut diff_machine = DiffMachine::new(
|
||||
edits,
|
||||
realdom,
|
||||
&self.components,
|
||||
trigger.originator,
|
||||
self.event_queue.clone(),
|
||||
&self.tasks,
|
||||
);
|
||||
let mut diff_machine = DiffMachine::new(edits, realdom, trigger.originator, &self.shared);
|
||||
|
||||
match &trigger.event {
|
||||
VirtualEvent::OtherEvent => todo!(),
|
||||
|
||||
// Nothing yet
|
||||
VirtualEvent::AsyncEvent { .. } => {}
|
||||
|
||||
// Suspense Events! A component's suspended node is updated
|
||||
VirtualEvent::SuspenseEvent { hook_idx, domnode } => {
|
||||
let scope = self.components.get_mut(trigger.originator).unwrap();
|
||||
// Safety: this handler is the only thing that can mutate shared items at this moment in tim
|
||||
let scope = diff_machine.get_scope_mut(&trigger.originator).unwrap();
|
||||
|
||||
// safety: we are sure that there are no other references to the inner content of this hook
|
||||
// safety: we are sure that there are no other references to the inner content of suspense hooks
|
||||
let hook = unsafe { scope.hooks.get_mut::<SuspenseHook>(*hook_idx) }.unwrap();
|
||||
|
||||
let cx = Context { scope, props: &() };
|
||||
@@ -325,17 +286,24 @@ impl VirtualDom {
|
||||
|
||||
// generate the new node!
|
||||
let nodes: Option<VNode<'s>> = (&hook.callback)(scx);
|
||||
let nodes = nodes.unwrap_or_else(|| errored_fragment());
|
||||
let nodes = scope.cur_frame().bump.alloc(nodes);
|
||||
match nodes {
|
||||
None => {
|
||||
log::warn!("Suspense event came through, but there was no mounted node to update >:(");
|
||||
}
|
||||
Some(nodes) => {
|
||||
let nodes = scope.cur_frame().bump.alloc(nodes);
|
||||
|
||||
// push the old node's root onto the stack
|
||||
diff_machine.edits.push(domnode.get());
|
||||
// push the old node's root onto the stack
|
||||
let real_id = domnode.get().ok_or(Error::NotMounted)?;
|
||||
diff_machine.edits.push_root(real_id);
|
||||
|
||||
// push these new nodes onto the diff machines stack
|
||||
let meta = diff_machine.create(&*nodes);
|
||||
// push these new nodes onto the diff machines stack
|
||||
let meta = diff_machine.create(&*nodes);
|
||||
|
||||
// replace the placeholder with the new nodes we just pushed on the stack
|
||||
diff_machine.edits.replace_with(meta.added_to_stack);
|
||||
// replace the placeholder with the new nodes we just pushed on the stack
|
||||
diff_machine.edits.replace_with(meta.added_to_stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is the "meat" of our cooperative scheduler
|
||||
@@ -343,12 +311,12 @@ impl VirtualDom {
|
||||
//
|
||||
// We use the reconciler to request new IDs and then commit/uncommit the IDs when the scheduler is finished
|
||||
_ => {
|
||||
self.components
|
||||
.get_mut(trigger.originator)
|
||||
diff_machine
|
||||
.get_scope_mut(&trigger.originator)
|
||||
.map(|f| f.call_listener(trigger));
|
||||
|
||||
// Now, there are events in the queue
|
||||
let mut updates = self.event_queue.queue.as_ref().borrow_mut();
|
||||
let mut updates = self.shared.borrow_queue();
|
||||
|
||||
// Order the nodes by their height, we want the nodes with the smallest depth on top
|
||||
// This prevents us from running the same component multiple times
|
||||
@@ -370,8 +338,10 @@ impl VirtualDom {
|
||||
diff_machine.seen_nodes.insert(update.idx.clone());
|
||||
|
||||
// Start a new mutable borrow to components
|
||||
// We are guaranteeed that this scope is unique because we are tracking which nodes have modified
|
||||
let cur_component = self.components.get_mut(update.idx).unwrap();
|
||||
// We are guaranteeed that this scope is unique because we are tracking which nodes have modified in the diff machine
|
||||
let cur_component = diff_machine
|
||||
.get_scope_mut(&update.idx)
|
||||
.expect("Failed to find scope or borrow would be aliasing");
|
||||
|
||||
if cur_component.run_scope().is_ok() {
|
||||
let (old, new) = (cur_component.old_frame(), cur_component.next_frame());
|
||||
@@ -385,7 +355,7 @@ impl VirtualDom {
|
||||
}
|
||||
|
||||
pub fn base_scope(&self) -> &Scope {
|
||||
self.components.get(self.base_scope).unwrap()
|
||||
unsafe { self.shared.get_scope(self.base_scope).unwrap() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
|
||||
"{:}",
|
||||
TextRenderer {
|
||||
cfg: SsrConfig::default(),
|
||||
root: vdom.components.get(scope).unwrap().root(),
|
||||
root: vdom.shared.get_scope(scope).unwrap().root(),
|
||||
vdom: Some(vdom)
|
||||
}
|
||||
))
|
||||
@@ -163,7 +163,7 @@ impl<'a> TextRenderer<'a> {
|
||||
VNodeKind::Component(vcomp) => {
|
||||
let idx = vcomp.ass_scope.get().unwrap();
|
||||
if let Some(vdom) = self.vdom {
|
||||
let new_node = vdom.components.get(idx).unwrap().root();
|
||||
let new_node = vdom.shared.get_scope(idx).unwrap().root();
|
||||
self.html_render(new_node, f, il + 1)?;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-11
@@ -13,7 +13,7 @@ use web_sys::{
|
||||
|
||||
pub struct WebsysDom {
|
||||
pub stack: Stack,
|
||||
nodes: slotmap::SlotMap<DefaultKey, Option<Node>>,
|
||||
nodes: slotmap::SlotMap<DefaultKey, Node>,
|
||||
document: Document,
|
||||
root: Element,
|
||||
|
||||
@@ -51,7 +51,7 @@ impl WebsysDom {
|
||||
|
||||
let mut nodes = slotmap::SlotMap::with_capacity(1000);
|
||||
|
||||
let root_id = nodes.insert(Some(root.clone().dyn_into::<Node>().unwrap()));
|
||||
let root_id = nodes.insert(root.clone().dyn_into::<Node>().unwrap());
|
||||
|
||||
Self {
|
||||
stack: Stack::with_capacity(10),
|
||||
@@ -101,11 +101,16 @@ impl WebsysDom {
|
||||
let key = DefaultKey::from(KeyData::from_ffi(root));
|
||||
let domnode = self.nodes.get_mut(key);
|
||||
|
||||
let domnode = domnode.unwrap().as_mut().unwrap();
|
||||
let real_node: Node = match domnode {
|
||||
Some(n) => n.clone(),
|
||||
None => todo!(),
|
||||
};
|
||||
|
||||
// let domnode = domnode.unwrap().as_mut().unwrap();
|
||||
// .expect(&format!("Failed to pop know root: {:#?}", key))
|
||||
// .unwrap();
|
||||
|
||||
self.stack.push(domnode.clone());
|
||||
self.stack.push(real_node);
|
||||
}
|
||||
// drop the node off the stack
|
||||
fn pop(&mut self) {
|
||||
@@ -202,7 +207,7 @@ impl WebsysDom {
|
||||
*self
|
||||
.nodes
|
||||
.get_mut(DefaultKey::from(KeyData::from_ffi(id)))
|
||||
.unwrap() = Some(textnode);
|
||||
.unwrap() = textnode;
|
||||
}
|
||||
|
||||
fn create_element(&mut self, tag: &str, ns: Option<&'static str>, id: u64) {
|
||||
@@ -224,7 +229,7 @@ impl WebsysDom {
|
||||
let id = DefaultKey::from(KeyData::from_ffi(id));
|
||||
|
||||
self.stack.push(el.clone());
|
||||
*self.nodes.get_mut(id).unwrap() = Some(el);
|
||||
*self.nodes.get_mut(id).unwrap() = el;
|
||||
// let nid = self.node_counter.?next();
|
||||
// let nid = self.nodes.insert(el).data().as_ffi();
|
||||
// log::debug!("Called [`create_element`]: {}, {:?}", tag, nid);
|
||||
@@ -344,11 +349,11 @@ impl WebsysDom {
|
||||
}
|
||||
|
||||
impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
|
||||
fn request_available_node(&mut self) -> RealDomNode {
|
||||
let key = self.nodes.insert(None);
|
||||
log::debug!("making new key: {:#?}", key);
|
||||
RealDomNode(key.data().as_ffi())
|
||||
}
|
||||
// fn request_available_node(&mut self) -> RealDomNode {
|
||||
// let key = self.nodes.insert(None);
|
||||
// log::debug!("making new key: {:#?}", key);
|
||||
// RealDomNode(key.data().as_ffi())
|
||||
// }
|
||||
|
||||
fn raw_node_as_any(&self) -> &mut dyn std::any::Any {
|
||||
todo!()
|
||||
|
||||
Reference in new issue
Block a user