2021-02-12 21:11:33 +00:00
|
|
|
use crate::innerlude::*;
|
2021-02-07 22:38:17 +00:00
|
|
|
use crate::nodes::VNode;
|
2021-02-12 08:07:35 +00:00
|
|
|
use crate::{context::hooks::Hook, diff::diff};
|
2021-02-07 22:38:17 +00:00
|
|
|
use bumpalo::Bump;
|
2021-02-08 21:57:34 +00:00
|
|
|
use generational_arena::Index;
|
2021-02-07 22:38:17 +00:00
|
|
|
use std::{
|
2021-02-12 04:03:01 +00:00
|
|
|
any::TypeId, borrow::Borrow, cell::RefCell, future::Future, marker::PhantomData,
|
|
|
|
sync::atomic::AtomicUsize,
|
2021-02-07 22:38:17 +00:00
|
|
|
};
|
|
|
|
|
2021-02-08 00:14:04 +00:00
|
|
|
/// Every component in Dioxus is represented by a `Scope`.
|
|
|
|
///
|
|
|
|
/// Scopes contain the state for hooks, the component's props, and other lifecycle information.
|
|
|
|
///
|
|
|
|
/// 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.
|
2021-02-07 22:38:17 +00:00
|
|
|
pub struct Scope {
|
2021-02-12 08:07:35 +00:00
|
|
|
// TODO @Jon
|
2021-02-08 00:14:04 +00:00
|
|
|
// These hooks are actually references into the hook arena
|
|
|
|
// These two could be combined with "OwningRef" to remove unsafe usage
|
2021-02-12 08:07:35 +00:00
|
|
|
// could also use ourborous
|
2021-02-12 21:11:33 +00:00
|
|
|
pub hooks: RefCell<Vec<*mut Hook>>,
|
|
|
|
pub hook_arena: typed_arena::Arena<Hook>,
|
2021-02-08 00:14:04 +00:00
|
|
|
|
2021-02-08 21:57:34 +00:00
|
|
|
// Map to the parent
|
2021-02-12 21:11:33 +00:00
|
|
|
pub parent: Option<Index>,
|
2021-02-08 21:57:34 +00:00
|
|
|
|
2021-02-12 08:07:35 +00:00
|
|
|
// todo, do better with the active frame stuff
|
2021-02-12 21:11:33 +00:00
|
|
|
pub frames: [Bump; 2],
|
2021-02-12 08:07:35 +00:00
|
|
|
|
|
|
|
// somehow build this vnode with a lifetime tied to self
|
2021-02-12 21:11:33 +00:00
|
|
|
pub cur_node: *mut VNode<'static>,
|
2021-02-12 08:07:35 +00:00
|
|
|
|
2021-02-12 21:11:33 +00:00
|
|
|
pub active_frame: u8,
|
2021-02-12 08:07:35 +00:00
|
|
|
|
|
|
|
// IE Which listeners need to be woken up?
|
2021-02-12 21:11:33 +00:00
|
|
|
pub listeners: Vec<Box<dyn Fn()>>,
|
2021-02-12 08:07:35 +00:00
|
|
|
|
|
|
|
//
|
2021-02-12 21:11:33 +00:00
|
|
|
pub props_type: TypeId,
|
|
|
|
pub caller: *const i32,
|
2021-02-07 22:38:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Scope {
|
|
|
|
// create a new scope from a function
|
2021-02-12 08:07:35 +00:00
|
|
|
pub(crate) fn new<T: 'static>(f: FC<T>, parent: Option<Index>) -> Self {
|
2021-02-07 22:38:17 +00:00
|
|
|
// Capture the props type
|
|
|
|
let props_type = TypeId::of::<T>();
|
2021-02-08 21:57:34 +00:00
|
|
|
let hook_arena = typed_arena::Arena::new();
|
2021-02-07 22:38:17 +00:00
|
|
|
let hooks = RefCell::new(Vec::new());
|
|
|
|
|
|
|
|
let caller = f as *const i32;
|
|
|
|
|
2021-02-12 08:07:35 +00:00
|
|
|
let frames = [Bump::new(), Bump::new()];
|
|
|
|
|
|
|
|
let listeners = Vec::new();
|
|
|
|
|
|
|
|
let active_frame = 1;
|
|
|
|
|
|
|
|
let new = frames[0].alloc(VNode::Text(VText::new("")));
|
|
|
|
let cur_node = new as *mut _;
|
|
|
|
|
2021-02-07 22:38:17 +00:00
|
|
|
Self {
|
2021-02-08 21:57:34 +00:00
|
|
|
hook_arena,
|
2021-02-07 22:38:17 +00:00
|
|
|
hooks,
|
|
|
|
props_type,
|
|
|
|
caller,
|
2021-02-12 08:07:35 +00:00
|
|
|
active_frame,
|
|
|
|
listeners,
|
2021-02-08 21:57:34 +00:00
|
|
|
parent,
|
2021-02-12 08:07:35 +00:00
|
|
|
frames,
|
|
|
|
cur_node,
|
2021-02-07 22:38:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-12 08:07:35 +00:00
|
|
|
/// Create a new context and run the component with references from the Virtual Dom
|
|
|
|
/// This function downcasts the function pointer based on the stored props_type
|
|
|
|
///
|
2021-02-12 21:11:33 +00:00
|
|
|
/// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
|
2021-02-12 08:07:35 +00:00
|
|
|
pub(crate) fn run<'a, P: Properties + ?Sized>(&self, props: &'a P) {
|
|
|
|
let bump = &self.frames[0];
|
|
|
|
|
|
|
|
let ctx = Context {
|
2021-02-12 05:29:46 +00:00
|
|
|
scope: &*self,
|
2021-02-07 22:38:17 +00:00
|
|
|
_p: PhantomData {},
|
2021-02-08 00:14:04 +00:00
|
|
|
arena: &self.hook_arena,
|
2021-02-07 22:38:17 +00:00
|
|
|
hooks: &self.hooks,
|
|
|
|
idx: 0.into(),
|
2021-02-12 08:07:35 +00:00
|
|
|
bump,
|
|
|
|
};
|
2021-02-07 22:38:17 +00:00
|
|
|
|
2021-02-12 08:07:35 +00:00
|
|
|
/*
|
|
|
|
SAFETY ALERT
|
|
|
|
|
|
|
|
This particular usage of transmute is outlined in its docs https://doc.rust-lang.org/std/mem/fn.transmute.html
|
|
|
|
We hide the generic bound on the function item by casting it to raw pointer. When the function is actually called,
|
|
|
|
we transmute the function back using the props as reference.
|
2021-02-07 22:38:17 +00:00
|
|
|
|
2021-02-12 21:11:33 +00:00
|
|
|
we could do a better check to make sure that the TypeID is correct before casting
|
2021-02-12 08:07:35 +00:00
|
|
|
--
|
|
|
|
This is safe because we check that the generic type matches before casting.
|
|
|
|
*/
|
|
|
|
let caller = unsafe { std::mem::transmute::<*const i32, FC<P>>(self.caller) };
|
|
|
|
let new_nodes = caller(ctx, props);
|
|
|
|
let old_nodes: &mut VNode<'static> = unsafe { &mut *self.cur_node };
|
|
|
|
|
2021-02-12 21:11:33 +00:00
|
|
|
// TODO: Iterate through the new nodes
|
|
|
|
// move any listeners into ourself
|
|
|
|
|
|
|
|
// perform the diff, dumping into the mutable change list
|
|
|
|
// this doesnt perform any "diff compression" where an event and a re-render
|
2021-02-12 08:07:35 +00:00
|
|
|
crate::diff::diff(old_nodes, &new_nodes);
|
|
|
|
}
|
2021-02-07 22:38:17 +00:00
|
|
|
}
|