wip: more work on suspense and documentation
This commit is contained in:
1 parent
4837d8e741
commit
37ed4bed8c
14 files changed
+240
-192
No files matched your search
@@ -27,7 +27,7 @@ impl SharedArena {
|
||||
|
||||
/// 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> {
|
||||
pub fn try_get(&self, idx: ScopeId) -> Result<&Scope> {
|
||||
let inner = unsafe { &*self.components.get() };
|
||||
let scope = inner.get(idx);
|
||||
scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||
@@ -35,7 +35,7 @@ impl SharedArena {
|
||||
|
||||
/// 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> {
|
||||
pub fn try_get_mut(&self, idx: ScopeId) -> Result<&mut Scope> {
|
||||
let inner = unsafe { &mut *self.components.get() };
|
||||
let scope = inner.get_mut(idx);
|
||||
scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||
@@ -59,7 +59,7 @@ impl SharedArena {
|
||||
|
||||
pub fn with_scope<'b, O: 'static>(
|
||||
&'b self,
|
||||
_id: ScopeIdx,
|
||||
_id: ScopeId,
|
||||
_f: impl FnOnce(&'b mut Scope) -> O,
|
||||
) -> Result<O> {
|
||||
todo!()
|
||||
@@ -69,13 +69,13 @@ impl SharedArena {
|
||||
// this is useful for merging lifetimes
|
||||
pub fn with_scope_vnode<'b>(
|
||||
&self,
|
||||
_id: ScopeIdx,
|
||||
_id: ScopeId,
|
||||
_f: impl FnOnce(&mut Scope) -> &VNode<'b>,
|
||||
) -> Result<&VNode<'b>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn try_remove(&self, id: ScopeIdx) -> Result<Scope> {
|
||||
pub fn try_remove(&self, id: ScopeId) -> Result<Scope> {
|
||||
let inner = unsafe { &mut *self.components.get() };
|
||||
inner
|
||||
.remove(id)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::innerlude::*;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use futures_util::FutureExt;
|
||||
|
||||
use std::any::Any;
|
||||
@@ -12,7 +11,6 @@ use std::{
|
||||
cell::RefCell,
|
||||
future::Future,
|
||||
ops::Deref,
|
||||
pin::Pin,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
@@ -34,13 +32,20 @@ use std::{
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
// todo: force lifetime of source into T as a valid lifetime too
|
||||
// it's definitely possible, just needs some more messing around
|
||||
///
|
||||
/// ## Available Methods:
|
||||
/// - render
|
||||
/// - use_hook
|
||||
/// - use_task
|
||||
/// - use_suspense
|
||||
/// - submit_task
|
||||
/// - children
|
||||
/// - use_effect
|
||||
///
|
||||
pub struct Context<'src, T> {
|
||||
pub props: &'src T,
|
||||
pub scope: &'src Scope,
|
||||
}
|
||||
// pub type PinnedTask = Pin<Box<dyn Future<Output = ()>>>;
|
||||
|
||||
impl<'src, T> Copy for Context<'src, T> {}
|
||||
impl<'src, T> Clone for Context<'src, T> {
|
||||
@@ -52,9 +57,10 @@ impl<'src, T> Clone for Context<'src, T> {
|
||||
}
|
||||
}
|
||||
|
||||
// We currently deref to props, but it might make more sense to deref to Scope?
|
||||
// This allows for code that takes cx.xyz instead of cx.props.xyz
|
||||
impl<'a, T> Deref for Context<'a, T> {
|
||||
type Target = &'a T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.props
|
||||
}
|
||||
@@ -62,14 +68,42 @@ impl<'a, T> Deref for Context<'a, T> {
|
||||
|
||||
impl<'src, P> Context<'src, P> {
|
||||
/// Access the children elements passed into the component
|
||||
///
|
||||
/// This enables patterns where a component is passed children from its parent.
|
||||
///
|
||||
/// ## Details
|
||||
///
|
||||
/// Unlike React, Dioxus allows *only* lists of children to be passed from parent to child - not arbitrary functions
|
||||
/// or classes. If you want to generate nodes instead of accepting them as a list, consider declaring a closure
|
||||
/// on the props that takes Context.
|
||||
///
|
||||
/// If a parent passes children into a component, the child will always re-render when the parent re-renders. In other
|
||||
/// words, a component cannot be automatically memoized if it borrows nodes from its parent, even if the component's
|
||||
/// props are valid for the static lifetime.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// const App: FC<()> = |cx| {
|
||||
/// cx.render(rsx!{
|
||||
/// CustomCard {
|
||||
/// h1 {}
|
||||
/// p {}
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
///
|
||||
/// const CustomCard: FC<()> = |cx| {
|
||||
/// cx.render(rsx!{
|
||||
/// div {
|
||||
/// h1 {"Title card"}
|
||||
/// {cx.children()}
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn children(&self) -> &'src [VNode<'src>] {
|
||||
// We're re-casting the nodes back out
|
||||
// They don't really have a static lifetime
|
||||
unsafe {
|
||||
let scope = self.scope;
|
||||
let nodes = scope.child_nodes;
|
||||
nodes
|
||||
}
|
||||
self.scope.child_nodes
|
||||
}
|
||||
|
||||
/// Create a subscription that schedules a future render for the reference component
|
||||
@@ -227,7 +261,10 @@ Any function prefixed with "use" should not be called conditionally.
|
||||
|
||||
let ty = TypeId::of::<T>();
|
||||
while let Some(inner) = scope {
|
||||
log::debug!("Searching {:#?} for valid shared_context", inner.arena_idx);
|
||||
log::debug!(
|
||||
"Searching {:#?} for valid shared_context",
|
||||
inner.our_arena_idx
|
||||
);
|
||||
let shared_contexts = inner.shared_contexts.borrow();
|
||||
|
||||
if let Some(shared_cx) = shared_contexts.get(&ty) {
|
||||
@@ -241,7 +278,7 @@ Any function prefixed with "use" should not be called conditionally.
|
||||
hook.par = Some(rc);
|
||||
return Ok(hook.par.as_ref().unwrap());
|
||||
} else {
|
||||
match inner.parent {
|
||||
match inner.parent_idx {
|
||||
Some(parent_id) => {
|
||||
let parent = inner
|
||||
.arena_link
|
||||
@@ -324,7 +361,7 @@ Any function prefixed with "use" should not be called conditionally.
|
||||
|
||||
let slot = task_dump.clone();
|
||||
let update = self.schedule_update();
|
||||
let originator = self.scope.arena_idx.clone();
|
||||
let originator = self.scope.our_arena_idx.clone();
|
||||
|
||||
self.submit_task(Box::pin(task_fut.then(move |output| async move {
|
||||
*slot.as_ref().borrow_mut() = Some(output);
|
||||
@@ -411,7 +448,7 @@ impl<'src, P> Context<'src, P> {
|
||||
}
|
||||
});
|
||||
|
||||
let originator = self.scope.arena_idx.clone();
|
||||
let originator = self.scope.our_arena_idx.clone();
|
||||
let task_fut = task_initializer();
|
||||
let domnode = dom_node_id.clone();
|
||||
|
||||
|
||||
@@ -75,10 +75,10 @@ pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> {
|
||||
pub edits: DomEditor<'real, 'bump>,
|
||||
pub components: &'bump SharedArena,
|
||||
pub task_queue: &'bump TaskQueue,
|
||||
pub cur_idx: ScopeIdx,
|
||||
pub diffed: FxHashSet<ScopeIdx>,
|
||||
pub cur_idx: ScopeId,
|
||||
pub diffed: FxHashSet<ScopeId>,
|
||||
pub event_queue: EventQueue,
|
||||
pub seen_nodes: FxHashSet<ScopeIdx>,
|
||||
pub seen_nodes: FxHashSet<ScopeId>,
|
||||
}
|
||||
|
||||
impl<'real, 'bump, Dom> DiffMachine<'real, 'bump, Dom>
|
||||
@@ -89,7 +89,7 @@ where
|
||||
edits: &'real mut Vec<DomEdit<'bump>>,
|
||||
dom: &'real mut Dom,
|
||||
components: &'bump SharedArena,
|
||||
cur_idx: ScopeIdx,
|
||||
cur_idx: ScopeId,
|
||||
event_queue: EventQueue,
|
||||
task_queue: &'bump TaskQueue,
|
||||
) -> Self {
|
||||
@@ -488,7 +488,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
||||
///
|
||||
/// Calling this will run the destuctors on all hooks in the tree.
|
||||
/// It will also add the destroyed nodes to the `seen_nodes` cache to prevent them from being renderered.
|
||||
fn destroy_scopes(&mut self, old_scope: ScopeIdx) {
|
||||
fn destroy_scopes(&mut self, old_scope: ScopeId) {
|
||||
let mut nodes_to_delete = vec![old_scope];
|
||||
let mut scopes_to_explore = vec![old_scope];
|
||||
|
||||
|
||||
+26
-11
@@ -1,5 +1,3 @@
|
||||
//! Serialization
|
||||
//! -------------
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
@@ -7,7 +5,7 @@
|
||||
//!
|
||||
//!
|
||||
|
||||
use crate::{innerlude::ScopeIdx, RealDomNode};
|
||||
use crate::{innerlude::ScopeId, RealDomNode};
|
||||
|
||||
/// The `DomEditor` provides an imperative interface for the Diffing algorithm to plan out its changes.
|
||||
///
|
||||
@@ -18,6 +16,7 @@ use crate::{innerlude::ScopeIdx, RealDomNode};
|
||||
pub struct DomEditor<'real, 'bump> {
|
||||
pub edits: &'real mut Vec<DomEdit<'bump>>,
|
||||
}
|
||||
|
||||
use DomEdit::*;
|
||||
impl<'real, 'bump> DomEditor<'real, 'bump> {
|
||||
pub fn new(edits: &'real mut Vec<DomEdit<'bump>>) -> Self {
|
||||
@@ -29,35 +28,44 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
|
||||
let id = root.as_u64();
|
||||
self.edits.push(PushRoot { id });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn pop(&mut self) {
|
||||
self.edits.push(PopRoot {});
|
||||
}
|
||||
|
||||
// Add Nodes to the dom
|
||||
// add m nodes from the stack
|
||||
#[inline]
|
||||
pub(crate) fn append_children(&mut self, many: u32) {
|
||||
self.edits.push(AppendChildren { many });
|
||||
}
|
||||
|
||||
// replace the n-m node on the stack with the m nodes
|
||||
// ends with the last element of the chain on the top of the stack
|
||||
#[inline]
|
||||
pub(crate) fn replace_with(&mut self, many: u32) {
|
||||
self.edits.push(ReplaceWith { many });
|
||||
}
|
||||
|
||||
// Remove Nodesfrom the dom
|
||||
#[inline]
|
||||
pub(crate) fn remove(&mut self) {
|
||||
self.edits.push(Remove);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn remove_all_children(&mut self) {
|
||||
self.edits.push(RemoveAllChildren);
|
||||
}
|
||||
|
||||
// Create
|
||||
#[inline]
|
||||
pub(crate) fn create_text_node(&mut self, text: &'bump str, id: RealDomNode) {
|
||||
let id = id.as_u64();
|
||||
self.edits.push(CreateTextNode { text, id });
|
||||
}
|
||||
|
||||
pub(crate) fn create_element(
|
||||
&mut self,
|
||||
tag: &'static str,
|
||||
@@ -81,25 +89,30 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
|
||||
pub(crate) fn new_event_listener(
|
||||
&mut self,
|
||||
event: &'static str,
|
||||
scope: ScopeIdx,
|
||||
scope: ScopeId,
|
||||
element_id: usize,
|
||||
realnode: RealDomNode,
|
||||
) {
|
||||
self.edits.push(NewEventListener {
|
||||
scope,
|
||||
event,
|
||||
idx: element_id,
|
||||
node: realnode.as_u64(),
|
||||
event_name: event,
|
||||
element_id,
|
||||
mounted_node_id: realnode.as_u64(),
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn remove_event_listener(&mut self, event: &'static str) {
|
||||
self.edits.push(RemoveEventListener { event });
|
||||
}
|
||||
|
||||
// modify
|
||||
#[inline]
|
||||
pub(crate) fn set_text(&mut self, text: &'bump str) {
|
||||
self.edits.push(SetText { text });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_attribute(
|
||||
&mut self,
|
||||
field: &'static str,
|
||||
@@ -108,6 +121,8 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
|
||||
) {
|
||||
self.edits.push(SetAttribute { field, value, ns });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn remove_attribute(&mut self, name: &'static str) {
|
||||
self.edits.push(RemoveAttribute { name });
|
||||
}
|
||||
@@ -151,10 +166,10 @@ pub enum DomEdit<'bump> {
|
||||
id: u64,
|
||||
},
|
||||
NewEventListener {
|
||||
event: &'static str,
|
||||
scope: ScopeIdx,
|
||||
node: u64,
|
||||
idx: usize,
|
||||
event_name: &'static str,
|
||||
scope: ScopeId,
|
||||
mounted_node_id: u64,
|
||||
element_id: usize,
|
||||
},
|
||||
RemoveEventListener {
|
||||
event: &'static str,
|
||||
|
||||
@@ -4,17 +4,14 @@
|
||||
//! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
|
||||
//! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
|
||||
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
rc::Rc,
|
||||
};
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
|
||||
use crate::innerlude::{RealDomNode, ScopeIdx};
|
||||
use crate::innerlude::{RealDomNode, ScopeId};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventTrigger {
|
||||
/// The originator of the event trigger
|
||||
pub originator: ScopeIdx,
|
||||
pub originator: ScopeId,
|
||||
|
||||
/// The optional real node associated with the trigger
|
||||
pub real_node_id: Option<RealDomNode>,
|
||||
@@ -27,7 +24,7 @@ pub struct EventTrigger {
|
||||
}
|
||||
|
||||
impl EventTrigger {
|
||||
pub fn new_from_task(originator: ScopeIdx, hook_idx: usize) -> Self {
|
||||
pub fn new_from_task(originator: ScopeId, hook_idx: usize) -> Self {
|
||||
Self {
|
||||
originator,
|
||||
event: VirtualEvent::AsyncEvent { hook_idx },
|
||||
@@ -74,7 +71,7 @@ pub enum EventPriority {
|
||||
impl EventTrigger {
|
||||
pub fn new(
|
||||
event: VirtualEvent,
|
||||
scope: ScopeIdx,
|
||||
scope: ScopeId,
|
||||
mounted_dom_id: Option<RealDomNode>,
|
||||
priority: EventPriority,
|
||||
) -> Self {
|
||||
@@ -184,7 +181,7 @@ pub mod on {
|
||||
Listener {
|
||||
event: stringify!($name),
|
||||
mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
|
||||
scope: c.scope_ref.arena_idx,
|
||||
scope: c.scope_ref.our_arena_idx,
|
||||
callback: bump.alloc(move |evt: VirtualEvent| match evt {
|
||||
VirtualEvent::$wrapper(event) => callback(event),
|
||||
_ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![allow(non_snake_case, dead_code, unused_must_use, unreachable_code)]
|
||||
#![allow(non_snake_case)]
|
||||
//! Dioxus Core
|
||||
//! ----------
|
||||
//!
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
pub use crate::innerlude::{
|
||||
format_args_f, html, rsx, DioxusElement, DomEdit, EventTrigger, LazyNodes, NodeFactory,
|
||||
Properties, RealDom, RealDomNode, ScopeIdx, VNode, VNodeKind, VirtualDom, VirtualEvent, FC,
|
||||
Properties, RealDom, RealDomNode, ScopeId, VNode, VNodeKind, VirtualDom, VirtualEvent, FC,
|
||||
};
|
||||
|
||||
pub mod prelude {
|
||||
|
||||
+64
-42
@@ -5,10 +5,10 @@
|
||||
|
||||
use crate::{
|
||||
events::VirtualEvent,
|
||||
innerlude::{Context, Properties, RealDomNode, Scope, ScopeIdx, FC},
|
||||
innerlude::{Context, Properties, RealDomNode, Scope, ScopeId, FC},
|
||||
};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
cell::Cell,
|
||||
fmt::{Arguments, Debug, Formatter},
|
||||
marker::PhantomData,
|
||||
rc::Rc,
|
||||
@@ -87,7 +87,7 @@ pub struct Attribute<'a> {
|
||||
pub struct Listener<'bump> {
|
||||
/// The type of event to listen for.
|
||||
pub(crate) event: &'static str,
|
||||
pub scope: ScopeIdx,
|
||||
pub scope: ScopeId,
|
||||
pub mounted_node: &'bump Cell<RealDomNode>,
|
||||
pub(crate) callback: &'bump dyn FnMut(VirtualEvent),
|
||||
}
|
||||
@@ -95,7 +95,7 @@ pub struct Listener<'bump> {
|
||||
/// Virtual Components for custom user-defined components
|
||||
/// Only supports the functional syntax
|
||||
pub struct VComponent<'src> {
|
||||
pub ass_scope: Cell<Option<ScopeIdx>>,
|
||||
pub ass_scope: Cell<Option<ScopeId>>,
|
||||
pub(crate) caller: Rc<dyn Fn(&Scope) -> VNode>,
|
||||
pub(crate) children: &'src [VNode<'src>],
|
||||
pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
|
||||
@@ -328,6 +328,26 @@ impl<'a> NodeFactory<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait implementations for use in the rsx! and html! macros.
|
||||
///
|
||||
/// ## Details
|
||||
///
|
||||
/// This section provides convenience methods and trait implementations for converting common structs into a format accepted
|
||||
/// by the macros.
|
||||
///
|
||||
/// All dynamic content in the macros must flow in through `fragment_from_iter`. Everything else must be statically layed out.
|
||||
/// We pipe basically everything through `fragment_from_iter`, so we expect a very specific type:
|
||||
/// ```
|
||||
/// impl IntoIterator<Item = impl IntoVNode<'a>>
|
||||
/// ```
|
||||
///
|
||||
/// As such, all node creation must go through the factory, which is only availble in the component context.
|
||||
/// These strict requirements make it possible to manage lifetimes and state.
|
||||
pub trait IntoVNode<'a> {
|
||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
|
||||
}
|
||||
|
||||
// For the case where a rendered VNode is passed into the rsx! macro through curly braces
|
||||
impl<'a> IntoIterator for VNode<'a> {
|
||||
type Item = VNode<'a>;
|
||||
type IntoIter = std::iter::Once<Self::Item>;
|
||||
@@ -335,25 +355,53 @@ impl<'a> IntoIterator for VNode<'a> {
|
||||
std::iter::once(self)
|
||||
}
|
||||
}
|
||||
|
||||
// For the case where a rendered VNode is passed into the rsx! macro through curly braces
|
||||
impl<'a> IntoVNode<'a> for VNode<'a> {
|
||||
fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// For the case where a rendered VNode is by reference passed into the rsx! macro through curly braces
|
||||
// This behavior is designed for the cx.children method where child nodes are passed by reference.
|
||||
//
|
||||
// Designed to support indexing
|
||||
impl<'a> IntoVNode<'a> for &VNode<'a> {
|
||||
fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
|
||||
self.clone()
|
||||
let kind = match &self.kind {
|
||||
VNodeKind::Element(element) => VNodeKind::Element(element),
|
||||
VNodeKind::Text(old) => VNodeKind::Text(VText {
|
||||
text: old.text,
|
||||
is_static: old.is_static,
|
||||
}),
|
||||
VNodeKind::Fragment(fragment) => VNodeKind::Fragment(VFragment {
|
||||
children: fragment.children,
|
||||
is_static: fragment.is_static,
|
||||
}),
|
||||
VNodeKind::Component(component) => VNodeKind::Component(component),
|
||||
|
||||
// todo: it doesn't make much sense to pass in suspended nodes
|
||||
// I think this is right but I'm not too sure.
|
||||
VNodeKind::Suspended { node } => VNodeKind::Suspended { node: node.clone() },
|
||||
};
|
||||
VNode {
|
||||
kind,
|
||||
dom_id: self.dom_id.clone(),
|
||||
key: self.key.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoVNode<'a> {
|
||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
|
||||
}
|
||||
|
||||
// Wrap the the node-builder closure in a concrete type.
|
||||
// ---
|
||||
// This is a bit of a hack to implement the IntoVNode trait for closure types.
|
||||
/// A concrete type provider for closures that build VNode structures.
|
||||
///
|
||||
/// This struct wraps lazy structs that build VNode trees Normally, we cannot perform a blanket implementation over
|
||||
/// closures, but if we wrap the closure in a concrete type, we can maintain separate implementations of IntoVNode.
|
||||
///
|
||||
///
|
||||
/// ```rust
|
||||
/// LazyNodes::new(|f| f.element("div", [], [], [] None))
|
||||
/// ```
|
||||
pub struct LazyNodes<'a, G>
|
||||
where
|
||||
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
||||
@@ -374,11 +422,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// Cover the cases where nodes are used by macro.
|
||||
// Likely used directly.
|
||||
// ---
|
||||
// let nodes = rsx!{ ... };
|
||||
// rsx! { {nodes } }
|
||||
// Our blanket impl
|
||||
impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
|
||||
where
|
||||
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
||||
@@ -388,7 +432,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// Required because anything that enters brackets in the rsx! macro needs to implement IntoIterator
|
||||
// Our blanket impl
|
||||
impl<'a, G> IntoIterator for LazyNodes<'a, G>
|
||||
where
|
||||
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
||||
@@ -400,12 +444,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// Conveniently, we also support "null" (nothing) passed in
|
||||
impl IntoVNode<'_> for () {
|
||||
fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||
cx.fragment_from_iter(None as Option<VNode>)
|
||||
}
|
||||
}
|
||||
|
||||
// Conveniently, we also support "None"
|
||||
impl IntoVNode<'_> for Option<()> {
|
||||
fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||
cx.fragment_from_iter(None as Option<VNode>)
|
||||
@@ -418,30 +464,6 @@ impl Debug for NodeFactory<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// it's okay to clone because vnodes are just references to places into the bump
|
||||
impl<'a> Clone for VNode<'a> {
|
||||
fn clone(&self) -> Self {
|
||||
let kind = match &self.kind {
|
||||
VNodeKind::Element(element) => VNodeKind::Element(element),
|
||||
VNodeKind::Text(old) => VNodeKind::Text(VText {
|
||||
text: old.text,
|
||||
is_static: old.is_static,
|
||||
}),
|
||||
VNodeKind::Fragment(fragment) => VNodeKind::Fragment(VFragment {
|
||||
children: fragment.children,
|
||||
is_static: fragment.is_static,
|
||||
}),
|
||||
VNodeKind::Component(component) => VNodeKind::Component(component),
|
||||
VNodeKind::Suspended { node } => VNodeKind::Suspended { node: node.clone() },
|
||||
};
|
||||
VNode {
|
||||
kind,
|
||||
dom_id: self.dom_id.clone(),
|
||||
key: self.key.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for VNode<'_> {
|
||||
fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
match &self.kind {
|
||||
|
||||
+30
-47
@@ -22,53 +22,36 @@ pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> VNode<'b>;
|
||||
/// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components.
|
||||
/// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
|
||||
pub struct Scope {
|
||||
// The parent's scope ID
|
||||
pub parent: Option<ScopeIdx>,
|
||||
// Book-keeping about the arena
|
||||
pub(crate) parent_idx: Option<ScopeId>,
|
||||
pub(crate) descendents: RefCell<HashSet<ScopeId>>,
|
||||
pub(crate) our_arena_idx: ScopeId,
|
||||
pub(crate) height: u32,
|
||||
|
||||
// IDs of children that this scope has created
|
||||
// This enables us to drop the children and their children when this scope is destroyed
|
||||
pub(crate) descendents: RefCell<HashSet<ScopeIdx>>,
|
||||
// Nodes
|
||||
// an internal, highly efficient storage of vnodes
|
||||
pub(crate) frames: ActiveFrame,
|
||||
pub(crate) child_nodes: &'static [VNode<'static>],
|
||||
pub(crate) caller: Rc<WrappedCaller>,
|
||||
|
||||
pub child_nodes: &'static [VNode<'static>],
|
||||
// Listeners
|
||||
pub(crate) listeners: RefCell<Vec<(*mut Cell<RealDomNode>, *mut dyn FnMut(VirtualEvent))>>,
|
||||
pub(crate) listener_idx: Cell<usize>,
|
||||
|
||||
// State
|
||||
pub(crate) hooks: HookList,
|
||||
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
|
||||
|
||||
// Events
|
||||
pub(crate) event_channel: Rc<dyn Fn() + 'static>,
|
||||
|
||||
// Tasks
|
||||
pub(crate) task_submitter: TaskSubmitter,
|
||||
pub(crate) suspended_tasks: Vec<*mut Pin<Box<dyn Future<Output = VNode<'static>>>>>,
|
||||
|
||||
// A reference to the list of components.
|
||||
// This lets us traverse the component list whenever we need to access our parent or children.
|
||||
pub arena_link: SharedArena,
|
||||
|
||||
pub shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
|
||||
|
||||
// Our own ID accessible from the component map
|
||||
pub arena_idx: ScopeIdx,
|
||||
|
||||
pub height: u32,
|
||||
|
||||
pub event_channel: Rc<dyn Fn() + 'static>,
|
||||
|
||||
pub caller: Rc<WrappedCaller>,
|
||||
|
||||
// ==========================
|
||||
// slightly unsafe stuff
|
||||
// ==========================
|
||||
// an internal, highly efficient storage of vnodes
|
||||
pub frames: ActiveFrame,
|
||||
|
||||
// These hooks are actually references into the hook arena
|
||||
// These two could be combined with "OwningRef" to remove unsafe usage
|
||||
// or we could dedicate a tiny bump arena just for them
|
||||
// could also use ourborous
|
||||
pub hooks: HookList,
|
||||
|
||||
pub(crate) listener_idx: Cell<usize>,
|
||||
|
||||
// Unsafety:
|
||||
// - is self-refenrential and therefore needs to point into the bump
|
||||
// Stores references into the listeners attached to the vnodes
|
||||
// NEEDS TO BE PRIVATE
|
||||
pub(crate) listeners: RefCell<Vec<(*mut Cell<RealDomNode>, *mut dyn FnMut(VirtualEvent))>>,
|
||||
|
||||
pub task_submitter: TaskSubmitter,
|
||||
|
||||
pub(crate) suspended_tasks: Vec<*mut Pin<Box<dyn Future<Output = VNode<'static>>>>>,
|
||||
pub(crate) arena_link: SharedArena,
|
||||
}
|
||||
|
||||
pub type FiberTask = Pin<Box<dyn Future<Output = EventTrigger>>>;
|
||||
@@ -83,8 +66,8 @@ impl Scope {
|
||||
// Therefore, their lifetimes are connected exclusively to the virtual dom
|
||||
pub fn new<'creator_node>(
|
||||
caller: Rc<WrappedCaller>,
|
||||
arena_idx: ScopeIdx,
|
||||
parent: Option<ScopeIdx>,
|
||||
arena_idx: ScopeId,
|
||||
parent: Option<ScopeId>,
|
||||
height: u32,
|
||||
event_channel: EventChannel,
|
||||
arena_link: SharedArena,
|
||||
@@ -102,8 +85,8 @@ impl Scope {
|
||||
Self {
|
||||
child_nodes,
|
||||
caller,
|
||||
parent,
|
||||
arena_idx,
|
||||
parent_idx: parent,
|
||||
our_arena_idx: arena_idx,
|
||||
height,
|
||||
event_channel,
|
||||
arena_link,
|
||||
@@ -190,7 +173,7 @@ impl Scope {
|
||||
log::debug!(
|
||||
"There are {:?} listeners associated with this scope {:#?}",
|
||||
self.listeners.borrow().len(),
|
||||
self.arena_idx
|
||||
self.our_arena_idx
|
||||
);
|
||||
|
||||
let listners = self.listeners.borrow_mut();
|
||||
|
||||
@@ -11,15 +11,13 @@
|
||||
|
||||
use std::{
|
||||
cell::Cell,
|
||||
pin::Pin,
|
||||
sync::{Arc, RwLock},
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use futures_util::{stream::FuturesUnordered, Future, Stream, StreamExt};
|
||||
use slotmap::{DefaultKey, SlotMap};
|
||||
|
||||
use crate::innerlude::{EventTrigger, FiberTask, ScopeIdx};
|
||||
use crate::innerlude::{EventTrigger, FiberTask, ScopeId};
|
||||
|
||||
pub type TaskSubmitter = Arc<dyn Fn(FiberTask)>;
|
||||
|
||||
@@ -117,11 +115,11 @@ pub struct TaskHandle {
|
||||
|
||||
pub struct DTask {
|
||||
fut: FiberTask,
|
||||
originator: ScopeIdx,
|
||||
originator: ScopeId,
|
||||
dead: Cell<bool>,
|
||||
}
|
||||
impl DTask {
|
||||
pub fn new(fut: FiberTask, originator: ScopeIdx) -> Self {
|
||||
pub fn new(fut: FiberTask, originator: ScopeId) -> Self {
|
||||
Self {
|
||||
fut,
|
||||
originator,
|
||||
@@ -129,7 +127,7 @@ impl DTask {
|
||||
}
|
||||
}
|
||||
pub fn debug_new(fut: FiberTask) -> Self {
|
||||
let originator = ScopeIdx::default();
|
||||
let originator = ScopeId::default();
|
||||
Self {
|
||||
fut,
|
||||
originator,
|
||||
|
||||
+16
-10
@@ -14,7 +14,7 @@ pub struct EventQueue {
|
||||
}
|
||||
|
||||
impl EventQueue {
|
||||
pub fn new_channel(&self, height: u32, idx: ScopeIdx) -> Rc<dyn Fn()> {
|
||||
pub fn new_channel(&self, height: u32, idx: ScopeId) -> Rc<dyn Fn()> {
|
||||
let inner = self.clone();
|
||||
let marker = HeightMarker { height, idx };
|
||||
Rc::new(move || {
|
||||
@@ -35,7 +35,7 @@ impl EventQueue {
|
||||
/// A helper type that lets scopes be ordered by their height
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct HeightMarker {
|
||||
pub idx: ScopeIdx,
|
||||
pub idx: ScopeId,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
@@ -59,23 +59,29 @@ impl PartialOrd for HeightMarker {
|
||||
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct RealDomNode(pub DefaultKey);
|
||||
pub struct RealDomNode(pub u64);
|
||||
impl RealDomNode {
|
||||
#[inline]
|
||||
pub fn empty() -> Self {
|
||||
let data = KeyData::from_ffi(u64::MIN);
|
||||
let key: DefaultKey = data.into();
|
||||
Self(key)
|
||||
// let data = KeyData::from_ffi(u64::MIN);
|
||||
// let key: DefaultKey = data.into();
|
||||
Self(u64::MIN)
|
||||
}
|
||||
#[inline]
|
||||
pub fn empty_cell() -> Cell<Self> {
|
||||
Cell::new(Self::empty())
|
||||
}
|
||||
#[inline]
|
||||
pub fn from_u64(id: u64) -> Self {
|
||||
let data = KeyData::from_ffi(id);
|
||||
let key: DefaultKey = data.into();
|
||||
Self(key)
|
||||
// let data = KeyData::from_ffi(id);
|
||||
// let key: DefaultKey = data.into();
|
||||
Self(id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_u64(&self) -> u64 {
|
||||
self.0.data().as_ffi()
|
||||
// self.0.data().as_ffi()
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,10 +30,17 @@ use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
use std::pin::Pin;
|
||||
|
||||
pub type ScopeIdx = DefaultKey;
|
||||
pub type ScopeId = DefaultKey;
|
||||
|
||||
/// An integrated virtual node system that progresses events and diffs UI trees.
|
||||
/// Differences are converted into patches which a renderer can use to draw the UI.
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
pub struct VirtualDom {
|
||||
/// All mounted components are arena allocated to make additions, removals, and references easy to work with
|
||||
/// A generational arena is used to re-use slots of deleted scopes without having to resize the underlying arena.
|
||||
@@ -44,7 +51,7 @@ pub struct VirtualDom {
|
||||
|
||||
/// The index of the root component
|
||||
/// Should always be the first (gen=0, id=0)
|
||||
pub base_scope: ScopeIdx,
|
||||
pub base_scope: ScopeId,
|
||||
|
||||
pub triggers: RefCell<Vec<EventTrigger>>,
|
||||
|
||||
@@ -55,7 +62,7 @@ pub struct VirtualDom {
|
||||
|
||||
root_props: std::pin::Pin<Box<dyn std::any::Any>>,
|
||||
|
||||
/// Type of the original cx. This is stored as TypeId so VirtualDom does not need to be generic.
|
||||
/// Type of the original props. This is stored as TypeId so VirtualDom does not need to be generic.
|
||||
///
|
||||
/// Whenver props need to be updated, an Error will be thrown if the new props do not
|
||||
/// match the props used to create the VirtualDom.
|
||||
|
||||
@@ -130,7 +130,9 @@ impl<'a> TextRenderer<'a> {
|
||||
|
||||
self.html_render(new_node, f, il + 1)?;
|
||||
}
|
||||
VNodeKind::Suspended => todo!(),
|
||||
VNodeKind::Suspended { .. } => {
|
||||
// we can't do anything with suspended nodes
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+14
-33
@@ -2,7 +2,7 @@ use std::{collections::HashMap, rc::Rc, sync::Arc};
|
||||
|
||||
use dioxus_core::{
|
||||
events::{EventTrigger, VirtualEvent},
|
||||
DomEdit, RealDomNode, ScopeIdx,
|
||||
DomEdit, RealDomNode, ScopeId,
|
||||
};
|
||||
use fxhash::FxHashMap;
|
||||
use slotmap::{DefaultKey, Key, KeyData};
|
||||
@@ -85,10 +85,10 @@ impl WebsysDom {
|
||||
DomEdit::CreateElementNs { tag, id, ns } => self.create_element(tag, Some(ns), id),
|
||||
DomEdit::CreatePlaceholder { id } => self.create_placeholder(id),
|
||||
DomEdit::NewEventListener {
|
||||
event,
|
||||
event_name: event,
|
||||
scope,
|
||||
node,
|
||||
idx,
|
||||
mounted_node_id: node,
|
||||
element_id: idx,
|
||||
} => self.new_event_listener(event, scope, idx, node),
|
||||
DomEdit::RemoveEventListener { event } => todo!(),
|
||||
DomEdit::SetText { text } => self.set_text(text),
|
||||
@@ -234,7 +234,7 @@ impl WebsysDom {
|
||||
fn new_event_listener(
|
||||
&mut self,
|
||||
event: &'static str,
|
||||
scope: ScopeIdx,
|
||||
scope: ScopeId,
|
||||
_element_id: usize,
|
||||
real_id: u64,
|
||||
) {
|
||||
@@ -335,7 +335,7 @@ impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
|
||||
fn request_available_node(&mut self) -> RealDomNode {
|
||||
let key = self.nodes.insert(None);
|
||||
log::debug!("making new key: {:#?}", key);
|
||||
RealDomNode(key)
|
||||
RealDomNode(key.data().as_ffi())
|
||||
}
|
||||
|
||||
fn raw_node_as_any(&self) -> &mut dyn std::any::Any {
|
||||
@@ -349,22 +349,24 @@ pub struct Stack {
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
#[inline]
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
Stack {
|
||||
list: Vec::with_capacity(cap),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push(&mut self, node: Node) {
|
||||
// debug!("stack-push: {:?}", node);
|
||||
self.list.push(node);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pop(&mut self) -> Node {
|
||||
let res = self.list.pop().unwrap();
|
||||
res
|
||||
self.list.pop().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.list.clear();
|
||||
}
|
||||
@@ -588,16 +590,11 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<EventTrigger> {
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
// for attr in {
|
||||
let attrs = target.attributes();
|
||||
for x in 0..attrs.length() {
|
||||
let attr = attrs.item(x).unwrap();
|
||||
log::debug!("attrs include: {:#?}", attr);
|
||||
}
|
||||
// }
|
||||
// for attr in target.attributes() {
|
||||
// log::debug!("attrs include: {:#?}", attr);
|
||||
// }
|
||||
|
||||
// The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
|
||||
let val: String = target
|
||||
@@ -613,34 +610,18 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<EventTrigger> {
|
||||
|
||||
let real_id = fields
|
||||
.next()
|
||||
.and_then(|raw_id| {
|
||||
raw_id
|
||||
.parse::<u64>()
|
||||
.ok()
|
||||
.map(|value| KeyData::from_ffi(value))
|
||||
.map(|f| RealDomNode(f.into()))
|
||||
})
|
||||
.and_then(|raw_id| raw_id.parse::<u64>().ok())
|
||||
.context("failed to parse real id")?;
|
||||
|
||||
// Call the trigger
|
||||
log::debug!("decoded scope_id: {}, node_id: {:#?}", gi_id, real_id);
|
||||
|
||||
let triggered_scope: ScopeIdx = KeyData::from_ffi(gi_id).into();
|
||||
let triggered_scope: ScopeId = KeyData::from_ffi(gi_id).into();
|
||||
log::debug!("Triggered scope is {:#?}", triggered_scope);
|
||||
Ok(EventTrigger::new(
|
||||
virtual_event_from_websys_event(event),
|
||||
triggered_scope,
|
||||
Some(real_id),
|
||||
Some(RealDomNode::from_u64(real_id)),
|
||||
dioxus_core::events::EventPriority::High,
|
||||
))
|
||||
}
|
||||
|
||||
struct ListenerMap {}
|
||||
impl ListenerMap {
|
||||
fn get(&self, event: &'static str) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct JsStringCache {}
|
||||
impl JsStringCache {}
|
||||
@@ -1,6 +1,6 @@
|
||||
//! webview dom
|
||||
|
||||
use dioxus_core::{DomEdit, RealDom, RealDomNode, ScopeIdx};
|
||||
use dioxus_core::{DomEdit, RealDom, RealDomNode, ScopeId};
|
||||
use DomEdit::*;
|
||||
|
||||
pub struct WebviewRegistry {}
|
||||
|
||||
Reference in new issue
Block a user