use crate::context::hooks::Hook;
use crate::innerlude::*;
use crate::nodes::VNode;
use bumpalo::Bump;
use generational_arena::Index;
use std::{
any::TypeId,
borrow::{Borrow, BorrowMut},
cell::{RefCell, UnsafeCell},
future::Future,
marker::PhantomData,
ops::{Deref, DerefMut},
sync::atomic::AtomicUsize,
sync::atomic::Ordering,
todo,
};
pub struct Scope {
pub hooks: RefCell<Vec<*mut Hook>>,
pub hook_arena: typed_arena::Arena<Hook>,
pub parent: Option<Index>,
pub frames: ActiveFrame,
pub listeners: Vec<*const dyn Fn(crate::events::VirtualEvent)>,
pub props: Box<dyn std::any::Any>,
pub caller: *const (),
}
impl Scope {
pub fn new<'a, P1, P2: 'static>(f: FC<P1>, props: P1, parent: Option<Index>) -> Self {
let hook_arena = typed_arena::Arena::new();
let hooks = RefCell::new(Vec::new());
let caller = f as *const ();
let listeners = vec![];
let old_frame = BumpFrame {
bump: Bump::new(),
head_node: VNode::text(""),
};
let new_frame = BumpFrame {
bump: Bump::new(),
head_node: VNode::text(""),
};
let frames = ActiveFrame::from_frames(old_frame, new_frame);
let props = Box::new(props);
let props = unsafe { std::mem::transmute::<_, Box<P2>>(props) };
Self {
hook_arena,
hooks,
caller,
frames,
listeners,
parent,
props,
}
}
pub fn run<'bump, PLocked: Sized + 'static>(&'bump mut self) {
let frame = {
let frame = self.frames.next();
frame.bump.reset();
log::debug!("Rednering into frame {:?}", frame as *const _);
frame
};
let node_slot = std::rc::Rc::new(RefCell::new(None));
let ctx: Context<'bump> = Context {
arena: &self.hook_arena,
hooks: &self.hooks,
bump: &frame.bump,
idx: 0.into(),
_p: PhantomData {},
final_nodes: node_slot.clone(),
};
unsafe {
let caller = std::mem::transmute::<*const (), FC<PLocked>>(self.caller);
let props = self.props.downcast_ref::<PLocked>().unwrap();
let _nodes: DomTree = caller(ctx, props);
frame.head_node = node_slot
.deref()
.borrow_mut()
.take()
.expect("Viewing did not happen");
let mut listeners = vec![];
retrieve_listeners(&frame.head_node, &mut listeners);
self.listeners = listeners
.into_iter()
.map(|f| {
let g = f.callback;
g as *const _
})
.collect();
}
}
pub fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
self.frames.current_head_node()
}
pub fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
self.frames.prev_head_node()
}
}
fn retrieve_listeners(node: &VNode<'static>, listeners: &mut Vec<&Listener>) {
if let VNode::Element(el) = *node {
for listener in el.listeners {
listeners.push(listener);
}
for child in el.children {
retrieve_listeners(child, listeners);
}
}
}
pub struct ActiveFrame {
pub idx: AtomicUsize,
pub frames: [BumpFrame; 2],
}
pub struct BumpFrame {
pub bump: Bump,
pub head_node: VNode<'static>,
}
impl ActiveFrame {
fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
Self {
idx: 0.into(),
frames: [a, b],
}
}
fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 == 0 {
true => &self.frames[0],
false => &self.frames[1],
};
unsafe {
let unsafe_head = &raw_node.head_node;
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
safe_node
}
}
fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 != 0 {
true => &self.frames[0],
false => &self.frames[1],
};
unsafe {
let unsafe_head = &raw_node.head_node;
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
safe_node
}
}
fn next(&mut self) -> &mut BumpFrame {
self.idx.fetch_add(1, Ordering::Relaxed);
let cur = self.idx.borrow().load(Ordering::Relaxed);
log::debug!("Next frame! {}", cur);
if cur % 2 == 0 {
log::debug!("Chosing frame 0");
&mut self.frames[0]
} else {
log::debug!("Chosing frame 1");
&mut self.frames[1]
}
}
}
mod tests {
use super::*;
use crate::prelude::bumpalo;
use crate::prelude::format_args_f;
static ListenerTest: FC<()> = |ctx, props| {
ctx.view(html! {
<div onclick={|_| println!("Hell owlrld")}>
"hello"
</div>
})
};
#[test]
fn check_listeners() -> Result<()> {
let mut scope = Scope::new::<(), ()>(ListenerTest, (), None);
scope.run::<()>();
let nodes = scope.new_frame();
dbg!(nodes);
Ok(())
}
#[test]
fn test_scope() {
let example: FC<()> = |ctx, props| {
use crate::builder::*;
ctx.view(|b| div(b).child(text("a")).finish())
};
let props = ();
let parent = None;
let scope = Scope::new::<(), ()>(example, props, parent);
}
#[derive(Debug)]
struct ExampleProps<'src> {
name: &'src String,
}
#[derive(Debug)]
struct EmptyProps<'src> {
name: &'src String,
}
use crate::{builder::*, hooks::use_ref};
fn example_fc<'a>(ctx: Context<'a>, props: &'a EmptyProps) -> DomTree {
let (content, _): (&'a String, _) = crate::hooks::use_state(&ctx, || "abcd".to_string());
let childprops: ExampleProps<'a> = ExampleProps { name: content };
ctx.view(move |b: &'a Bump| {
div(b)
.child(text(props.name))
.child(virtual_child::<ExampleProps>(b, childprops, child_example))
.finish()
})
}
fn child_example<'b>(ctx: Context<'b>, props: &'b ExampleProps) -> DomTree {
ctx.view(move |b| {
div(b)
.child(text(props.name))
.finish()
})
}
static CHILD: FC<ExampleProps> = |ctx, props: &'_ ExampleProps| {
ctx.view(move |b| {
div(b)
.child(text(props.name))
.finish()
})
};
#[test]
fn test_borrowed_scope() {
let example: FC<EmptyProps> = |ctx, props| {
ctx.view(move |b| {
todo!()
})
};
let source_text = "abcd123".to_string();
let props = ExampleProps { name: &source_text };
}
}