Feat: wire up some of the changelist for diff
This commit is contained in:
1 parent
ea2aa4b0c9
commit
d063a19939
6 files changed
+1088
-15
No files matched your search
@@ -0,0 +1,671 @@
|
|||||||
|
//! Changelist
|
||||||
|
//! ----------
|
||||||
|
//!
|
||||||
|
//! This module exposes the "changelist" object which allows 3rd party implementors to handle diffs to the virtual dom.
|
||||||
|
//!
|
||||||
|
//! # Design
|
||||||
|
//! ---
|
||||||
|
//! In essence, the changelist object connects a diff of two vdoms to the actual edits required to update the output renderer.
|
||||||
|
//!
|
||||||
|
//! This abstraction relies on the assumption that the final renderer accepts a tree of elements. For most target platforms,
|
||||||
|
//! this is an appropriate abstraction .
|
||||||
|
//!
|
||||||
|
//! During the diff phase, the change list is built. Once the diff phase is over, the change list is finished and returned back
|
||||||
|
//! to the renderer. The renderer is responsible for propogating the updates (a stream of u32) to the final display.
|
||||||
|
//!
|
||||||
|
//! Because the change list references data internal to the vdom, it needs to be consumed by the renderer before the vdom
|
||||||
|
//! can continue to work. This means once a change list is generated, it should be consumed as fast as possible, otherwise the
|
||||||
|
//! dom will be blocked from progressing. This is enforced by lifetimes on the returend changelist object.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
|
||||||
|
use crate::innerlude::Listener;
|
||||||
|
|
||||||
|
/// Renderers need to implement the interpreter trait
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
trait Inrerpreter {
|
||||||
|
fn set_text(&mut self, text: &str);
|
||||||
|
fn remove_self_and_next_siblings(&mut self);
|
||||||
|
fn replace_with(&mut self);
|
||||||
|
fn set_attribute(&mut self, name: &str, value: &str);
|
||||||
|
fn remove_attribute(&mut self, name: &str);
|
||||||
|
fn push_reverse_child(&mut self, n: u32);
|
||||||
|
fn pop_push_child(&mut self, n: u32);
|
||||||
|
fn pop(&mut self);
|
||||||
|
fn append_child(&mut self);
|
||||||
|
fn create_text_node(&mut self, text: &str);
|
||||||
|
fn create_element(&mut self, tag_name: &str);
|
||||||
|
fn new_event_listener(&mut self, event_type: &str, a: u32, b: u32);
|
||||||
|
fn update_event_listener(&mut self, event_type: &str, a: u32, b: u32);
|
||||||
|
fn remove_event_listener(&mut self, event_type: &str);
|
||||||
|
fn create_element_ns(&mut self, tag_name: &str, ns: &str);
|
||||||
|
fn save_children_to_temporaries(&mut self, temp: u32, start: u32, end: u32);
|
||||||
|
// fn save_children_to_temporaries(&mut self, mut temp: u32, start: u32, end: u32);
|
||||||
|
fn push_child(&mut self, n: u32);
|
||||||
|
fn push_temporary(&mut self, temp: u32);
|
||||||
|
fn insert_before(&mut self);
|
||||||
|
fn pop_push_reverse_child(&mut self, n: u32);
|
||||||
|
fn remove_child(&mut self, n: u32);
|
||||||
|
fn set_class(&mut self, class_name: &str);
|
||||||
|
// fn save_template(&mut self, id: CacheId);
|
||||||
|
// fn push_template(&mut self, id: CacheId);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ChangeList<'src> {
|
||||||
|
traversal: Traversal,
|
||||||
|
next_temporary: u32,
|
||||||
|
forcing_new_listeners: bool,
|
||||||
|
emitter: &'src mut dyn Inrerpreter,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Traversal methods.
|
||||||
|
impl ChangeList<'_> {
|
||||||
|
pub fn go_down(&mut self) {
|
||||||
|
self.traversal.down();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_down_to_child(&mut self, index: usize) {
|
||||||
|
self.traversal.down();
|
||||||
|
self.traversal.sibling(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_down_to_reverse_child(&mut self, index: usize) {
|
||||||
|
self.traversal.down();
|
||||||
|
self.traversal.reverse_sibling(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_up(&mut self) {
|
||||||
|
self.traversal.up();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_to_sibling(&mut self, index: usize) {
|
||||||
|
self.traversal.sibling(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_to_temp_sibling(&mut self, temp: u32) {
|
||||||
|
self.traversal.up();
|
||||||
|
self.traversal.down_to_temp(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_down_to_temp_child(&mut self, temp: u32) {
|
||||||
|
self.traversal.down_to_temp(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commit_traversal(&mut self) {
|
||||||
|
if self.traversal.is_committed() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for mv in self.traversal.commit() {
|
||||||
|
// match mv {
|
||||||
|
// MoveTo::Parent => {
|
||||||
|
// // debug!("emit: pop");
|
||||||
|
// self.emitter.pop();
|
||||||
|
// }
|
||||||
|
// MoveTo::Child(n) => {
|
||||||
|
// // debug!("emit: push_child({})", n);
|
||||||
|
// self.emitter.push_child(n);
|
||||||
|
// }
|
||||||
|
// MoveTo::ReverseChild(n) => {
|
||||||
|
// // debug!("emit: push_reverse_child({})", n);
|
||||||
|
// self.emitter.push_reverse_child(n);
|
||||||
|
// }
|
||||||
|
// MoveTo::Sibling(n) => {
|
||||||
|
// // debug!("emit: pop_push_child({})", n);
|
||||||
|
// self.emitter.pop_push_child(n);
|
||||||
|
// }
|
||||||
|
// MoveTo::ReverseSibling(n) => {
|
||||||
|
// // debug!("emit: pop_push_reverse_child({})", n);
|
||||||
|
// self.emitter.pop_push_reverse_child(n);
|
||||||
|
// }
|
||||||
|
// MoveTo::TempChild(temp) => {
|
||||||
|
// // debug!("emit: push_temporary({})", temp);
|
||||||
|
// self.emitter.push_temporary(temp);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn traversal_is_committed(&self) -> bool {
|
||||||
|
self.traversal.is_committed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChangeList<'_> {
|
||||||
|
pub fn next_temporary(&self) -> u32 {
|
||||||
|
self.next_temporary
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_next_temporary(&mut self, next_temporary: u32) {
|
||||||
|
self.next_temporary = next_temporary;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_children_to_temporaries(&mut self, start: usize, end: usize) -> u32 {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
debug_assert!(start < end);
|
||||||
|
let temp_base = self.next_temporary;
|
||||||
|
// debug!(
|
||||||
|
// "emit: save_children_to_temporaries({}, {}, {})",
|
||||||
|
// temp_base, start, end
|
||||||
|
// );
|
||||||
|
self.next_temporary = temp_base + (end - start) as u32;
|
||||||
|
self.emitter
|
||||||
|
.save_children_to_temporaries(temp_base, start as u32, end as u32);
|
||||||
|
temp_base
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_temporary(&self, temp: u32) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: push_temporary({})", temp);
|
||||||
|
self.emitter.push_temporary(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_child(&self, child: usize) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: remove_child({})", child);
|
||||||
|
self.emitter.remove_child(child as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_before(&self) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: insert_before()");
|
||||||
|
self.emitter.insert_before();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_string(&mut self, string: &str) -> StringKey {
|
||||||
|
todo!()
|
||||||
|
// self.strings.ensure_string(string, &self.emitter)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_text(&self, text: &str) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: set_text({:?})", text);
|
||||||
|
self.emitter.set_text(text);
|
||||||
|
// .set_text(text.as_ptr() as u32, text.len() as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_self_and_next_siblings(&self) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: remove_self_and_next_siblings()");
|
||||||
|
self.emitter.remove_self_and_next_siblings();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn replace_with(&self) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: replace_with()");
|
||||||
|
self.emitter.replace_with();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
todo!()
|
||||||
|
// if name == "class" && !is_namespaced {
|
||||||
|
// let class_id = self.ensure_string(value);
|
||||||
|
// // debug!("emit: set_class({:?})", value);
|
||||||
|
// self.emitter.set_class(class_id.into());
|
||||||
|
// } else {
|
||||||
|
// let name_id = self.ensure_string(name);
|
||||||
|
// let value_id = self.ensure_string(value);
|
||||||
|
// // debug!("emit: set_attribute({:?}, {:?})", name, value);
|
||||||
|
// self.state
|
||||||
|
// .emitter
|
||||||
|
// .set_attribute(name_id.into(), value_id.into());
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_attribute(&mut self, name: &str) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: remove_attribute({:?})", name);
|
||||||
|
let name_id = self.ensure_string(name);
|
||||||
|
self.emitter.remove_attribute(name_id.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_child(&self) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: append_child()");
|
||||||
|
self.emitter.append_child();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_text_node(&self, text: &str) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: create_text_node({:?})", text);
|
||||||
|
self.emitter.create_text_node(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_element(&mut self, tag_name: &str) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: create_element({:?})", tag_name);
|
||||||
|
let tag_name_id = self.ensure_string(tag_name);
|
||||||
|
self.emitter.create_element(tag_name_id.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_element_ns(&mut self, tag_name: &str, ns: &str) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: create_element_ns({:?}, {:?})", tag_name, ns);
|
||||||
|
let tag_name_id = self.ensure_string(tag_name);
|
||||||
|
let ns_id = self.ensure_string(ns);
|
||||||
|
self.emitter
|
||||||
|
.create_element_ns(tag_name_id.into(), ns_id.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_force_new_listeners(&mut self) -> bool {
|
||||||
|
let old = self.forcing_new_listeners;
|
||||||
|
self.forcing_new_listeners = true;
|
||||||
|
old
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_force_new_listeners(&mut self, previous: bool) {
|
||||||
|
debug_assert!(self.forcing_new_listeners);
|
||||||
|
self.forcing_new_listeners = previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_event_listener(&mut self, listener: &Listener) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: new_event_listener({:?})", listener);
|
||||||
|
let (a, b) = listener.get_callback_parts();
|
||||||
|
debug_assert!(a != 0);
|
||||||
|
let event_id = self.ensure_string(listener.event);
|
||||||
|
self.emitter.new_event_listener(event_id.into(), a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_event_listener(&mut self, listener: &Listener) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
|
||||||
|
if self.forcing_new_listeners {
|
||||||
|
self.new_event_listener(listener);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug!("emit: update_event_listener({:?})", listener);
|
||||||
|
let (a, b) = listener.get_callback_parts();
|
||||||
|
debug_assert!(a != 0);
|
||||||
|
let event_id = self.ensure_string(listener.event);
|
||||||
|
self.emitter.update_event_listener(event_id.into(), a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_event_listener(&mut self, event: &str) {
|
||||||
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug!("emit: remove_event_listener({:?})", event);
|
||||||
|
let event_id = self.ensure_string(event);
|
||||||
|
self.emitter.remove_event_listener(event_id.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[inline]
|
||||||
|
// pub fn has_template(&mut self, id: CacheId) -> bool {
|
||||||
|
// self.templates.contains(&id)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn save_template(&mut self, id: CacheId) {
|
||||||
|
// debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug_assert!(!self.has_template(id));
|
||||||
|
// // debug!("emit: save_template({:?})", id);
|
||||||
|
// self.templates.insert(id);
|
||||||
|
// self.emitter.save_template(id.into());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn push_template(&mut self, id: CacheId) {
|
||||||
|
// debug_assert!(self.traversal_is_committed());
|
||||||
|
// debug_assert!(self.has_template(id));
|
||||||
|
// // debug!("emit: push_template({:?})", id);
|
||||||
|
// self.emitter.push_template(id.into());
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keeps track of where we are moving in a DOM tree, and shortens traversal
|
||||||
|
// paths between mutations to their minimal number of operations.
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum MoveTo {
|
||||||
|
/// Move from the current node up to its parent.
|
||||||
|
Parent,
|
||||||
|
|
||||||
|
/// Move to the current node's n^th child.
|
||||||
|
Child(u32),
|
||||||
|
|
||||||
|
/// Move to the current node's n^th from last child.
|
||||||
|
ReverseChild(u32),
|
||||||
|
|
||||||
|
/// Move to the n^th sibling. Not relative from the current
|
||||||
|
/// location. Absolute indexed within all of the current siblings.
|
||||||
|
Sibling(u32),
|
||||||
|
|
||||||
|
/// Move to the n^th from last sibling. Not relative from the current
|
||||||
|
/// location. Absolute indexed within all of the current siblings.
|
||||||
|
ReverseSibling(u32),
|
||||||
|
|
||||||
|
/// Move down to the given saved temporary child.
|
||||||
|
TempChild(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Traversal {
|
||||||
|
uncommitted: Vec<MoveTo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Traversal {
|
||||||
|
/// Construct a new `Traversal` with its internal storage backed by the
|
||||||
|
/// given bump arena.
|
||||||
|
pub fn new() -> Traversal {
|
||||||
|
Traversal {
|
||||||
|
uncommitted: Vec::with_capacity(32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move the traversal up in the tree.
|
||||||
|
pub fn up(&mut self) {
|
||||||
|
match self.uncommitted.last() {
|
||||||
|
Some(MoveTo::Sibling(_)) | Some(MoveTo::ReverseSibling(_)) => {
|
||||||
|
self.uncommitted.pop();
|
||||||
|
self.uncommitted.push(MoveTo::Parent);
|
||||||
|
}
|
||||||
|
Some(MoveTo::TempChild(_)) | Some(MoveTo::Child(_)) | Some(MoveTo::ReverseChild(_)) => {
|
||||||
|
self.uncommitted.pop();
|
||||||
|
// And we're back at the parent.
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.uncommitted.push(MoveTo::Parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move the traversal down in the tree to the first child of the current
|
||||||
|
/// node.
|
||||||
|
pub fn down(&mut self) {
|
||||||
|
if let Some(&MoveTo::Parent) = self.uncommitted.last() {
|
||||||
|
self.uncommitted.pop();
|
||||||
|
self.sibling(0);
|
||||||
|
} else {
|
||||||
|
self.uncommitted.push(MoveTo::Child(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move the traversal to the n^th sibling.
|
||||||
|
pub fn sibling(&mut self, index: usize) {
|
||||||
|
let index = index as u32;
|
||||||
|
match self.uncommitted.last_mut() {
|
||||||
|
Some(MoveTo::Sibling(ref mut n)) | Some(MoveTo::Child(ref mut n)) => {
|
||||||
|
*n = index;
|
||||||
|
}
|
||||||
|
Some(MoveTo::ReverseSibling(_)) => {
|
||||||
|
self.uncommitted.pop();
|
||||||
|
self.uncommitted.push(MoveTo::Sibling(index));
|
||||||
|
}
|
||||||
|
Some(MoveTo::TempChild(_)) | Some(MoveTo::ReverseChild(_)) => {
|
||||||
|
self.uncommitted.pop();
|
||||||
|
self.uncommitted.push(MoveTo::Child(index))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.uncommitted.push(MoveTo::Sibling(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move the the n^th from last sibling.
|
||||||
|
pub fn reverse_sibling(&mut self, index: usize) {
|
||||||
|
let index = index as u32;
|
||||||
|
match self.uncommitted.last_mut() {
|
||||||
|
Some(MoveTo::ReverseSibling(ref mut n)) | Some(MoveTo::ReverseChild(ref mut n)) => {
|
||||||
|
*n = index;
|
||||||
|
}
|
||||||
|
Some(MoveTo::Sibling(_)) => {
|
||||||
|
self.uncommitted.pop();
|
||||||
|
self.uncommitted.push(MoveTo::ReverseSibling(index));
|
||||||
|
}
|
||||||
|
Some(MoveTo::TempChild(_)) | Some(MoveTo::Child(_)) => {
|
||||||
|
self.uncommitted.pop();
|
||||||
|
self.uncommitted.push(MoveTo::ReverseChild(index))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.uncommitted.push(MoveTo::ReverseSibling(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Go to the given saved temporary.
|
||||||
|
pub fn down_to_temp(&mut self, temp: u32) {
|
||||||
|
match self.uncommitted.last() {
|
||||||
|
Some(MoveTo::Sibling(_)) | Some(MoveTo::ReverseSibling(_)) => {
|
||||||
|
self.uncommitted.pop();
|
||||||
|
}
|
||||||
|
Some(MoveTo::Parent)
|
||||||
|
| Some(MoveTo::TempChild(_))
|
||||||
|
| Some(MoveTo::Child(_))
|
||||||
|
| Some(MoveTo::ReverseChild(_))
|
||||||
|
| None => {
|
||||||
|
// Can't remove moves to parents since we rely on their stack
|
||||||
|
// pops.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.uncommitted.push(MoveTo::TempChild(temp));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Are all the traversal's moves committed? That is, are there no moves
|
||||||
|
/// that have *not* been committed yet?
|
||||||
|
#[inline]
|
||||||
|
pub fn is_committed(&self) -> bool {
|
||||||
|
self.uncommitted.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commit this traversals moves and return the optimized path from the last
|
||||||
|
/// commit.
|
||||||
|
#[inline]
|
||||||
|
pub fn commit(&mut self) -> Moves {
|
||||||
|
Moves {
|
||||||
|
inner: self.uncommitted.drain(..),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.uncommitted.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Moves<'a> {
|
||||||
|
inner: std::vec::Drain<'a, MoveTo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Moves<'_> {
|
||||||
|
type Item = MoveTo;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<MoveTo> {
|
||||||
|
self.inner.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_traversal() {
|
||||||
|
fn t<F>(f: F) -> Box<dyn FnMut(&mut Traversal)>
|
||||||
|
where
|
||||||
|
F: 'static + FnMut(&mut Traversal),
|
||||||
|
{
|
||||||
|
Box::new(f) as _
|
||||||
|
}
|
||||||
|
|
||||||
|
for (mut traverse, expected_moves) in vec![
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down();
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Child(0)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.up();
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Parent],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.sibling(42);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Sibling(42)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down();
|
||||||
|
t.up();
|
||||||
|
}),
|
||||||
|
vec![],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down();
|
||||||
|
t.sibling(2);
|
||||||
|
t.up();
|
||||||
|
}),
|
||||||
|
vec![],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down();
|
||||||
|
t.sibling(3);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Child(3)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down();
|
||||||
|
t.sibling(4);
|
||||||
|
t.sibling(8);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Child(8)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.sibling(1);
|
||||||
|
t.sibling(1);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Sibling(1)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.reverse_sibling(3);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::ReverseSibling(3)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down();
|
||||||
|
t.reverse_sibling(3);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::ReverseChild(3)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down();
|
||||||
|
t.reverse_sibling(3);
|
||||||
|
t.up();
|
||||||
|
}),
|
||||||
|
vec![],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down();
|
||||||
|
t.reverse_sibling(3);
|
||||||
|
t.reverse_sibling(6);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::ReverseChild(6)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.up();
|
||||||
|
t.reverse_sibling(3);
|
||||||
|
t.reverse_sibling(6);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Parent, MoveTo::ReverseSibling(6)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.up();
|
||||||
|
t.sibling(3);
|
||||||
|
t.sibling(6);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Parent, MoveTo::Sibling(6)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.sibling(3);
|
||||||
|
t.sibling(6);
|
||||||
|
t.up();
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Parent],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.reverse_sibling(3);
|
||||||
|
t.reverse_sibling(6);
|
||||||
|
t.up();
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Parent],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down();
|
||||||
|
t.down_to_temp(3);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Child(0), MoveTo::TempChild(3)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down_to_temp(3);
|
||||||
|
t.sibling(5);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Child(5)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down_to_temp(3);
|
||||||
|
t.reverse_sibling(5);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::ReverseChild(5)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.down_to_temp(3);
|
||||||
|
t.up();
|
||||||
|
}),
|
||||||
|
vec![],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.sibling(2);
|
||||||
|
t.up();
|
||||||
|
t.down_to_temp(3);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Parent, MoveTo::TempChild(3)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
t(|t| {
|
||||||
|
t.up();
|
||||||
|
t.down_to_temp(3);
|
||||||
|
}),
|
||||||
|
vec![MoveTo::Parent, MoveTo::TempChild(3)],
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
let mut traversal = Traversal::new();
|
||||||
|
traverse(&mut traversal);
|
||||||
|
let actual_moves: Vec<_> = traversal.commit().collect();
|
||||||
|
assert_eq!(actual_moves, expected_moves);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct StringKey(u32);
|
||||||
|
|
||||||
|
impl From<StringKey> for u32 {
|
||||||
|
#[inline]
|
||||||
|
fn from(key: StringKey) -> u32 {
|
||||||
|
key.0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,6 +65,7 @@
|
|||||||
//! - dioxus-liveview (SSR + StringRenderer)
|
//! - dioxus-liveview (SSR + StringRenderer)
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
pub mod changelist;
|
||||||
pub mod component;
|
pub mod component;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod debug_renderer;
|
pub mod debug_renderer;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use std::{
|
|||||||
cell::{RefCell, UnsafeCell},
|
cell::{RefCell, UnsafeCell},
|
||||||
future::Future,
|
future::Future,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
|
rc::Rc,
|
||||||
sync::atomic::AtomicUsize,
|
sync::atomic::AtomicUsize,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -21,11 +22,14 @@ pub struct VirtualDom<P: Properties> {
|
|||||||
/// The index of the root component.
|
/// The index of the root component.
|
||||||
base_scope: Index,
|
base_scope: Index,
|
||||||
|
|
||||||
/// Components generate lifecycle events
|
///
|
||||||
event_queue: Vec<LifecycleEvent>, // wrap in a lock or something so
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
event_queue: Rc<RefCell<Vec<LifecycleEvent>>>,
|
||||||
|
|
||||||
// mark the root props with P, even though they're held by the root component
|
#[doc(hidden)] // mark the root props with P, even though they're held by the root component
|
||||||
_p: PhantomData<P>,
|
_root_props: PhantomData<P>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VirtualDom<()> {
|
impl VirtualDom<()> {
|
||||||
@@ -61,19 +65,19 @@ impl<P: Properties + 'static> VirtualDom<P> {
|
|||||||
let first_event = LifecycleEvent::mount(base_scope, None, 0, root_props);
|
let first_event = LifecycleEvent::mount(base_scope, None, 0, root_props);
|
||||||
|
|
||||||
// Create an event queue with a mount for the base scope
|
// Create an event queue with a mount for the base scope
|
||||||
let event_queue = vec![first_event];
|
let event_queue = Rc::new(RefCell::new(vec![first_event]));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
components,
|
components,
|
||||||
base_scope,
|
base_scope,
|
||||||
event_queue,
|
event_queue,
|
||||||
_p: PhantomData {},
|
_root_props: PhantomData {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// With access to the virtual dom, schedule an update to the Root component's props
|
/// With access to the virtual dom, schedule an update to the Root component's props
|
||||||
pub fn update_props(&mut self, new_props: P) {
|
pub fn update_props(&mut self, new_props: P) {
|
||||||
self.event_queue.push(LifecycleEvent {
|
self.event_queue.borrow_mut().push(LifecycleEvent {
|
||||||
event_type: LifecycleType::PropsChanged {
|
event_type: LifecycleType::PropsChanged {
|
||||||
props: Box::new(new_props),
|
props: Box::new(new_props),
|
||||||
},
|
},
|
||||||
@@ -86,8 +90,8 @@ impl<P: Properties + 'static> VirtualDom<P> {
|
|||||||
/// Takes a bump arena to allocate into, making the diff phase as fast as possible
|
/// Takes a bump arena to allocate into, making the diff phase as fast as possible
|
||||||
///
|
///
|
||||||
pub fn progress(&mut self) -> Result<()> {
|
pub fn progress(&mut self) -> Result<()> {
|
||||||
let event = self.event_queue.pop().ok_or(Error::NoEvent)?;
|
let event = self.event_queue.borrow_mut().pop().ok_or(Error::NoEvent)?;
|
||||||
process_event(self, event)
|
process_event(&mut self.components, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
|
/// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
|
||||||
@@ -125,9 +129,21 @@ impl<P: Properties + 'static> VirtualDom<P> {
|
|||||||
.as_ref();
|
.as_ref();
|
||||||
|
|
||||||
// Run the callback
|
// Run the callback
|
||||||
// Theoretically, this should
|
// This should cause internal state to progress, dumping events into the event queue
|
||||||
|
// todo: integrate this with a tracing mechanism exposed to a dev tool
|
||||||
listener();
|
listener();
|
||||||
|
|
||||||
|
// Run through our events, tagging which Indexes are receiving updates
|
||||||
|
// Prop updates take prescedence over subscription updates
|
||||||
|
// Run all prop updates *first* as they will cascade into children.
|
||||||
|
// *then* run the non-prop updates that were not already covered by props
|
||||||
|
let mut events = self.event_queue.borrow_mut();
|
||||||
|
|
||||||
|
// for now, just naively process each event in the queue
|
||||||
|
for event in events.drain(..) {
|
||||||
|
process_event(&mut self.components, event)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,11 +159,12 @@ impl<P: Properties + 'static> VirtualDom<P> {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
fn process_event<P: Properties>(
|
fn process_event(
|
||||||
dom: &mut VirtualDom<P>,
|
// dom: &mut VirtualDom<P>,
|
||||||
|
components: &mut Arena<Scope>,
|
||||||
LifecycleEvent { index, event_type }: LifecycleEvent,
|
LifecycleEvent { index, event_type }: LifecycleEvent,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let scope = dom.components.get(index).ok_or(Error::NoEvent)?;
|
let scope = components.get(index).ok_or(Error::NoEvent)?;
|
||||||
|
|
||||||
match event_type {
|
match event_type {
|
||||||
// Component needs to be mounted to the virtual dom
|
// Component needs to be mounted to the virtual dom
|
||||||
@@ -176,7 +193,8 @@ fn process_event<P: Properties>(
|
|||||||
// Component was removed from the DOM
|
// Component was removed from the DOM
|
||||||
// Run any destructors and cleanup for the hooks and the dump the component
|
// Run any destructors and cleanup for the hooks and the dump the component
|
||||||
LifecycleType::Removed {} => {
|
LifecycleType::Removed {} => {
|
||||||
let f = dom.components.remove(index);
|
let f = components.remove(index);
|
||||||
|
// let f = dom.components.remove(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Component was messaged via the internal subscription service
|
// Component was messaged via the internal subscription service
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ wasm-bindgen = "0.2.70"
|
|||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
wasm-bindgen-futures = "0.4.20"
|
wasm-bindgen-futures = "0.4.20"
|
||||||
futures = "0.3.12"
|
futures = "0.3.12"
|
||||||
|
wasm-logger = "0.2.0"
|
||||||
|
log = "0.4.14"
|
||||||
# html-validation = { path = "../html-validation", version = "0.1.1" }
|
# html-validation = { path = "../html-validation", version = "0.1.1" }
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
|
|||||||
@@ -0,0 +1,381 @@
|
|||||||
|
use crate::cached_set::CacheId;
|
||||||
|
use crate::{Element, EventsTrampoline};
|
||||||
|
use fxhash::FxHashMap;
|
||||||
|
use log::{debug, info, log};
|
||||||
|
use wasm_bindgen::{closure::Closure, JsCast};
|
||||||
|
use web_sys::{window, Document, Event, Node};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct ChangeListInterpreter {
|
||||||
|
container: Element,
|
||||||
|
stack: Stack,
|
||||||
|
temporaries: FxHashMap<u32, Node>,
|
||||||
|
templates: FxHashMap<CacheId, Node>,
|
||||||
|
callback: Option<Closure<dyn FnMut(&Event)>>,
|
||||||
|
document: Document,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Stack {
|
||||||
|
list: Vec<Node>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stack {
|
||||||
|
pub fn with_capacity(cap: usize) -> Self {
|
||||||
|
Stack {
|
||||||
|
list: Vec::with_capacity(cap),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, node: Node) {
|
||||||
|
debug!("stack-push: {:?}", node);
|
||||||
|
self.list.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Node {
|
||||||
|
let res = self.list.pop().unwrap();
|
||||||
|
debug!("stack-pop: {:?}", res);
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn top(&self) -> &Node {
|
||||||
|
&self.list[self.list.len() - 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChangeListInterpreter {
|
||||||
|
pub fn new(container: Element) -> Self {
|
||||||
|
let document = window()
|
||||||
|
.expect("must have access to the window")
|
||||||
|
.document()
|
||||||
|
.expect("must have access to the Document");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
container,
|
||||||
|
stack: Stack::with_capacity(20),
|
||||||
|
temporaries: Default::default(),
|
||||||
|
templates: Default::default(),
|
||||||
|
callback: None,
|
||||||
|
document,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmount(&mut self) {
|
||||||
|
self.stack.clear();
|
||||||
|
self.temporaries.clear();
|
||||||
|
self.templates.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self) {
|
||||||
|
if let Some(child) = self.container.first_child() {
|
||||||
|
self.stack.push(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.stack.clear();
|
||||||
|
self.temporaries.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_template(&self, id: CacheId) -> Option<&Node> {
|
||||||
|
self.templates.get(&id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_events_trampoline(&mut self, mut trampoline: EventsTrampoline) {
|
||||||
|
self.callback = Some(Closure::wrap(Box::new(move |event: &web_sys::Event| {
|
||||||
|
let target = event
|
||||||
|
.target()
|
||||||
|
.expect("missing target")
|
||||||
|
.dyn_into::<Element>()
|
||||||
|
.expect("not a valid element");
|
||||||
|
let typ = event.type_();
|
||||||
|
let a: u32 = target
|
||||||
|
.get_attribute(&format!("dodrio-a-{}", typ))
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let b: u32 = target
|
||||||
|
.get_attribute(&format!("dodrio-b-{}", typ))
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// get a and b from the target
|
||||||
|
trampoline(event.clone(), a, b);
|
||||||
|
}) as Box<dyn FnMut(&Event)>));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0
|
||||||
|
pub fn set_text(&mut self, text: &str) {
|
||||||
|
self.stack.top().set_text_content(Some(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1
|
||||||
|
pub fn remove_self_and_next_siblings(&mut self) {
|
||||||
|
let node = self.stack.pop();
|
||||||
|
let mut sibling = node.next_sibling();
|
||||||
|
|
||||||
|
while let Some(inner) = sibling {
|
||||||
|
let temp = inner.next_sibling();
|
||||||
|
if let Some(sibling) = inner.dyn_ref::<Element>() {
|
||||||
|
sibling.remove();
|
||||||
|
}
|
||||||
|
sibling = temp;
|
||||||
|
}
|
||||||
|
if let Some(node) = node.dyn_ref::<Element>() {
|
||||||
|
node.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2
|
||||||
|
pub fn replace_with(&mut self) {
|
||||||
|
let new_node = self.stack.pop();
|
||||||
|
let old_node = self.stack.pop();
|
||||||
|
|
||||||
|
if old_node.has_type::<Element>() {
|
||||||
|
old_node
|
||||||
|
.dyn_ref::<Element>()
|
||||||
|
.unwrap()
|
||||||
|
.replace_with_with_node_1(&new_node)
|
||||||
|
.unwrap();
|
||||||
|
} else if old_node.has_type::<web_sys::CharacterData>() {
|
||||||
|
old_node
|
||||||
|
.dyn_ref::<web_sys::CharacterData>()
|
||||||
|
.unwrap()
|
||||||
|
.replace_with_with_node_1(&new_node)
|
||||||
|
.unwrap();
|
||||||
|
} else if old_node.has_type::<web_sys::DocumentType>() {
|
||||||
|
old_node
|
||||||
|
.dyn_ref::<web_sys::DocumentType>()
|
||||||
|
.unwrap()
|
||||||
|
.replace_with_with_node_1(&new_node)
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
panic!("Cannot replace node: {:?}", old_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stack.push(new_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3
|
||||||
|
pub fn set_attribute(&mut self, name: &str, value: &str) {
|
||||||
|
let node = self.stack.top();
|
||||||
|
|
||||||
|
if let Some(node) = node.dyn_ref::<Element>() {
|
||||||
|
node.set_attribute(name, value).unwrap();
|
||||||
|
|
||||||
|
// Some attributes are "volatile" and don't work through `setAttribute`.
|
||||||
|
// TODO:
|
||||||
|
// if name == "value" {
|
||||||
|
// node.set_value(value);
|
||||||
|
// }
|
||||||
|
// if name == "checked" {
|
||||||
|
// node.set_checked(true);
|
||||||
|
// }
|
||||||
|
// if name == "selected" {
|
||||||
|
// node.set_selected(true);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4
|
||||||
|
pub fn remove_attribute(&mut self, name: &str) {
|
||||||
|
let node = self.stack.top();
|
||||||
|
if let Some(node) = node.dyn_ref::<Element>() {
|
||||||
|
node.remove_attribute(name).unwrap();
|
||||||
|
|
||||||
|
// Some attributes are "volatile" and don't work through `removeAttribute`.
|
||||||
|
// TODO:
|
||||||
|
// if name == "value" {
|
||||||
|
// node.set_value("");
|
||||||
|
// }
|
||||||
|
// if name == "checked" {
|
||||||
|
// node.set_checked(false);
|
||||||
|
// }
|
||||||
|
// if name == "selected" {
|
||||||
|
// node.set_selected(false);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5
|
||||||
|
pub fn push_reverse_child(&mut self, n: u32) {
|
||||||
|
let parent = self.stack.top();
|
||||||
|
let children = parent.child_nodes();
|
||||||
|
let child = children.get(children.length() - n - 1).unwrap();
|
||||||
|
self.stack.push(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6
|
||||||
|
pub fn pop_push_child(&mut self, n: u32) {
|
||||||
|
self.stack.pop();
|
||||||
|
let parent = self.stack.top();
|
||||||
|
let children = parent.child_nodes();
|
||||||
|
let child = children.get(n).unwrap();
|
||||||
|
self.stack.push(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7
|
||||||
|
pub fn pop(&mut self) {
|
||||||
|
self.stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8
|
||||||
|
pub fn append_child(&mut self) {
|
||||||
|
let child = self.stack.pop();
|
||||||
|
self.stack.top().append_child(&child).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9
|
||||||
|
pub fn create_text_node(&mut self, text: &str) {
|
||||||
|
self.stack.push(
|
||||||
|
self.document
|
||||||
|
.create_text_node(text)
|
||||||
|
.dyn_into::<Node>()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10
|
||||||
|
pub fn create_element(&mut self, tag_name: &str) {
|
||||||
|
let el = self
|
||||||
|
.document
|
||||||
|
.create_element(tag_name)
|
||||||
|
.unwrap()
|
||||||
|
.dyn_into::<Node>()
|
||||||
|
.unwrap();
|
||||||
|
self.stack.push(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11
|
||||||
|
pub fn new_event_listener(&mut self, event_type: &str, a: u32, b: u32) {
|
||||||
|
let el = self.stack.top();
|
||||||
|
|
||||||
|
let el = el
|
||||||
|
.dyn_ref::<Element>()
|
||||||
|
.expect(&format!("not an element: {:?}", el));
|
||||||
|
el.add_event_listener_with_callback(
|
||||||
|
event_type,
|
||||||
|
self.callback.as_ref().unwrap().as_ref().unchecked_ref(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
debug!("adding attributes: {}, {}", a, b);
|
||||||
|
el.set_attribute(&format!("dodrio-a-{}", event_type), &a.to_string())
|
||||||
|
.unwrap();
|
||||||
|
el.set_attribute(&format!("dodrio-b-{}", event_type), &b.to_string())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12
|
||||||
|
pub fn update_event_listener(&mut self, event_type: &str, a: u32, b: u32) {
|
||||||
|
if let Some(el) = self.stack.top().dyn_ref::<Element>() {
|
||||||
|
el.set_attribute(&format!("dodrio-a-{}", event_type), &a.to_string())
|
||||||
|
.unwrap();
|
||||||
|
el.set_attribute(&format!("dodrio-b-{}", event_type), &b.to_string())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 13
|
||||||
|
pub fn remove_event_listener(&mut self, event_type: &str) {
|
||||||
|
if let Some(el) = self.stack.top().dyn_ref::<Element>() {
|
||||||
|
el.remove_event_listener_with_callback(
|
||||||
|
event_type,
|
||||||
|
self.callback.as_ref().unwrap().as_ref().unchecked_ref(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 16
|
||||||
|
pub fn create_element_ns(&mut self, tag_name: &str, ns: &str) {
|
||||||
|
let el = self
|
||||||
|
.document
|
||||||
|
.create_element_ns(Some(ns), tag_name)
|
||||||
|
.unwrap()
|
||||||
|
.dyn_into::<Node>()
|
||||||
|
.unwrap();
|
||||||
|
self.stack.push(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 17
|
||||||
|
pub fn save_children_to_temporaries(&mut self, mut temp: u32, start: u32, end: u32) {
|
||||||
|
let parent = self.stack.top();
|
||||||
|
let children = parent.child_nodes();
|
||||||
|
for i in start..end {
|
||||||
|
self.temporaries.insert(temp, children.get(i).unwrap());
|
||||||
|
temp += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 18
|
||||||
|
pub fn push_child(&mut self, n: u32) {
|
||||||
|
let parent = self.stack.top();
|
||||||
|
let child = parent.child_nodes().get(n).unwrap();
|
||||||
|
self.stack.push(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 19
|
||||||
|
pub fn push_temporary(&mut self, temp: u32) {
|
||||||
|
let t = self.temporaries.get(&temp).unwrap().clone();
|
||||||
|
self.stack.push(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 20
|
||||||
|
pub fn insert_before(&mut self) {
|
||||||
|
let before = self.stack.pop();
|
||||||
|
let after = self.stack.pop();
|
||||||
|
after
|
||||||
|
.parent_node()
|
||||||
|
.unwrap()
|
||||||
|
.insert_before(&before, Some(&after))
|
||||||
|
.unwrap();
|
||||||
|
self.stack.push(before);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 21
|
||||||
|
pub fn pop_push_reverse_child(&mut self, n: u32) {
|
||||||
|
self.stack.pop();
|
||||||
|
let parent = self.stack.top();
|
||||||
|
let children = parent.child_nodes();
|
||||||
|
let child = children.get(children.length() - n - 1).unwrap();
|
||||||
|
self.stack.push(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 22
|
||||||
|
pub fn remove_child(&mut self, n: u32) {
|
||||||
|
let parent = self.stack.top();
|
||||||
|
if let Some(child) = parent.child_nodes().get(n).unwrap().dyn_ref::<Element>() {
|
||||||
|
child.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 23
|
||||||
|
pub fn set_class(&mut self, class_name: &str) {
|
||||||
|
if let Some(el) = self.stack.top().dyn_ref::<Element>() {
|
||||||
|
el.set_class_name(class_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 24
|
||||||
|
pub fn save_template(&mut self, id: CacheId) {
|
||||||
|
let template = self.stack.top();
|
||||||
|
let t = template.clone_node_with_deep(true).unwrap();
|
||||||
|
self.templates.insert(id, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 25
|
||||||
|
pub fn push_template(&mut self, id: CacheId) {
|
||||||
|
let template = self.get_template(id).unwrap();
|
||||||
|
let t = template.clone_node_with_deep(true).unwrap();
|
||||||
|
self.stack.push(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_template(&self, id: CacheId) -> bool {
|
||||||
|
self.templates.contains_key(&id)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ use dioxus_core::{
|
|||||||
};
|
};
|
||||||
use futures::{channel::mpsc, future, SinkExt, StreamExt};
|
use futures::{channel::mpsc, future, SinkExt, StreamExt};
|
||||||
use mpsc::UnboundedSender;
|
use mpsc::UnboundedSender;
|
||||||
|
pub mod interpreter;
|
||||||
/// The `WebsysRenderer` provides a way of rendering a Dioxus Virtual DOM to the browser's DOM.
|
/// The `WebsysRenderer` provides a way of rendering a Dioxus Virtual DOM to the browser's DOM.
|
||||||
/// Under the hood, we leverage WebSys and interact directly with the DOM
|
/// Under the hood, we leverage WebSys and interact directly with the DOM
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user