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]
|
[features]
|
||||||
default = []
|
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.
|
// 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| {
|
listeners.drain(..).for_each(|listener| {
|
||||||
let listener = unsafe { &*listener };
|
let listener = unsafe { &*listener };
|
||||||
if let AttributeValue::Listener(l) = &listener.value {
|
match &listener.value {
|
||||||
_ = l.take();
|
AttributeValue::Listener(l) => {
|
||||||
|
_ = l.take();
|
||||||
|
}
|
||||||
|
AttributeValue::Any(a) => {
|
||||||
|
_ = a.take();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-20
@@ -1,5 +1,5 @@
|
|||||||
use crate::any_props::AnyProps;
|
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::mutations::Mutation::*;
|
use crate::mutations::Mutation::*;
|
||||||
use crate::nodes::VNode;
|
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
|
// Make sure we set the attribute's associated id
|
||||||
attribute.mounted_element.set(id);
|
attribute.mounted_element.set(id);
|
||||||
|
|
||||||
@@ -293,9 +293,17 @@ impl<'b> VirtualDom {
|
|||||||
let unbounded_name: &str = unsafe { std::mem::transmute(attribute.name) };
|
let unbounded_name: &str = unsafe { std::mem::transmute(attribute.name) };
|
||||||
|
|
||||||
match &attribute.value {
|
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
|
// 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 {
|
self.mutations.push(SetAttribute {
|
||||||
name: unbounded_name,
|
name: unbounded_name,
|
||||||
@@ -304,22 +312,6 @@ impl<'b> VirtualDom {
|
|||||||
id,
|
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::{
|
use crate::{
|
||||||
any_props::AnyProps,
|
any_props::AnyProps,
|
||||||
arena::ElementId,
|
arena::ElementId,
|
||||||
innerlude::{DirtyScope, VComponent, VPlaceholder, VText},
|
innerlude::{BorrowedAttributeValue, DirtyScope, VComponent, VPlaceholder, VText},
|
||||||
mutations::Mutation,
|
mutations::Mutation,
|
||||||
nodes::RenderReturn,
|
nodes::RenderReturn,
|
||||||
nodes::{DynamicNode, VNode},
|
nodes::{DynamicNode, VNode},
|
||||||
@@ -150,22 +150,16 @@ impl<'b> VirtualDom {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_attribute(&mut self, right_attr: &Attribute, left_attr: &Attribute) {
|
fn update_attribute(&mut self, right_attr: &'b Attribute<'b>, left_attr: &'b Attribute) {
|
||||||
// todo: add more types of attribute values
|
let name = unsafe { std::mem::transmute(left_attr.name) };
|
||||||
match right_attr.value {
|
let value: BorrowedAttributeValue<'b> = (&right_attr.value).into();
|
||||||
AttributeValue::Text(text) => {
|
let value = unsafe { std::mem::transmute(value) };
|
||||||
let name = unsafe { std::mem::transmute(left_attr.name) };
|
self.mutations.push(Mutation::SetAttribute {
|
||||||
let value = unsafe { std::mem::transmute(text) };
|
id: left_attr.mounted_element.get(),
|
||||||
self.mutations.push(Mutation::SetAttribute {
|
ns: right_attr.namespace,
|
||||||
id: left_attr.mounted_element.get(),
|
name,
|
||||||
ns: right_attr.namespace,
|
value,
|
||||||
name,
|
});
|
||||||
value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// todo: more types of attribute values
|
|
||||||
_ => todo!("other attribute types"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff_vcomponent(
|
fn diff_vcomponent(
|
||||||
|
|||||||
@@ -70,10 +70,10 @@ pub(crate) mod innerlude {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
fc_to_builder, Attribute, AttributeValue, CapturedError, Component, DynamicNode, Element,
|
fc_to_builder, AnyValue, Attribute, AttributeValue, BorrowedAttributeValue, CapturedError,
|
||||||
ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation, Mutations, Properties,
|
Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation,
|
||||||
RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, TaskId, Template,
|
Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext,
|
||||||
TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom,
|
TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The purpose of this module is to alleviate imports of many common types
|
/// 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`].
|
/// This includes types like [`Scope`], [`Element`], and [`Component`].
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
fc_to_builder, Component, Element, Event, EventHandler, Fragment, LazyNodes, Properties,
|
fc_to_builder, AnyValue, Component, Element, Event, EventHandler, Fragment,
|
||||||
Scope, ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode,
|
IntoAttributeValue, LazyNodes, Properties, Scope, ScopeId, ScopeState, Scoped, TaskId,
|
||||||
Throw, VNode, VirtualDom,
|
Template, TemplateAttribute, TemplateNode, Throw, VNode, VirtualDom,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use rustc_hash::FxHashSet;
|
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
|
/// 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),
|
derive(serde::Serialize, serde::Deserialize),
|
||||||
serde(tag = "type")
|
serde(tag = "type")
|
||||||
)]
|
)]
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Mutation<'a> {
|
pub enum Mutation<'a> {
|
||||||
/// Add these m children to the target element
|
/// Add these m children to the target element
|
||||||
AppendChildren {
|
AppendChildren {
|
||||||
@@ -193,8 +193,9 @@ pub enum Mutation<'a> {
|
|||||||
SetAttribute {
|
SetAttribute {
|
||||||
/// The name of the attribute to set.
|
/// The name of the attribute to set.
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
|
|
||||||
/// The value of the attribute.
|
/// The value of the attribute.
|
||||||
value: &'a str,
|
value: BorrowedAttributeValue<'a>,
|
||||||
|
|
||||||
/// The ID of the node to set the attribute of.
|
/// The ID of the node to set the attribute of.
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
@@ -204,18 +205,6 @@ pub enum Mutation<'a> {
|
|||||||
ns: Option<&'a str>,
|
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.
|
/// Set the textcontent of a node.
|
||||||
SetText {
|
SetText {
|
||||||
/// The textcontent of the node
|
/// The textcontent of the node
|
||||||
|
|||||||
+133
-15
@@ -5,8 +5,8 @@ use bumpalo::boxed::Box as BumpBox;
|
|||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
cell::{Cell, RefCell, UnsafeCell},
|
cell::{self, Cell, RefCell, UnsafeCell},
|
||||||
fmt::Arguments,
|
fmt::{Arguments, Debug},
|
||||||
future::Future,
|
future::Future,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -523,13 +523,108 @@ pub enum AttributeValue<'a> {
|
|||||||
Listener(RefCell<Option<ListenerCb<'a>>>),
|
Listener(RefCell<Option<ListenerCb<'a>>>),
|
||||||
|
|
||||||
/// An arbitrary value that implements PartialEq and is static
|
/// 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
|
/// A "none" value, resulting in the removal of an attribute from the dom
|
||||||
None,
|
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> {
|
impl<'a> std::fmt::Debug for AttributeValue<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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::Int(l0), Self::Int(r0)) => l0 == r0,
|
||||||
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
||||||
(Self::Listener(_), Self::Listener(_)) => true,
|
(Self::Listener(_), Self::Listener(_)) => true,
|
||||||
(Self::Any(l0), Self::Any(r0)) => l0.any_cmp(r0.as_ref()),
|
(Self::Any(l0), Self::Any(r0)) => {
|
||||||
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
let l0 = l0.borrow();
|
||||||
|
let r0 = r0.borrow();
|
||||||
|
l0.as_ref().unwrap().any_cmp(&**r0.as_ref().unwrap())
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub trait AnyValue {
|
pub trait AnyValue: 'static {
|
||||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
|
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 {
|
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
|
||||||
if self.type_id() != other.our_typeid() {
|
if let Some(other) = other.as_any().downcast_ref() {
|
||||||
return false;
|
self == other
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
self == unsafe { &*(other as *const _ as *const T) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn our_typeid(&self) -> TypeId {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self.type_id()
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,26 +844,36 @@ pub trait IntoAttributeValue<'a> {
|
|||||||
fn into_value(self, bump: &'a Bump) -> AttributeValue<'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 {
|
impl<'a> IntoAttributeValue<'a> for &'a str {
|
||||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||||
AttributeValue::Text(self)
|
AttributeValue::Text(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoAttributeValue<'a> for f64 {
|
impl<'a> IntoAttributeValue<'a> for f64 {
|
||||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||||
AttributeValue::Float(self)
|
AttributeValue::Float(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoAttributeValue<'a> for i64 {
|
impl<'a> IntoAttributeValue<'a> for i64 {
|
||||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||||
AttributeValue::Int(self)
|
AttributeValue::Int(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoAttributeValue<'a> for bool {
|
impl<'a> IntoAttributeValue<'a> for bool {
|
||||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||||
AttributeValue::Bool(self)
|
AttributeValue::Bool(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
|
impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
|
||||||
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
|
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
|
||||||
use bumpalo::core_alloc::fmt::Write;
|
use bumpalo::core_alloc::fmt::Write;
|
||||||
@@ -770,3 +882,9 @@ impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
|
|||||||
AttributeValue::Text(str_buf.into_bump_str())
|
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(),
|
hook_idx: Default::default(),
|
||||||
shared_contexts: Default::default(),
|
shared_contexts: Default::default(),
|
||||||
borrowed_props: Default::default(),
|
borrowed_props: Default::default(),
|
||||||
listeners: Default::default(),
|
attributes_to_drop: Default::default(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::{
|
|||||||
innerlude::{ErrorBoundary, Scheduler, SchedulerMsg},
|
innerlude::{ErrorBoundary, Scheduler, SchedulerMsg},
|
||||||
lazynodes::LazyNodes,
|
lazynodes::LazyNodes,
|
||||||
nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
|
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 bumpalo::{boxed::Box as BumpBox, Bump};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
@@ -88,7 +88,7 @@ pub struct ScopeState {
|
|||||||
pub(crate) spawned_tasks: FxHashSet<TaskId>,
|
pub(crate) spawned_tasks: FxHashSet<TaskId>,
|
||||||
|
|
||||||
pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
|
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) props: Option<Box<dyn AnyProps<'static>>>,
|
||||||
pub(crate) placeholder: Cell<Option<ElementId>>,
|
pub(crate) placeholder: Cell<Option<ElementId>>,
|
||||||
@@ -374,11 +374,15 @@ impl<'src> ScopeState {
|
|||||||
pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
|
pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
|
||||||
let element = rsx.call(self);
|
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 {
|
for attr in element.dynamic_attrs {
|
||||||
if let AttributeValue::Listener(_) = attr.value {
|
match attr.value {
|
||||||
let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
|
AttributeValue::Any(_) | AttributeValue::Listener(_) => {
|
||||||
listeners.push(unbounded);
|
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)))
|
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
|
/// 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
|
/// 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
|
/// Swap the current mutations with a new
|
||||||
fn finalize(&mut self) -> Mutations {
|
fn finalize(&mut self) -> Mutations {
|
||||||
// todo: make this a routine
|
std::mem::take(&mut self.mutations)
|
||||||
let mut out = Mutations::default();
|
|
||||||
std::mem::swap(&mut self.mutations, &mut out);
|
|
||||||
out
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
//!
|
//!
|
||||||
//! This tests to ensure we clean it up
|
//! This tests to ensure we clean it up
|
||||||
|
|
||||||
|
use bumpalo::Bump;
|
||||||
use dioxus::core::{ElementId, Mutation::*};
|
use dioxus::core::{ElementId, Mutation::*};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_core::{AttributeValue, BorrowedAttributeValue};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn attrs_cycle() {
|
fn attrs_cycle() {
|
||||||
@@ -22,6 +24,8 @@ fn attrs_cycle() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let bump = Bump::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().edits,
|
||||||
[
|
[
|
||||||
@@ -36,8 +40,18 @@ fn attrs_cycle() {
|
|||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
AssignId { path: &[0,], id: ElementId(3,) },
|
AssignId { path: &[0,], id: ElementId(3,) },
|
||||||
SetAttribute { name: "class", value: "1", id: ElementId(3,), ns: None },
|
SetAttribute {
|
||||||
SetAttribute { name: "id", value: "1", id: ElementId(3,), ns: None },
|
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 },
|
ReplaceWith { id: ElementId(1,), m: 1 },
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@@ -57,8 +71,18 @@ fn attrs_cycle() {
|
|||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
||||||
AssignId { path: &[0], id: ElementId(3) },
|
AssignId { path: &[0], id: ElementId(3) },
|
||||||
SetAttribute { name: "class", value: "3", id: ElementId(3), ns: None },
|
SetAttribute {
|
||||||
SetAttribute { name: "id", value: "3", id: ElementId(3), ns: None },
|
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 }
|
ReplaceWith { id: ElementId(1), m: 1 }
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
|
use bumpalo::Bump;
|
||||||
use dioxus::core::{ElementId, Mutation::*};
|
use dioxus::core::{ElementId, Mutation::*};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bool_test() {
|
fn bool_test() {
|
||||||
let mut app = VirtualDom::new(|cx| cx.render(rsx!(div { hidden: false })));
|
let mut app = VirtualDom::new(|cx| cx.render(rsx!(div { hidden: false })));
|
||||||
|
let bump = Bump::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.rebuild().santize().edits,
|
app.rebuild().santize().edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
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) },
|
AppendChildren { m: 1, id: ElementId(0) },
|
||||||
]
|
]
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ fn test_borrowed_state() {
|
|||||||
ReplacePlaceholder { path: &[0,], m: 1 },
|
ReplacePlaceholder { path: &[0,], m: 1 },
|
||||||
AppendChildren { m: 1, id: ElementId(0) },
|
AppendChildren { m: 1, id: ElementId(0) },
|
||||||
]
|
]
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Parent(cx: Scope) -> Element {
|
fn Parent(cx: Scope) -> Element {
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ fn app(cx: Scope) -> Element {
|
|||||||
fn bubbles_error() {
|
fn bubbles_error() {
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
|
|
||||||
let _edits = dom.rebuild().santize();
|
{
|
||||||
|
let _edits = dom.rebuild().santize();
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
|
|
||||||
|
|||||||
@@ -12,14 +12,16 @@ fn cycling_elements() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let edits = dom.rebuild().santize();
|
{
|
||||||
assert_eq!(
|
let edits = dom.rebuild().santize();
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
[
|
||||||
AppendChildren { m: 1, id: ElementId(0) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
]
|
AppendChildren { m: 1, id: ElementId(0) },
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -59,19 +59,21 @@ fn component_swap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
let edits = dom.rebuild().santize();
|
{
|
||||||
assert_eq!(
|
let edits = dom.rebuild().santize();
|
||||||
edits.edits,
|
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(1) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(4) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
||||||
ReplacePlaceholder { path: &[1], m: 3 },
|
LoadTemplate { name: "template", index: 0, id: ElementId(4) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
ReplacePlaceholder { path: &[1], m: 3 },
|
||||||
AppendChildren { m: 2, id: ElementId(0) }
|
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
||||||
]
|
AppendChildren { m: 2, id: ElementId(0) }
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -20,22 +20,24 @@ fn keyed_diffing_out_of_order() {
|
|||||||
cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
|
cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
{
|
||||||
dom.rebuild().santize().edits,
|
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(1,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(3,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(4,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(3,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(4,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(6,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(6,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(8,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(8,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(10,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
||||||
AppendChildren { m: 10, id: ElementId(0) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(10,) },
|
||||||
]
|
AppendChildren { m: 10, id: ElementId(0) },
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -44,7 +46,7 @@ fn keyed_diffing_out_of_order() {
|
|||||||
PushRoot { id: ElementId(7,) },
|
PushRoot { id: ElementId(7,) },
|
||||||
InsertBefore { id: ElementId(5,), m: 1 },
|
InsertBefore { id: ElementId(5,), m: 1 },
|
||||||
]
|
]
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Should result in moves only
|
/// Should result in moves only
|
||||||
@@ -70,7 +72,7 @@ fn keyed_diffing_out_of_order_adds() {
|
|||||||
PushRoot { id: ElementId(4,) },
|
PushRoot { id: ElementId(4,) },
|
||||||
InsertBefore { id: ElementId(1,), m: 2 },
|
InsertBefore { id: ElementId(1,), m: 2 },
|
||||||
]
|
]
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Should result in moves only
|
/// Should result in moves only
|
||||||
|
|||||||
@@ -315,66 +315,76 @@ fn remove_many() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let edits = dom.rebuild().santize();
|
{
|
||||||
assert!(edits.templates.is_empty());
|
let edits = dom.rebuild().santize();
|
||||||
assert_eq!(
|
assert!(edits.templates.is_empty());
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
CreatePlaceholder { id: ElementId(1,) },
|
[
|
||||||
AppendChildren { id: ElementId(0), m: 1 },
|
CreatePlaceholder { id: ElementId(1,) },
|
||||||
]
|
AppendChildren { id: ElementId(0), m: 1 },
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
{
|
||||||
let edits = dom.render_immediate().santize();
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
let edits = dom.render_immediate().santize();
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
[
|
||||||
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
ReplaceWith { id: ElementId(1,), m: 1 },
|
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();
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
let edits = dom.render_immediate().santize();
|
||||||
edits.edits,
|
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(1,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
HydrateText { path: &[0,], value: "hello 1", id: ElementId(4,) },
|
||||||
HydrateText { path: &[0,], value: "hello 2", id: ElementId(6,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
HydrateText { path: &[0,], value: "hello 2", id: ElementId(6,) },
|
||||||
HydrateText { path: &[0,], value: "hello 3", id: ElementId(8,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
HydrateText { path: &[0,], value: "hello 3", id: ElementId(8,) },
|
||||||
HydrateText { path: &[0,], value: "hello 4", id: ElementId(10,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
||||||
InsertAfter { id: ElementId(2,), m: 4 },
|
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();
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
let edits = dom.render_immediate().santize();
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
CreatePlaceholder { id: ElementId(11,) },
|
[
|
||||||
Remove { id: ElementId(9,) },
|
CreatePlaceholder { id: ElementId(11,) },
|
||||||
Remove { id: ElementId(7,) },
|
Remove { id: ElementId(9,) },
|
||||||
Remove { id: ElementId(5,) },
|
Remove { id: ElementId(7,) },
|
||||||
Remove { id: ElementId(1,) },
|
Remove { id: ElementId(5,) },
|
||||||
ReplaceWith { id: ElementId(2,), m: 1 },
|
Remove { id: ElementId(1,) },
|
||||||
]
|
ReplaceWith { id: ElementId(2,), m: 1 },
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
{
|
||||||
let edits = dom.render_immediate().santize();
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
let edits = dom.render_immediate().santize();
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
[
|
||||||
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
ReplaceWith { id: ElementId(11,), m: 1 },
|
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::core::{ElementId, Mutation};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
@@ -26,17 +27,22 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element {
|
|||||||
#[test]
|
#[test]
|
||||||
fn dual_stream() {
|
fn dual_stream() {
|
||||||
let mut dom = VirtualDom::new(basic_syntax_is_a_template);
|
let mut dom = VirtualDom::new(basic_syntax_is_a_template);
|
||||||
|
let bump = Bump::new();
|
||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
|
|
||||||
use Mutation::*;
|
use Mutation::*;
|
||||||
assert_eq!(
|
assert_eq!(edits.edits, {
|
||||||
edits.edits,
|
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
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) },
|
NewEventListener { name: "click", id: ElementId(1) },
|
||||||
HydrateText { path: &[0, 0], value: "123", id: ElementId(2) },
|
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() {
|
async fn it_works() {
|
||||||
let mut dom = VirtualDom::new(app);
|
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
|
// We should at least get the top-level template in before pausing for the children
|
||||||
// note: we dont test template edits anymore
|
// note: we dont test template edits anymore
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// mutations.templates,
|
// mutations.templates,
|
||||||
// [
|
// [
|
||||||
// CreateElement { name: "div" },
|
// CreateElement { name: "div" },
|
||||||
// CreateStaticText { value: "Waiting for child..." },
|
// CreateStaticText { value: "Waiting for child..." },
|
||||||
// CreateStaticPlaceholder,
|
// CreateStaticPlaceholder,
|
||||||
// AppendChildren { m: 2 },
|
// AppendChildren { m: 2 },
|
||||||
// SaveTemplate { name: "template", m: 1 }
|
// SaveTemplate { name: "template", m: 1 }
|
||||||
// ]
|
// ]
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// And we should load it in and assign the placeholder properly
|
// And we should load it in and assign the placeholder properly
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mutations.edits,
|
mutations.edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
// hmmmmmmmmm.... with suspense how do we guarantee that IDs increase linearly?
|
// hmmmmmmmmm.... with suspense how do we guarantee that IDs increase linearly?
|
||||||
// can we even?
|
// can we even?
|
||||||
AssignId { path: &[1], id: ElementId(3) },
|
AssignId { path: &[1], id: ElementId(3) },
|
||||||
AppendChildren { m: 1, id: ElementId(0) },
|
AppendChildren { m: 1, id: ElementId(0) },
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// wait just a moment, not enough time for the boundary to resolve
|
// 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"] }
|
syn = { version = "1.0.11", features = ["extra-traits"] }
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
|
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
dioxus = { path = "../dioxus" }
|
dioxus = { path = "../dioxus" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
smallvec = "1.6"
|
smallvec = "1.6"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
anymap = "0.12.1"
|
anymap = "0.12.1"
|
||||||
@@ -393,7 +393,8 @@ impl<'a> StateMember<'a> {
|
|||||||
|
|
||||||
let ty = &self.mem.ty;
|
let ty = &self.mem.ty;
|
||||||
let unit_type = &self.mem.unit_type;
|
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 dep_idents = self.dep_mems.iter().map(|m| &m.ident);
|
||||||
let impl_specific = match self.dep_kind {
|
let impl_specific = match self.dep_kind {
|
||||||
DependencyKind::Node => {
|
DependencyKind::Node => {
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ impl ParentDepState for ParentDepCallCounter {
|
|||||||
const NODE_MASK: NodeMask = NodeMask::ALL;
|
const NODE_MASK: NodeMask = NodeMask::ALL;
|
||||||
fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
|
fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
|
||||||
self.0 += 1;
|
self.0 += 1;
|
||||||
println!("ParentDepCallCounter::reduce on {:?}\n{}", _node, self.0);
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" }
|
|||||||
taffy = "0.2.1"
|
taffy = "0.2.1"
|
||||||
smallvec = "1.6"
|
smallvec = "1.6"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
anymap = "0.12.1"
|
anymap = "1.0.0-beta.2"
|
||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
crossbeam-deque = "0.8.2"
|
crossbeam-deque = "0.8.2"
|
||||||
@@ -26,4 +26,4 @@ dashmap = "5.4.0"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand = "0.8.5"
|
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;
|
use std::hash::BuildHasherDefault;
|
||||||
|
|
||||||
pub use node_ref::NodeMask;
|
pub use node_ref::NodeMask;
|
||||||
@@ -20,7 +21,7 @@ pub mod utils;
|
|||||||
pub type RealNodeId = NodeId;
|
pub type RealNodeId = NodeId;
|
||||||
pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
|
pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||||
pub type FxDashSet<K> = dashmap::DashSet<K, 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
|
/// Used in derived state macros
|
||||||
#[derive(Eq, PartialEq)]
|
#[derive(Eq, PartialEq)]
|
||||||
|
|||||||
@@ -1,43 +1,44 @@
|
|||||||
use crate::{state::State, tree::NodeId};
|
use crate::{state::State, tree::NodeId};
|
||||||
use dioxus_core::ElementId;
|
use dioxus_core::{AnyValue, BorrowedAttributeValue, ElementId};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
/// The node is stored client side and stores only basic data about the node.
|
/// The node is stored client side and stores only basic data about the node.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Node<S: State> {
|
pub struct Node<S: State, V: FromAnyValue = ()> {
|
||||||
/// The transformed state of the node.
|
/// The transformed state of the node.
|
||||||
pub state: S,
|
pub state: S,
|
||||||
/// The raw data for the node
|
/// The raw data for the node
|
||||||
pub node_data: NodeData,
|
pub node_data: NodeData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct NodeData {
|
pub struct NodeData<V: FromAnyValue = ()> {
|
||||||
/// The id of the node
|
/// The id of the node
|
||||||
pub node_id: NodeId,
|
pub node_id: NodeId,
|
||||||
/// The id of the node in the vdom.
|
/// The id of the node in the vdom.
|
||||||
pub element_id: Option<ElementId>,
|
pub element_id: Option<ElementId>,
|
||||||
/// Additional inforation specific to the node type
|
/// 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.
|
/// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum NodeType {
|
pub enum NodeType<V: FromAnyValue = ()> {
|
||||||
Text {
|
Text {
|
||||||
text: String,
|
text: String,
|
||||||
},
|
},
|
||||||
Element {
|
Element {
|
||||||
tag: String,
|
tag: String,
|
||||||
namespace: Option<String>,
|
namespace: Option<String>,
|
||||||
attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue>,
|
attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
|
||||||
listeners: FxHashSet<String>,
|
listeners: FxHashSet<String>,
|
||||||
},
|
},
|
||||||
Placeholder,
|
Placeholder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: State> Node<S> {
|
impl<S: State, V: FromAnyValue> Node<S, V> {
|
||||||
pub(crate) fn new(node_type: NodeType) -> Self {
|
pub(crate) fn new(node_type: NodeType<V>) -> Self {
|
||||||
Node {
|
Node {
|
||||||
state: S::default(),
|
state: S::default(),
|
||||||
node_data: NodeData {
|
node_data: NodeData {
|
||||||
@@ -64,24 +65,59 @@ pub struct OwnedAttributeDiscription {
|
|||||||
/// An attribute on a DOM node, such as `id="my-thing"` or
|
/// An attribute on a DOM node, such as `id="my-thing"` or
|
||||||
/// `href="https://example.com"`.
|
/// `href="https://example.com"`.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct OwnedAttributeView<'a> {
|
pub struct OwnedAttributeView<'a, V: FromAnyValue = ()> {
|
||||||
/// The discription of the attribute.
|
/// The discription of the attribute.
|
||||||
pub attribute: &'a OwnedAttributeDiscription,
|
pub attribute: &'a OwnedAttributeDiscription,
|
||||||
|
|
||||||
/// The value of the attribute.
|
/// The value of the attribute.
|
||||||
pub value: &'a OwnedAttributeValue,
|
pub value: &'a OwnedAttributeValue<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone)]
|
||||||
pub enum OwnedAttributeValue {
|
pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
|
||||||
Text(String),
|
Text(String),
|
||||||
Float(f32),
|
Float(f64),
|
||||||
Int(i32),
|
Int(i64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
Custom(V),
|
||||||
None,
|
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> {
|
pub fn as_text(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
OwnedAttributeValue::Text(text) => Some(text),
|
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 {
|
match self {
|
||||||
OwnedAttributeValue::Float(float) => Some(*float),
|
OwnedAttributeValue::Float(float) => Some(*float),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_int(&self) -> Option<i32> {
|
pub fn as_int(&self) -> Option<i64> {
|
||||||
match self {
|
match self {
|
||||||
OwnedAttributeValue::Int(int) => Some(*int),
|
OwnedAttributeValue::Int(int) => Some(*int),
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -116,4 +152,11 @@ impl OwnedAttributeValue {
|
|||||||
_ => None,
|
_ => 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 dioxus_core::ElementId;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
node::{NodeData, NodeType, OwnedAttributeView},
|
node::{FromAnyValue, NodeData, NodeType, OwnedAttributeView},
|
||||||
state::union_ordered_iter,
|
state::union_ordered_iter,
|
||||||
RealNodeId,
|
RealNodeId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A view into a [VNode] with limited access.
|
/// A view into a [VNode] with limited access.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NodeView<'a> {
|
pub struct NodeView<'a, V: FromAnyValue = ()> {
|
||||||
inner: &'a NodeData,
|
inner: &'a NodeData<V>,
|
||||||
mask: NodeMask,
|
mask: NodeMask,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NodeView<'a> {
|
impl<'a, V: FromAnyValue> NodeView<'a, V> {
|
||||||
/// Create a new NodeView from a VNode, and mask.
|
/// 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 {
|
Self {
|
||||||
inner: node,
|
inner: node,
|
||||||
mask: view,
|
mask: view,
|
||||||
@@ -55,7 +55,9 @@ impl<'a> NodeView<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get any attributes that are enabled in the mask
|
/// 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 {
|
match &self.inner.node_type {
|
||||||
NodeType::Element { attributes, .. } => Some(
|
NodeType::Element { attributes, .. } => Some(
|
||||||
attributes
|
attributes
|
||||||
|
|||||||
@@ -310,11 +310,65 @@ impl<T> AnyPass<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RawPointer<T>(*mut T);
|
pub fn resolve_passes<T, Tr: TreeView<T> + Sync + Send>(
|
||||||
unsafe impl<T> Send for RawPointer<T> {}
|
tree: &mut Tr,
|
||||||
unsafe impl<T> Sync for RawPointer<T> {}
|
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,
|
tree: &mut Tr,
|
||||||
dirty_nodes: DirtyNodeStates,
|
dirty_nodes: DirtyNodeStates,
|
||||||
mut passes: Vec<&AnyPass<T>>,
|
mut passes: Vec<&AnyPass<T>>,
|
||||||
@@ -327,41 +381,31 @@ pub fn resolve_passes<T, Tr: TreeView<T>>(
|
|||||||
let ctx = Arc::new(ctx);
|
let ctx = Arc::new(ctx);
|
||||||
while !passes.is_empty() {
|
while !passes.is_empty() {
|
||||||
let mut currently_borrowed = MemberMask::default();
|
let mut currently_borrowed = MemberMask::default();
|
||||||
std::thread::scope(|s| {
|
let mut i = 0;
|
||||||
let mut i = 0;
|
while i < passes.len() {
|
||||||
while i < passes.len() {
|
let pass = &passes[i];
|
||||||
let pass = &passes[i];
|
let pass_id = pass.pass_id();
|
||||||
let pass_id = pass.pass_id();
|
let pass_mask = pass.mask();
|
||||||
let pass_mask = pass.mask();
|
if pass
|
||||||
if pass
|
.dependancies()
|
||||||
.dependancies()
|
.iter()
|
||||||
.iter()
|
.all(|d| resolved_passes.contains(d) || *d == pass_id)
|
||||||
.all(|d| resolved_passes.contains(d) || *d == pass_id)
|
&& !pass_mask.overlaps(currently_borrowed)
|
||||||
&& !pass_mask.overlaps(currently_borrowed)
|
{
|
||||||
{
|
let pass = passes.remove(i);
|
||||||
let pass = passes.remove(i);
|
resolving.push(pass_id);
|
||||||
resolving.push(pass_id);
|
currently_borrowed |= pass_mask;
|
||||||
currently_borrowed |= pass_mask;
|
let dirty_states = dirty_states.clone();
|
||||||
let tree_mut = tree as *mut _;
|
let nodes_updated = nodes_updated.clone();
|
||||||
let raw_ptr = RawPointer(tree_mut);
|
let ctx = ctx.clone();
|
||||||
let dirty_states = dirty_states.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 nodes_updated = nodes_updated.clone();
|
let mut dirty = DirtyNodes::default();
|
||||||
let ctx = ctx.clone();
|
dirty_states.all_dirty(pass_id, &mut dirty, tree);
|
||||||
s.spawn(move || unsafe {
|
pass.resolve(tree, dirty, &dirty_states, &nodes_updated, &ctx);
|
||||||
// let tree_mut: &mut Tr = &mut *raw_ptr.0;
|
} else {
|
||||||
let raw = raw_ptr;
|
i += 1;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// all passes are resolved at the end of the scope
|
}
|
||||||
});
|
|
||||||
resolved_passes.extend(resolving.iter().copied());
|
resolved_passes.extend(resolving.iter().copied());
|
||||||
resolving.clear()
|
resolving.clear()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,26 +270,7 @@ impl<S: State> RealDom<S> {
|
|||||||
namespace: ns.map(|s| s.to_string()),
|
namespace: ns.map(|s| s.to_string()),
|
||||||
volatile: false,
|
volatile: false,
|
||||||
},
|
},
|
||||||
crate::node::OwnedAttributeValue::Text(value.to_string()),
|
OwnedAttributeValue::from(value),
|
||||||
);
|
|
||||||
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),
|
|
||||||
);
|
);
|
||||||
mark_dirty(
|
mark_dirty(
|
||||||
node_id,
|
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.
|
/// 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,
|
&mut self,
|
||||||
nodes_updated: DirtyNodeStates,
|
nodes_updated: DirtyNodeStates,
|
||||||
ctx: SendAnyMap,
|
ctx: SendAnyMap,
|
||||||
) -> FxDashSet<RealNodeId> {
|
) -> 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.
|
/// 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> {
|
impl<S: State> Deref for RealDom<S> {
|
||||||
type Target = Tree<Node<S>>;
|
type Target = Tree<Node<S>>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use crate::node::Node;
|
use crate::node::{FromAnyValue, Node};
|
||||||
use crate::node_ref::{NodeMask, NodeView};
|
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::tree::TreeView;
|
||||||
use crate::{FxDashSet, RealNodeId, SendAnyMap};
|
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.
|
/// The context is passed to the [ChildDepState::reduce] when it is resolved.
|
||||||
type Ctx;
|
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.
|
/// 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.
|
/// 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>(
|
fn reduce<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
node: NodeView,
|
node: NodeView<'a, V>,
|
||||||
children: impl Iterator<Item = <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
|
children: impl Iterator<Item = <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
|
||||||
ctx: &Self::Ctx,
|
ctx: &Self::Ctx,
|
||||||
) -> bool
|
) -> 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.
|
/// The context is passed to the [ParentDepState::reduce] when it is resolved.
|
||||||
type Ctx;
|
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.
|
/// 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.
|
/// 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>(
|
fn reduce<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
node: NodeView,
|
node: NodeView<'a, V>,
|
||||||
parent: Option<<Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
|
parent: Option<<Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
|
||||||
ctx: &Self::Ctx,
|
ctx: &Self::Ctx,
|
||||||
) -> bool;
|
) -> 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].
|
/// Depstate must be a tuple containing any number of borrowed elements that are either [ChildDepState], [ParentDepState] or [NodeDepState].
|
||||||
type DepState: ElementBorrowable;
|
type DepState: ElementBorrowable;
|
||||||
/// The state passed to [NodeDepState::reduce] when it is resolved.
|
/// 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.
|
/// 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>(
|
fn reduce<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
node: NodeView,
|
node: NodeView<'a, V>,
|
||||||
node_state: <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>,
|
node_state: <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>,
|
||||||
ctx: &Self::Ctx,
|
ctx: &Self::Ctx,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
@@ -217,7 +217,7 @@ pub trait State: Default + Clone + 'static {
|
|||||||
const MASKS: &'static [NodeMask];
|
const MASKS: &'static [NodeMask];
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn update<T: TreeView<Node<Self>>>(
|
fn update<T: TreeView<Node<Self>> + Sync + Send>(
|
||||||
dirty: DirtyNodeStates,
|
dirty: DirtyNodeStates,
|
||||||
tree: &mut T,
|
tree: &mut T,
|
||||||
ctx: SendAnyMap,
|
ctx: SendAnyMap,
|
||||||
@@ -225,12 +225,22 @@ pub trait State: Default + Clone + 'static {
|
|||||||
let passes = Self::PASSES.iter().collect();
|
let passes = Self::PASSES.iter().collect();
|
||||||
resolve_passes(tree, dirty, passes, ctx)
|
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 () {
|
impl ChildDepState for () {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
type DepState = ();
|
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
|
where
|
||||||
Self::DepState: 'a,
|
Self::DepState: 'a,
|
||||||
{
|
{
|
||||||
@@ -241,7 +251,7 @@ impl ChildDepState for () {
|
|||||||
impl ParentDepState for () {
|
impl ParentDepState for () {
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
type DepState = ();
|
type DepState = ();
|
||||||
fn reduce<'a>(&mut self, _: NodeView, _: Option<()>, _: &Self::Ctx) -> bool {
|
fn reduce<'a>(&mut self, _: NodeView<'a>, _: Option<()>, _: &Self::Ctx) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -249,7 +259,7 @@ impl ParentDepState for () {
|
|||||||
impl NodeDepState for () {
|
impl NodeDepState for () {
|
||||||
type DepState = ();
|
type DepState = ();
|
||||||
type Ctx = ();
|
type Ctx = ();
|
||||||
fn reduce(&mut self, _: NodeView, _sibling: (), _: &Self::Ctx) -> bool {
|
fn reduce<'a>(&mut self, _: NodeView<'a>, _sibling: (), _: &Self::Ctx) -> bool {
|
||||||
false
|
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::collections::VecDeque;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
|
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
|
||||||
pub struct NodeId(pub usize);
|
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 children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>>;
|
||||||
|
|
||||||
fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, 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 children_ids(&self, id: NodeId) -> Option<&[NodeId]>;
|
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 parent_mut(&mut self, id: NodeId) -> Option<&mut T>;
|
||||||
|
|
||||||
fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, 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 parent_id(&self, id: NodeId) -> Option<NodeId>;
|
fn parent_id(&self, id: NodeId) -> Option<NodeId>;
|
||||||
|
|
||||||
fn height(&self, id: NodeId) -> Option<u16>;
|
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 size(&self) -> usize;
|
||||||
|
|
||||||
fn traverse_depth_first(&self, mut f: impl FnMut(&T)) {
|
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);
|
fn insert_after(&mut self, id: NodeId, new: NodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChildNodeIterator<'a, T, Tr: TreeView<T>> {
|
pub struct ChildNodeIterator<'a, T> {
|
||||||
tree: &'a Tr,
|
nodes: &'a Slab<Node<T>>,
|
||||||
children_ids: &'a [NodeId],
|
children_ids: Vec<NodeId>,
|
||||||
index: usize,
|
index: usize,
|
||||||
node_type: PhantomData<T>,
|
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;
|
type Item = &'a T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
self.children_ids.get(self.index).map(|id| {
|
self.children_ids.get(self.index).map(|id| {
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
self.tree.get_unchecked(*id)
|
&self.nodes.get(id.0).unwrap().value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChildNodeIteratorMut<'a, T, Tr: TreeView<T> + 'a> {
|
pub struct ChildNodeIteratorMut<'a, T> {
|
||||||
tree: *mut Tr,
|
nodes: Vec<&'a mut Node<T>>,
|
||||||
children_ids: &'a [NodeId],
|
|
||||||
index: usize,
|
|
||||||
node_type: PhantomData<T>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'a, T, Tr: TreeView<T> + 'a> Sync for ChildNodeIteratorMut<'a, T, Tr> {}
|
impl<'a, T: 'a> Iterator for ChildNodeIteratorMut<'a, T> {
|
||||||
|
|
||||||
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> {
|
|
||||||
type Item = &'a mut T;
|
type Item = &'a mut T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let owned = self.children_ids.get(self.index).copied();
|
self.nodes.pop().map(|node| &mut node.value)
|
||||||
match owned {
|
|
||||||
Some(id) => {
|
|
||||||
self.index += 1;
|
|
||||||
|
|
||||||
Some(self.tree_mut().get_mut_unchecked(id))
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TreeView<T> for Tree<T> {
|
impl<T> TreeView<T> for Tree<T> {
|
||||||
type Iterator<'a> = ChildNodeIterator<'a, T, Tree<T>> where T: 'a;
|
type Iterator<'a> = ChildNodeIterator<'a, T> where T: 'a;
|
||||||
type IteratorMut<'a> = ChildNodeIteratorMut<'a, T, Tree<T>> where T: 'a;
|
type IteratorMut<'a> = ChildNodeIteratorMut<'a, T> where T: 'a;
|
||||||
|
|
||||||
fn root(&self) -> NodeId {
|
fn root(&self) -> NodeId {
|
||||||
self.root
|
self.root
|
||||||
@@ -275,24 +224,26 @@ impl<T> TreeView<T> for Tree<T> {
|
|||||||
|
|
||||||
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
|
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
|
||||||
self.children_ids(id).map(|children_ids| ChildNodeIterator {
|
self.children_ids(id).map(|children_ids| ChildNodeIterator {
|
||||||
tree: self,
|
nodes: &self.nodes,
|
||||||
children_ids,
|
children_ids: children_ids.to_vec(),
|
||||||
index: 0,
|
index: 0,
|
||||||
node_type: PhantomData,
|
node_type: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
|
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
|
||||||
let raw_ptr = self as *mut Self;
|
// Safety: No node has itself as a parent.
|
||||||
unsafe {
|
if let Some(children_ids) = self.children_ids(id) {
|
||||||
// Safety: No node will appear as a child twice
|
let children_ids = children_ids.to_vec();
|
||||||
self.children_ids(id)
|
Some(ChildNodeIteratorMut {
|
||||||
.map(|children_ids| ChildNodeIteratorMut {
|
nodes: unsafe {
|
||||||
tree: &mut *raw_ptr,
|
self.nodes
|
||||||
children_ids,
|
.get_many_mut_unchecked(children_ids.into_iter().rev().map(|id| id.0))
|
||||||
index: 0,
|
.unwrap()
|
||||||
node_type: PhantomData,
|
},
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,6 +291,39 @@ impl<T> TreeView<T> for Tree<T> {
|
|||||||
fn size(&self) -> usize {
|
fn size(&self) -> usize {
|
||||||
self.nodes.len()
|
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> {
|
impl<T> TreeLike<T> for Tree<T> {
|
||||||
@@ -435,248 +419,6 @@ impl<T> TreeLike<T> for Tree<T> {
|
|||||||
self.set_height(new, height);
|
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]
|
#[test]
|
||||||
fn creation() {
|
fn creation() {
|
||||||
let mut tree = Tree::new(1);
|
let mut tree = Tree::new(1);
|
||||||
@@ -785,59 +527,6 @@ fn deletion() {
|
|||||||
assert_eq!(tree.children_ids(parent).unwrap(), &[]);
|
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]
|
#[test]
|
||||||
fn traverse_depth_first() {
|
fn traverse_depth_first() {
|
||||||
let mut tree = Tree::new(0);
|
let mut tree = Tree::new(0);
|
||||||
@@ -857,3 +546,124 @@ fn traverse_depth_first() {
|
|||||||
node_count += 1;
|
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)]
|
#[derive(Default, Clone, Debug)]
|
||||||
struct Empty {}
|
struct Empty {}
|
||||||
impl State for 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] = &[];
|
const MASKS: &'static [crate::NodeMask] = &[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ futures = "0.3.19"
|
|||||||
taffy = "0.2.1"
|
taffy = "0.2.1"
|
||||||
smallvec = "1.6"
|
smallvec = "1.6"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
anymap = "0.12.1"
|
anymap = "1.0.0-beta.2"
|
||||||
futures-channel = "0.3.25"
|
futures-channel = "0.3.25"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ impl NodeDepState for Focus {
|
|||||||
if let Some(index) = a
|
if let Some(index) = a
|
||||||
.value
|
.value
|
||||||
.as_int()
|
.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) {
|
match index.cmp(&0) {
|
||||||
Ordering::Less => FocusLevel::Unfocusable,
|
Ordering::Less => FocusLevel::Unfocusable,
|
||||||
|
|||||||
+26
-7
@@ -7,7 +7,9 @@
|
|||||||
//! - tests to ensure dyn_into works for various event types.
|
//! - tests to ensure dyn_into works for various event types.
|
||||||
//! - Partial delegation?>
|
//! - 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_html::{event_bubbles, CompositionData, FormData};
|
||||||
use dioxus_interpreter_js::{save_template, Channel};
|
use dioxus_interpreter_js::{save_template, Channel};
|
||||||
use futures_channel::mpsc;
|
use futures_channel::mpsc;
|
||||||
@@ -175,16 +177,33 @@ impl WebsysDom {
|
|||||||
value,
|
value,
|
||||||
id,
|
id,
|
||||||
ns,
|
ns,
|
||||||
} => i.set_attribute(id.0 as u32, name, value, ns.unwrap_or_default()),
|
} => match value {
|
||||||
SetBoolAttribute { name, value, id } => {
|
BorrowedAttributeValue::Text(txt) => {
|
||||||
i.set_attribute(id.0 as u32, name, if *value { "true" } else { "false" }, "")
|
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),
|
SetText { value, id } => i.set_text(id.0 as u32, value),
|
||||||
NewEventListener { name, id, .. } => {
|
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 } => {
|
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),
|
Remove { id } => i.remove(id.0 as u32),
|
||||||
PushRoot { id } => i.push_root(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 {
|
// if should_hydrate {
|
||||||
// } else {
|
// } else {
|
||||||
let edits = dom.rebuild();
|
{
|
||||||
|
let edits = dom.rebuild();
|
||||||
|
|
||||||
websys_dom.load_templates(&edits.templates);
|
websys_dom.load_templates(&edits.templates);
|
||||||
websys_dom.apply_edits(edits.edits);
|
websys_dom.apply_edits(edits.edits);
|
||||||
|
}
|
||||||
|
|
||||||
// the mutations come back with nothing - we need to actually mount them
|
// the mutations come back with nothing - we need to actually mount them
|
||||||
websys_dom.mount();
|
websys_dom.mount();
|
||||||
|
|||||||
Reference in new issue
Block a user