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
|
/// THIS METHOD IS CURRENTLY UNSAFE
|
||||||
/// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
|
/// 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 inner = unsafe { &*self.components.get() };
|
||||||
let scope = inner.get(idx);
|
let scope = inner.get(idx);
|
||||||
scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||||
@@ -35,7 +35,7 @@ impl SharedArena {
|
|||||||
|
|
||||||
/// THIS METHOD IS CURRENTLY UNSAFE
|
/// THIS METHOD IS CURRENTLY UNSAFE
|
||||||
/// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
|
/// 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 inner = unsafe { &mut *self.components.get() };
|
||||||
let scope = inner.get_mut(idx);
|
let scope = inner.get_mut(idx);
|
||||||
scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||||
@@ -59,7 +59,7 @@ impl SharedArena {
|
|||||||
|
|
||||||
pub fn with_scope<'b, O: 'static>(
|
pub fn with_scope<'b, O: 'static>(
|
||||||
&'b self,
|
&'b self,
|
||||||
_id: ScopeIdx,
|
_id: ScopeId,
|
||||||
_f: impl FnOnce(&'b mut Scope) -> O,
|
_f: impl FnOnce(&'b mut Scope) -> O,
|
||||||
) -> Result<O> {
|
) -> Result<O> {
|
||||||
todo!()
|
todo!()
|
||||||
@@ -69,13 +69,13 @@ impl SharedArena {
|
|||||||
// this is useful for merging lifetimes
|
// this is useful for merging lifetimes
|
||||||
pub fn with_scope_vnode<'b>(
|
pub fn with_scope_vnode<'b>(
|
||||||
&self,
|
&self,
|
||||||
_id: ScopeIdx,
|
_id: ScopeId,
|
||||||
_f: impl FnOnce(&mut Scope) -> &VNode<'b>,
|
_f: impl FnOnce(&mut Scope) -> &VNode<'b>,
|
||||||
) -> Result<&VNode<'b>> {
|
) -> Result<&VNode<'b>> {
|
||||||
todo!()
|
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() };
|
let inner = unsafe { &mut *self.components.get() };
|
||||||
inner
|
inner
|
||||||
.remove(id)
|
.remove(id)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
|
|
||||||
use bumpalo::Bump;
|
|
||||||
use futures_util::FutureExt;
|
use futures_util::FutureExt;
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
@@ -12,7 +11,6 @@ use std::{
|
|||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
future::Future,
|
future::Future,
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
pin::Pin,
|
|
||||||
rc::{Rc, Weak},
|
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 struct Context<'src, T> {
|
||||||
pub props: &'src T,
|
pub props: &'src T,
|
||||||
pub scope: &'src Scope,
|
pub scope: &'src Scope,
|
||||||
}
|
}
|
||||||
// pub type PinnedTask = Pin<Box<dyn Future<Output = ()>>>;
|
|
||||||
|
|
||||||
impl<'src, T> Copy for Context<'src, T> {}
|
impl<'src, T> Copy for Context<'src, T> {}
|
||||||
impl<'src, T> Clone 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> {
|
impl<'a, T> Deref for Context<'a, T> {
|
||||||
type Target = &'a T;
|
type Target = &'a T;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.props
|
&self.props
|
||||||
}
|
}
|
||||||
@@ -62,14 +68,42 @@ impl<'a, T> Deref for Context<'a, T> {
|
|||||||
|
|
||||||
impl<'src, P> Context<'src, P> {
|
impl<'src, P> Context<'src, P> {
|
||||||
/// Access the children elements passed into the component
|
/// 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>] {
|
pub fn children(&self) -> &'src [VNode<'src>] {
|
||||||
// We're re-casting the nodes back out
|
self.scope.child_nodes
|
||||||
// They don't really have a static lifetime
|
|
||||||
unsafe {
|
|
||||||
let scope = self.scope;
|
|
||||||
let nodes = scope.child_nodes;
|
|
||||||
nodes
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a subscription that schedules a future render for the reference component
|
/// 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>();
|
let ty = TypeId::of::<T>();
|
||||||
while let Some(inner) = scope {
|
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();
|
let shared_contexts = inner.shared_contexts.borrow();
|
||||||
|
|
||||||
if let Some(shared_cx) = shared_contexts.get(&ty) {
|
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);
|
hook.par = Some(rc);
|
||||||
return Ok(hook.par.as_ref().unwrap());
|
return Ok(hook.par.as_ref().unwrap());
|
||||||
} else {
|
} else {
|
||||||
match inner.parent {
|
match inner.parent_idx {
|
||||||
Some(parent_id) => {
|
Some(parent_id) => {
|
||||||
let parent = inner
|
let parent = inner
|
||||||
.arena_link
|
.arena_link
|
||||||
@@ -324,7 +361,7 @@ Any function prefixed with "use" should not be called conditionally.
|
|||||||
|
|
||||||
let slot = task_dump.clone();
|
let slot = task_dump.clone();
|
||||||
let update = self.schedule_update();
|
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 {
|
self.submit_task(Box::pin(task_fut.then(move |output| async move {
|
||||||
*slot.as_ref().borrow_mut() = Some(output);
|
*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 task_fut = task_initializer();
|
||||||
let domnode = dom_node_id.clone();
|
let domnode = dom_node_id.clone();
|
||||||
|
|
||||||
|
|||||||
@@ -75,10 +75,10 @@ pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> {
|
|||||||
pub edits: DomEditor<'real, 'bump>,
|
pub edits: DomEditor<'real, 'bump>,
|
||||||
pub components: &'bump SharedArena,
|
pub components: &'bump SharedArena,
|
||||||
pub task_queue: &'bump TaskQueue,
|
pub task_queue: &'bump TaskQueue,
|
||||||
pub cur_idx: ScopeIdx,
|
pub cur_idx: ScopeId,
|
||||||
pub diffed: FxHashSet<ScopeIdx>,
|
pub diffed: FxHashSet<ScopeId>,
|
||||||
pub event_queue: EventQueue,
|
pub event_queue: EventQueue,
|
||||||
pub seen_nodes: FxHashSet<ScopeIdx>,
|
pub seen_nodes: FxHashSet<ScopeId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'real, 'bump, Dom> DiffMachine<'real, 'bump, Dom>
|
impl<'real, 'bump, Dom> DiffMachine<'real, 'bump, Dom>
|
||||||
@@ -89,7 +89,7 @@ where
|
|||||||
edits: &'real mut Vec<DomEdit<'bump>>,
|
edits: &'real mut Vec<DomEdit<'bump>>,
|
||||||
dom: &'real mut Dom,
|
dom: &'real mut Dom,
|
||||||
components: &'bump SharedArena,
|
components: &'bump SharedArena,
|
||||||
cur_idx: ScopeIdx,
|
cur_idx: ScopeId,
|
||||||
event_queue: EventQueue,
|
event_queue: EventQueue,
|
||||||
task_queue: &'bump TaskQueue,
|
task_queue: &'bump TaskQueue,
|
||||||
) -> Self {
|
) -> 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.
|
/// 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.
|
/// 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 nodes_to_delete = vec![old_scope];
|
||||||
let mut scopes_to_explore = 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.
|
/// 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 struct DomEditor<'real, 'bump> {
|
||||||
pub edits: &'real mut Vec<DomEdit<'bump>>,
|
pub edits: &'real mut Vec<DomEdit<'bump>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
use DomEdit::*;
|
use DomEdit::*;
|
||||||
impl<'real, 'bump> DomEditor<'real, 'bump> {
|
impl<'real, 'bump> DomEditor<'real, 'bump> {
|
||||||
pub fn new(edits: &'real mut Vec<DomEdit<'bump>>) -> Self {
|
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();
|
let id = root.as_u64();
|
||||||
self.edits.push(PushRoot { id });
|
self.edits.push(PushRoot { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn pop(&mut self) {
|
pub(crate) fn pop(&mut self) {
|
||||||
self.edits.push(PopRoot {});
|
self.edits.push(PopRoot {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Nodes to the dom
|
// Add Nodes to the dom
|
||||||
// add m nodes from the stack
|
// add m nodes from the stack
|
||||||
|
#[inline]
|
||||||
pub(crate) fn append_children(&mut self, many: u32) {
|
pub(crate) fn append_children(&mut self, many: u32) {
|
||||||
self.edits.push(AppendChildren { many });
|
self.edits.push(AppendChildren { many });
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace the n-m node on the stack with the m nodes
|
// 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
|
// ends with the last element of the chain on the top of the stack
|
||||||
|
#[inline]
|
||||||
pub(crate) fn replace_with(&mut self, many: u32) {
|
pub(crate) fn replace_with(&mut self, many: u32) {
|
||||||
self.edits.push(ReplaceWith { many });
|
self.edits.push(ReplaceWith { many });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove Nodesfrom the dom
|
// Remove Nodesfrom the dom
|
||||||
|
#[inline]
|
||||||
pub(crate) fn remove(&mut self) {
|
pub(crate) fn remove(&mut self) {
|
||||||
self.edits.push(Remove);
|
self.edits.push(Remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn remove_all_children(&mut self) {
|
pub(crate) fn remove_all_children(&mut self) {
|
||||||
self.edits.push(RemoveAllChildren);
|
self.edits.push(RemoveAllChildren);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
|
#[inline]
|
||||||
pub(crate) fn create_text_node(&mut self, text: &'bump str, id: RealDomNode) {
|
pub(crate) fn create_text_node(&mut self, text: &'bump str, id: RealDomNode) {
|
||||||
let id = id.as_u64();
|
let id = id.as_u64();
|
||||||
self.edits.push(CreateTextNode { text, id });
|
self.edits.push(CreateTextNode { text, id });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn create_element(
|
pub(crate) fn create_element(
|
||||||
&mut self,
|
&mut self,
|
||||||
tag: &'static str,
|
tag: &'static str,
|
||||||
@@ -81,25 +89,30 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
|
|||||||
pub(crate) fn new_event_listener(
|
pub(crate) fn new_event_listener(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &'static str,
|
event: &'static str,
|
||||||
scope: ScopeIdx,
|
scope: ScopeId,
|
||||||
element_id: usize,
|
element_id: usize,
|
||||||
realnode: RealDomNode,
|
realnode: RealDomNode,
|
||||||
) {
|
) {
|
||||||
self.edits.push(NewEventListener {
|
self.edits.push(NewEventListener {
|
||||||
scope,
|
scope,
|
||||||
event,
|
event_name: event,
|
||||||
idx: element_id,
|
element_id,
|
||||||
node: realnode.as_u64(),
|
mounted_node_id: realnode.as_u64(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn remove_event_listener(&mut self, event: &'static str) {
|
pub(crate) fn remove_event_listener(&mut self, event: &'static str) {
|
||||||
self.edits.push(RemoveEventListener { event });
|
self.edits.push(RemoveEventListener { event });
|
||||||
}
|
}
|
||||||
|
|
||||||
// modify
|
// modify
|
||||||
|
#[inline]
|
||||||
pub(crate) fn set_text(&mut self, text: &'bump str) {
|
pub(crate) fn set_text(&mut self, text: &'bump str) {
|
||||||
self.edits.push(SetText { text });
|
self.edits.push(SetText { text });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn set_attribute(
|
pub(crate) fn set_attribute(
|
||||||
&mut self,
|
&mut self,
|
||||||
field: &'static str,
|
field: &'static str,
|
||||||
@@ -108,6 +121,8 @@ impl<'real, 'bump> DomEditor<'real, 'bump> {
|
|||||||
) {
|
) {
|
||||||
self.edits.push(SetAttribute { field, value, ns });
|
self.edits.push(SetAttribute { field, value, ns });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn remove_attribute(&mut self, name: &'static str) {
|
pub(crate) fn remove_attribute(&mut self, name: &'static str) {
|
||||||
self.edits.push(RemoveAttribute { name });
|
self.edits.push(RemoveAttribute { name });
|
||||||
}
|
}
|
||||||
@@ -151,10 +166,10 @@ pub enum DomEdit<'bump> {
|
|||||||
id: u64,
|
id: u64,
|
||||||
},
|
},
|
||||||
NewEventListener {
|
NewEventListener {
|
||||||
event: &'static str,
|
event_name: &'static str,
|
||||||
scope: ScopeIdx,
|
scope: ScopeId,
|
||||||
node: u64,
|
mounted_node_id: u64,
|
||||||
idx: usize,
|
element_id: usize,
|
||||||
},
|
},
|
||||||
RemoveEventListener {
|
RemoveEventListener {
|
||||||
event: &'static str,
|
event: &'static str,
|
||||||
|
|||||||
@@ -4,17 +4,14 @@
|
|||||||
//! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
|
//! 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.
|
//! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
|
||||||
|
|
||||||
use std::{
|
use std::{cell::Cell, rc::Rc};
|
||||||
cell::{Cell, RefCell},
|
|
||||||
rc::Rc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::innerlude::{RealDomNode, ScopeIdx};
|
use crate::innerlude::{RealDomNode, ScopeId};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EventTrigger {
|
pub struct EventTrigger {
|
||||||
/// The originator of the event trigger
|
/// The originator of the event trigger
|
||||||
pub originator: ScopeIdx,
|
pub originator: ScopeId,
|
||||||
|
|
||||||
/// The optional real node associated with the trigger
|
/// The optional real node associated with the trigger
|
||||||
pub real_node_id: Option<RealDomNode>,
|
pub real_node_id: Option<RealDomNode>,
|
||||||
@@ -27,7 +24,7 @@ pub struct EventTrigger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
Self {
|
||||||
originator,
|
originator,
|
||||||
event: VirtualEvent::AsyncEvent { hook_idx },
|
event: VirtualEvent::AsyncEvent { hook_idx },
|
||||||
@@ -74,7 +71,7 @@ pub enum EventPriority {
|
|||||||
impl EventTrigger {
|
impl EventTrigger {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
event: VirtualEvent,
|
event: VirtualEvent,
|
||||||
scope: ScopeIdx,
|
scope: ScopeId,
|
||||||
mounted_dom_id: Option<RealDomNode>,
|
mounted_dom_id: Option<RealDomNode>,
|
||||||
priority: EventPriority,
|
priority: EventPriority,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -184,7 +181,7 @@ pub mod on {
|
|||||||
Listener {
|
Listener {
|
||||||
event: stringify!($name),
|
event: stringify!($name),
|
||||||
mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
|
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 {
|
callback: bump.alloc(move |evt: VirtualEvent| match evt {
|
||||||
VirtualEvent::$wrapper(event) => callback(event),
|
VirtualEvent::$wrapper(event) => callback(event),
|
||||||
_ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!")
|
_ => 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
|
//! Dioxus Core
|
||||||
//! ----------
|
//! ----------
|
||||||
//!
|
//!
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
format_args_f, html, rsx, DioxusElement, DomEdit, EventTrigger, LazyNodes, NodeFactory,
|
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 {
|
pub mod prelude {
|
||||||
|
|||||||
+64
-42
@@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::VirtualEvent,
|
events::VirtualEvent,
|
||||||
innerlude::{Context, Properties, RealDomNode, Scope, ScopeIdx, FC},
|
innerlude::{Context, Properties, RealDomNode, Scope, ScopeId, FC},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Cell, RefCell},
|
cell::Cell,
|
||||||
fmt::{Arguments, Debug, Formatter},
|
fmt::{Arguments, Debug, Formatter},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
@@ -87,7 +87,7 @@ pub struct Attribute<'a> {
|
|||||||
pub struct Listener<'bump> {
|
pub struct Listener<'bump> {
|
||||||
/// The type of event to listen for.
|
/// The type of event to listen for.
|
||||||
pub(crate) event: &'static str,
|
pub(crate) event: &'static str,
|
||||||
pub scope: ScopeIdx,
|
pub scope: ScopeId,
|
||||||
pub mounted_node: &'bump Cell<RealDomNode>,
|
pub mounted_node: &'bump Cell<RealDomNode>,
|
||||||
pub(crate) callback: &'bump dyn FnMut(VirtualEvent),
|
pub(crate) callback: &'bump dyn FnMut(VirtualEvent),
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ pub struct Listener<'bump> {
|
|||||||
/// Virtual Components for custom user-defined components
|
/// Virtual Components for custom user-defined components
|
||||||
/// Only supports the functional syntax
|
/// Only supports the functional syntax
|
||||||
pub struct VComponent<'src> {
|
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) caller: Rc<dyn Fn(&Scope) -> VNode>,
|
||||||
pub(crate) children: &'src [VNode<'src>],
|
pub(crate) children: &'src [VNode<'src>],
|
||||||
pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
|
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> {
|
impl<'a> IntoIterator for VNode<'a> {
|
||||||
type Item = VNode<'a>;
|
type Item = VNode<'a>;
|
||||||
type IntoIter = std::iter::Once<Self::Item>;
|
type IntoIter = std::iter::Once<Self::Item>;
|
||||||
@@ -335,25 +355,53 @@ impl<'a> IntoIterator for VNode<'a> {
|
|||||||
std::iter::once(self)
|
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> {
|
impl<'a> IntoVNode<'a> for VNode<'a> {
|
||||||
fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
|
fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
|
||||||
self
|
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> {
|
impl<'a> IntoVNode<'a> for &VNode<'a> {
|
||||||
fn into_vnode(self, _: NodeFactory<'a>) -> 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> {
|
/// A concrete type provider for closures that build VNode structures.
|
||||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
|
///
|
||||||
}
|
/// 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.
|
||||||
// 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.
|
/// ```rust
|
||||||
|
/// LazyNodes::new(|f| f.element("div", [], [], [] None))
|
||||||
|
/// ```
|
||||||
pub struct LazyNodes<'a, G>
|
pub struct LazyNodes<'a, G>
|
||||||
where
|
where
|
||||||
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
||||||
@@ -374,11 +422,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cover the cases where nodes are used by macro.
|
// Our blanket impl
|
||||||
// Likely used directly.
|
|
||||||
// ---
|
|
||||||
// let nodes = rsx!{ ... };
|
|
||||||
// rsx! { {nodes } }
|
|
||||||
impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
|
impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
|
||||||
where
|
where
|
||||||
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
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>
|
impl<'a, G> IntoIterator for LazyNodes<'a, G>
|
||||||
where
|
where
|
||||||
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
||||||
@@ -400,12 +444,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Conveniently, we also support "null" (nothing) passed in
|
||||||
impl IntoVNode<'_> for () {
|
impl IntoVNode<'_> for () {
|
||||||
fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||||
cx.fragment_from_iter(None as Option<VNode>)
|
cx.fragment_from_iter(None as Option<VNode>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Conveniently, we also support "None"
|
||||||
impl IntoVNode<'_> for Option<()> {
|
impl IntoVNode<'_> for Option<()> {
|
||||||
fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||||
cx.fragment_from_iter(None as Option<VNode>)
|
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<'_> {
|
impl Debug for VNode<'_> {
|
||||||
fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
match &self.kind {
|
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.
|
/// 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.
|
/// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
// The parent's scope ID
|
// Book-keeping about the arena
|
||||||
pub parent: Option<ScopeIdx>,
|
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
|
// Nodes
|
||||||
// This enables us to drop the children and their children when this scope is destroyed
|
// an internal, highly efficient storage of vnodes
|
||||||
pub(crate) descendents: RefCell<HashSet<ScopeIdx>>,
|
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.
|
// A reference to the list of components.
|
||||||
// This lets us traverse the component list whenever we need to access our parent or children.
|
// This lets us traverse the component list whenever we need to access our parent or children.
|
||||||
pub arena_link: SharedArena,
|
pub(crate) 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 type FiberTask = Pin<Box<dyn Future<Output = EventTrigger>>>;
|
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
|
// Therefore, their lifetimes are connected exclusively to the virtual dom
|
||||||
pub fn new<'creator_node>(
|
pub fn new<'creator_node>(
|
||||||
caller: Rc<WrappedCaller>,
|
caller: Rc<WrappedCaller>,
|
||||||
arena_idx: ScopeIdx,
|
arena_idx: ScopeId,
|
||||||
parent: Option<ScopeIdx>,
|
parent: Option<ScopeId>,
|
||||||
height: u32,
|
height: u32,
|
||||||
event_channel: EventChannel,
|
event_channel: EventChannel,
|
||||||
arena_link: SharedArena,
|
arena_link: SharedArena,
|
||||||
@@ -102,8 +85,8 @@ impl Scope {
|
|||||||
Self {
|
Self {
|
||||||
child_nodes,
|
child_nodes,
|
||||||
caller,
|
caller,
|
||||||
parent,
|
parent_idx: parent,
|
||||||
arena_idx,
|
our_arena_idx: arena_idx,
|
||||||
height,
|
height,
|
||||||
event_channel,
|
event_channel,
|
||||||
arena_link,
|
arena_link,
|
||||||
@@ -190,7 +173,7 @@ impl Scope {
|
|||||||
log::debug!(
|
log::debug!(
|
||||||
"There are {:?} listeners associated with this scope {:#?}",
|
"There are {:?} listeners associated with this scope {:#?}",
|
||||||
self.listeners.borrow().len(),
|
self.listeners.borrow().len(),
|
||||||
self.arena_idx
|
self.our_arena_idx
|
||||||
);
|
);
|
||||||
|
|
||||||
let listners = self.listeners.borrow_mut();
|
let listners = self.listeners.borrow_mut();
|
||||||
|
|||||||
@@ -11,15 +11,13 @@
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
pin::Pin,
|
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use futures_util::{stream::FuturesUnordered, Future, Stream, StreamExt};
|
use futures_util::{stream::FuturesUnordered, Future, Stream, StreamExt};
|
||||||
use slotmap::{DefaultKey, SlotMap};
|
use slotmap::{DefaultKey, SlotMap};
|
||||||
|
|
||||||
use crate::innerlude::{EventTrigger, FiberTask, ScopeIdx};
|
use crate::innerlude::{EventTrigger, FiberTask, ScopeId};
|
||||||
|
|
||||||
pub type TaskSubmitter = Arc<dyn Fn(FiberTask)>;
|
pub type TaskSubmitter = Arc<dyn Fn(FiberTask)>;
|
||||||
|
|
||||||
@@ -117,11 +115,11 @@ pub struct TaskHandle {
|
|||||||
|
|
||||||
pub struct DTask {
|
pub struct DTask {
|
||||||
fut: FiberTask,
|
fut: FiberTask,
|
||||||
originator: ScopeIdx,
|
originator: ScopeId,
|
||||||
dead: Cell<bool>,
|
dead: Cell<bool>,
|
||||||
}
|
}
|
||||||
impl DTask {
|
impl DTask {
|
||||||
pub fn new(fut: FiberTask, originator: ScopeIdx) -> Self {
|
pub fn new(fut: FiberTask, originator: ScopeId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
fut,
|
fut,
|
||||||
originator,
|
originator,
|
||||||
@@ -129,7 +127,7 @@ impl DTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn debug_new(fut: FiberTask) -> Self {
|
pub fn debug_new(fut: FiberTask) -> Self {
|
||||||
let originator = ScopeIdx::default();
|
let originator = ScopeId::default();
|
||||||
Self {
|
Self {
|
||||||
fut,
|
fut,
|
||||||
originator,
|
originator,
|
||||||
|
|||||||
+16
-10
@@ -14,7 +14,7 @@ pub struct EventQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 inner = self.clone();
|
||||||
let marker = HeightMarker { height, idx };
|
let marker = HeightMarker { height, idx };
|
||||||
Rc::new(move || {
|
Rc::new(move || {
|
||||||
@@ -35,7 +35,7 @@ impl EventQueue {
|
|||||||
/// A helper type that lets scopes be ordered by their height
|
/// A helper type that lets scopes be ordered by their height
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct HeightMarker {
|
pub struct HeightMarker {
|
||||||
pub idx: ScopeIdx,
|
pub idx: ScopeId,
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,23 +59,29 @@ impl PartialOrd for HeightMarker {
|
|||||||
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct RealDomNode(pub DefaultKey);
|
pub struct RealDomNode(pub u64);
|
||||||
impl RealDomNode {
|
impl RealDomNode {
|
||||||
|
#[inline]
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
let data = KeyData::from_ffi(u64::MIN);
|
// let data = KeyData::from_ffi(u64::MIN);
|
||||||
let key: DefaultKey = data.into();
|
// let key: DefaultKey = data.into();
|
||||||
Self(key)
|
Self(u64::MIN)
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
pub fn empty_cell() -> Cell<Self> {
|
pub fn empty_cell() -> Cell<Self> {
|
||||||
Cell::new(Self::empty())
|
Cell::new(Self::empty())
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
pub fn from_u64(id: u64) -> Self {
|
pub fn from_u64(id: u64) -> Self {
|
||||||
let data = KeyData::from_ffi(id);
|
// let data = KeyData::from_ffi(id);
|
||||||
let key: DefaultKey = data.into();
|
// let key: DefaultKey = data.into();
|
||||||
Self(key)
|
Self(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn as_u64(&self) -> u64 {
|
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::cell::RefCell;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
pub type ScopeIdx = DefaultKey;
|
pub type ScopeId = DefaultKey;
|
||||||
|
|
||||||
/// An integrated virtual node system that progresses events and diffs UI trees.
|
/// 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.
|
/// Differences are converted into patches which a renderer can use to draw the UI.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
pub struct VirtualDom {
|
pub struct VirtualDom {
|
||||||
/// All mounted components are arena allocated to make additions, removals, and references easy to work with
|
/// 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.
|
/// 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
|
/// The index of the root component
|
||||||
/// Should always be the first (gen=0, id=0)
|
/// Should always be the first (gen=0, id=0)
|
||||||
pub base_scope: ScopeIdx,
|
pub base_scope: ScopeId,
|
||||||
|
|
||||||
pub triggers: RefCell<Vec<EventTrigger>>,
|
pub triggers: RefCell<Vec<EventTrigger>>,
|
||||||
|
|
||||||
@@ -55,7 +62,7 @@ pub struct VirtualDom {
|
|||||||
|
|
||||||
root_props: std::pin::Pin<Box<dyn std::any::Any>>,
|
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
|
/// 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.
|
/// match the props used to create the VirtualDom.
|
||||||
|
|||||||
@@ -130,7 +130,9 @@ impl<'a> TextRenderer<'a> {
|
|||||||
|
|
||||||
self.html_render(new_node, f, il + 1)?;
|
self.html_render(new_node, f, il + 1)?;
|
||||||
}
|
}
|
||||||
VNodeKind::Suspended => todo!(),
|
VNodeKind::Suspended { .. } => {
|
||||||
|
// we can't do anything with suspended nodes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-33
@@ -2,7 +2,7 @@ use std::{collections::HashMap, rc::Rc, sync::Arc};
|
|||||||
|
|
||||||
use dioxus_core::{
|
use dioxus_core::{
|
||||||
events::{EventTrigger, VirtualEvent},
|
events::{EventTrigger, VirtualEvent},
|
||||||
DomEdit, RealDomNode, ScopeIdx,
|
DomEdit, RealDomNode, ScopeId,
|
||||||
};
|
};
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
use slotmap::{DefaultKey, Key, KeyData};
|
use slotmap::{DefaultKey, Key, KeyData};
|
||||||
@@ -85,10 +85,10 @@ impl WebsysDom {
|
|||||||
DomEdit::CreateElementNs { tag, id, ns } => self.create_element(tag, Some(ns), id),
|
DomEdit::CreateElementNs { tag, id, ns } => self.create_element(tag, Some(ns), id),
|
||||||
DomEdit::CreatePlaceholder { id } => self.create_placeholder(id),
|
DomEdit::CreatePlaceholder { id } => self.create_placeholder(id),
|
||||||
DomEdit::NewEventListener {
|
DomEdit::NewEventListener {
|
||||||
event,
|
event_name: event,
|
||||||
scope,
|
scope,
|
||||||
node,
|
mounted_node_id: node,
|
||||||
idx,
|
element_id: idx,
|
||||||
} => self.new_event_listener(event, scope, idx, node),
|
} => self.new_event_listener(event, scope, idx, node),
|
||||||
DomEdit::RemoveEventListener { event } => todo!(),
|
DomEdit::RemoveEventListener { event } => todo!(),
|
||||||
DomEdit::SetText { text } => self.set_text(text),
|
DomEdit::SetText { text } => self.set_text(text),
|
||||||
@@ -234,7 +234,7 @@ impl WebsysDom {
|
|||||||
fn new_event_listener(
|
fn new_event_listener(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &'static str,
|
event: &'static str,
|
||||||
scope: ScopeIdx,
|
scope: ScopeId,
|
||||||
_element_id: usize,
|
_element_id: usize,
|
||||||
real_id: u64,
|
real_id: u64,
|
||||||
) {
|
) {
|
||||||
@@ -335,7 +335,7 @@ impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
|
|||||||
fn request_available_node(&mut self) -> RealDomNode {
|
fn request_available_node(&mut self) -> RealDomNode {
|
||||||
let key = self.nodes.insert(None);
|
let key = self.nodes.insert(None);
|
||||||
log::debug!("making new key: {:#?}", key);
|
log::debug!("making new key: {:#?}", key);
|
||||||
RealDomNode(key)
|
RealDomNode(key.data().as_ffi())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn raw_node_as_any(&self) -> &mut dyn std::any::Any {
|
fn raw_node_as_any(&self) -> &mut dyn std::any::Any {
|
||||||
@@ -349,22 +349,24 @@ pub struct Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Stack {
|
impl Stack {
|
||||||
|
#[inline]
|
||||||
pub fn with_capacity(cap: usize) -> Self {
|
pub fn with_capacity(cap: usize) -> Self {
|
||||||
Stack {
|
Stack {
|
||||||
list: Vec::with_capacity(cap),
|
list: Vec::with_capacity(cap),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn push(&mut self, node: Node) {
|
pub fn push(&mut self, node: Node) {
|
||||||
// debug!("stack-push: {:?}", node);
|
|
||||||
self.list.push(node);
|
self.list.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn pop(&mut self) -> Node {
|
pub fn pop(&mut self) -> Node {
|
||||||
let res = self.list.pop().unwrap();
|
self.list.pop().unwrap()
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.list.clear();
|
self.list.clear();
|
||||||
}
|
}
|
||||||
@@ -588,16 +590,11 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<EventTrigger> {
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
|
||||||
// for attr in {
|
|
||||||
let attrs = target.attributes();
|
let attrs = target.attributes();
|
||||||
for x in 0..attrs.length() {
|
for x in 0..attrs.length() {
|
||||||
let attr = attrs.item(x).unwrap();
|
let attr = attrs.item(x).unwrap();
|
||||||
log::debug!("attrs include: {:#?}", attr);
|
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
|
// The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
|
||||||
let val: String = target
|
let val: String = target
|
||||||
@@ -613,34 +610,18 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<EventTrigger> {
|
|||||||
|
|
||||||
let real_id = fields
|
let real_id = fields
|
||||||
.next()
|
.next()
|
||||||
.and_then(|raw_id| {
|
.and_then(|raw_id| raw_id.parse::<u64>().ok())
|
||||||
raw_id
|
|
||||||
.parse::<u64>()
|
|
||||||
.ok()
|
|
||||||
.map(|value| KeyData::from_ffi(value))
|
|
||||||
.map(|f| RealDomNode(f.into()))
|
|
||||||
})
|
|
||||||
.context("failed to parse real id")?;
|
.context("failed to parse real id")?;
|
||||||
|
|
||||||
// Call the trigger
|
// Call the trigger
|
||||||
log::debug!("decoded scope_id: {}, node_id: {:#?}", gi_id, real_id);
|
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);
|
log::debug!("Triggered scope is {:#?}", triggered_scope);
|
||||||
Ok(EventTrigger::new(
|
Ok(EventTrigger::new(
|
||||||
virtual_event_from_websys_event(event),
|
virtual_event_from_websys_event(event),
|
||||||
triggered_scope,
|
triggered_scope,
|
||||||
Some(real_id),
|
Some(RealDomNode::from_u64(real_id)),
|
||||||
dioxus_core::events::EventPriority::High,
|
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
|
//! webview dom
|
||||||
|
|
||||||
use dioxus_core::{DomEdit, RealDom, RealDomNode, ScopeIdx};
|
use dioxus_core::{DomEdit, RealDom, RealDomNode, ScopeId};
|
||||||
use DomEdit::*;
|
use DomEdit::*;
|
||||||
|
|
||||||
pub struct WebviewRegistry {}
|
pub struct WebviewRegistry {}
|
||||||
|
|||||||
Reference in new issue
Block a user