use BumpBox<Any> and FromAnyValue instead of sync bounds
This commit is contained in:
1 parent
64db1def9f
commit
51dba50c04
20 files changed
+184
-200
No files matched your search
@@ -158,10 +158,10 @@ impl VirtualDom {
|
||||
let listener = unsafe { &*listener };
|
||||
match &listener.value {
|
||||
AttributeValue::Listener(l) => {
|
||||
_ = l.0.take();
|
||||
_ = l.take();
|
||||
}
|
||||
AttributeValue::Any(a) => {
|
||||
_ = a.take();
|
||||
AttributeValue::Any(_) => {
|
||||
todo!()
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::any_props::AnyProps;
|
||||
use crate::innerlude::{VComponent, VPlaceholder, VText};
|
||||
use crate::innerlude::{BorrowedAttributeValue, VComponent, VPlaceholder, VText};
|
||||
use crate::mutations::Mutation;
|
||||
use crate::mutations::Mutation::*;
|
||||
use crate::nodes::VNode;
|
||||
@@ -285,7 +285,7 @@ impl<'b> VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_attribute(&mut self, attribute: &crate::Attribute, id: ElementId) {
|
||||
fn write_attribute(&mut self, attribute: &'b crate::Attribute<'b>, id: ElementId) {
|
||||
// Make sure we set the attribute's associated id
|
||||
attribute.mounted_element.set(id);
|
||||
|
||||
@@ -302,7 +302,8 @@ impl<'b> VirtualDom {
|
||||
}
|
||||
_ => {
|
||||
// Safety: we promise not to re-alias this text later on after committing it to the mutation
|
||||
let unbounded_value = unsafe { std::mem::transmute(attribute.value.clone()) };
|
||||
let value: BorrowedAttributeValue<'b> = (&attribute.value).into();
|
||||
let unbounded_value = unsafe { std::mem::transmute(value) };
|
||||
|
||||
self.mutations.push(SetAttribute {
|
||||
name: unbounded_name,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
any_props::AnyProps,
|
||||
arena::ElementId,
|
||||
innerlude::{DirtyScope, VComponent, VPlaceholder, VText},
|
||||
innerlude::{BorrowedAttributeValue, DirtyScope, VComponent, VPlaceholder, VText},
|
||||
mutations::Mutation,
|
||||
nodes::RenderReturn,
|
||||
nodes::{DynamicNode, VNode},
|
||||
@@ -150,10 +150,10 @@ impl<'b> VirtualDom {
|
||||
};
|
||||
}
|
||||
|
||||
fn update_attribute(&mut self, right_attr: &Attribute, left_attr: &Attribute) {
|
||||
// todo: add more types of attribute values
|
||||
fn update_attribute(&mut self, right_attr: &'b Attribute<'b>, left_attr: &'b Attribute) {
|
||||
let name = unsafe { std::mem::transmute(left_attr.name) };
|
||||
let value = unsafe { std::mem::transmute(right_attr.value.clone()) };
|
||||
let value: BorrowedAttributeValue<'b> = (&right_attr.value).into();
|
||||
let value = unsafe { std::mem::transmute(value) };
|
||||
self.mutations.push(Mutation::SetAttribute {
|
||||
id: left_attr.mounted_element.get(),
|
||||
ns: right_attr.namespace,
|
||||
|
||||
@@ -70,10 +70,10 @@ pub(crate) mod innerlude {
|
||||
}
|
||||
|
||||
pub use crate::innerlude::{
|
||||
fc_to_builder, AnyValueContainer, Attribute, AttributeValue, CapturedError, Component,
|
||||
DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation, Mutations,
|
||||
Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, TaskId,
|
||||
Template, TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom,
|
||||
fc_to_builder, AnyValue, Attribute, AttributeValue, BorrowedAttributeValue, CapturedError,
|
||||
Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation,
|
||||
Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext,
|
||||
TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom,
|
||||
};
|
||||
|
||||
/// The purpose of this module is to alleviate imports of many common types
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{arena::ElementId, AttributeValue, ScopeId, Template};
|
||||
use crate::{arena::ElementId, innerlude::BorrowedAttributeValue, ScopeId, Template};
|
||||
|
||||
/// A container for all the relevant steps to modify the Real DOM
|
||||
///
|
||||
@@ -208,7 +208,7 @@ pub enum Mutation<'a> {
|
||||
name: &'a str,
|
||||
|
||||
/// The value of the attribute.
|
||||
value: AttributeValue<'a>,
|
||||
value: BorrowedAttributeValue<'a>,
|
||||
|
||||
/// The ID of the node to set the attribute of.
|
||||
id: ElementId,
|
||||
|
||||
+85
-120
@@ -6,9 +6,8 @@ use bumpalo::Bump;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell, UnsafeCell},
|
||||
fmt::Arguments,
|
||||
fmt::{Arguments, Debug},
|
||||
future::Future,
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
pub type TemplateId = &'static str;
|
||||
@@ -200,7 +199,7 @@ impl<'a> VNode<'a> {
|
||||
pub(crate) fn clear_listeners(&self) {
|
||||
for attr in self.dynamic_attrs {
|
||||
if let AttributeValue::Listener(l) = &attr.value {
|
||||
l.0.borrow_mut().take();
|
||||
l.borrow_mut().take();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -507,9 +506,6 @@ pub struct Attribute<'a> {
|
||||
///
|
||||
/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
|
||||
/// variant.
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serialize", serde(untagged))]
|
||||
#[derive(Clone)]
|
||||
pub enum AttributeValue<'a> {
|
||||
/// Text attribute
|
||||
Text(&'a str),
|
||||
@@ -524,107 +520,105 @@ pub enum AttributeValue<'a> {
|
||||
Bool(bool),
|
||||
|
||||
/// A listener, like "onclick"
|
||||
Listener(ListenerCb<'a>),
|
||||
Listener(RefCell<Option<ListenerCb<'a>>>),
|
||||
|
||||
/// An arbitrary value that implements PartialEq and is static
|
||||
Any(RefCell<Option<AnyValueContainer>>),
|
||||
Any(BumpBox<'a, dyn AnyValue>),
|
||||
|
||||
/// A "none" value, resulting in the removal of an attribute from the dom
|
||||
None,
|
||||
}
|
||||
|
||||
pub type ListenerCbInner<'a> = RefCell<Option<BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>>>;
|
||||
pub struct ListenerCb<'a>(pub ListenerCbInner<'a>);
|
||||
pub type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
|
||||
|
||||
impl Clone for ListenerCb<'_> {
|
||||
fn clone(&self) -> Self {
|
||||
panic!("ListenerCb cannot be cloned")
|
||||
/// 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(&'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) => BorrowedAttributeValue::Any(&**value),
|
||||
AttributeValue::None => BorrowedAttributeValue::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a> serde::Serialize for ListenerCb<'a> {
|
||||
fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
panic!("ListenerCb cannot be serialized")
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'de, 'a> serde::Deserialize<'de> for ListenerCb<'a> {
|
||||
fn deserialize<D>(_: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
panic!("ListenerCb cannot be deserialized")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg(not(feature = "sync_attributes"))]
|
||||
/// A boxed value that implements PartialEq, and Any
|
||||
pub struct AnyValueContainer(pub std::rc::Rc<dyn AnyValue>);
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg(feature = "sync_attributes")]
|
||||
/// A boxed value that implements PartialEq, Any, Sync, and Send
|
||||
pub struct AnyValueContainer(pub std::sync::Arc<dyn AnyValue>);
|
||||
|
||||
impl PartialEq for AnyValueContainer {
|
||||
impl PartialEq for BorrowedAttributeValue<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.any_cmp(other.0.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl AnyValueContainer {
|
||||
/// Create a new AnyValueContainer containing the specified data.
|
||||
pub fn new<T: AnyValue + 'static>(value: T) -> Self {
|
||||
#[cfg(feature = "sync_attributes")]
|
||||
return Self(std::sync::Arc::new(value));
|
||||
#[cfg(not(feature = "sync_attributes"))]
|
||||
return Self(std::rc::Rc::new(value));
|
||||
}
|
||||
|
||||
/// Returns a reference to the inner value.
|
||||
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
|
||||
self.0.deref().as_any().downcast_ref()
|
||||
}
|
||||
|
||||
/// Checks if the type of the inner value is 'T'.
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
self.0.deref().as_any().is::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_any_value_rc() {
|
||||
let a = AnyValueContainer::new(1i32);
|
||||
assert_eq!(a.downcast_ref::<i32>(), Some(&1i32));
|
||||
assert_eq!(a.downcast_ref::<i64>(), None);
|
||||
assert!(a.is::<i32>());
|
||||
assert!(!a.is::<i64>());
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl serde::Serialize for AnyValueContainer {
|
||||
fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
panic!("AnyValueBox cannot be serialized")
|
||||
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")]
|
||||
impl<'de> serde::Deserialize<'de> for AnyValueContainer {
|
||||
fn deserialize<D>(_: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
panic!("AnyValueBox cannot be deserialized")
|
||||
}
|
||||
fn serialize_any_value<S>(_: &&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<&'a dyn AnyValue, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
panic!("Any cannot be deserialized")
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Debug for AttributeValue<'a> {
|
||||
@@ -649,41 +643,13 @@ impl<'a> PartialEq for AttributeValue<'a> {
|
||||
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
|
||||
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
||||
(Self::Listener(_), Self::Listener(_)) => true,
|
||||
(Self::Any(l0), Self::Any(r0)) => l0 == r0,
|
||||
(Self::Any(l0), Self::Any(r0)) => l0.any_cmp(&**r0),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some renderers need attributes to be sync and send. The rest of the attributes are already sync and send, but there is no way to force Any values to be sync and send on the renderer side.
|
||||
// The sync_attributes flag restricts any valuse to be sync and send.
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "sync_attributes")]
|
||||
pub trait AnyValue: Sync + Send + 'static {
|
||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn type_id(&self) -> TypeId {
|
||||
self.as_any().type_id()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sync_attributes")]
|
||||
impl<T: Any + PartialEq + Send + Sync + 'static> AnyValue for T {
|
||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref() {
|
||||
self == other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(feature = "sync_attributes"))]
|
||||
pub trait AnyValue: 'static {
|
||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
@@ -692,7 +658,6 @@ pub trait AnyValue: 'static {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "sync_attributes"))]
|
||||
impl<T: Any + PartialEq + 'static> AnyValue for T {
|
||||
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
|
||||
if let Some(other) = other.as_any().downcast_ref() {
|
||||
@@ -903,8 +868,8 @@ impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAttributeValue<'a> for AnyValueContainer {
|
||||
impl<'a> IntoAttributeValue<'a> for BumpBox<'a, dyn AnyValue> {
|
||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||
AttributeValue::Any(RefCell::new(Some(self)))
|
||||
AttributeValue::Any(self)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
arena::ElementId,
|
||||
bump_frame::BumpFrame,
|
||||
innerlude::{DynamicNode, EventHandler, VComponent, VText},
|
||||
innerlude::{ErrorBoundary, ListenerCb, Scheduler, SchedulerMsg},
|
||||
innerlude::{ErrorBoundary, Scheduler, SchedulerMsg},
|
||||
lazynodes::LazyNodes,
|
||||
nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
|
||||
Attribute, AttributeValue, Element, Event, Properties, TaskId,
|
||||
@@ -510,7 +510,7 @@ impl<'src> ScopeState {
|
||||
}))
|
||||
};
|
||||
|
||||
AttributeValue::Listener(ListenerCb(RefCell::new(Some(boxed))))
|
||||
AttributeValue::Listener(RefCell::new(Some(boxed)))
|
||||
}
|
||||
|
||||
/// Inject an error into the nearest error boundary and quit rendering
|
||||
|
||||
@@ -384,7 +384,7 @@ impl VirtualDom {
|
||||
// We check the bubble state between each call to see if the event has been stopped from bubbling
|
||||
for listener in listeners.drain(..).rev() {
|
||||
if let AttributeValue::Listener(listener) = listener {
|
||||
if let Some(cb) = listener.0.borrow_mut().as_deref_mut() {
|
||||
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
|
||||
cb(uievent.clone());
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user