allow changing the number of root nodes

This commit is contained in:
Evan Almloff 2022-12-21 12:50:48 -06:00
parent e5a5850354
commit 4c1fe1d9bb
6 changed files with 112 additions and 21 deletions

View file

@ -126,11 +126,9 @@ impl VirtualDom {
}
});
for root in node.root_ids {
if let Some(id) = root.get() {
if id.0 != 0 {
self.try_reclaim(id);
}
for id in &node.root_ids {
if id.0 != 0 {
self.try_reclaim(id);
}
}
}

View file

@ -25,6 +25,10 @@ impl<'b> VirtualDom {
/// Create this template and write its mutations
pub(crate) fn create(&mut self, node: &'b VNode<'b>) -> usize {
// Intialize the root nodes slice
node.root_ids
.intialize(vec![ElementId(0); node.template.get().roots.len()].into_boxed_slice());
// The best renderers will have templates prehydrated and registered
// Just in case, let's create the template using instructions anyways
if !self.templates.contains_key(&node.template.get().name) {
@ -213,7 +217,7 @@ impl<'b> VirtualDom {
fn load_template_root(&mut self, template: &VNode, root_idx: usize) -> ElementId {
// Get an ID for this root since it's a real root
let this_id = self.next_root(template, root_idx);
template.root_ids[root_idx].set(Some(this_id));
template.root_ids.set(root_idx, this_id);
self.mutations.push(LoadTemplate {
name: template.template.get().name,

View file

@ -108,11 +108,7 @@ impl<'b> VirtualDom {
});
// Make sure the roots get transferred over while we're here
left_template
.root_ids
.iter()
.zip(right_template.root_ids.iter())
.for_each(|(left, right)| right.set(left.get()));
right_template.root_ids.transfer(&left_template.root_ids);
}
fn diff_dynamic_node(
@ -662,7 +658,7 @@ impl<'b> VirtualDom {
Some(node) => node,
None => {
self.mutations.push(Mutation::PushRoot {
id: node.root_ids[idx].get().unwrap(),
id: node.root_ids.get(idx).unwrap(),
});
return 1;
}
@ -793,7 +789,7 @@ impl<'b> VirtualDom {
if let Some(dy) = node.dynamic_root(idx) {
self.remove_dynamic_node(dy, gen_muts);
} else {
let id = node.root_ids[idx].get().unwrap();
let id = node.root_ids.get(idx).unwrap();
if gen_muts {
self.mutations.push(Mutation::Remove { id });
}
@ -889,7 +885,7 @@ impl<'b> VirtualDom {
fn find_first_element(&self, node: &'b VNode<'b>) -> ElementId {
match node.dynamic_root(0) {
None => node.root_ids[0].get().unwrap(),
None => node.root_ids.get(0).unwrap(),
Some(Text(t)) => t.id.get().unwrap(),
Some(Fragment(t)) => self.find_first_element(&t[0]),
Some(Placeholder(t)) => t.id.get().unwrap(),
@ -905,7 +901,7 @@ impl<'b> VirtualDom {
fn find_last_element(&self, node: &'b VNode<'b>) -> ElementId {
match node.dynamic_root(node.template.get().roots.len() - 1) {
None => node.root_ids.last().unwrap().get().unwrap(),
None => node.root_ids.last().unwrap(),
Some(Text(t)) => t.id.get().unwrap(),
Some(Fragment(t)) => self.find_last_element(t.last().unwrap()),
Some(Placeholder(t)) => t.id.get().unwrap(),

View file

@ -32,7 +32,7 @@ pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
key: children.key,
parent: children.parent,
template: children.template.clone(),
root_ids: children.root_ids,
root_ids: children.root_ids.clone(),
dynamic_nodes: children.dynamic_nodes,
dynamic_attrs: children.dynamic_attrs,
})

View file

@ -5,7 +5,7 @@ use bumpalo::boxed::Box as BumpBox;
use bumpalo::Bump;
use std::{
any::{Any, TypeId},
cell::{Cell, RefCell},
cell::{Cell, RefCell, UnsafeCell},
fmt::Arguments,
future::Future,
};
@ -46,7 +46,7 @@ pub struct VNode<'a> {
/// The IDs for the roots of this template - to be used when moving the template around and removing it from
/// the actual Dom
pub root_ids: bumpalo::collections::Vec<'a, Cell<Option<ElementId>>>,
pub root_ids: BoxedCellSlice,
/// The dynamic parts of the template
pub dynamic_nodes: &'a [DynamicNode<'a>],
@ -55,13 +55,106 @@ pub struct VNode<'a> {
pub dynamic_attrs: &'a [Attribute<'a>],
}
// Saftey: There is no way to get references to the internal data of this struct so no refrences will be invalidated by mutating the data with a immutable reference (The same principle behind Cell)
#[derive(Debug, Default)]
pub struct BoxedCellSlice(UnsafeCell<Option<Box<[ElementId]>>>);
impl Clone for BoxedCellSlice {
fn clone(&self) -> Self {
Self(UnsafeCell::new(unsafe { (*self.0.get()).clone() }))
}
}
impl BoxedCellSlice {
pub fn last(&self) -> Option<ElementId> {
unsafe {
(*self.0.get())
.as_ref()
.and_then(|inner| inner.as_ref().last().copied())
}
}
pub fn get(&self, idx: usize) -> Option<ElementId> {
unsafe {
(*self.0.get())
.as_ref()
.and_then(|inner| inner.as_ref().get(idx).copied())
}
}
pub unsafe fn get_unchecked(&self, idx: usize) -> Option<ElementId> {
(*self.0.get())
.as_ref()
.and_then(|inner| inner.as_ref().get(idx).copied())
}
pub fn set(&self, idx: usize, new: ElementId) {
unsafe {
if let Some(inner) = &mut *self.0.get() {
inner[idx] = new;
}
}
}
pub fn intialize(&self, contents: Box<[ElementId]>) {
unsafe {
*self.0.get() = Some(contents);
}
}
pub fn transfer(&self, other: &Self) {
unsafe {
*self.0.get() = (*other.0.get()).take();
}
}
pub fn len(&self) -> usize {
unsafe {
(*self.0.get())
.as_ref()
.map(|inner| inner.len())
.unwrap_or(0)
}
}
}
impl<'a> IntoIterator for &'a BoxedCellSlice {
type Item = ElementId;
type IntoIter = BoxedCellSliceIter<'a>;
fn into_iter(self) -> Self::IntoIter {
BoxedCellSliceIter {
index: 0,
borrow: self,
}
}
}
pub struct BoxedCellSliceIter<'a> {
index: usize,
borrow: &'a BoxedCellSlice,
}
impl Iterator for BoxedCellSliceIter<'_> {
type Item = ElementId;
fn next(&mut self) -> Option<Self::Item> {
let result = self.borrow.get(self.index);
if result.is_some() {
self.index += 1;
}
result
}
}
impl<'a> VNode<'a> {
/// Create a template with no nodes that will be skipped over during diffing
pub fn empty() -> Element<'a> {
Some(VNode {
key: None,
parent: None,
root_ids: bumpalo::collections::Vec::new_in(&Bump::new()),
root_ids: BoxedCellSlice::default(),
dynamic_nodes: &[],
dynamic_attrs: &[],
template: Cell::new(Template {
@ -572,7 +665,7 @@ impl<'a> IntoDynNode<'a> for &'a VNode<'a> {
DynamicNode::Fragment(_cx.bump().alloc([VNode {
parent: self.parent,
template: self.template.clone(),
root_ids: self.root_ids,
root_ids: self.root_ids.clone(),
key: self.key,
dynamic_nodes: self.dynamic_nodes,
dynamic_attrs: self.dynamic_attrs,

View file

@ -212,7 +212,7 @@ impl<'a, Ctx: HotReloadingContext> ToTokens for TemplateRenderer<'a, Ctx> {
parent: None,
key: #key_tokens,
template: std::cell::Cell::new(TEMPLATE),
root_ids: std::cell::Cell::from_mut( __cx.bump().alloc([None; #num_roots]) as &mut _).as_slice_of_cells(),
root_ids: Default::default(),
// root_ids: std::cell::Cell::from_mut( __cx.bump().alloc([None; #num_roots]) as &mut [::dioxus::core::ElementId]).as_slice_of_cells(),
dynamic_nodes: __cx.bump().alloc([ #( #node_printer ),* ]),
dynamic_attrs: __cx.bump().alloc([ #( #dyn_attr_printer ),* ]),