wip: more work on suspense and documentation

This commit is contained in:
Jonathan Kelley committed 2021-07-15 03:38:09 -04:00
1 parent 4837d8e741
commit 37ed4bed8c
14 files changed
+240 -192

No files matched your search

+5 -5
View File
@@ -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)
+54 -17
View File
@@ -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();
+5 -5
View File
@@ -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
View File
@@ -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,
+6 -9
View File
@@ -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!")
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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();
+4 -6
View File
@@ -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
View File
@@ -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
}
}
+10 -3
View File
@@ -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.
+3 -1
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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 {}