mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 06:30:20 +00:00
wip: more modifications to templates
This commit is contained in:
parent
90982e0ccb
commit
7cbb4d52dd
16 changed files with 342 additions and 1520 deletions
|
@ -77,9 +77,12 @@
|
||||||
//! More info on how to improve this diffing algorithm:
|
//! More info on how to improve this diffing algorithm:
|
||||||
//! - <https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/>
|
//! - <https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/>
|
||||||
|
|
||||||
use crate::innerlude::{
|
use crate::{
|
||||||
AnyProps, ElementId, Renderer, ScopeArena, ScopeId, TemplateNode, VComponent, VElement,
|
innerlude::{
|
||||||
VFragment, VNode, VTemplate, VText,
|
AnyProps, ElementId, Renderer, ScopeArena, ScopeId, TemplateNode, VComponent, VElement,
|
||||||
|
VFragment, VNode, VTemplate, VText,
|
||||||
|
},
|
||||||
|
AttributeValue,
|
||||||
};
|
};
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
@ -119,7 +122,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diff_node(&mut self, old_node: &'b VNode<'b>, new_node: &'b VNode<'b>) {
|
pub fn diff_node(&mut self, old_node: &'b VNode<'b>, new_node: &'b VNode<'b>) {
|
||||||
use VNode::{Component, Element, Fragment, Template, Text};
|
use VNode::{Component, Element, Fragment, Placeholder, Template, Text};
|
||||||
match (old_node, new_node) {
|
match (old_node, new_node) {
|
||||||
(Text(old), Text(new)) => {
|
(Text(old), Text(new)) => {
|
||||||
self.diff_text_nodes(old, new, old_node, new_node);
|
self.diff_text_nodes(old, new, old_node, new_node);
|
||||||
|
@ -141,9 +144,13 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
self.diff_templates(old, new, old_node, new_node);
|
self.diff_templates(old, new, old_node, new_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(Placeholder(_), Placeholder(_)) => {
|
||||||
|
self.diff_placeholder_nodes(old_node, new_node);
|
||||||
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
Component(_) | Text(_) | Element(_) | Template(_) | Fragment(_),
|
Component(_) | Text(_) | Element(_) | Template(_) | Fragment(_) | Placeholder(_),
|
||||||
Component(_) | Text(_) | Element(_) | Template(_) | Fragment(_),
|
Component(_) | Text(_) | Element(_) | Template(_) | Fragment(_) | Placeholder(_),
|
||||||
) => self.replace_node(old_node, new_node),
|
) => self.replace_node(old_node, new_node),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,6 +162,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
VNode::Fragment(frag) => self.create_fragment_node(*frag),
|
VNode::Fragment(frag) => self.create_fragment_node(*frag),
|
||||||
VNode::Component(component) => self.create_component_node(*component),
|
VNode::Component(component) => self.create_component_node(*component),
|
||||||
VNode::Template(template) => self.create_template_node(template, node),
|
VNode::Template(template) => self.create_template_node(template, node),
|
||||||
|
VNode::Placeholder(placeholder) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +203,8 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for attr in attributes.iter() {
|
for attr in attributes.iter() {
|
||||||
self.mutations.set_attribute(attr, real_id);
|
self.mutations
|
||||||
|
.set_attribute(attr.name, attr.value, attr.namespace, real_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !children.is_empty() {
|
if !children.is_empty() {
|
||||||
|
@ -303,23 +312,90 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
for (left, right) in old.dynamic_nodes.iter().zip(new.dynamic_nodes.iter()) {
|
for (left, right) in old.dynamic_nodes.iter().zip(new.dynamic_nodes.iter()) {
|
||||||
self.diff_node(left, right);
|
self.diff_node(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: need a way to load up the element bound to these attributes
|
||||||
|
for (left, right) in old.dynamic_attrs.iter().zip(new.dynamic_attrs.iter()) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
// hmm, what we do here?
|
||||||
|
for (left, right) in old.listeners.iter().zip(new.listeners.iter()) {
|
||||||
|
//
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// else, diff them manually, taking the slow path
|
// else, diff them manually, taking the slow path
|
||||||
self.replace_node(old_node, new_node);
|
self.replace_node(old_node, new_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_static_template_nodes(&mut self, node: &'b TemplateNode) {
|
||||||
|
let id = ElementId(999999);
|
||||||
|
match node {
|
||||||
|
TemplateNode::Element {
|
||||||
|
tag,
|
||||||
|
attrs,
|
||||||
|
children,
|
||||||
|
} => {
|
||||||
|
self.mutations.create_element(tag, None, id);
|
||||||
|
|
||||||
|
for attr in *attrs {
|
||||||
|
match attr {
|
||||||
|
crate::TemplateAttribute::Dynamic(_) => todo!(),
|
||||||
|
crate::TemplateAttribute::Static { name, value } => {
|
||||||
|
self.mutations.set_attribute(
|
||||||
|
name,
|
||||||
|
AttributeValue::Text(value),
|
||||||
|
None,
|
||||||
|
id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for child in children.iter() {
|
||||||
|
self.create_static_template_nodes(child);
|
||||||
|
}
|
||||||
|
self.mutations.append_children(children.len() as u32);
|
||||||
|
}
|
||||||
|
TemplateNode::Text(ref text) => self.mutations.create_text_node(text, id),
|
||||||
|
TemplateNode::Dynamic(_) => self.mutations.create_placeholder(id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create the template from scratch using instructions, cache it, and then use the instructions to build it
|
/// Create the template from scratch using instructions, cache it, and then use the instructions to build it
|
||||||
fn create_template_node(
|
fn create_template_node(&mut self, template: &'b VTemplate<'b>, node: &'b VNode<'b>) -> usize {
|
||||||
&mut self,
|
let template_id = template.template.id;
|
||||||
template: &'b VTemplate<'b>,
|
let templates = self.scopes.template_cache.borrow_mut();
|
||||||
temp_node: &'b VNode<'b>,
|
|
||||||
) -> usize {
|
// create and insert the template if it doesn't exist within the VirtualDom (it won't exist on the renderer either)
|
||||||
/*
|
if !templates.contains(&template.template) {
|
||||||
- Use a document fragment for holding nodes
|
template
|
||||||
- Assign IDs to any root nodes so we can find them later for shuffling around
|
.template
|
||||||
- Build dynamic nodes in reverse order so indexing is preserved
|
.roots
|
||||||
*/
|
.into_iter()
|
||||||
|
.for_each(|node| self.create_static_template_nodes(node));
|
||||||
|
|
||||||
|
self.mutations
|
||||||
|
.save(template_id, template.template.roots.len() as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mutations.load(template_id);
|
||||||
|
|
||||||
|
let mut created = 0;
|
||||||
|
|
||||||
|
// create the dynamic nodes
|
||||||
|
for node in template.dynamic_nodes.iter() {
|
||||||
|
created += self.create_node(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set any dynamic attributes
|
||||||
|
for attr in template.dynamic_attrs.iter() {
|
||||||
|
// assign an ID to the element
|
||||||
|
let id = self.scopes.reserve_node(node);
|
||||||
|
|
||||||
|
self.mutations
|
||||||
|
.set_attribute(attr.name, attr.value, attr.namespace, id);
|
||||||
|
}
|
||||||
|
|
||||||
todo!()
|
todo!()
|
||||||
|
|
||||||
|
@ -357,7 +433,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
// nodes_created
|
// nodes_created
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_template_static_node(&mut self, nodes: &'static [TemplateNode]) -> usize {
|
fn create_template_static_node(&mut self, nodes: &'static [VNode<'static>]) -> usize {
|
||||||
todo!()
|
todo!()
|
||||||
// let mut created = 0;
|
// let mut created = 0;
|
||||||
// for node in nodes {
|
// for node in nodes {
|
||||||
|
@ -442,7 +518,12 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
if old.attributes.len() == new.attributes.len() {
|
if old.attributes.len() == new.attributes.len() {
|
||||||
for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
|
for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
|
||||||
if old_attr.value != new_attr.value || new_attr.volatile {
|
if old_attr.value != new_attr.value || new_attr.volatile {
|
||||||
self.mutations.set_attribute(new_attr, root);
|
self.mutations.set_attribute(
|
||||||
|
new_attr.name,
|
||||||
|
new_attr.value,
|
||||||
|
new_attr.namespace,
|
||||||
|
root,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -450,7 +531,12 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
self.mutations.remove_attribute(attribute, root);
|
self.mutations.remove_attribute(attribute, root);
|
||||||
}
|
}
|
||||||
for attribute in new.attributes {
|
for attribute in new.attributes {
|
||||||
self.mutations.set_attribute(attribute, root);
|
self.mutations.set_attribute(
|
||||||
|
attribute.name,
|
||||||
|
attribute.value,
|
||||||
|
attribute.namespace,
|
||||||
|
root,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1000,7 +1086,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
self.scopes.collect_garbage(id);
|
self.scopes.collect_garbage(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
VNode::Text(_) => {
|
VNode::Text(_) | VNode::Placeholder(_) => {
|
||||||
let id = old
|
let id = old
|
||||||
.try_mounted_id()
|
.try_mounted_id()
|
||||||
.unwrap_or_else(|| panic!("broke on {:?}", old));
|
.unwrap_or_else(|| panic!("broke on {:?}", old));
|
||||||
|
@ -1059,15 +1145,15 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// VNode::Placeholder(a) => {
|
VNode::Placeholder(a) => {
|
||||||
// let id = a.id.get().unwrap();
|
let id = a.id.get().unwrap();
|
||||||
// self.scopes.collect_garbage(id);
|
self.scopes.collect_garbage(id);
|
||||||
// a.id.set(None);
|
a.id.set(None);
|
||||||
|
|
||||||
// if gen_muts {
|
if gen_muts {
|
||||||
// self.mutations.remove(id);
|
self.mutations.remove(id);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
VNode::Element(e) => {
|
VNode::Element(e) => {
|
||||||
let id = e.id.get().unwrap();
|
let id = e.id.get().unwrap();
|
||||||
|
|
||||||
|
@ -1157,6 +1243,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
VNode::Template(c) => {
|
VNode::Template(c) => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
VNode::Placeholder(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1175,6 +1262,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
VNode::Template(t) => {
|
VNode::Template(t) => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
VNode::Placeholder(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1182,7 +1270,7 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
// recursively push all the nodes of a tree onto the stack and return how many are there
|
// recursively push all the nodes of a tree onto the stack and return how many are there
|
||||||
fn push_all_real_nodes(&mut self, node: &'b VNode<'b>) -> usize {
|
fn push_all_real_nodes(&mut self, node: &'b VNode<'b>) -> usize {
|
||||||
match node {
|
match node {
|
||||||
VNode::Text(_) | VNode::Element(_) => {
|
VNode::Text(_) | VNode::Element(_) | VNode::Placeholder(_) => {
|
||||||
self.mutations.push_root(node.mounted_id());
|
self.mutations.push_root(node.mounted_id());
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
@ -1205,4 +1293,8 @@ impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn diff_placeholder_nodes(&self, old_node: &VNode, new_node: &VNode) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ pub(crate) mod innerlude {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
AnyEvent, ArbitraryAttributeValue, Attribute, AttributeValue, Component, Element, ElementId,
|
AnyAttributeValue, AnyEvent, Attribute, AttributeValue, Component, Element, ElementId,
|
||||||
EventHandler, EventPriority, IntoVNode, LazyNodes, Listener, NodeFactory, Properties, Renderer,
|
EventHandler, EventPriority, IntoVNode, LazyNodes, Listener, NodeFactory, Properties, Renderer,
|
||||||
SchedulerMsg, Scope, ScopeId, ScopeState, TaskId, Template, TemplateAttribute, TemplateNode,
|
SchedulerMsg, Scope, ScopeId, ScopeState, TaskId, Template, TemplateAttribute, TemplateNode,
|
||||||
UiEvent, UserEvent, VComponent, VElement, VNode, VTemplate, VText, VirtualDom,
|
UiEvent, UserEvent, VComponent, VElement, VNode, VTemplate, VText, VirtualDom,
|
||||||
|
|
|
@ -50,7 +50,13 @@ pub trait Renderer<'a> {
|
||||||
/// Set the text content of a node
|
/// Set the text content of a node
|
||||||
fn set_text(&mut self, text: &'a str, root: ElementId);
|
fn set_text(&mut self, text: &'a str, root: ElementId);
|
||||||
/// Set an attribute on an element
|
/// Set an attribute on an element
|
||||||
fn set_attribute(&mut self, attribute: &'a Attribute<'a>, root: ElementId);
|
fn set_attribute(
|
||||||
|
&mut self,
|
||||||
|
name: &'static str,
|
||||||
|
value: AttributeValue<'a>,
|
||||||
|
namespace: Option<&'a str>,
|
||||||
|
root: ElementId,
|
||||||
|
);
|
||||||
|
|
||||||
/// Save the current n nodes to the ID to be loaded later
|
/// Save the current n nodes to the ID to be loaded later
|
||||||
fn save(&mut self, id: &str, num: u32);
|
fn save(&mut self, id: &str, num: u32);
|
||||||
|
|
|
@ -4,19 +4,28 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// Possible values for an attribute
|
/// Possible values for an attribute
|
||||||
// trying to keep values at 3 bytes
|
#[derive(Clone, Copy)]
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "serialize", serde(untagged))]
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum AttributeValue<'a> {
|
pub enum AttributeValue<'a> {
|
||||||
Text(&'a str),
|
Text(&'a str),
|
||||||
Float32(f32),
|
Float32(f32),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Any(ArbitraryAttributeValue<'a>),
|
Any(&'a dyn AnyAttributeValue),
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[cfg(feature = "serialize")]
|
||||||
|
|
||||||
|
impl<'a> PartialEq for AttributeValue<'a> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
|
||||||
|
(Self::Float32(l0), Self::Float32(r0)) => l0 == r0,
|
||||||
|
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
||||||
|
// (Self::Any(l0), Self::Any(r0)) => l0.cmp(r0),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Display for AttributeValue<'a> {
|
impl<'a> Display for AttributeValue<'a> {
|
||||||
|
@ -112,61 +121,25 @@ impl<'a> AttributeValue<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
/// A trait that allows for comparing two values of the same type through the Any trait
|
||||||
#[allow(missing_docs)]
|
///
|
||||||
pub struct ArbitraryAttributeValue<'a> {
|
/// Defaults to false if the types are not the same
|
||||||
pub value: &'a dyn Any,
|
///
|
||||||
// pub value: &'a dyn AnyClone,
|
/// This is an implicit trait, so any value that is 'static and PartialEq can be used directly
|
||||||
// pub cmp: fn(&dyn AnyClone, &dyn AnyClone) -> bool,
|
///
|
||||||
}
|
/// If you want to override the default behavior, you should implement PartialEq through a wrapper type
|
||||||
|
pub trait AnyAttributeValue: Any {
|
||||||
#[cfg(feature = "serialize")]
|
/// Perform a comparison between two values
|
||||||
impl<'a> Serialize for ArbitraryAttributeValue<'a> {
|
fn cmp_any(&self, _other: &dyn Any) -> bool {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
false
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl<'a, 'de> Deserialize<'de> for ArbitraryAttributeValue<'a> {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for ArbitraryAttributeValue<'_> {
|
impl<T: Any + PartialEq> AnyAttributeValue for T {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn cmp_any(&self, other: &dyn Any) -> bool {
|
||||||
todo!()
|
match other.downcast_ref::<T>() {
|
||||||
// (self.cmp)(self.value, other.value)
|
Some(t) => self == t,
|
||||||
}
|
None => false,
|
||||||
}
|
|
||||||
|
|
||||||
// todo
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
impl<'a> AttributeValue<'a> {
|
|
||||||
pub fn as_text(&self) -> Option<&'a str> {
|
|
||||||
match self {
|
|
||||||
AttributeValue::Text(s) => Some(s),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_float32(&self) -> Option<f32> {
|
|
||||||
match self {
|
|
||||||
AttributeValue::Float32(f) => Some(*f),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_any(&self) -> Option<&'a ArbitraryAttributeValue> {
|
|
||||||
match self {
|
|
||||||
AttributeValue::Any(a) => Some(a),
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,10 @@ pub struct Attribute<'a> {
|
||||||
/// Used in controlled components to ensure changes are propagated.
|
/// Used in controlled components to ensure changes are propagated.
|
||||||
pub volatile: bool,
|
pub volatile: bool,
|
||||||
|
|
||||||
|
/// A reverse lookup for tracking down attributes for templates
|
||||||
|
/// Not used for anything else
|
||||||
|
pub mounted_node: Cell<Option<ElementId>>,
|
||||||
|
|
||||||
/// The value of the attribute.
|
/// The value of the attribute.
|
||||||
pub value: AttributeValue<'a>,
|
pub value: AttributeValue<'a>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ impl<'a> NodeFactory<'a> {
|
||||||
VNode::Text(self.bump.alloc(VText {
|
VNode::Text(self.bump.alloc(VText {
|
||||||
id: Default::default(),
|
id: Default::default(),
|
||||||
text,
|
text,
|
||||||
is_static: true,
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,11 +56,10 @@ impl<'a> NodeFactory<'a> {
|
||||||
/// Create some text that's allocated along with the other vnodes
|
/// Create some text that's allocated along with the other vnodes
|
||||||
///
|
///
|
||||||
pub fn text(&self, args: Arguments) -> VNode<'a> {
|
pub fn text(&self, args: Arguments) -> VNode<'a> {
|
||||||
let (text, is_static) = self.raw_text(args);
|
let (text, _is_static) = self.raw_text(args);
|
||||||
|
|
||||||
VNode::Text(self.bump.alloc(VText {
|
VNode::Text(self.bump.alloc(VText {
|
||||||
text,
|
text,
|
||||||
is_static,
|
|
||||||
id: Default::default(),
|
id: Default::default(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -110,6 +108,7 @@ impl<'a> NodeFactory<'a> {
|
||||||
name,
|
name,
|
||||||
namespace,
|
namespace,
|
||||||
volatile: is_volatile,
|
volatile: is_volatile,
|
||||||
|
mounted_node: Default::default(),
|
||||||
value: val.into_value(self.bump),
|
value: val.into_value(self.bump),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +125,7 @@ impl<'a> NodeFactory<'a> {
|
||||||
name,
|
name,
|
||||||
namespace,
|
namespace,
|
||||||
volatile: is_volatile,
|
volatile: is_volatile,
|
||||||
|
mounted_node: Default::default(),
|
||||||
value,
|
value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,7 @@ impl<'a> NodeFactory<'a> {
|
||||||
/// Create a refrence to a template
|
/// Create a refrence to a template
|
||||||
pub fn template_ref(
|
pub fn template_ref(
|
||||||
&self,
|
&self,
|
||||||
template: Template,
|
template: fn() -> Template<'static>,
|
||||||
nodes: &'a [VNode<'a>],
|
nodes: &'a [VNode<'a>],
|
||||||
attributes: &'a [Attribute<'a>],
|
attributes: &'a [Attribute<'a>],
|
||||||
listeners: &'a [Listener<'a>],
|
listeners: &'a [Listener<'a>],
|
||||||
|
|
|
@ -5,11 +5,13 @@
|
||||||
|
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
mod Placeholder;
|
||||||
mod arbitrary_value;
|
mod arbitrary_value;
|
||||||
mod component;
|
mod component;
|
||||||
mod element;
|
mod element;
|
||||||
mod factory;
|
mod factory;
|
||||||
mod fragment;
|
mod fragment;
|
||||||
|
mod placeholder;
|
||||||
mod suspense;
|
mod suspense;
|
||||||
mod template;
|
mod template;
|
||||||
mod text;
|
mod text;
|
||||||
|
@ -23,6 +25,8 @@ pub use suspense::*;
|
||||||
pub use template::*;
|
pub use template::*;
|
||||||
pub use text::*;
|
pub use text::*;
|
||||||
|
|
||||||
|
use self::Placeholder::VPlaceholder;
|
||||||
|
|
||||||
/// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
|
/// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
|
||||||
///
|
///
|
||||||
/// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
|
/// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
|
||||||
|
@ -113,6 +117,9 @@ pub enum VNode<'src> {
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
Template(&'src VTemplate<'src>),
|
Template(&'src VTemplate<'src>),
|
||||||
|
|
||||||
|
///
|
||||||
|
Placeholder(&'src VPlaceholder),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An Element's unique identifier.
|
/// An Element's unique identifier.
|
||||||
|
@ -131,8 +138,9 @@ impl<'src> VNode<'src> {
|
||||||
VNode::Element(el) => el.key,
|
VNode::Element(el) => el.key,
|
||||||
VNode::Component(c) => c.key,
|
VNode::Component(c) => c.key,
|
||||||
VNode::Fragment(f) => f.key,
|
VNode::Fragment(f) => f.key,
|
||||||
VNode::Text(_t) => None,
|
|
||||||
VNode::Template(t) => t.key,
|
VNode::Template(t) => t.key,
|
||||||
|
VNode::Text(_t) => None,
|
||||||
|
VNode::Placeholder(_p) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,6 +161,7 @@ impl<'src> VNode<'src> {
|
||||||
VNode::Fragment(_) => None,
|
VNode::Fragment(_) => None,
|
||||||
VNode::Component(_) => None,
|
VNode::Component(_) => None,
|
||||||
VNode::Template(_) => None,
|
VNode::Template(_) => None,
|
||||||
|
VNode::Placeholder(el) => el.id.get(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +173,7 @@ impl<'src> VNode<'src> {
|
||||||
VNode::Component(c) => VNode::Component(c),
|
VNode::Component(c) => VNode::Component(c),
|
||||||
VNode::Fragment(f) => VNode::Fragment(f),
|
VNode::Fragment(f) => VNode::Fragment(f),
|
||||||
VNode::Template(t) => VNode::Template(t),
|
VNode::Template(t) => VNode::Template(t),
|
||||||
|
VNode::Placeholder(p) => VNode::Placeholder(p),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,6 +209,10 @@ impl Debug for VNode<'_> {
|
||||||
.debug_struct("VNode::Templates")
|
.debug_struct("VNode::Templates")
|
||||||
.field("template_id", &temp.template.id)
|
.field("template_id", &temp.template.id)
|
||||||
.finish(),
|
.finish(),
|
||||||
|
VNode::Placeholder(place) => s
|
||||||
|
.debug_struct("VNode::Placeholder")
|
||||||
|
.field("id", &place.id)
|
||||||
|
.finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
8
packages/core/src/nodes/placeholder.rs
Normal file
8
packages/core/src/nodes/placeholder.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
use crate::ElementId;
|
||||||
|
|
||||||
|
pub struct VPlaceholder {
|
||||||
|
pub id: Cell<Option<ElementId>>,
|
||||||
|
pub dynamic_index: Option<usize>,
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::{Attribute, ElementId, Listener, VNode};
|
use crate::{Attribute, Listener, VNode};
|
||||||
|
|
||||||
/// A reference to a template along with any context needed to hydrate it
|
/// A reference to a template along with any context needed to hydrate it
|
||||||
pub struct VTemplate<'a> {
|
pub struct VTemplate<'a> {
|
||||||
|
@ -43,18 +43,15 @@ impl<'a> Hash for Template<'a> {
|
||||||
pub enum TemplateNode<'a> {
|
pub enum TemplateNode<'a> {
|
||||||
Element {
|
Element {
|
||||||
tag: &'static str,
|
tag: &'static str,
|
||||||
attrs: &'a [TemplateAttribute],
|
attrs: &'a [TemplateAttribute<'a>],
|
||||||
children: &'a [TemplateNode<'a>],
|
children: &'a [TemplateNode<'a>],
|
||||||
},
|
},
|
||||||
Text(&'static str),
|
Text(&'static str),
|
||||||
Dynamic(usize),
|
Dynamic(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum TemplateAttribute {
|
pub enum TemplateAttribute<'a> {
|
||||||
// todo: more values
|
// todo: more values
|
||||||
Static {
|
Static { name: &'static str, value: &'a str },
|
||||||
name: &'static str,
|
|
||||||
value: &'static str,
|
|
||||||
},
|
|
||||||
Dynamic(usize),
|
Dynamic(usize),
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,4 @@ pub struct VText<'src> {
|
||||||
|
|
||||||
/// The text of the VText.
|
/// The text of the VText.
|
||||||
pub text: &'src str,
|
pub text: &'src str,
|
||||||
|
|
||||||
/// An indiciation if this VText can be ignored during diffing
|
|
||||||
/// Is usually only when there are no strings to be formatted (so the text is &'static str)
|
|
||||||
pub is_static: bool,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,7 +306,6 @@ impl ScopeArena {
|
||||||
let node = frame.bump.alloc(VNode::Text(frame.bump.alloc(VText {
|
let node = frame.bump.alloc(VNode::Text(frame.bump.alloc(VText {
|
||||||
id: Cell::default(),
|
id: Cell::default(),
|
||||||
text: "asd",
|
text: "asd",
|
||||||
is_static: false,
|
|
||||||
})));
|
})));
|
||||||
frame.node.set(unsafe { extend_vnode(node) });
|
frame.node.set(unsafe { extend_vnode(node) });
|
||||||
}
|
}
|
||||||
|
@ -943,7 +942,6 @@ impl BumpFrame {
|
||||||
let node = bump.alloc(VText {
|
let node = bump.alloc(VText {
|
||||||
text: "placeholdertext",
|
text: "placeholdertext",
|
||||||
id: Cell::default(),
|
id: Cell::default(),
|
||||||
is_static: false,
|
|
||||||
});
|
});
|
||||||
let node = bump.alloc(VNode::Text(unsafe {
|
let node = bump.alloc(VNode::Text(unsafe {
|
||||||
&*(node as *mut VText as *const VText)
|
&*(node as *mut VText as *const VText)
|
||||||
|
@ -957,7 +955,6 @@ impl BumpFrame {
|
||||||
let node = self.bump.alloc(VText {
|
let node = self.bump.alloc(VText {
|
||||||
text: "placeholdertext",
|
text: "placeholdertext",
|
||||||
id: Cell::default(),
|
id: Cell::default(),
|
||||||
is_static: false,
|
|
||||||
});
|
});
|
||||||
let node = self.bump.alloc(VNode::Text(unsafe {
|
let node = self.bump.alloc(VNode::Text(unsafe {
|
||||||
&*(node as *mut VText as *const VText)
|
&*(node as *mut VText as *const VText)
|
||||||
|
|
|
@ -1,17 +1,87 @@
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_core::{Attribute, TemplateAttribute};
|
||||||
|
|
||||||
#[test]
|
fn basic_syntax_is_a_template(cx: Scope) -> Element {
|
||||||
fn basic_syntax_is_a_template() {
|
|
||||||
//
|
|
||||||
let var = 123;
|
|
||||||
let asd = 123;
|
let asd = 123;
|
||||||
|
|
||||||
let g = rsx! {
|
let g = rsx! {
|
||||||
div {
|
div {
|
||||||
class: "asd",
|
class: "asd",
|
||||||
class: "{asd}",
|
// class: "{asd}",
|
||||||
onclick: move |_| {},
|
// onclick: move |_| {},
|
||||||
div { "{var}" }
|
// div { "{var}" }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let __cx = NodeFactory::new(&cx);
|
||||||
|
|
||||||
|
static attrs: &'static [TemplateAttribute<'static>] =
|
||||||
|
&[::dioxus::core::TemplateAttribute::Static(
|
||||||
|
::dioxus::core::Attribute {
|
||||||
|
name: "class",
|
||||||
|
namespace: None,
|
||||||
|
volatile: false,
|
||||||
|
mounted_node: Default::default(),
|
||||||
|
value: ::dioxus::core::AttributeValue::Text("asd"),
|
||||||
|
},
|
||||||
|
)];
|
||||||
|
|
||||||
|
__cx . template_ref (
|
||||||
|
|| :: dioxus :: core :: Template {
|
||||||
|
id : "packages/dioxus/tests/rsx_syntax.rs:7:13:/Users/jonkelley/Development/dioxus/packages/dioxus" ,
|
||||||
|
roots : &[
|
||||||
|
:: dioxus :: core :: TemplateNode :: Element {
|
||||||
|
tag : dioxus_elements :: div :: TAG_NAME ,
|
||||||
|
attrs : attrs,
|
||||||
|
children : & [] ,
|
||||||
|
}] ,
|
||||||
|
} ,
|
||||||
|
__cx . bump () . alloc ([]) , __cx . bump () . alloc ([]) , __cx . bump () . alloc ([]) ,
|
||||||
|
None
|
||||||
|
);
|
||||||
|
|
||||||
|
// let static_attr = ::dioxus::core::TemplateAttribute::Static(::dioxus::core::Attribute {
|
||||||
|
// name: "class",
|
||||||
|
// namespace: None,
|
||||||
|
// volatile: false,
|
||||||
|
// mounted_node: Default::default(),
|
||||||
|
// value: ::dioxus::core::AttributeValue::Text("asd"),
|
||||||
|
// });
|
||||||
|
|
||||||
|
// __cx . template_ref (|| :: dioxus :: core :: Template { id : "packages/dioxus/tests/rsx_syntax.rs:7:13:/Users/jonkelley/Development/dioxus/packages/dioxus" , roots : & [:: dioxus :: core :: TemplateNode :: Element { tag : dioxus_elements :: div :: TAG_NAME , attrs : & [static_attr , :: dioxus :: core :: TemplateAttribute :: Dynamic (0usize)] , children : & [] , }] , } , __cx . bump () . alloc ([]) , __cx . bump () . alloc ([__cx . attr (dioxus_elements :: div :: class . 0 , :: core :: fmt :: Arguments :: new_v1 (& [""] , & [:: core :: fmt :: ArgumentV1 :: new_display (& asd)]) , None , false)]) , __cx . bump () . alloc ([]) , None);
|
||||||
|
|
||||||
|
cx.render(g)
|
||||||
|
|
||||||
|
// let __cx = NodeFactory::new(&cx);
|
||||||
|
|
||||||
|
// let t = __cx.template_ref (
|
||||||
|
// || :: dioxus :: core :: Template {
|
||||||
|
// id : "packages/dioxus/tests/rsx_syntax.rs:8:13:/Users/jonkelley/Development/dioxus/packages/dioxus" ,
|
||||||
|
// roots : & [
|
||||||
|
// :: dioxus :: core :: TemplateNode :: Element {
|
||||||
|
// tag : dioxus_elements :: div :: TAG_NAME ,
|
||||||
|
// attrs : & [:: dioxus :: core :: TemplateAttribute :: Dynamic (0usize)] ,
|
||||||
|
// children : & [] ,
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// &[] ,
|
||||||
|
// {
|
||||||
|
// let mut arr = dioxus_core::exports::bumpalo::vec![in __cx.bump()];
|
||||||
|
// arr.push(Attribute {
|
||||||
|
// name: "asd",
|
||||||
|
// namespace: None,
|
||||||
|
// volatile: false,
|
||||||
|
// mounted_node: Default::default(),
|
||||||
|
// value: dioxus_core::AttributeValue::Text(
|
||||||
|
// __cx.raw_text(format_args!("{asd}")).0
|
||||||
|
// ),
|
||||||
|
// });
|
||||||
|
// arr.into_bump_slice() as &[::dioxus::core::Attribute]
|
||||||
|
// },
|
||||||
|
// & [] ,
|
||||||
|
// None
|
||||||
|
// );
|
||||||
|
|
||||||
|
// Some(t)
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,22 +244,42 @@ impl ToTokens for ElementAttrNamed {
|
||||||
tokens.append_all(match attr {
|
tokens.append_all(match attr {
|
||||||
ElementAttr::AttrText { name, value } => {
|
ElementAttr::AttrText { name, value } => {
|
||||||
quote! {
|
quote! {
|
||||||
__cx.attr_disciption( dioxus_elements::#el_name::#name, #value)
|
__cx.attr(
|
||||||
|
dioxus_elements::#el_name::#name.0,
|
||||||
|
#value,
|
||||||
|
None,
|
||||||
|
false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ElementAttr::AttrExpression { name, value } => {
|
ElementAttr::AttrExpression { name, value } => {
|
||||||
quote! {
|
quote! {
|
||||||
__cx.attr_disciption( dioxus_elements::#el_name::#name, #value)
|
__cx.attr(
|
||||||
|
dioxus_elements::#el_name::#name.0,
|
||||||
|
#value,
|
||||||
|
None,
|
||||||
|
false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ElementAttr::CustomAttrText { name, value } => {
|
ElementAttr::CustomAttrText { name, value } => {
|
||||||
quote! {
|
quote! {
|
||||||
__cx.attr( #name, #value, None, false )
|
__cx.attr(
|
||||||
|
dioxus_elements::#el_name::#name.0,
|
||||||
|
#value,
|
||||||
|
None,
|
||||||
|
false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ElementAttr::CustomAttrExpression { name, value } => {
|
ElementAttr::CustomAttrExpression { name, value } => {
|
||||||
quote! {
|
quote! {
|
||||||
__cx.attr( #name, #value, None, false )
|
__cx.attr(
|
||||||
|
dioxus_elements::#el_name::#name.0,
|
||||||
|
#value,
|
||||||
|
None,
|
||||||
|
false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ElementAttr::EventTokens { name, tokens } => {
|
ElementAttr::EventTokens { name, tokens } => {
|
||||||
|
@ -270,3 +290,11 @@ impl ToTokens for ElementAttrNamed {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ::dioxus::core::Attribute {
|
||||||
|
// name: stringify!(#name),
|
||||||
|
// namespace: None,
|
||||||
|
// volatile: false,
|
||||||
|
// mounted_node: Default::default(),
|
||||||
|
// value: ::dioxus::core::AttributeValue::Text(#value),
|
||||||
|
// }
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -67,7 +67,7 @@ impl Parse for CallBody {
|
||||||
impl ToTokens for CallBody {
|
impl ToTokens for CallBody {
|
||||||
fn to_tokens(&self, out_tokens: &mut TokenStream2) {
|
fn to_tokens(&self, out_tokens: &mut TokenStream2) {
|
||||||
// As we print out the dynamic nodes, we want to keep track of them in a linear fashion
|
// As we print out the dynamic nodes, we want to keep track of them in a linear fashion
|
||||||
// We'll use the size of the vecs to determine the index of the dynamic node in the final output
|
// We'll use the size of the vecs to determine the index of the dynamic node in the final
|
||||||
struct DynamicContext<'a> {
|
struct DynamicContext<'a> {
|
||||||
dynamic_nodes: Vec<&'a BodyNode>,
|
dynamic_nodes: Vec<&'a BodyNode>,
|
||||||
dynamic_attributes: Vec<&'a ElementAttrNamed>,
|
dynamic_attributes: Vec<&'a ElementAttrNamed>,
|
||||||
|
@ -95,11 +95,28 @@ impl ToTokens for CallBody {
|
||||||
match &attr.attr {
|
match &attr.attr {
|
||||||
ElementAttr::AttrText { name, value } if value.is_static() => {
|
ElementAttr::AttrText { name, value } if value.is_static() => {
|
||||||
let value = value.source.as_ref().unwrap();
|
let value = value.source.as_ref().unwrap();
|
||||||
quote! { ::dioxus::core::TemplateAttribute::Static { name: stringify!(#name), value: #value } }
|
quote! {
|
||||||
|
::dioxus::core::TemplateAttribute::Static(::dioxus::core::Attribute {
|
||||||
|
name: stringify!(#name),
|
||||||
|
namespace: None,
|
||||||
|
volatile: false,
|
||||||
|
mounted_node: Default::default(),
|
||||||
|
value: ::dioxus::core::AttributeValue::Text(#value),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementAttr::CustomAttrText { name, value } if value.is_static() => {
|
ElementAttr::CustomAttrText { name, value } if value.is_static() => {
|
||||||
quote! { ::dioxus::core::TemplateAttribute::Static { name: #name, value: #value } }
|
let value = value.source.as_ref().unwrap();
|
||||||
|
quote! {
|
||||||
|
::dioxus::core::TemplateAttribute::Static(::dioxus::core::Attribute {
|
||||||
|
name: stringify!(#name),
|
||||||
|
namespace: None,
|
||||||
|
volatile: false,
|
||||||
|
mounted_node: Default::default(),
|
||||||
|
value: ::dioxus::core::AttributeValue::Text(#value),
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
ElementAttr::AttrExpression { .. }
|
ElementAttr::AttrExpression { .. }
|
||||||
|
@ -155,16 +172,20 @@ impl ToTokens for CallBody {
|
||||||
|
|
||||||
out_tokens.append_all(quote! {
|
out_tokens.append_all(quote! {
|
||||||
LazyNodes::new(move | __cx: ::dioxus::core::NodeFactory| -> ::dioxus::core::VNode {
|
LazyNodes::new(move | __cx: ::dioxus::core::NodeFactory| -> ::dioxus::core::VNode {
|
||||||
static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template {
|
|
||||||
id: ::dioxus::core::get_line_num!(),
|
|
||||||
roots: &[ #roots ]
|
|
||||||
};
|
|
||||||
|
|
||||||
__cx.template_ref(
|
__cx.template_ref(
|
||||||
TEMPLATE,
|
|| ::dioxus::core::Template {
|
||||||
&[ #( #dyn_printer ),* ],
|
id: ::dioxus::core::get_line_num!(),
|
||||||
&[ #( #attr_printer ),* ],
|
roots: &[ #roots ]
|
||||||
&[ #( #listener_printer ),* ],
|
},
|
||||||
|
__cx.bump().alloc([
|
||||||
|
#( #dyn_printer ),*
|
||||||
|
]),
|
||||||
|
__cx.bump().alloc([
|
||||||
|
#( #attr_printer ),*
|
||||||
|
]),
|
||||||
|
__cx.bump().alloc([
|
||||||
|
#( #listener_printer ),*
|
||||||
|
]),
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -237,6 +237,9 @@ impl<'a: 'c, 'c> TextRenderer<'a, '_, 'c> {
|
||||||
panic!("Cannot render template without vdom");
|
panic!("Cannot render template without vdom");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
VNode::Placeholder(_) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue