Merge pull request #648 from Demonthos/fix-non-str-attributes
fix custom attribute value support
This commit is contained in:
36 files changed
+806
-711
No files matched your search
@@ -44,4 +44,4 @@ pretty_assertions = "1.3.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
serialize = ["serde"]
|
||||
serialize = ["serde"]
|
||||
@@ -153,11 +153,17 @@ impl VirtualDom {
|
||||
});
|
||||
|
||||
// Now that all the references are gone, we can safely drop our own references in our listeners.
|
||||
let mut listeners = scope.listeners.borrow_mut();
|
||||
let mut listeners = scope.attributes_to_drop.borrow_mut();
|
||||
listeners.drain(..).for_each(|listener| {
|
||||
let listener = unsafe { &*listener };
|
||||
if let AttributeValue::Listener(l) = &listener.value {
|
||||
_ = l.take();
|
||||
match &listener.value {
|
||||
AttributeValue::Listener(l) => {
|
||||
_ = l.take();
|
||||
}
|
||||
AttributeValue::Any(a) => {
|
||||
_ = a.take();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+12
-20
@@ -1,5 +1,5 @@
|
||||
use crate::any_props::AnyProps;
|
||||
use crate::innerlude::{VComponent, VPlaceholder, VText};
|
||||
use crate::innerlude::{BorrowedAttributeValue, VComponent, VPlaceholder, VText};
|
||||
use crate::mutations::Mutation;
|
||||
use crate::mutations::Mutation::*;
|
||||
use crate::nodes::VNode;
|
||||
@@ -285,7 +285,7 @@ impl<'b> VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_attribute(&mut self, attribute: &crate::Attribute, id: ElementId) {
|
||||
fn write_attribute(&mut self, attribute: &'b crate::Attribute<'b>, id: ElementId) {
|
||||
// Make sure we set the attribute's associated id
|
||||
attribute.mounted_element.set(id);
|
||||
|
||||
@@ -293,9 +293,17 @@ impl<'b> VirtualDom {
|
||||
let unbounded_name: &str = unsafe { std::mem::transmute(attribute.name) };
|
||||
|
||||
match &attribute.value {
|
||||
AttributeValue::Text(value) => {
|
||||
AttributeValue::Listener(_) => {
|
||||
self.mutations.push(NewEventListener {
|
||||
// all listeners start with "on"
|
||||
name: &unbounded_name[2..],
|
||||
id,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
// Safety: we promise not to re-alias this text later on after committing it to the mutation
|
||||
let unbounded_value: &str = unsafe { std::mem::transmute(*value) };
|
||||
let value: BorrowedAttributeValue<'b> = (&attribute.value).into();
|
||||
let unbounded_value = unsafe { std::mem::transmute(value) };
|
||||
|
||||
self.mutations.push(SetAttribute {
|
||||
name: unbounded_name,
|
||||
@@ -304,22 +312,6 @@ impl<'b> VirtualDom {
|
||||
id,
|
||||
})
|
||||
}
|
||||
AttributeValue::Bool(value) => self.mutations.push(SetBoolAttribute {
|
||||
name: unbounded_name,
|
||||
value: *value,
|
||||
id,
|
||||
}),
|
||||
AttributeValue::Listener(_) => {
|
||||
self.mutations.push(NewEventListener {
|
||||
// all listeners start with "on"
|
||||
name: &unbounded_name[2..],
|
||||
id,
|
||||
})
|
||||
}
|
||||
AttributeValue::Float(_) => todo!(),
|
||||
AttributeValue::Int(_) => todo!(),
|
||||
AttributeValue::Any(_) => todo!(),
|
||||
AttributeValue::None => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-17
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
any_props::AnyProps,
|
||||
arena::ElementId,
|
||||
innerlude::{DirtyScope, VComponent, VPlaceholder, VText},
|
||||
innerlude::{BorrowedAttributeValue, DirtyScope, VComponent, VPlaceholder, VText},
|
||||
mutations::Mutation,
|
||||
nodes::RenderReturn,
|
||||
nodes::{DynamicNode, VNode},
|
||||
@@ -150,22 +150,16 @@ impl<'b> VirtualDom {
|
||||
};
|
||||
}
|
||||
|
||||
fn update_attribute(&mut self, right_attr: &Attribute, left_attr: &Attribute) {
|
||||
// todo: add more types of attribute values
|
||||
match right_attr.value {
|
||||
AttributeValue::Text(text) => {
|
||||
let name = unsafe { std::mem::transmute(left_attr.name) };
|
||||
let value = unsafe { std::mem::transmute(text) };
|
||||
self.mutations.push(Mutation::SetAttribute {
|
||||
id: left_attr.mounted_element.get(),
|
||||
ns: right_attr.namespace,
|
||||
name,
|
||||
value,
|
||||
});
|
||||
}
|
||||
// todo: more types of attribute values
|
||||
_ => todo!("other attribute types"),
|
||||
}
|
||||
fn update_attribute(&mut self, right_attr: &'b Attribute<'b>, left_attr: &'b Attribute) {
|
||||
let name = unsafe { std::mem::transmute(left_attr.name) };
|
||||
let value: BorrowedAttributeValue<'b> = (&right_attr.value).into();
|
||||
let value = unsafe { std::mem::transmute(value) };
|
||||
self.mutations.push(Mutation::SetAttribute {
|
||||
id: left_attr.mounted_element.get(),
|
||||
ns: right_attr.namespace,
|
||||
name,
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
fn diff_vcomponent(
|
||||
|
||||
@@ -70,10 +70,10 @@ pub(crate) mod innerlude {
|
||||
}
|
||||
|
||||
pub use crate::innerlude::{
|
||||
fc_to_builder, Attribute, AttributeValue, CapturedError, Component, DynamicNode, Element,
|
||||
ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation, Mutations, Properties,
|
||||
RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, TaskId, Template,
|
||||
TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom,
|
||||
fc_to_builder, AnyValue, Attribute, AttributeValue, BorrowedAttributeValue, CapturedError,
|
||||
Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation,
|
||||
Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext,
|
||||
TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom,
|
||||
};
|
||||
|
||||
/// The purpose of this module is to alleviate imports of many common types
|
||||
@@ -81,9 +81,9 @@ pub use crate::innerlude::{
|
||||
/// This includes types like [`Scope`], [`Element`], and [`Component`].
|
||||
pub mod prelude {
|
||||
pub use crate::innerlude::{
|
||||
fc_to_builder, Component, Element, Event, EventHandler, Fragment, LazyNodes, Properties,
|
||||
Scope, ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode,
|
||||
Throw, VNode, VirtualDom,
|
||||
fc_to_builder, AnyValue, Component, Element, Event, EventHandler, Fragment,
|
||||
IntoAttributeValue, LazyNodes, Properties, Scope, ScopeId, ScopeState, Scoped, TaskId,
|
||||
Template, TemplateAttribute, TemplateNode, Throw, VNode, VirtualDom,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{arena::ElementId, ScopeId, Template};
|
||||
use crate::{arena::ElementId, innerlude::BorrowedAttributeValue, ScopeId, Template};
|
||||
|
||||
/// A container for all the relevant steps to modify the Real DOM
|
||||
///
|
||||
@@ -61,7 +61,7 @@ impl<'a> Mutations<'a> {
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(tag = "type")
|
||||
)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Mutation<'a> {
|
||||
/// Add these m children to the target element
|
||||
AppendChildren {
|
||||
@@ -193,8 +193,9 @@ pub enum Mutation<'a> {
|
||||
SetAttribute {
|
||||
/// The name of the attribute to set.
|
||||
name: &'a str,
|
||||
|
||||
/// The value of the attribute.
|
||||
value: &'a str,
|
||||
value: BorrowedAttributeValue<'a>,
|
||||
|
||||
/// The ID of the node to set the attribute of.
|
||||
id: ElementId,
|
||||
@@ -204,18 +205,6 @@ pub enum Mutation<'a> {
|
||||
ns: Option<&'a str>,
|
||||
},
|
||||
|
||||
/// Set the value of a node's attribute.
|
||||
SetBoolAttribute {
|
||||
/// The name of the attribute to set.
|
||||
name: &'a str,
|
||||
|
||||
/// The value of the attribute.
|
||||
value: bool,
|
||||
|
||||
/// The ID of the node to set the attribute of.
|
||||
id: ElementId,
|
||||
},
|
||||
|
||||
/// Set the textcontent of a node.
|
||||
SetText {
|
||||
/// The textcontent of the node
|
||||
|
||||
+133
-15
@@ -5,8 +5,8 @@ use bumpalo::boxed::Box as BumpBox;
|
||||
use bumpalo::Bump;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell, UnsafeCell},
|
||||
fmt::Arguments,
|
||||
cell::{self, Cell, RefCell, UnsafeCell},
|
||||
fmt::{Arguments, Debug},
|
||||
future::Future,
|
||||
};
|
||||
|
||||
@@ -523,13 +523,108 @@ pub enum AttributeValue<'a> {
|
||||
Listener(RefCell<Option<ListenerCb<'a>>>),
|
||||
|
||||
/// An arbitrary value that implements PartialEq and is static
|
||||
Any(BumpBox<'a, dyn AnyValue>),
|
||||
Any(RefCell<Option<BumpBox<'a, dyn AnyValue>>>),
|
||||
|
||||
/// A "none" value, resulting in the removal of an attribute from the dom
|
||||
None,
|
||||
}
|
||||
|
||||
type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
|
||||
pub type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
|
||||
|
||||
/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements that are borrowed
|
||||
///
|
||||
/// These varients are used to communicate what the value of an attribute is that needs to be updated
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serialize", serde(untagged))]
|
||||
pub enum BorrowedAttributeValue<'a> {
|
||||
/// Text attribute
|
||||
Text(&'a str),
|
||||
|
||||
/// A float
|
||||
Float(f64),
|
||||
|
||||
/// Signed integer
|
||||
Int(i64),
|
||||
|
||||
/// Boolean
|
||||
Bool(bool),
|
||||
|
||||
/// An arbitrary value that implements PartialEq and is static
|
||||
#[cfg_attr(
|
||||
feature = "serialize",
|
||||
serde(
|
||||
deserialize_with = "deserialize_any_value",
|
||||
serialize_with = "serialize_any_value"
|
||||
)
|
||||
)]
|
||||
Any(std::cell::Ref<'a, dyn AnyValue>),
|
||||
|
||||
/// A "none" value, resulting in the removal of an attribute from the dom
|
||||
None,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a AttributeValue<'a>> for BorrowedAttributeValue<'a> {
|
||||
fn from(value: &'a AttributeValue<'a>) -> Self {
|
||||
match value {
|
||||
AttributeValue::Text(value) => BorrowedAttributeValue::Text(value),
|
||||
AttributeValue::Float(value) => BorrowedAttributeValue::Float(*value),
|
||||
AttributeValue::Int(value) => BorrowedAttributeValue::Int(*value),
|
||||
AttributeValue::Bool(value) => BorrowedAttributeValue::Bool(*value),
|
||||
AttributeValue::Listener(_) => {
|
||||
panic!("A listener cannot be turned into a borrowed value")
|
||||
}
|
||||
AttributeValue::Any(value) => {
|
||||
let value = value.borrow();
|
||||
BorrowedAttributeValue::Any(std::cell::Ref::map(value, |value| {
|
||||
&**value.as_ref().unwrap()
|
||||
}))
|
||||
}
|
||||
AttributeValue::None => BorrowedAttributeValue::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for BorrowedAttributeValue<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
|
||||
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
|
||||
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
|
||||
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
|
||||
Self::Any(_) => f.debug_tuple("Any").field(&"...").finish(),
|
||||
Self::None => write!(f, "None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BorrowedAttributeValue<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
|
||||
(Self::Float(l0), Self::Float(r0)) => l0 == r0,
|
||||
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
|
||||
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
||||
(Self::Any(l0), Self::Any(r0)) => l0.any_cmp(&**r0),
|
||||
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
fn serialize_any_value<S>(_: &cell::Ref<'_, dyn AnyValue>, _: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
panic!("Any cannot be serialized")
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
fn deserialize_any_value<'de, 'a, D>(_: D) -> Result<cell::Ref<'a, dyn AnyValue>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
panic!("Any cannot be deserialized")
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Debug for AttributeValue<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
@@ -553,29 +648,36 @@ impl<'a> PartialEq for AttributeValue<'a> {
|
||||
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
|
||||
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
||||
(Self::Listener(_), Self::Listener(_)) => true,
|
||||
(Self::Any(l0), Self::Any(r0)) => l0.any_cmp(r0.as_ref()),
|
||||
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
||||
(Self::Any(l0), Self::Any(r0)) => {
|
||||
let l0 = l0.borrow();
|
||||
let r0 = r0.borrow();
|
||||
l0.as_ref().unwrap().any_cmp(&**r0.as_ref().unwrap())
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait AnyValue {
|
||||
pub trait AnyValue: 'static {
|
||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
|
||||
fn our_typeid(&self) -> TypeId;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn type_id(&self) -> TypeId {
|
||||
self.as_any().type_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Any> AnyValue for T {
|
||||
impl<T: Any + PartialEq + 'static> AnyValue for T {
|
||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
|
||||
if self.type_id() != other.our_typeid() {
|
||||
return false;
|
||||
if let Some(other) = other.as_any().downcast_ref() {
|
||||
self == other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
self == unsafe { &*(other as *const _ as *const T) }
|
||||
}
|
||||
|
||||
fn our_typeid(&self) -> TypeId {
|
||||
self.type_id()
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,26 +844,36 @@ pub trait IntoAttributeValue<'a> {
|
||||
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a>;
|
||||
}
|
||||
|
||||
impl<'a> IntoAttributeValue<'a> for AttributeValue<'a> {
|
||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAttributeValue<'a> for &'a str {
|
||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||
AttributeValue::Text(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAttributeValue<'a> for f64 {
|
||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||
AttributeValue::Float(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAttributeValue<'a> for i64 {
|
||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||
AttributeValue::Int(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAttributeValue<'a> for bool {
|
||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||
AttributeValue::Bool(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
|
||||
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
|
||||
use bumpalo::core_alloc::fmt::Write;
|
||||
@@ -770,3 +882,9 @@ impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
|
||||
AttributeValue::Text(str_buf.into_bump_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAttributeValue<'a> for BumpBox<'a, dyn AnyValue> {
|
||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||
AttributeValue::Any(RefCell::new(Some(self)))
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ impl VirtualDom {
|
||||
hook_idx: Default::default(),
|
||||
shared_contexts: Default::default(),
|
||||
borrowed_props: Default::default(),
|
||||
listeners: Default::default(),
|
||||
attributes_to_drop: Default::default(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
innerlude::{ErrorBoundary, Scheduler, SchedulerMsg},
|
||||
lazynodes::LazyNodes,
|
||||
nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
|
||||
Attribute, AttributeValue, Element, Event, Properties, TaskId,
|
||||
AnyValue, Attribute, AttributeValue, Element, Event, Properties, TaskId,
|
||||
};
|
||||
use bumpalo::{boxed::Box as BumpBox, Bump};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
@@ -88,7 +88,7 @@ pub struct ScopeState {
|
||||
pub(crate) spawned_tasks: FxHashSet<TaskId>,
|
||||
|
||||
pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
|
||||
pub(crate) listeners: RefCell<Vec<*const Attribute<'static>>>,
|
||||
pub(crate) attributes_to_drop: RefCell<Vec<*const Attribute<'static>>>,
|
||||
|
||||
pub(crate) props: Option<Box<dyn AnyProps<'static>>>,
|
||||
pub(crate) placeholder: Cell<Option<ElementId>>,
|
||||
@@ -374,11 +374,15 @@ impl<'src> ScopeState {
|
||||
pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
|
||||
let element = rsx.call(self);
|
||||
|
||||
let mut listeners = self.listeners.borrow_mut();
|
||||
let mut listeners = self.attributes_to_drop.borrow_mut();
|
||||
for attr in element.dynamic_attrs {
|
||||
if let AttributeValue::Listener(_) = attr.value {
|
||||
let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
|
||||
listeners.push(unbounded);
|
||||
match attr.value {
|
||||
AttributeValue::Any(_) | AttributeValue::Listener(_) => {
|
||||
let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
|
||||
listeners.push(unbounded);
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,6 +513,17 @@ impl<'src> ScopeState {
|
||||
AttributeValue::Listener(RefCell::new(Some(boxed)))
|
||||
}
|
||||
|
||||
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
|
||||
pub fn any_value<T: AnyValue>(&'src self, value: T) -> AttributeValue<'src> {
|
||||
// safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
|
||||
// This is the suggested way to build a bumpbox
|
||||
//
|
||||
// In theory, we could just use regular boxes
|
||||
let boxed: BumpBox<'src, dyn AnyValue> =
|
||||
unsafe { BumpBox::from_raw(self.bump().alloc(value)) };
|
||||
AttributeValue::Any(RefCell::new(Some(boxed)))
|
||||
}
|
||||
|
||||
/// Inject an error into the nearest error boundary and quit rendering
|
||||
///
|
||||
/// The error doesn't need to implement Error or any specific traits since the boundary
|
||||
|
||||
@@ -641,10 +641,7 @@ impl VirtualDom {
|
||||
|
||||
/// Swap the current mutations with a new
|
||||
fn finalize(&mut self) -> Mutations {
|
||||
// todo: make this a routine
|
||||
let mut out = Mutations::default();
|
||||
std::mem::swap(&mut self.mutations, &mut out);
|
||||
out
|
||||
std::mem::take(&mut self.mutations)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
//!
|
||||
//! This tests to ensure we clean it up
|
||||
|
||||
use bumpalo::Bump;
|
||||
use dioxus::core::{ElementId, Mutation::*};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_core::{AttributeValue, BorrowedAttributeValue};
|
||||
|
||||
#[test]
|
||||
fn attrs_cycle() {
|
||||
@@ -22,6 +24,8 @@ fn attrs_cycle() {
|
||||
}
|
||||
});
|
||||
|
||||
let bump = Bump::new();
|
||||
|
||||
assert_eq!(
|
||||
dom.rebuild().santize().edits,
|
||||
[
|
||||
@@ -36,8 +40,18 @@ fn attrs_cycle() {
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||
AssignId { path: &[0,], id: ElementId(3,) },
|
||||
SetAttribute { name: "class", value: "1", id: ElementId(3,), ns: None },
|
||||
SetAttribute { name: "id", value: "1", id: ElementId(3,), ns: None },
|
||||
SetAttribute {
|
||||
name: "class",
|
||||
value: (&*bump.alloc("1".into_value(&bump))).into(),
|
||||
id: ElementId(3,),
|
||||
ns: None
|
||||
},
|
||||
SetAttribute {
|
||||
name: "id",
|
||||
value: (&*bump.alloc("1".into_value(&bump))).into(),
|
||||
id: ElementId(3,),
|
||||
ns: None
|
||||
},
|
||||
ReplaceWith { id: ElementId(1,), m: 1 },
|
||||
]
|
||||
);
|
||||
@@ -57,8 +71,18 @@ fn attrs_cycle() {
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
||||
AssignId { path: &[0], id: ElementId(3) },
|
||||
SetAttribute { name: "class", value: "3", id: ElementId(3), ns: None },
|
||||
SetAttribute { name: "id", value: "3", id: ElementId(3), ns: None },
|
||||
SetAttribute {
|
||||
name: "class",
|
||||
value: BorrowedAttributeValue::Text("3"),
|
||||
id: ElementId(3),
|
||||
ns: None
|
||||
},
|
||||
SetAttribute {
|
||||
name: "id",
|
||||
value: BorrowedAttributeValue::Text("3"),
|
||||
id: ElementId(3),
|
||||
ns: None
|
||||
},
|
||||
ReplaceWith { id: ElementId(1), m: 1 }
|
||||
]
|
||||
);
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
use bumpalo::Bump;
|
||||
use dioxus::core::{ElementId, Mutation::*};
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn bool_test() {
|
||||
let mut app = VirtualDom::new(|cx| cx.render(rsx!(div { hidden: false })));
|
||||
let bump = Bump::new();
|
||||
|
||||
assert_eq!(
|
||||
app.rebuild().santize().edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||
SetBoolAttribute { name: "hidden", value: false, id: ElementId(1,) },
|
||||
SetAttribute {
|
||||
name: "hidden",
|
||||
value: (&*bump.alloc(false.into_value(&bump))).into(),
|
||||
id: ElementId(1,),
|
||||
ns: None
|
||||
},
|
||||
AppendChildren { m: 1, id: ElementId(0) },
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -18,7 +18,7 @@ fn test_borrowed_state() {
|
||||
ReplacePlaceholder { path: &[0,], m: 1 },
|
||||
AppendChildren { m: 1, id: ElementId(0) },
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fn Parent(cx: Scope) -> Element {
|
||||
|
||||
@@ -20,7 +20,9 @@ fn app(cx: Scope) -> Element {
|
||||
fn bubbles_error() {
|
||||
let mut dom = VirtualDom::new(app);
|
||||
|
||||
let _edits = dom.rebuild().santize();
|
||||
{
|
||||
let _edits = dom.rebuild().santize();
|
||||
}
|
||||
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
|
||||
|
||||
@@ -12,14 +12,16 @@ fn cycling_elements() {
|
||||
})
|
||||
});
|
||||
|
||||
let edits = dom.rebuild().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||
AppendChildren { m: 1, id: ElementId(0) },
|
||||
]
|
||||
);
|
||||
{
|
||||
let edits = dom.rebuild().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||
AppendChildren { m: 1, id: ElementId(0) },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
assert_eq!(
|
||||
|
||||
@@ -59,19 +59,21 @@ fn component_swap() {
|
||||
}
|
||||
|
||||
let mut dom = VirtualDom::new(app);
|
||||
let edits = dom.rebuild().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(4) },
|
||||
ReplacePlaceholder { path: &[1], m: 3 },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
||||
AppendChildren { m: 2, id: ElementId(0) }
|
||||
]
|
||||
);
|
||||
{
|
||||
let edits = dom.rebuild().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(4) },
|
||||
ReplacePlaceholder { path: &[1], m: 3 },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
||||
AppendChildren { m: 2, id: ElementId(0) }
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
assert_eq!(
|
||||
|
||||
@@ -20,22 +20,24 @@ fn keyed_diffing_out_of_order() {
|
||||
cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
dom.rebuild().santize().edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(3,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(4,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(6,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(8,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(10,) },
|
||||
AppendChildren { m: 10, id: ElementId(0) },
|
||||
]
|
||||
);
|
||||
{
|
||||
assert_eq!(
|
||||
dom.rebuild().santize().edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(3,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(4,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(6,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(8,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(10,) },
|
||||
AppendChildren { m: 10, id: ElementId(0) },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
assert_eq!(
|
||||
@@ -44,7 +46,7 @@ fn keyed_diffing_out_of_order() {
|
||||
PushRoot { id: ElementId(7,) },
|
||||
InsertBefore { id: ElementId(5,), m: 1 },
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// Should result in moves only
|
||||
@@ -70,7 +72,7 @@ fn keyed_diffing_out_of_order_adds() {
|
||||
PushRoot { id: ElementId(4,) },
|
||||
InsertBefore { id: ElementId(1,), m: 2 },
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// Should result in moves only
|
||||
|
||||
@@ -315,66 +315,76 @@ fn remove_many() {
|
||||
})
|
||||
});
|
||||
|
||||
let edits = dom.rebuild().santize();
|
||||
assert!(edits.templates.is_empty());
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
CreatePlaceholder { id: ElementId(1,) },
|
||||
AppendChildren { id: ElementId(0), m: 1 },
|
||||
]
|
||||
);
|
||||
{
|
||||
let edits = dom.rebuild().santize();
|
||||
assert!(edits.templates.is_empty());
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
CreatePlaceholder { id: ElementId(1,) },
|
||||
AppendChildren { id: ElementId(0), m: 1 },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
let edits = dom.render_immediate().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
|
||||
ReplaceWith { id: ElementId(1,), m: 1 },
|
||||
]
|
||||
);
|
||||
{
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
let edits = dom.render_immediate().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
|
||||
ReplaceWith { id: ElementId(1,), m: 1 },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
let edits = dom.render_immediate().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||
HydrateText { path: &[0,], value: "hello 1", id: ElementId(4,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
||||
HydrateText { path: &[0,], value: "hello 2", id: ElementId(6,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
||||
HydrateText { path: &[0,], value: "hello 3", id: ElementId(8,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
||||
HydrateText { path: &[0,], value: "hello 4", id: ElementId(10,) },
|
||||
InsertAfter { id: ElementId(2,), m: 4 },
|
||||
]
|
||||
);
|
||||
{
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
let edits = dom.render_immediate().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||
HydrateText { path: &[0,], value: "hello 1", id: ElementId(4,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
||||
HydrateText { path: &[0,], value: "hello 2", id: ElementId(6,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
||||
HydrateText { path: &[0,], value: "hello 3", id: ElementId(8,) },
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
||||
HydrateText { path: &[0,], value: "hello 4", id: ElementId(10,) },
|
||||
InsertAfter { id: ElementId(2,), m: 4 },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
let edits = dom.render_immediate().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
CreatePlaceholder { id: ElementId(11,) },
|
||||
Remove { id: ElementId(9,) },
|
||||
Remove { id: ElementId(7,) },
|
||||
Remove { id: ElementId(5,) },
|
||||
Remove { id: ElementId(1,) },
|
||||
ReplaceWith { id: ElementId(2,), m: 1 },
|
||||
]
|
||||
);
|
||||
{
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
let edits = dom.render_immediate().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
CreatePlaceholder { id: ElementId(11,) },
|
||||
Remove { id: ElementId(9,) },
|
||||
Remove { id: ElementId(7,) },
|
||||
Remove { id: ElementId(5,) },
|
||||
Remove { id: ElementId(1,) },
|
||||
ReplaceWith { id: ElementId(2,), m: 1 },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
let edits = dom.render_immediate().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
|
||||
ReplaceWith { id: ElementId(11,), m: 1 },
|
||||
]
|
||||
)
|
||||
{
|
||||
dom.mark_dirty(ScopeId(0));
|
||||
let edits = dom.render_immediate().santize();
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
|
||||
ReplaceWith { id: ElementId(11,), m: 1 },
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use bumpalo::Bump;
|
||||
use dioxus::core::{ElementId, Mutation};
|
||||
use dioxus::prelude::*;
|
||||
|
||||
@@ -26,17 +27,22 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element {
|
||||
#[test]
|
||||
fn dual_stream() {
|
||||
let mut dom = VirtualDom::new(basic_syntax_is_a_template);
|
||||
let bump = Bump::new();
|
||||
let edits = dom.rebuild().santize();
|
||||
|
||||
use Mutation::*;
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
assert_eq!(edits.edits, {
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||
SetAttribute { name: "class", value: "123", id: ElementId(1), ns: None },
|
||||
SetAttribute {
|
||||
name: "class",
|
||||
value: (&*bump.alloc("123".into_value(&bump))).into(),
|
||||
id: ElementId(1),
|
||||
ns: None,
|
||||
},
|
||||
NewEventListener { name: "click", id: ElementId(1) },
|
||||
HydrateText { path: &[0, 0], value: "123", id: ElementId(2) },
|
||||
AppendChildren { id: ElementId(0), m: 1 }
|
||||
],
|
||||
);
|
||||
AppendChildren { id: ElementId(0), m: 1 },
|
||||
]
|
||||
});
|
||||
}
|
||||
@@ -9,32 +9,34 @@ use std::time::Duration;
|
||||
async fn it_works() {
|
||||
let mut dom = VirtualDom::new(app);
|
||||
|
||||
let mutations = dom.rebuild().santize();
|
||||
{
|
||||
let mutations = dom.rebuild().santize();
|
||||
|
||||
// We should at least get the top-level template in before pausing for the children
|
||||
// note: we dont test template edits anymore
|
||||
// assert_eq!(
|
||||
// mutations.templates,
|
||||
// [
|
||||
// CreateElement { name: "div" },
|
||||
// CreateStaticText { value: "Waiting for child..." },
|
||||
// CreateStaticPlaceholder,
|
||||
// AppendChildren { m: 2 },
|
||||
// SaveTemplate { name: "template", m: 1 }
|
||||
// ]
|
||||
// );
|
||||
// We should at least get the top-level template in before pausing for the children
|
||||
// note: we dont test template edits anymore
|
||||
// assert_eq!(
|
||||
// mutations.templates,
|
||||
// [
|
||||
// CreateElement { name: "div" },
|
||||
// CreateStaticText { value: "Waiting for child..." },
|
||||
// CreateStaticPlaceholder,
|
||||
// AppendChildren { m: 2 },
|
||||
// SaveTemplate { name: "template", m: 1 }
|
||||
// ]
|
||||
// );
|
||||
|
||||
// And we should load it in and assign the placeholder properly
|
||||
assert_eq!(
|
||||
mutations.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||
// hmmmmmmmmm.... with suspense how do we guarantee that IDs increase linearly?
|
||||
// can we even?
|
||||
AssignId { path: &[1], id: ElementId(3) },
|
||||
AppendChildren { m: 1, id: ElementId(0) },
|
||||
]
|
||||
);
|
||||
// And we should load it in and assign the placeholder properly
|
||||
assert_eq!(
|
||||
mutations.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||
// hmmmmmmmmm.... with suspense how do we guarantee that IDs increase linearly?
|
||||
// can we even?
|
||||
AssignId { path: &[1], id: ElementId(3) },
|
||||
AppendChildren { m: 1, id: ElementId(0) },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// wait just a moment, not enough time for the boundary to resolve
|
||||
|
||||
|
||||
@@ -18,10 +18,9 @@ proc-macro = true
|
||||
syn = { version = "1.0.11", features = ["extra-traits"] }
|
||||
quote = "1.0"
|
||||
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
dioxus = { path = "../dioxus" }
|
||||
|
||||
[dev-dependencies]
|
||||
smallvec = "1.6"
|
||||
rustc-hash = "1.1.0"
|
||||
anymap = "0.12.1"
|
||||
@@ -393,7 +393,8 @@ impl<'a> StateMember<'a> {
|
||||
|
||||
let ty = &self.mem.ty;
|
||||
let unit_type = &self.mem.unit_type;
|
||||
let node_view = quote!(dioxus_native_core::node_ref::NodeView::new(unsafe{&*{&node.node_data as *const _}}, #ty::NODE_MASK));
|
||||
let node_view =
|
||||
quote!(dioxus_native_core::node_ref::NodeView::new(&node.node_data, #ty::NODE_MASK));
|
||||
let dep_idents = self.dep_mems.iter().map(|m| &m.ident);
|
||||
let impl_specific = match self.dep_kind {
|
||||
DependencyKind::Node => {
|
||||
|
||||
@@ -43,7 +43,6 @@ impl ParentDepState for ParentDepCallCounter {
|
||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
||||
fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
|
||||
self.0 += 1;
|
||||
println!("ParentDepCallCounter::reduce on {:?}\n{}", _node, self.0);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" }
|
||||
taffy = "0.2.1"
|
||||
smallvec = "1.6"
|
||||
rustc-hash = "1.1.0"
|
||||
anymap = "0.12.1"
|
||||
anymap = "1.0.0-beta.2"
|
||||
slab = "0.4"
|
||||
parking_lot = "0.12.1"
|
||||
crossbeam-deque = "0.8.2"
|
||||
@@ -26,4 +26,4 @@ dashmap = "5.4.0"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8.5"
|
||||
dioxus = { path = "../dioxus", version = "^0.2.1" }
|
||||
dioxus = { path = "../dioxus", version = "^0.2.1" }
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::any::Any;
|
||||
use std::hash::BuildHasherDefault;
|
||||
|
||||
pub use node_ref::NodeMask;
|
||||
@@ -20,7 +21,7 @@ pub mod utils;
|
||||
pub type RealNodeId = NodeId;
|
||||
pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||
pub type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
|
||||
pub type SendAnyMap = anymap::Map<dyn anymap::any::Any + Send + Sync + 'static>;
|
||||
pub type SendAnyMap = anymap::Map<dyn Any + Send + Sync + 'static>;
|
||||
|
||||
/// Used in derived state macros
|
||||
#[derive(Eq, PartialEq)]
|
||||
|
||||
@@ -1,43 +1,44 @@
|
||||
use crate::{state::State, tree::NodeId};
|
||||
use dioxus_core::ElementId;
|
||||
use dioxus_core::{AnyValue, BorrowedAttributeValue, ElementId};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// The node is stored client side and stores only basic data about the node.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Node<S: State> {
|
||||
pub struct Node<S: State, V: FromAnyValue = ()> {
|
||||
/// The transformed state of the node.
|
||||
pub state: S,
|
||||
/// The raw data for the node
|
||||
pub node_data: NodeData,
|
||||
pub node_data: NodeData<V>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NodeData {
|
||||
pub struct NodeData<V: FromAnyValue = ()> {
|
||||
/// The id of the node
|
||||
pub node_id: NodeId,
|
||||
/// The id of the node in the vdom.
|
||||
pub element_id: Option<ElementId>,
|
||||
/// Additional inforation specific to the node type
|
||||
pub node_type: NodeType,
|
||||
pub node_type: NodeType<V>,
|
||||
}
|
||||
|
||||
/// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NodeType {
|
||||
pub enum NodeType<V: FromAnyValue = ()> {
|
||||
Text {
|
||||
text: String,
|
||||
},
|
||||
Element {
|
||||
tag: String,
|
||||
namespace: Option<String>,
|
||||
attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue>,
|
||||
attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
|
||||
listeners: FxHashSet<String>,
|
||||
},
|
||||
Placeholder,
|
||||
}
|
||||
|
||||
impl<S: State> Node<S> {
|
||||
pub(crate) fn new(node_type: NodeType) -> Self {
|
||||
impl<S: State, V: FromAnyValue> Node<S, V> {
|
||||
pub(crate) fn new(node_type: NodeType<V>) -> Self {
|
||||
Node {
|
||||
state: S::default(),
|
||||
node_data: NodeData {
|
||||
@@ -64,24 +65,59 @@ pub struct OwnedAttributeDiscription {
|
||||
/// An attribute on a DOM node, such as `id="my-thing"` or
|
||||
/// `href="https://example.com"`.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct OwnedAttributeView<'a> {
|
||||
pub struct OwnedAttributeView<'a, V: FromAnyValue = ()> {
|
||||
/// The discription of the attribute.
|
||||
pub attribute: &'a OwnedAttributeDiscription,
|
||||
|
||||
/// The value of the attribute.
|
||||
pub value: &'a OwnedAttributeValue,
|
||||
pub value: &'a OwnedAttributeValue<V>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum OwnedAttributeValue {
|
||||
#[derive(Clone)]
|
||||
pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
|
||||
Text(String),
|
||||
Float(f32),
|
||||
Int(i32),
|
||||
Float(f64),
|
||||
Int(i64),
|
||||
Bool(bool),
|
||||
Custom(V),
|
||||
None,
|
||||
}
|
||||
|
||||
impl OwnedAttributeValue {
|
||||
pub trait FromAnyValue {
|
||||
fn from_any_value(value: &dyn AnyValue) -> Self;
|
||||
}
|
||||
|
||||
impl FromAnyValue for () {
|
||||
fn from_any_value(_: &dyn AnyValue) -> Self {}
|
||||
}
|
||||
|
||||
impl<V: FromAnyValue> Debug for OwnedAttributeValue<V> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
|
||||
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
|
||||
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
|
||||
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
|
||||
Self::Custom(_) => f.debug_tuple("Any").finish(),
|
||||
Self::None => write!(f, "None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: FromAnyValue> From<BorrowedAttributeValue<'_>> for OwnedAttributeValue<V> {
|
||||
fn from(value: BorrowedAttributeValue<'_>) -> Self {
|
||||
match value {
|
||||
BorrowedAttributeValue::Text(text) => Self::Text(text.to_string()),
|
||||
BorrowedAttributeValue::Float(float) => Self::Float(float),
|
||||
BorrowedAttributeValue::Int(int) => Self::Int(int),
|
||||
BorrowedAttributeValue::Bool(bool) => Self::Bool(bool),
|
||||
BorrowedAttributeValue::Any(any) => Self::Custom(V::from_any_value(&*any)),
|
||||
BorrowedAttributeValue::None => Self::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: FromAnyValue> OwnedAttributeValue<V> {
|
||||
pub fn as_text(&self) -> Option<&str> {
|
||||
match self {
|
||||
OwnedAttributeValue::Text(text) => Some(text),
|
||||
@@ -89,14 +125,14 @@ impl OwnedAttributeValue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_float(&self) -> Option<f32> {
|
||||
pub fn as_float(&self) -> Option<f64> {
|
||||
match self {
|
||||
OwnedAttributeValue::Float(float) => Some(*float),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_int(&self) -> Option<i32> {
|
||||
pub fn as_int(&self) -> Option<i64> {
|
||||
match self {
|
||||
OwnedAttributeValue::Int(int) => Some(*int),
|
||||
_ => None,
|
||||
@@ -116,4 +152,11 @@ impl OwnedAttributeValue {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_custom(&self) -> Option<&V> {
|
||||
match self {
|
||||
OwnedAttributeValue::Custom(custom) => Some(custom),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
use dioxus_core::ElementId;
|
||||
|
||||
use crate::{
|
||||
node::{NodeData, NodeType, OwnedAttributeView},
|
||||
node::{FromAnyValue, NodeData, NodeType, OwnedAttributeView},
|
||||
state::union_ordered_iter,
|
||||
RealNodeId,
|
||||
};
|
||||
|
||||
/// A view into a [VNode] with limited access.
|
||||
#[derive(Debug)]
|
||||
pub struct NodeView<'a> {
|
||||
inner: &'a NodeData,
|
||||
pub struct NodeView<'a, V: FromAnyValue = ()> {
|
||||
inner: &'a NodeData<V>,
|
||||
mask: NodeMask,
|
||||
}
|
||||
|
||||
impl<'a> NodeView<'a> {
|
||||
impl<'a, V: FromAnyValue> NodeView<'a, V> {
|
||||
/// Create a new NodeView from a VNode, and mask.
|
||||
pub fn new(node: &'a NodeData, view: NodeMask) -> Self {
|
||||
pub fn new(node: &'a NodeData<V>, view: NodeMask) -> Self {
|
||||
Self {
|
||||
inner: node,
|
||||
mask: view,
|
||||
@@ -55,7 +55,9 @@ impl<'a> NodeView<'a> {
|
||||
}
|
||||
|
||||
/// Get any attributes that are enabled in the mask
|
||||
pub fn attributes<'b>(&'b self) -> Option<impl Iterator<Item = OwnedAttributeView<'a>> + 'b> {
|
||||
pub fn attributes<'b>(
|
||||
&'b self,
|
||||
) -> Option<impl Iterator<Item = OwnedAttributeView<'a, V>> + 'b> {
|
||||
match &self.inner.node_type {
|
||||
NodeType::Element { attributes, .. } => Some(
|
||||
attributes
|
||||
|
||||
@@ -310,11 +310,65 @@ impl<T> AnyPass<T> {
|
||||
}
|
||||
}
|
||||
|
||||
struct RawPointer<T>(*mut T);
|
||||
unsafe impl<T> Send for RawPointer<T> {}
|
||||
unsafe impl<T> Sync for RawPointer<T> {}
|
||||
pub fn resolve_passes<T, Tr: TreeView<T> + Sync + Send>(
|
||||
tree: &mut Tr,
|
||||
dirty_nodes: DirtyNodeStates,
|
||||
passes: Vec<&AnyPass<T>>,
|
||||
ctx: SendAnyMap,
|
||||
) -> FxDashSet<NodeId> {
|
||||
resolve_passes_single_threaded(tree, dirty_nodes, passes, ctx)
|
||||
// TODO: multithreadeding has some safety issues currently that need to be resolved before it can be used
|
||||
// let dirty_states = Arc::new(dirty_nodes);
|
||||
// let mut resolved_passes: FxHashSet<PassId> = FxHashSet::default();
|
||||
// let mut resolving = Vec::new();
|
||||
// let nodes_updated = Arc::new(FxDashSet::default());
|
||||
// let ctx = Arc::new(ctx);
|
||||
// while !passes.is_empty() {
|
||||
// let mut currently_borrowed = MemberMask::default();
|
||||
// std::thread::scope(|s| {
|
||||
// let mut i = 0;
|
||||
// while i < passes.len() {
|
||||
// let pass = &passes[i];
|
||||
// let pass_id = pass.pass_id();
|
||||
// let pass_mask = pass.mask();
|
||||
// if pass
|
||||
// .dependancies()
|
||||
// .iter()
|
||||
// .all(|d| resolved_passes.contains(d) || *d == pass_id)
|
||||
// && !pass_mask.overlaps(currently_borrowed)
|
||||
// {
|
||||
// let pass = passes.remove(i);
|
||||
// resolving.push(pass_id);
|
||||
// currently_borrowed |= pass_mask;
|
||||
// let dirty_states = dirty_states.clone();
|
||||
// let nodes_updated = nodes_updated.clone();
|
||||
// let ctx = ctx.clone();
|
||||
// let mut dirty = DirtyNodes::default();
|
||||
// // dirty_states.all_dirty(pass_id, &mut dirty, tree);
|
||||
// // this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass
|
||||
// let tree_mut_unbounded = unsafe { &mut *(tree as *mut Tr) };
|
||||
// s.spawn(move || {
|
||||
// pass.resolve(
|
||||
// tree_mut_unbounded,
|
||||
// dirty,
|
||||
// &dirty_states,
|
||||
// &nodes_updated,
|
||||
// &ctx,
|
||||
// );
|
||||
// });
|
||||
// } else {
|
||||
// i += 1;
|
||||
// }
|
||||
// }
|
||||
// // all passes are resolved at the end of the scope
|
||||
// });
|
||||
// resolved_passes.extend(resolving.iter().copied());
|
||||
// resolving.clear()
|
||||
// }
|
||||
// std::sync::Arc::try_unwrap(nodes_updated).unwrap()
|
||||
}
|
||||
|
||||
pub fn resolve_passes<T, Tr: TreeView<T>>(
|
||||
pub fn resolve_passes_single_threaded<T, Tr: TreeView<T>>(
|
||||
tree: &mut Tr,
|
||||
dirty_nodes: DirtyNodeStates,
|
||||
mut passes: Vec<&AnyPass<T>>,
|
||||
@@ -327,41 +381,31 @@ pub fn resolve_passes<T, Tr: TreeView<T>>(
|
||||
let ctx = Arc::new(ctx);
|
||||
while !passes.is_empty() {
|
||||
let mut currently_borrowed = MemberMask::default();
|
||||
std::thread::scope(|s| {
|
||||
let mut i = 0;
|
||||
while i < passes.len() {
|
||||
let pass = &passes[i];
|
||||
let pass_id = pass.pass_id();
|
||||
let pass_mask = pass.mask();
|
||||
if pass
|
||||
.dependancies()
|
||||
.iter()
|
||||
.all(|d| resolved_passes.contains(d) || *d == pass_id)
|
||||
&& !pass_mask.overlaps(currently_borrowed)
|
||||
{
|
||||
let pass = passes.remove(i);
|
||||
resolving.push(pass_id);
|
||||
currently_borrowed |= pass_mask;
|
||||
let tree_mut = tree as *mut _;
|
||||
let raw_ptr = RawPointer(tree_mut);
|
||||
let dirty_states = dirty_states.clone();
|
||||
let nodes_updated = nodes_updated.clone();
|
||||
let ctx = ctx.clone();
|
||||
s.spawn(move || unsafe {
|
||||
// let tree_mut: &mut Tr = &mut *raw_ptr.0;
|
||||
let raw = raw_ptr;
|
||||
// this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass
|
||||
let tree_mut: &mut Tr = &mut *raw.0;
|
||||
let mut dirty = DirtyNodes::default();
|
||||
dirty_states.all_dirty(pass_id, &mut dirty, tree_mut);
|
||||
pass.resolve(tree_mut, dirty, &dirty_states, &nodes_updated, &ctx);
|
||||
});
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
let mut i = 0;
|
||||
while i < passes.len() {
|
||||
let pass = &passes[i];
|
||||
let pass_id = pass.pass_id();
|
||||
let pass_mask = pass.mask();
|
||||
if pass
|
||||
.dependancies()
|
||||
.iter()
|
||||
.all(|d| resolved_passes.contains(d) || *d == pass_id)
|
||||
&& !pass_mask.overlaps(currently_borrowed)
|
||||
{
|
||||
let pass = passes.remove(i);
|
||||
resolving.push(pass_id);
|
||||
currently_borrowed |= pass_mask;
|
||||
let dirty_states = dirty_states.clone();
|
||||
let nodes_updated = nodes_updated.clone();
|
||||
let ctx = ctx.clone();
|
||||
// this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass
|
||||
let mut dirty = DirtyNodes::default();
|
||||
dirty_states.all_dirty(pass_id, &mut dirty, tree);
|
||||
pass.resolve(tree, dirty, &dirty_states, &nodes_updated, &ctx);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
// all passes are resolved at the end of the scope
|
||||
});
|
||||
}
|
||||
resolved_passes.extend(resolving.iter().copied());
|
||||
resolving.clear()
|
||||
}
|
||||
|
||||
@@ -270,26 +270,7 @@ impl<S: State> RealDom<S> {
|
||||
namespace: ns.map(|s| s.to_string()),
|
||||
volatile: false,
|
||||
},
|
||||
crate::node::OwnedAttributeValue::Text(value.to_string()),
|
||||
);
|
||||
mark_dirty(
|
||||
node_id,
|
||||
NodeMask::new_with_attrs(AttributeMask::single(name)),
|
||||
&mut nodes_updated,
|
||||
);
|
||||
}
|
||||
}
|
||||
SetBoolAttribute { name, value, id } => {
|
||||
let node_id = self.element_to_node_id(id);
|
||||
let node = self.tree.get_mut(node_id).unwrap();
|
||||
if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type {
|
||||
attributes.insert(
|
||||
OwnedAttributeDiscription {
|
||||
name: name.to_string(),
|
||||
namespace: None,
|
||||
volatile: false,
|
||||
},
|
||||
crate::node::OwnedAttributeValue::Bool(value),
|
||||
OwnedAttributeValue::from(value),
|
||||
);
|
||||
mark_dirty(
|
||||
node_id,
|
||||
@@ -358,12 +339,12 @@ impl<S: State> RealDom<S> {
|
||||
}
|
||||
|
||||
/// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
|
||||
pub fn update_state(
|
||||
pub fn update_state_single_threaded(
|
||||
&mut self,
|
||||
nodes_updated: DirtyNodeStates,
|
||||
ctx: SendAnyMap,
|
||||
) -> FxDashSet<RealNodeId> {
|
||||
S::update(nodes_updated, &mut self.tree, ctx)
|
||||
S::update_single_threaded(nodes_updated, &mut self.tree, ctx)
|
||||
}
|
||||
|
||||
/// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up.
|
||||
@@ -415,6 +396,21 @@ impl<S: State> RealDom<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State + Sync> RealDom<S>
|
||||
where
|
||||
Tree<Node<S>>: Sync + Send,
|
||||
{
|
||||
/// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
|
||||
/// This will resolve the state in parallel
|
||||
pub fn update_state(
|
||||
&mut self,
|
||||
nodes_updated: DirtyNodeStates,
|
||||
ctx: SendAnyMap,
|
||||
) -> FxDashSet<RealNodeId> {
|
||||
S::update(nodes_updated, &mut self.tree, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State> Deref for RealDom<S> {
|
||||
type Target = Tree<Node<S>>;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::node::Node;
|
||||
use crate::node::{FromAnyValue, Node};
|
||||
use crate::node_ref::{NodeMask, NodeView};
|
||||
use crate::passes::{resolve_passes, AnyPass, DirtyNodeStates};
|
||||
use crate::passes::{resolve_passes, resolve_passes_single_threaded, AnyPass, DirtyNodeStates};
|
||||
use crate::tree::TreeView;
|
||||
use crate::{FxDashSet, RealNodeId, SendAnyMap};
|
||||
|
||||
@@ -76,7 +76,7 @@ pub(crate) fn union_ordered_iter<'a>(
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait ChildDepState {
|
||||
pub trait ChildDepState<V: FromAnyValue = ()> {
|
||||
/// The context is passed to the [ChildDepState::reduce] when it is resolved.
|
||||
type Ctx;
|
||||
/// A state from each child node that this node depends on. Typically this is Self, but it could be any state that is within the state tree.
|
||||
@@ -87,7 +87,7 @@ pub trait ChildDepState {
|
||||
/// Resolve the state current node's state from the state of the children, the state of the node, and some external context.
|
||||
fn reduce<'a>(
|
||||
&mut self,
|
||||
node: NodeView,
|
||||
node: NodeView<'a, V>,
|
||||
children: impl Iterator<Item = <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
|
||||
ctx: &Self::Ctx,
|
||||
) -> bool
|
||||
@@ -138,7 +138,7 @@ pub trait ChildDepState {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait ParentDepState {
|
||||
pub trait ParentDepState<V: FromAnyValue = ()> {
|
||||
/// The context is passed to the [ParentDepState::reduce] when it is resolved.
|
||||
type Ctx;
|
||||
/// A state from from the parent node that this node depends on. Typically this is Self, but it could be any state that is within the state tree.
|
||||
@@ -149,7 +149,7 @@ pub trait ParentDepState {
|
||||
/// Resolve the state current node's state from the state of the parent node, the state of the node, and some external context.
|
||||
fn reduce<'a>(
|
||||
&mut self,
|
||||
node: NodeView,
|
||||
node: NodeView<'a, V>,
|
||||
parent: Option<<Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
|
||||
ctx: &Self::Ctx,
|
||||
) -> bool;
|
||||
@@ -193,7 +193,7 @@ pub trait ParentDepState {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait NodeDepState {
|
||||
pub trait NodeDepState<V: FromAnyValue = ()> {
|
||||
/// Depstate must be a tuple containing any number of borrowed elements that are either [ChildDepState], [ParentDepState] or [NodeDepState].
|
||||
type DepState: ElementBorrowable;
|
||||
/// The state passed to [NodeDepState::reduce] when it is resolved.
|
||||
@@ -203,7 +203,7 @@ pub trait NodeDepState {
|
||||
/// Resolve the state current node's state from the state of the sibling states, the state of the node, and some external context.
|
||||
fn reduce<'a>(
|
||||
&mut self,
|
||||
node: NodeView,
|
||||
node: NodeView<'a, V>,
|
||||
node_state: <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>,
|
||||
ctx: &Self::Ctx,
|
||||
) -> bool;
|
||||
@@ -217,7 +217,7 @@ pub trait State: Default + Clone + 'static {
|
||||
const MASKS: &'static [NodeMask];
|
||||
|
||||
#[doc(hidden)]
|
||||
fn update<T: TreeView<Node<Self>>>(
|
||||
fn update<T: TreeView<Node<Self>> + Sync + Send>(
|
||||
dirty: DirtyNodeStates,
|
||||
tree: &mut T,
|
||||
ctx: SendAnyMap,
|
||||
@@ -225,12 +225,22 @@ pub trait State: Default + Clone + 'static {
|
||||
let passes = Self::PASSES.iter().collect();
|
||||
resolve_passes(tree, dirty, passes, ctx)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn update_single_threaded<T: TreeView<Node<Self>>>(
|
||||
dirty: DirtyNodeStates,
|
||||
tree: &mut T,
|
||||
ctx: SendAnyMap,
|
||||
) -> FxDashSet<RealNodeId> {
|
||||
let passes = Self::PASSES.iter().collect();
|
||||
resolve_passes_single_threaded(tree, dirty, passes, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildDepState for () {
|
||||
type Ctx = ();
|
||||
type DepState = ();
|
||||
fn reduce<'a>(&mut self, _: NodeView, _: impl Iterator<Item = ()>, _: &Self::Ctx) -> bool
|
||||
fn reduce<'a>(&mut self, _: NodeView<'a>, _: impl Iterator<Item = ()>, _: &Self::Ctx) -> bool
|
||||
where
|
||||
Self::DepState: 'a,
|
||||
{
|
||||
@@ -241,7 +251,7 @@ impl ChildDepState for () {
|
||||
impl ParentDepState for () {
|
||||
type Ctx = ();
|
||||
type DepState = ();
|
||||
fn reduce<'a>(&mut self, _: NodeView, _: Option<()>, _: &Self::Ctx) -> bool {
|
||||
fn reduce<'a>(&mut self, _: NodeView<'a>, _: Option<()>, _: &Self::Ctx) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -249,7 +259,7 @@ impl ParentDepState for () {
|
||||
impl NodeDepState for () {
|
||||
type DepState = ();
|
||||
type Ctx = ();
|
||||
fn reduce(&mut self, _: NodeView, _sibling: (), _: &Self::Ctx) -> bool {
|
||||
fn reduce<'a>(&mut self, _: NodeView<'a>, _sibling: (), _: &Self::Ctx) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
+181
-371
@@ -1,11 +1,5 @@
|
||||
use core::panic;
|
||||
use parking_lot::lock_api::RawMutex as _;
|
||||
use parking_lot::{RawMutex, RwLock};
|
||||
use slab::Slab;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
|
||||
pub struct NodeId(pub usize);
|
||||
@@ -93,17 +87,7 @@ pub trait TreeView<T>: Sized {
|
||||
|
||||
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>>;
|
||||
|
||||
fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)> {
|
||||
let mut_ptr: *mut Self = self;
|
||||
unsafe {
|
||||
// Safety: No node has itself as a child.
|
||||
(*mut_ptr).get_mut(id).and_then(|parent| {
|
||||
(*mut_ptr)
|
||||
.children_mut(id)
|
||||
.map(|children| (parent, children))
|
||||
})
|
||||
}
|
||||
}
|
||||
fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)>;
|
||||
|
||||
fn children_ids(&self, id: NodeId) -> Option<&[NodeId]>;
|
||||
|
||||
@@ -111,28 +95,12 @@ pub trait TreeView<T>: Sized {
|
||||
|
||||
fn parent_mut(&mut self, id: NodeId) -> Option<&mut T>;
|
||||
|
||||
fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, Option<&mut T>)> {
|
||||
let mut_ptr: *mut Self = self;
|
||||
unsafe {
|
||||
// Safety: No node has itself as a parent.
|
||||
(*mut_ptr)
|
||||
.get_mut(id)
|
||||
.map(|node| (node, (*mut_ptr).parent_mut(id)))
|
||||
}
|
||||
}
|
||||
fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, Option<&mut T>)>;
|
||||
|
||||
fn parent_id(&self, id: NodeId) -> Option<NodeId>;
|
||||
|
||||
fn height(&self, id: NodeId) -> Option<u16>;
|
||||
|
||||
fn map<T2, F: Fn(&T) -> &T2, FMut: Fn(&mut T) -> &mut T2>(
|
||||
&mut self,
|
||||
map: F,
|
||||
map_mut: FMut,
|
||||
) -> TreeMap<T, T2, Self, F, FMut> {
|
||||
TreeMap::new(self, map, map_mut)
|
||||
}
|
||||
|
||||
fn size(&self) -> usize;
|
||||
|
||||
fn traverse_depth_first(&self, mut f: impl FnMut(&T)) {
|
||||
@@ -208,58 +176,39 @@ pub trait TreeLike<T>: TreeView<T> {
|
||||
fn insert_after(&mut self, id: NodeId, new: NodeId);
|
||||
}
|
||||
|
||||
pub struct ChildNodeIterator<'a, T, Tr: TreeView<T>> {
|
||||
tree: &'a Tr,
|
||||
children_ids: &'a [NodeId],
|
||||
pub struct ChildNodeIterator<'a, T> {
|
||||
nodes: &'a Slab<Node<T>>,
|
||||
children_ids: Vec<NodeId>,
|
||||
index: usize,
|
||||
node_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, Tr: TreeView<T>> Iterator for ChildNodeIterator<'a, T, Tr> {
|
||||
impl<'a, T: 'a> Iterator for ChildNodeIterator<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.children_ids.get(self.index).map(|id| {
|
||||
self.index += 1;
|
||||
self.tree.get_unchecked(*id)
|
||||
&self.nodes.get(id.0).unwrap().value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChildNodeIteratorMut<'a, T, Tr: TreeView<T> + 'a> {
|
||||
tree: *mut Tr,
|
||||
children_ids: &'a [NodeId],
|
||||
index: usize,
|
||||
node_type: PhantomData<T>,
|
||||
pub struct ChildNodeIteratorMut<'a, T> {
|
||||
nodes: Vec<&'a mut Node<T>>,
|
||||
}
|
||||
|
||||
unsafe impl<'a, T, Tr: TreeView<T> + 'a> Sync for ChildNodeIteratorMut<'a, T, Tr> {}
|
||||
|
||||
impl<'a, T, Tr: TreeView<T>> ChildNodeIteratorMut<'a, T, Tr> {
|
||||
fn tree_mut(&mut self) -> &'a mut Tr {
|
||||
unsafe { &mut *self.tree }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a, Tr: TreeView<T>> Iterator for ChildNodeIteratorMut<'a, T, Tr> {
|
||||
impl<'a, T: 'a> Iterator for ChildNodeIteratorMut<'a, T> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let owned = self.children_ids.get(self.index).copied();
|
||||
match owned {
|
||||
Some(id) => {
|
||||
self.index += 1;
|
||||
|
||||
Some(self.tree_mut().get_mut_unchecked(id))
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
self.nodes.pop().map(|node| &mut node.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TreeView<T> for Tree<T> {
|
||||
type Iterator<'a> = ChildNodeIterator<'a, T, Tree<T>> where T: 'a;
|
||||
type IteratorMut<'a> = ChildNodeIteratorMut<'a, T, Tree<T>> where T: 'a;
|
||||
type Iterator<'a> = ChildNodeIterator<'a, T> where T: 'a;
|
||||
type IteratorMut<'a> = ChildNodeIteratorMut<'a, T> where T: 'a;
|
||||
|
||||
fn root(&self) -> NodeId {
|
||||
self.root
|
||||
@@ -275,24 +224,26 @@ impl<T> TreeView<T> for Tree<T> {
|
||||
|
||||
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
|
||||
self.children_ids(id).map(|children_ids| ChildNodeIterator {
|
||||
tree: self,
|
||||
children_ids,
|
||||
nodes: &self.nodes,
|
||||
children_ids: children_ids.to_vec(),
|
||||
index: 0,
|
||||
node_type: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
|
||||
let raw_ptr = self as *mut Self;
|
||||
unsafe {
|
||||
// Safety: No node will appear as a child twice
|
||||
self.children_ids(id)
|
||||
.map(|children_ids| ChildNodeIteratorMut {
|
||||
tree: &mut *raw_ptr,
|
||||
children_ids,
|
||||
index: 0,
|
||||
node_type: PhantomData,
|
||||
})
|
||||
// Safety: No node has itself as a parent.
|
||||
if let Some(children_ids) = self.children_ids(id) {
|
||||
let children_ids = children_ids.to_vec();
|
||||
Some(ChildNodeIteratorMut {
|
||||
nodes: unsafe {
|
||||
self.nodes
|
||||
.get_many_mut_unchecked(children_ids.into_iter().rev().map(|id| id.0))
|
||||
.unwrap()
|
||||
},
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,6 +291,39 @@ impl<T> TreeView<T> for Tree<T> {
|
||||
fn size(&self) -> usize {
|
||||
self.nodes.len()
|
||||
}
|
||||
|
||||
fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, Option<&mut T>)> {
|
||||
if let Some(parent_id) = self.parent_id(id) {
|
||||
self.nodes
|
||||
.get2_mut(id.0, parent_id.0)
|
||||
.map(|(node, parent)| (&mut node.value, Some(&mut parent.value)))
|
||||
} else {
|
||||
self.nodes.get_mut(id.0).map(|node| (&mut node.value, None))
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)> {
|
||||
// Safety: No node will appear as a child twice
|
||||
if let Some(children_ids) = self.children_ids(id) {
|
||||
debug_assert!(!children_ids.iter().any(|child_id| *child_id == id));
|
||||
let mut borrowed = unsafe {
|
||||
let as_vec = children_ids.to_vec();
|
||||
self.nodes
|
||||
.get_many_mut_unchecked(
|
||||
as_vec
|
||||
.into_iter()
|
||||
.rev()
|
||||
.map(|id| id.0)
|
||||
.chain(std::iter::once(id.0)),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
let node = &mut borrowed.pop().unwrap().value;
|
||||
Some((node, ChildNodeIteratorMut { nodes: borrowed }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TreeLike<T> for Tree<T> {
|
||||
@@ -435,248 +419,6 @@ impl<T> TreeLike<T> for Tree<T> {
|
||||
self.set_height(new, height);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TreeMap<'a, T1, T2, Tr, F, FMut>
|
||||
where
|
||||
Tr: TreeView<T1>,
|
||||
F: Fn(&T1) -> &T2,
|
||||
FMut: Fn(&mut T1) -> &mut T2,
|
||||
{
|
||||
tree: &'a mut Tr,
|
||||
map: F,
|
||||
map_mut: FMut,
|
||||
in_node_type: PhantomData<T1>,
|
||||
out_node_type: PhantomData<T2>,
|
||||
}
|
||||
|
||||
impl<'a, T1, T2, Tr, F, FMut> TreeMap<'a, T1, T2, Tr, F, FMut>
|
||||
where
|
||||
Tr: TreeView<T1>,
|
||||
F: Fn(&T1) -> &T2,
|
||||
FMut: Fn(&mut T1) -> &mut T2,
|
||||
{
|
||||
pub fn new(tree: &'a mut Tr, map: F, map_mut: FMut) -> Self {
|
||||
TreeMap {
|
||||
tree,
|
||||
map,
|
||||
map_mut,
|
||||
in_node_type: PhantomData,
|
||||
out_node_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T1, T2, Tr, F, FMut> TreeView<T2> for TreeMap<'a, T1, T2, Tr, F, FMut>
|
||||
where
|
||||
Tr: TreeView<T1>,
|
||||
F: Fn(&T1) -> &T2,
|
||||
FMut: Fn(&mut T1) -> &mut T2,
|
||||
{
|
||||
type Iterator<'b> = ChildNodeIterator<'b, T2, TreeMap<'a, T1, T2, Tr, F, FMut>>
|
||||
where
|
||||
T2: 'b,
|
||||
Self:'b;
|
||||
type IteratorMut<'b> = ChildNodeIteratorMut<'b, T2, TreeMap<'a, T1, T2, Tr, F, FMut>>
|
||||
where
|
||||
T2: 'b,
|
||||
Self:'b;
|
||||
|
||||
fn root(&self) -> NodeId {
|
||||
self.tree.root()
|
||||
}
|
||||
|
||||
fn get(&self, id: NodeId) -> Option<&T2> {
|
||||
self.tree.get(id).map(|node| (self.map)(node))
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, id: NodeId) -> Option<&mut T2> {
|
||||
self.tree.get_mut(id).map(|node| (self.map_mut)(node))
|
||||
}
|
||||
|
||||
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
|
||||
self.children_ids(id).map(|children_ids| ChildNodeIterator {
|
||||
tree: self,
|
||||
children_ids,
|
||||
index: 0,
|
||||
node_type: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
|
||||
let raw_ptr = self as *mut Self;
|
||||
unsafe {
|
||||
// Safety: No node can be a child twice.
|
||||
self.children_ids(id)
|
||||
.map(|children_ids| ChildNodeIteratorMut {
|
||||
tree: &mut *raw_ptr,
|
||||
children_ids,
|
||||
index: 0,
|
||||
node_type: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn children_ids(&self, id: NodeId) -> Option<&[NodeId]> {
|
||||
self.tree.children_ids(id)
|
||||
}
|
||||
|
||||
fn parent(&self, id: NodeId) -> Option<&T2> {
|
||||
self.tree.parent(id).map(|node| (self.map)(node))
|
||||
}
|
||||
|
||||
fn parent_mut(&mut self, id: NodeId) -> Option<&mut T2> {
|
||||
self.tree.parent_mut(id).map(|node| (self.map_mut)(node))
|
||||
}
|
||||
|
||||
fn parent_id(&self, id: NodeId) -> Option<NodeId> {
|
||||
self.tree.parent_id(id)
|
||||
}
|
||||
|
||||
fn height(&self, id: NodeId) -> Option<u16> {
|
||||
self.tree.height(id)
|
||||
}
|
||||
|
||||
fn get_unchecked(&self, id: NodeId) -> &T2 {
|
||||
(self.map)(self.tree.get_unchecked(id))
|
||||
}
|
||||
|
||||
fn get_mut_unchecked(&mut self, id: NodeId) -> &mut T2 {
|
||||
(self.map_mut)(self.tree.get_mut_unchecked(id))
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.tree.size()
|
||||
}
|
||||
}
|
||||
|
||||
/// A view into a tree that can be shared between multiple threads. Nodes are locked invividually.
|
||||
pub struct SharedView<'a, T, Tr: TreeView<T>> {
|
||||
tree: Arc<UnsafeCell<&'a mut Tr>>,
|
||||
node_locks: Arc<RwLock<Vec<RawMutex>>>,
|
||||
node_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T, Tr: TreeView<T>> SharedView<'a, T, Tr> {
|
||||
/// Checks if a node is currently locked. Returns None if the node does not exist.
|
||||
pub fn check_lock(&self, id: NodeId) -> Option<bool> {
|
||||
let locks = self.node_locks.read();
|
||||
locks.get(id.0).map(|lock| lock.is_locked())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, T, Tr: TreeView<T>> Send for SharedView<'a, T, Tr> {}
|
||||
unsafe impl<'a, T, Tr: TreeView<T>> Sync for SharedView<'a, T, Tr> {}
|
||||
impl<'a, T, Tr: TreeView<T>> Clone for SharedView<'a, T, Tr> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
tree: self.tree.clone(),
|
||||
node_locks: self.node_locks.clone(),
|
||||
node_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, Tr: TreeView<T>> SharedView<'a, T, Tr> {
|
||||
pub fn new(tree: &'a mut Tr) -> Self {
|
||||
let tree = Arc::new(UnsafeCell::new(tree));
|
||||
let mut node_locks = Vec::new();
|
||||
for _ in 0..unsafe { (*tree.get()).size() } {
|
||||
node_locks.push(RawMutex::INIT);
|
||||
}
|
||||
Self {
|
||||
tree,
|
||||
node_locks: Arc::new(RwLock::new(node_locks)),
|
||||
node_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn lock_node(&self, node: NodeId) {
|
||||
let read = self.node_locks.read();
|
||||
let lock = read.get(node.0);
|
||||
match lock {
|
||||
Some(lock) => lock.lock(),
|
||||
None => {
|
||||
drop(read);
|
||||
let mut write = self.node_locks.write();
|
||||
write.resize_with(node.0 + 1, || RawMutex::INIT);
|
||||
unsafe { write.get_unchecked(node.0).lock() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unlock_node(&self, node: NodeId) {
|
||||
let read = self.node_locks.read();
|
||||
let lock = read.get(node.0);
|
||||
match lock {
|
||||
Some(lock) => unsafe { lock.unlock() },
|
||||
None => {
|
||||
panic!("unlocking node that was not locked")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn with_node<R>(&self, node_id: NodeId, f: impl FnOnce(&'a mut Tr) -> R) -> R {
|
||||
self.lock_node(node_id);
|
||||
let tree = unsafe { &mut *self.tree.get() };
|
||||
let r = f(tree);
|
||||
self.unlock_node(node_id);
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, Tr: TreeView<T>> TreeView<T> for SharedView<'a, T, Tr> {
|
||||
type Iterator<'b> = Tr::Iterator<'b> where T: 'b, Self: 'b;
|
||||
|
||||
type IteratorMut<'b>=Tr::IteratorMut<'b>
|
||||
where
|
||||
T: 'b,
|
||||
Self: 'b;
|
||||
|
||||
fn root(&self) -> NodeId {
|
||||
unsafe { (*self.tree.get()).root() }
|
||||
}
|
||||
|
||||
fn get(&self, id: NodeId) -> Option<&T> {
|
||||
self.with_node(id, |t| t.get(id))
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, id: NodeId) -> Option<&mut T> {
|
||||
self.with_node(id, |t| t.get_mut(id))
|
||||
}
|
||||
|
||||
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
|
||||
self.with_node(id, |t| t.children(id))
|
||||
}
|
||||
|
||||
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
|
||||
self.with_node(id, |t| t.children_mut(id))
|
||||
}
|
||||
|
||||
fn children_ids(&self, id: NodeId) -> Option<&[NodeId]> {
|
||||
self.with_node(id, |t| t.children_ids(id))
|
||||
}
|
||||
|
||||
fn parent(&self, id: NodeId) -> Option<&T> {
|
||||
self.with_node(id, |t| t.get(id))
|
||||
}
|
||||
|
||||
fn parent_mut(&mut self, id: NodeId) -> Option<&mut T> {
|
||||
self.with_node(id, |t| t.parent_mut(id))
|
||||
}
|
||||
|
||||
fn parent_id(&self, id: NodeId) -> Option<NodeId> {
|
||||
self.with_node(id, |t| t.parent_id(id))
|
||||
}
|
||||
|
||||
fn height(&self, id: NodeId) -> Option<u16> {
|
||||
unsafe { (*self.tree.get()).height(id) }
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
unsafe { (*self.tree.get()).size() }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creation() {
|
||||
let mut tree = Tree::new(1);
|
||||
@@ -785,59 +527,6 @@ fn deletion() {
|
||||
assert_eq!(tree.children_ids(parent).unwrap(), &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shared_view() {
|
||||
use std::thread;
|
||||
let mut tree = Tree::new(1);
|
||||
let parent = tree.root();
|
||||
let child = tree.create_node(0);
|
||||
tree.add_child(parent, child);
|
||||
|
||||
let shared = SharedView::new(&mut tree);
|
||||
|
||||
thread::scope(|s| {
|
||||
let (mut shared1, mut shared2, mut shared3) =
|
||||
(shared.clone(), shared.clone(), shared.clone());
|
||||
s.spawn(move || {
|
||||
assert_eq!(*shared1.get_mut(parent).unwrap(), 1);
|
||||
assert_eq!(*shared1.get_mut(child).unwrap(), 0);
|
||||
});
|
||||
s.spawn(move || {
|
||||
assert_eq!(*shared2.get_mut(child).unwrap(), 0);
|
||||
assert_eq!(*shared2.get_mut(parent).unwrap(), 1);
|
||||
});
|
||||
s.spawn(move || {
|
||||
assert_eq!(*shared3.get_mut(parent).unwrap(), 1);
|
||||
assert_eq!(*shared3.get_mut(child).unwrap(), 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map() {
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Value {
|
||||
value: i32,
|
||||
}
|
||||
impl Value {
|
||||
fn new(value: i32) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
let mut tree = Tree::new(Value::new(1));
|
||||
let parent = tree.root();
|
||||
let child = tree.create_node(Value::new(0));
|
||||
tree.add_child(parent, child);
|
||||
|
||||
let mut mapped = tree.map(|x| &x.value, |x| &mut x.value);
|
||||
|
||||
*mapped.get_mut(child).unwrap() = 1;
|
||||
*mapped.get_mut(parent).unwrap() = 2;
|
||||
|
||||
assert_eq!(*tree.get(parent).unwrap(), Value::new(2));
|
||||
assert_eq!(*tree.get(child).unwrap(), Value::new(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn traverse_depth_first() {
|
||||
let mut tree = Tree::new(0);
|
||||
@@ -857,3 +546,124 @@ fn traverse_depth_first() {
|
||||
node_count += 1;
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_node_children_mut() {
|
||||
let mut tree = Tree::new(0);
|
||||
let parent = tree.root();
|
||||
let child1 = tree.create_node(1);
|
||||
tree.add_child(parent, child1);
|
||||
let child2 = tree.create_node(2);
|
||||
tree.add_child(parent, child2);
|
||||
let child3 = tree.create_node(3);
|
||||
tree.add_child(parent, child3);
|
||||
|
||||
let (parent, children) = tree.parent_child_mut(parent).unwrap();
|
||||
for (i, child) in children.enumerate() {
|
||||
assert_eq!(*child, i + 1);
|
||||
}
|
||||
println!("Parent: {:#?}", parent);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_many_mut_unchecked() {
|
||||
let mut slab = Slab::new();
|
||||
let parent = slab.insert(0);
|
||||
let child = slab.insert(1);
|
||||
let grandchild = slab.insert(2);
|
||||
|
||||
let all =
|
||||
unsafe { slab.get_many_mut_unchecked([parent, child, grandchild].into_iter()) }.unwrap();
|
||||
println!("All: {:#?}", all);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Slab<T> {
|
||||
data: Vec<Option<T>>,
|
||||
free: VecDeque<usize>,
|
||||
}
|
||||
|
||||
impl<T> Default for Slab<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Slab<T> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
data: Vec::new(),
|
||||
free: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, id: usize) -> Option<&T> {
|
||||
self.data.get(id).and_then(|x| x.as_ref())
|
||||
}
|
||||
|
||||
unsafe fn get_unchecked(&self, id: usize) -> &T {
|
||||
self.data.get_unchecked(id).as_ref().unwrap()
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, id: usize) -> Option<&mut T> {
|
||||
self.data.get_mut(id).and_then(|x| x.as_mut())
|
||||
}
|
||||
|
||||
unsafe fn get_unchecked_mut(&mut self, id: usize) -> &mut T {
|
||||
self.data.get_unchecked_mut(id).as_mut().unwrap()
|
||||
}
|
||||
|
||||
fn get2_mut(&mut self, id1: usize, id2: usize) -> Option<(&mut T, &mut T)> {
|
||||
assert!(id1 != id2);
|
||||
let ptr = self.data.as_mut_ptr();
|
||||
let first = unsafe { &mut *ptr.add(id1) };
|
||||
let second = unsafe { &mut *ptr.add(id2) };
|
||||
if let (Some(first), Some(second)) = (first, second) {
|
||||
Some((first, second))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_many_mut_unchecked(
|
||||
&mut self,
|
||||
ids: impl Iterator<Item = usize>,
|
||||
) -> Option<Vec<&mut T>> {
|
||||
let ptr = self.data.as_mut_ptr();
|
||||
let mut result = Vec::new();
|
||||
for id in ids {
|
||||
let item = unsafe { &mut *ptr.add(id) };
|
||||
if let Some(item) = item {
|
||||
result.push(item);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn insert(&mut self, value: T) -> usize {
|
||||
if let Some(id) = self.free.pop_front() {
|
||||
self.data[id] = Some(value);
|
||||
id
|
||||
} else {
|
||||
self.data.push(Some(value));
|
||||
self.data.len() - 1
|
||||
}
|
||||
}
|
||||
|
||||
fn try_remove(&mut self, id: usize) -> Option<T> {
|
||||
self.data.get_mut(id).and_then(|x| {
|
||||
self.free.push_back(id);
|
||||
x.take()
|
||||
})
|
||||
}
|
||||
|
||||
fn remove(&mut self, id: usize) -> T {
|
||||
self.try_remove(id).unwrap()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.data.len() - self.free.len()
|
||||
}
|
||||
}
|
||||
@@ -229,7 +229,7 @@ impl PersistantElementIter {
|
||||
#[derive(Default, Clone, Debug)]
|
||||
struct Empty {}
|
||||
impl State for Empty {
|
||||
const PASSES: &'static [crate::AnyPass<crate::node::Node<Self>>] = &[];
|
||||
const PASSES: &'static [crate::AnyPass<crate::node::Node<Self, ()>>] = &[];
|
||||
|
||||
const MASKS: &'static [crate::NodeMask] = &[];
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ futures = "0.3.19"
|
||||
taffy = "0.2.1"
|
||||
smallvec = "1.6"
|
||||
rustc-hash = "1.1.0"
|
||||
anymap = "0.12.1"
|
||||
anymap = "1.0.0-beta.2"
|
||||
futures-channel = "0.3.25"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -79,7 +79,7 @@ impl NodeDepState for Focus {
|
||||
if let Some(index) = a
|
||||
.value
|
||||
.as_int()
|
||||
.or_else(|| a.value.as_text().and_then(|v| v.parse::<i32>().ok()))
|
||||
.or_else(|| a.value.as_text().and_then(|v| v.parse::<i64>().ok()))
|
||||
{
|
||||
match index.cmp(&0) {
|
||||
Ordering::Less => FocusLevel::Unfocusable,
|
||||
|
||||
+26
-7
@@ -7,7 +7,9 @@
|
||||
//! - tests to ensure dyn_into works for various event types.
|
||||
//! - Partial delegation?>
|
||||
|
||||
use dioxus_core::{ElementId, Mutation, Template, TemplateAttribute, TemplateNode};
|
||||
use dioxus_core::{
|
||||
BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode,
|
||||
};
|
||||
use dioxus_html::{event_bubbles, CompositionData, FormData};
|
||||
use dioxus_interpreter_js::{save_template, Channel};
|
||||
use futures_channel::mpsc;
|
||||
@@ -175,16 +177,33 @@ impl WebsysDom {
|
||||
value,
|
||||
id,
|
||||
ns,
|
||||
} => i.set_attribute(id.0 as u32, name, value, ns.unwrap_or_default()),
|
||||
SetBoolAttribute { name, value, id } => {
|
||||
i.set_attribute(id.0 as u32, name, if *value { "true" } else { "false" }, "")
|
||||
}
|
||||
} => match value {
|
||||
BorrowedAttributeValue::Text(txt) => {
|
||||
i.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
|
||||
}
|
||||
BorrowedAttributeValue::Float(f) => {
|
||||
i.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default())
|
||||
}
|
||||
BorrowedAttributeValue::Int(n) => {
|
||||
i.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default())
|
||||
}
|
||||
BorrowedAttributeValue::Bool(b) => i.set_attribute(
|
||||
id.0 as u32,
|
||||
name,
|
||||
if *b { "true" } else { "false" },
|
||||
ns.unwrap_or_default(),
|
||||
),
|
||||
BorrowedAttributeValue::None => {
|
||||
i.remove_attribute(id.0 as u32, name, ns.unwrap_or_default())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
SetText { value, id } => i.set_text(id.0 as u32, value),
|
||||
NewEventListener { name, id, .. } => {
|
||||
i.new_event_listener(name, id.0 as u32, event_bubbles(&name[2..]) as u8);
|
||||
i.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
|
||||
}
|
||||
RemoveEventListener { name, id } => {
|
||||
i.remove_event_listener(name, id.0 as u32, event_bubbles(&name[2..]) as u8)
|
||||
i.remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8)
|
||||
}
|
||||
Remove { id } => i.remove(id.0 as u32),
|
||||
PushRoot { id } => i.push_root(id.0 as u32),
|
||||
|
||||
@@ -189,10 +189,12 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
|
||||
|
||||
// if should_hydrate {
|
||||
// } else {
|
||||
let edits = dom.rebuild();
|
||||
{
|
||||
let edits = dom.rebuild();
|
||||
|
||||
websys_dom.load_templates(&edits.templates);
|
||||
websys_dom.apply_edits(edits.edits);
|
||||
websys_dom.load_templates(&edits.templates);
|
||||
websys_dom.apply_edits(edits.edits);
|
||||
}
|
||||
|
||||
// the mutations come back with nothing - we need to actually mount them
|
||||
websys_dom.mount();
|
||||
|
||||
Reference in new issue
Block a user