mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 22:54:12 +00:00
feat: integrate serialization and string borrowing
This commit adds lifetimes to the diff and realdom methods so consumers may borrow the contents of the DOM for serialization or asynchronous modifications.
This commit is contained in:
parent
9813f23cdf
commit
f4fb5bb454
12 changed files with 480 additions and 86 deletions
|
@ -56,6 +56,7 @@ members = [
|
|||
"packages/core",
|
||||
"packages/html-namespace",
|
||||
"packages/web",
|
||||
"packages/webview"
|
||||
# "packages/cli",
|
||||
# "packages/atoms",
|
||||
# "packages/ssr",
|
||||
|
|
108
packages/core/examples/html.rs
Normal file
108
packages/core/examples/html.rs
Normal file
|
@ -0,0 +1,108 @@
|
|||
use bumpalo::Bump;
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn build(factory: Factory) {
|
||||
factory.text();
|
||||
div::new(factory)
|
||||
.r#class()
|
||||
.r#tag()
|
||||
.r#type()
|
||||
.add_children()
|
||||
.iter_children()
|
||||
.finish();
|
||||
}
|
||||
|
||||
/// # The `div` element
|
||||
///
|
||||
///
|
||||
/// The <div> HTML element is the generic container for flow content. It has no effect on the content or layout until
|
||||
/// styled in some way using CSS (e.g. styling is directly applied to it, or some kind of layout model like Flexbox is
|
||||
/// applied to its parent element).
|
||||
///
|
||||
/// As a "pure" container, the <div> element does not inherently represent anything. Instead, it's used to group content
|
||||
/// so it can be easily styled using the class or id attributes, marking a section of a document as being written in a
|
||||
/// different language (using the lang attribute), and so on.
|
||||
///
|
||||
/// ## Usage
|
||||
/// ```
|
||||
/// rsx!{
|
||||
/// div { class: "tall", id: "unique id"
|
||||
/// h1 {}
|
||||
/// p {}
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ## Specifications
|
||||
/// - Content categories: Flow content, palpable content.
|
||||
/// - Permitted content: Flow content.
|
||||
/// - Permitted parents: Any element that accepts flow content.
|
||||
#[allow(non_camel_case_types)]
|
||||
struct div {}
|
||||
|
||||
struct h1 {}
|
||||
|
||||
struct h2 {}
|
||||
|
||||
trait BasicElement: Sized {
|
||||
const TagName: &'static str;
|
||||
fn get_bump(&self) -> &Bump;
|
||||
fn new(factory: Factory) -> Self;
|
||||
fn add_children(self) -> Self {
|
||||
self
|
||||
}
|
||||
fn iter_children(self) -> Self {
|
||||
self
|
||||
}
|
||||
fn finish(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BasicElement for div {
|
||||
const TagName: &'static str = "div";
|
||||
|
||||
fn get_bump(&self) -> &Bump {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn new(factory: Factory) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl div {
|
||||
fn class(self) -> Self {
|
||||
self
|
||||
}
|
||||
fn tag(self) -> Self {
|
||||
self
|
||||
}
|
||||
fn r#type(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Factory<'a> {
|
||||
bump: &'a bumpalo::Bump,
|
||||
}
|
||||
|
||||
impl<'a> Factory<'a> {
|
||||
fn text(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
fn new_el(&'a self, tag: &'static str) -> ElementBuilder<'a> {
|
||||
ElementBuilder {
|
||||
bump: self.bump,
|
||||
tag,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ElementBuilder<'a> {
|
||||
tag: &'static str,
|
||||
bump: &'a bumpalo::Bump,
|
||||
}
|
|
@ -21,6 +21,75 @@ enum MutStatus {
|
|||
Mut,
|
||||
}
|
||||
|
||||
// impl ScopeArenaInner {
|
||||
// pub fn new(arena: Arena<Scope>) -> Self {
|
||||
// ScopeArenaInner {
|
||||
// arena: UnsafeCell::new(arena),
|
||||
// locks: Default::default(),
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// THIS METHOD IS CURRENTLY UNSAFE
|
||||
// /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
|
||||
// pub fn try_get(&self, idx: ScopeIdx) -> Result<&Scope> {
|
||||
// let inner = unsafe { &*self.arena.get() };
|
||||
// let scope = inner.get(idx);
|
||||
// scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||
// }
|
||||
|
||||
// /// THIS METHOD IS CURRENTLY UNSAFE
|
||||
// /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
|
||||
// pub fn try_get_mut(&self, idx: ScopeIdx) -> Result<&mut Scope> {
|
||||
// let inner = unsafe { &mut *self.arena.get() };
|
||||
// let scope = inner.get_mut(idx);
|
||||
// scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||
// }
|
||||
|
||||
// fn inner(&self) -> &Arena<Scope> {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
// fn inner_mut(&mut self) -> &mut Arena<Scope> {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
// /// THIS METHOD IS CURRENTLY UNSAFE
|
||||
// /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
|
||||
// pub fn with<T>(&self, f: impl FnOnce(&mut Arena<Scope>) -> T) -> Result<T> {
|
||||
// let inner = unsafe { &mut *self.arena.get() };
|
||||
// Ok(f(inner))
|
||||
// // todo!()
|
||||
// }
|
||||
|
||||
// pub fn with_scope<'b, O: 'static>(
|
||||
// &'b self,
|
||||
// id: ScopeIdx,
|
||||
// f: impl FnOnce(&'b mut Scope) -> O,
|
||||
// ) -> Result<O> {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
// // return a bumpframe with a lifetime attached to the arena borrow
|
||||
// // this is useful for merging lifetimes
|
||||
// pub fn with_scope_vnode<'b>(
|
||||
// &self,
|
||||
// id: ScopeIdx,
|
||||
// f: impl FnOnce(&mut Scope) -> &VNode<'b>,
|
||||
// ) -> Result<&VNode<'b>> {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
// pub fn try_remove(&mut self, id: ScopeIdx) -> Result<Scope> {
|
||||
// let inner = unsafe { &mut *self.arena.get() };
|
||||
// inner
|
||||
// .remove(id)
|
||||
// .ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||
// }
|
||||
|
||||
// unsafe fn inner_unchecked<'s>() -> &'s mut Arena<Scope> {
|
||||
// todo!()
|
||||
// }
|
||||
// }
|
||||
impl ScopeArena {
|
||||
pub fn new(arena: Arena<Scope>) -> Self {
|
||||
ScopeArena(Rc::new(RefCell::new(ScopeArenaInner {
|
||||
|
@ -79,7 +148,7 @@ impl ScopeArena {
|
|||
todo!()
|
||||
}
|
||||
|
||||
pub fn try_remove(&mut self, id: ScopeIdx) -> Result<Scope> {
|
||||
pub fn try_remove(&self, id: ScopeIdx) -> Result<Scope> {
|
||||
let inner = unsafe { &mut *self.0.borrow().arena.get() };
|
||||
inner
|
||||
.remove(id)
|
||||
|
|
|
@ -38,9 +38,9 @@ impl DebugRenderer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn step<Dom: RealDom>(&mut self, machine: &mut DiffMachine<Dom>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
// pub fn step<Dom: RealDom>(&mut self, machine: &mut DiffMachine<Dom>) -> Result<()> {
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// this does a "holy" compare - if something is missing in the rhs, it doesn't complain.
|
||||
// it only complains if something shows up that's not in the lhs, *or* if a value is different.
|
||||
|
|
|
@ -22,13 +22,17 @@
|
|||
//! More info on how to improve this diffing algorithm:
|
||||
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
||||
|
||||
use crate::{arena::ScopeArena, innerlude::*};
|
||||
use crate::{
|
||||
arena::{ScopeArena, ScopeArenaInner},
|
||||
innerlude::*,
|
||||
};
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::Cell,
|
||||
cmp::Ordering,
|
||||
marker::PhantomData,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
|
@ -42,7 +46,7 @@ use std::{
|
|||
/// will receive modifications. However, instead of using child-based methods for descending through the tree, we instead
|
||||
/// ask the RealDom to either push or pop real nodes onto the stack. This saves us the indexing cost while working on a
|
||||
/// single node
|
||||
pub trait RealDom {
|
||||
pub trait RealDom<'a> {
|
||||
// Navigation
|
||||
fn push_root(&mut self, root: RealDomNode);
|
||||
|
||||
|
@ -55,27 +59,27 @@ pub trait RealDom {
|
|||
fn remove_all_children(&mut self);
|
||||
|
||||
// Create
|
||||
fn create_text_node(&mut self, text: &str) -> RealDomNode;
|
||||
fn create_element(&mut self, tag: &str) -> RealDomNode;
|
||||
fn create_element_ns(&mut self, tag: &str, namespace: &str) -> RealDomNode;
|
||||
fn create_text_node(&mut self, text: &'a str) -> RealDomNode;
|
||||
fn create_element(&mut self, tag: &'static str) -> RealDomNode;
|
||||
fn create_element_ns(&mut self, tag: &'static str, namespace: &'static str) -> RealDomNode;
|
||||
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
|
||||
fn create_placeholder(&mut self) -> RealDomNode;
|
||||
|
||||
// events
|
||||
fn new_event_listener(
|
||||
&mut self,
|
||||
event: &str,
|
||||
event: &'static str,
|
||||
scope: ScopeIdx,
|
||||
element_id: usize,
|
||||
realnode: RealDomNode,
|
||||
);
|
||||
// fn new_event_listener(&mut self, event: &str);
|
||||
fn remove_event_listener(&mut self, event: &str);
|
||||
fn remove_event_listener(&mut self, event: &'static str);
|
||||
|
||||
// modify
|
||||
fn set_text(&mut self, text: &str);
|
||||
fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool);
|
||||
fn remove_attribute(&mut self, name: &str);
|
||||
fn set_text(&mut self, text: &'a str);
|
||||
fn set_attribute(&mut self, name: &'static str, value: &'a str, is_namespaced: bool);
|
||||
fn remove_attribute(&mut self, name: &'static str);
|
||||
|
||||
// node ref
|
||||
fn raw_node_as_any_mut(&self) -> &mut dyn Any;
|
||||
|
@ -93,19 +97,19 @@ pub trait RealDom {
|
|||
/// The order of these re-entrances is stored in the DiffState itself. The DiffState comes pre-loaded with a set of components
|
||||
/// that were modified by the eventtrigger. This prevents doubly evaluating components if they were both updated via
|
||||
/// subscriptions and props changes.
|
||||
pub struct DiffMachine<'a, Dom: RealDom> {
|
||||
pub dom: &'a mut Dom,
|
||||
pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> {
|
||||
pub dom: &'real mut Dom,
|
||||
pub components: &'bump ScopeArena,
|
||||
pub cur_idx: ScopeIdx,
|
||||
pub diffed: FxHashSet<ScopeIdx>,
|
||||
pub components: ScopeArena,
|
||||
pub event_queue: EventQueue,
|
||||
pub seen_nodes: FxHashSet<ScopeIdx>,
|
||||
}
|
||||
|
||||
impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
|
||||
pub fn new(
|
||||
dom: &'a mut Dom,
|
||||
components: ScopeArena,
|
||||
dom: &'real mut Dom,
|
||||
components: &'bump ScopeArena,
|
||||
cur_idx: ScopeIdx,
|
||||
event_queue: EventQueue,
|
||||
) -> Self {
|
||||
|
@ -128,8 +132,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
//
|
||||
// The change list stack is in the same state when this function exits.
|
||||
// In the case of Fragments, the parent node is on the stack
|
||||
pub fn diff_node(&mut self, old_node: &VNode<'a>, new_node: &VNode<'a>) {
|
||||
// pub fn diff_node(&self, old: &VNode<'a>, new: &VNode<'a>) {
|
||||
pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
|
||||
/*
|
||||
For each valid case, we "commit traversal", meaning we save this current position in the tree.
|
||||
Then, we diff and queue an edit event (via chagelist). s single trees - when components show up, we save that traversal and then re-enter later.
|
||||
|
@ -326,7 +329,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// When this function returns, the new node is on top of the change list stack:
|
||||
//
|
||||
// [... node]
|
||||
fn create(&mut self, node: &VNode<'a>) {
|
||||
fn create(&mut self, node: &'bump VNode<'bump>) {
|
||||
// debug_assert!(self.dom.traversal_is_committed());
|
||||
match node {
|
||||
VNode::Text(text) => {
|
||||
|
@ -434,7 +437,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
|
||||
// yaaaaay lifetimes out of thin air
|
||||
// really tho, we're merging the frame lifetimes together
|
||||
let inner: &'a mut _ = unsafe { &mut *self.components.0.borrow().arena.get() };
|
||||
let inner: &'bump mut _ = unsafe { &mut *self.components.0.borrow().arena.get() };
|
||||
let new_component = inner.get_mut(idx).unwrap();
|
||||
|
||||
// Actually initialize the caller's slot with the right address
|
||||
|
@ -467,7 +470,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
||||
/// Destroy a scope and all of its descendents.
|
||||
///
|
||||
/// Calling this will run the destuctors on all hooks in the tree.
|
||||
|
@ -559,8 +562,8 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// The change list stack is left unchanged.
|
||||
fn diff_attr(
|
||||
&mut self,
|
||||
old: &'a [Attribute<'a>],
|
||||
new: &'a [Attribute<'a>],
|
||||
old: &'bump [Attribute<'bump>],
|
||||
new: &'bump [Attribute<'bump>],
|
||||
is_namespaced: bool,
|
||||
) {
|
||||
// Do O(n^2) passes to add/update and remove attributes, since
|
||||
|
@ -613,7 +616,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// [... parent]
|
||||
//
|
||||
// the change list stack is in the same state when this function returns.
|
||||
fn diff_children(&mut self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
|
||||
fn diff_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
|
||||
if new.is_empty() {
|
||||
if !old.is_empty() {
|
||||
// self.dom.commit_traversal();
|
||||
|
@ -693,11 +696,11 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// [... parent]
|
||||
//
|
||||
// Upon exiting, the change list stack is in the same state.
|
||||
fn diff_keyed_children(&self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
|
||||
fn diff_keyed_children(&self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
|
||||
// todo!();
|
||||
if cfg!(debug_assertions) {
|
||||
let mut keys = fxhash::FxHashSet::default();
|
||||
let mut assert_unique_keys = |children: &'a [VNode<'a>]| {
|
||||
let mut assert_unique_keys = |children: &'bump [VNode<'bump>]| {
|
||||
keys.clear();
|
||||
for child in children {
|
||||
let key = child.key();
|
||||
|
@ -777,7 +780,11 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// [... parent]
|
||||
//
|
||||
// Upon exit, the change list stack is the same.
|
||||
fn diff_keyed_prefix(&self, old: &[VNode<'a>], new: &[VNode<'a>]) -> KeyedPrefixResult {
|
||||
fn diff_keyed_prefix(
|
||||
&self,
|
||||
old: &'bump [VNode<'bump>],
|
||||
new: &'bump [VNode<'bump>],
|
||||
) -> KeyedPrefixResult {
|
||||
todo!()
|
||||
// self.dom.go_down();
|
||||
// let mut shared_prefix_count = 0;
|
||||
|
@ -831,8 +838,8 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// Upon exit from this function, it will be restored to that same state.
|
||||
fn diff_keyed_middle(
|
||||
&self,
|
||||
old: &[VNode<'a>],
|
||||
mut new: &[VNode<'a>],
|
||||
old: &[VNode<'bump>],
|
||||
mut new: &[VNode<'bump>],
|
||||
shared_prefix_count: usize,
|
||||
shared_suffix_count: usize,
|
||||
old_shared_suffix_start: usize,
|
||||
|
@ -1059,8 +1066,8 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// When this function exits, the change list stack remains the same.
|
||||
fn diff_keyed_suffix(
|
||||
&self,
|
||||
old: &[VNode<'a>],
|
||||
new: &[VNode<'a>],
|
||||
old: &[VNode<'bump>],
|
||||
new: &[VNode<'bump>],
|
||||
new_shared_suffix_start: usize,
|
||||
) {
|
||||
todo!()
|
||||
|
@ -1088,7 +1095,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// [... parent]
|
||||
//
|
||||
// the change list stack is in the same state when this function returns.
|
||||
fn diff_non_keyed_children(&mut self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
|
||||
fn diff_non_keyed_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
|
||||
// Handled these cases in `diff_children` before calling this function.
|
||||
debug_assert!(!new.is_empty());
|
||||
debug_assert!(!old.is_empty());
|
||||
|
@ -1163,7 +1170,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// [... parent]
|
||||
//
|
||||
// When this function returns, the change list stack is in the same state.
|
||||
pub fn remove_all_children(&mut self, old: &[VNode<'a>]) {
|
||||
pub fn remove_all_children(&mut self, old: &'bump [VNode<'bump>]) {
|
||||
// debug_assert!(self.dom.traversal_is_committed());
|
||||
log::debug!("REMOVING CHILDREN");
|
||||
for _child in old {
|
||||
|
@ -1182,7 +1189,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// [... parent]
|
||||
//
|
||||
// When this function returns, the change list stack is in the same state.
|
||||
pub fn create_and_append_children(&mut self, new: &[VNode<'a>]) {
|
||||
pub fn create_and_append_children(&mut self, new: &'bump [VNode<'bump>]) {
|
||||
// debug_assert!(self.dom.traversal_is_committed());
|
||||
for child in new {
|
||||
// self.create_and_append(node, parent)
|
||||
|
@ -1200,7 +1207,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// After the function returns, the child is no longer on the change list stack:
|
||||
//
|
||||
// [... parent]
|
||||
pub fn remove_self_and_next_siblings(&self, old: &[VNode<'a>]) {
|
||||
pub fn remove_self_and_next_siblings(&self, old: &[VNode<'bump>]) {
|
||||
// debug_assert!(self.dom.traversal_is_committed());
|
||||
for child in old {
|
||||
if let VNode::Component(vcomp) = child {
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
//!
|
||||
//!
|
||||
|
||||
pub mod util;
|
||||
pub mod arena;
|
||||
pub mod component; // Logic for extending FC
|
||||
pub mod component;
|
||||
pub mod util; // Logic for extending FC
|
||||
|
||||
pub mod debug_renderer;
|
||||
pub mod diff;
|
||||
|
|
|
@ -8,7 +8,7 @@ impl DebugDom {
|
|||
Self { counter: 0 }
|
||||
}
|
||||
}
|
||||
impl RealDom for DebugDom {
|
||||
impl<'a> RealDom<'a> for DebugDom {
|
||||
fn push_root(&mut self, root: RealDomNode) {}
|
||||
|
||||
fn append_child(&mut self) {}
|
||||
|
|
|
@ -196,10 +196,10 @@ impl VirtualDom {
|
|||
impl VirtualDom {
|
||||
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
|
||||
/// Currently this doesn't do what we want it to do
|
||||
pub fn rebuild<'s, Dom: RealDom>(&'s mut self, realdom: &mut Dom) -> Result<()> {
|
||||
pub fn rebuild<'s, Dom: RealDom<'s>>(&'s mut self, realdom: &mut Dom) -> Result<()> {
|
||||
let mut diff_machine = DiffMachine::new(
|
||||
realdom,
|
||||
self.components.clone(),
|
||||
&self.components,
|
||||
self.base_scope,
|
||||
self.event_queue.clone(),
|
||||
);
|
||||
|
@ -207,7 +207,6 @@ impl VirtualDom {
|
|||
// Schedule an update and then immediately call it on the root component
|
||||
// This is akin to a hook being called from a listener and requring a re-render
|
||||
// Instead, this is done on top-level component
|
||||
|
||||
let base = self.components.try_get(self.base_scope)?;
|
||||
|
||||
let update = &base.event_channel;
|
||||
|
@ -260,21 +259,17 @@ impl VirtualDom {
|
|||
// but the guarantees provide a safe, fast, and efficient abstraction for the VirtualDOM updating framework.
|
||||
//
|
||||
// A good project would be to remove all unsafe from this crate and move the unsafety into safer abstractions.
|
||||
pub fn progress_with_event<Dom: RealDom>(
|
||||
&mut self,
|
||||
realdom: &mut Dom,
|
||||
pub fn progress_with_event<'s, Dom: RealDom<'s>>(
|
||||
&'s mut self,
|
||||
realdom: &'_ mut Dom,
|
||||
trigger: EventTrigger,
|
||||
) -> Result<()> {
|
||||
let id = trigger.component_id.clone();
|
||||
|
||||
self.components.try_get_mut(id)?.call_listener(trigger)?;
|
||||
|
||||
let mut diff_machine = DiffMachine::new(
|
||||
realdom,
|
||||
self.components.clone(),
|
||||
id,
|
||||
self.event_queue.clone(),
|
||||
);
|
||||
let mut diff_machine =
|
||||
DiffMachine::new(realdom, &self.components, id, self.event_queue.clone());
|
||||
|
||||
self.progress_completely(&mut diff_machine)?;
|
||||
|
||||
|
@ -285,9 +280,9 @@ impl VirtualDom {
|
|||
/// Only ever run each component once.
|
||||
///
|
||||
/// The DiffMachine logs its progress as it goes which might be useful for certain types of renderers.
|
||||
pub(crate) fn progress_completely<'s, Dom: RealDom>(
|
||||
&'s mut self,
|
||||
diff_machine: &'_ mut DiffMachine<'s, Dom>,
|
||||
pub(crate) fn progress_completely<'a, 'bump, Dom: RealDom<'bump>>(
|
||||
&'bump self,
|
||||
diff_machine: &'_ mut DiffMachine<'a, 'bump, Dom>,
|
||||
) -> Result<()> {
|
||||
// Add this component to the list of components that need to be difed
|
||||
// #[allow(unused_assignments)]
|
||||
|
|
|
@ -15,35 +15,35 @@ static Example: FC<()> = |cx| {
|
|||
log::debug!("Running component....");
|
||||
|
||||
cx.render(html! {
|
||||
<div>
|
||||
<section class="py-12 px-4 text-center">
|
||||
<div class="w-full max-w-2xl mx-auto">
|
||||
// Tagline
|
||||
<span class="text-sm font-semibold">
|
||||
"Dioxus Example: Jack and Jill"
|
||||
</span>
|
||||
<div>
|
||||
<section class="py-12 px-4 text-center">
|
||||
<div class="w-full max-w-2xl mx-auto">
|
||||
// Tagline
|
||||
<span class="text-sm font-semibold">
|
||||
"Dioxus Example: Jack and Jill"
|
||||
</span>
|
||||
|
||||
// Header
|
||||
<h2 class="text-5xl mt-2 mb-6 leading-tight font-semibold font-heading">
|
||||
"Hello, {name}"
|
||||
</h2>
|
||||
// Header
|
||||
<h2 class="text-5xl mt-2 mb-6 leading-tight font-semibold font-heading">
|
||||
"Hello, {name}"
|
||||
</h2>
|
||||
|
||||
// Control buttons
|
||||
<div>
|
||||
<button
|
||||
class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
onclick={move |_| set_name("jack")}>
|
||||
"Jack!"
|
||||
</button>
|
||||
// Control buttons
|
||||
<div>
|
||||
<button
|
||||
class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
onclick={move |_| set_name("jack")}>
|
||||
"Jack!"
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
onclick={move |_| set_name("jill")}>
|
||||
"Jill!"
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
onclick={move |_| set_name("jill")}>
|
||||
"Jill!"
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
})
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
})
|
||||
};
|
||||
|
|
37
packages/web/examples/jackjillrsx.rs
Normal file
37
packages/web/examples/jackjillrsx.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use dioxus::prelude::*;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_web::WebsysRenderer;
|
||||
|
||||
fn main() {
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
console_error_panic_hook::set_once();
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example))
|
||||
}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
let (name, set_name) = use_state(&cx, || "...?");
|
||||
cx.render(rsx! {
|
||||
section { class: "py-12 px-4 text-center"
|
||||
div { class: "w-full max-w-2xl mx-auto"
|
||||
span { class: "text-sm font-semibold"
|
||||
"Dioxus Example: Jack and Jill"
|
||||
}
|
||||
h2 { class: "text-5xl mt-2 mb-6 leading-tight font-semibold font-heading"
|
||||
"Hello, {name}"
|
||||
}
|
||||
div {
|
||||
button {
|
||||
class:"inline-block py-4 px-8 m-2 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
onclick: move |_| set_name("jack")
|
||||
"Jack!"
|
||||
}
|
||||
button {
|
||||
class:"inline-block py-4 px-8 m-2 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
onclick: move |_| set_name("jill")
|
||||
"Jill!"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
173
packages/webview/src/dom.rs
Normal file
173
packages/webview/src/dom.rs
Normal file
|
@ -0,0 +1,173 @@
|
|||
//! webview dom
|
||||
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core::{
|
||||
diff::RealDom,
|
||||
prelude::ScopeIdx,
|
||||
virtual_dom::{RealDomNode, VirtualDom},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
fn test() {
|
||||
const App: FC<()> = |cx| cx.render(rsx! { div {}});
|
||||
let mut vi = VirtualDom::new(App);
|
||||
let mut real = WebviewDom {
|
||||
edits: Vec::new(),
|
||||
node_counter: 0,
|
||||
};
|
||||
vi.rebuild(&mut real);
|
||||
}
|
||||
|
||||
pub struct WebviewDom<'bump> {
|
||||
pub edits: Vec<SerializedDom<'bump>>,
|
||||
pub node_counter: u64,
|
||||
}
|
||||
impl WebviewDom<'_> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
edits: Vec::new(),
|
||||
node_counter: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum SerializedDom<'bump> {
|
||||
PushRoot {
|
||||
root: u64,
|
||||
},
|
||||
AppendChild,
|
||||
ReplaceWith,
|
||||
Remove,
|
||||
RemoveAllChildren,
|
||||
CreateTextNode {
|
||||
text: &'bump str,
|
||||
id: u64,
|
||||
},
|
||||
CreateElement {
|
||||
tag: &'bump str,
|
||||
id: u64,
|
||||
},
|
||||
CreateElementNs {
|
||||
tag: &'bump str,
|
||||
id: u64,
|
||||
ns: &'bump str,
|
||||
},
|
||||
CreatePlaceholder {
|
||||
id: u64,
|
||||
},
|
||||
NewEventListener {
|
||||
scope: ScopeIdx,
|
||||
node: u64,
|
||||
idx: usize,
|
||||
},
|
||||
|
||||
RemoveEventListener {
|
||||
event: &'bump str,
|
||||
},
|
||||
SetText {
|
||||
text: &'bump str,
|
||||
},
|
||||
SetAttribute {
|
||||
field: &'bump str,
|
||||
value: &'bump str,
|
||||
},
|
||||
RemoveAttribute {
|
||||
name: &'bump str,
|
||||
},
|
||||
}
|
||||
|
||||
use SerializedDom::*;
|
||||
impl<'bump> RealDom<'bump> for WebviewDom<'bump> {
|
||||
fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) {
|
||||
self.edits.push(PushRoot { root: root.0 });
|
||||
}
|
||||
|
||||
fn append_child(&mut self) {
|
||||
self.edits.push(AppendChild {});
|
||||
}
|
||||
|
||||
fn replace_with(&mut self) {
|
||||
self.edits.push(ReplaceWith);
|
||||
}
|
||||
|
||||
fn remove(&mut self) {
|
||||
self.edits.push(Remove);
|
||||
}
|
||||
|
||||
fn remove_all_children(&mut self) {
|
||||
self.edits.push(RemoveAllChildren);
|
||||
}
|
||||
|
||||
fn create_text_node(&mut self, text: &'bump str) -> dioxus_core::virtual_dom::RealDomNode {
|
||||
self.node_counter += 1;
|
||||
let id = RealDomNode::new(self.node_counter);
|
||||
self.edits.push(CreateTextNode { text, id: id.0 });
|
||||
id
|
||||
}
|
||||
|
||||
fn create_element(&mut self, tag: &'bump str) -> dioxus_core::virtual_dom::RealDomNode {
|
||||
self.node_counter += 1;
|
||||
let id = RealDomNode::new(self.node_counter);
|
||||
self.edits.push(CreateElement { id: id.0, tag });
|
||||
id
|
||||
}
|
||||
|
||||
fn create_element_ns(
|
||||
&mut self,
|
||||
tag: &'static str,
|
||||
namespace: &'static str,
|
||||
) -> dioxus_core::virtual_dom::RealDomNode {
|
||||
self.node_counter += 1;
|
||||
let id = RealDomNode::new(self.node_counter);
|
||||
self.edits.push(CreateElementNs {
|
||||
id: id.0,
|
||||
ns: namespace,
|
||||
tag,
|
||||
});
|
||||
id
|
||||
}
|
||||
|
||||
fn create_placeholder(&mut self) -> dioxus_core::virtual_dom::RealDomNode {
|
||||
self.node_counter += 1;
|
||||
let id = RealDomNode::new(self.node_counter);
|
||||
self.edits.push(CreatePlaceholder { id: id.0 });
|
||||
id
|
||||
}
|
||||
|
||||
fn new_event_listener(
|
||||
&mut self,
|
||||
event: &str,
|
||||
scope: dioxus_core::prelude::ScopeIdx,
|
||||
element_id: usize,
|
||||
realnode: dioxus_core::virtual_dom::RealDomNode,
|
||||
) {
|
||||
self.edits.push(NewEventListener {
|
||||
scope,
|
||||
idx: element_id,
|
||||
node: realnode.0,
|
||||
});
|
||||
}
|
||||
|
||||
fn remove_event_listener(&mut self, event: &'static str) {
|
||||
self.edits.push(RemoveEventListener { event });
|
||||
}
|
||||
|
||||
fn set_text(&mut self, text: &'bump str) {
|
||||
self.edits.push(SetText { text });
|
||||
}
|
||||
|
||||
fn set_attribute(&mut self, field: &'static str, value: &'bump str, is_namespaced: bool) {
|
||||
self.edits.push(SetAttribute { field, value });
|
||||
}
|
||||
|
||||
fn remove_attribute(&mut self, name: &'static str) {
|
||||
self.edits.push(RemoveAttribute { name });
|
||||
}
|
||||
|
||||
fn raw_node_as_any_mut(&self) -> &mut dyn std::any::Any {
|
||||
todo!()
|
||||
// self.edits.push(PushRoot { root });
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ use dioxus_core::prelude::*;
|
|||
use dioxus_core::virtual_dom::VirtualDom;
|
||||
use web_view::Handle;
|
||||
use web_view::{WVResult, WebView, WebViewBuilder};
|
||||
mod dom;
|
||||
|
||||
static HTML_CONTENT: &'static str = include_str!("./../../liveview/index.html");
|
||||
|
||||
|
@ -22,6 +23,7 @@ pub struct WebviewRenderer<T> {
|
|||
/// The root component used to render the Webview
|
||||
root: FC<T>,
|
||||
}
|
||||
|
||||
enum InnerEvent {
|
||||
Initiate(Handle<()>),
|
||||
}
|
||||
|
@ -68,8 +70,10 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
|
|||
.unwrap();
|
||||
|
||||
let mut vdom = VirtualDom::new_with_props(root, props);
|
||||
let edits = vdom.rebuild()?;
|
||||
let ref_edits = Arc::new(serde_json::to_string(&edits)?);
|
||||
let mut real_dom = dom::WebviewDom::new();
|
||||
vdom.rebuild(&mut real_dom)?;
|
||||
|
||||
let ref_edits = Arc::new(serde_json::to_string(&real_dom.edits)?);
|
||||
|
||||
loop {
|
||||
view.step()
|
||||
|
|
Loading…
Reference in a new issue