Feat: major overhaul, wire up event system
This commit is contained in:
1 parent
f47651b32a
commit
8295ac4b3d
20 files changed
+1418
-188
No files matched your search
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"rust-analyzer.inlayHints.enable": false
|
||||
"rust-analyzer.inlayHints.enable": true
|
||||
}
|
||||
@@ -245,3 +245,15 @@ A few notes:
|
||||
## Concurrency
|
||||
|
||||
I don't even know yet
|
||||
|
||||
|
||||
## Execution Model
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# The Server-Side-Rendering Guide
|
||||
|
||||
This guide will help you build your first app that leverages server-side-rendering to display the user interface.
|
||||
@@ -0,0 +1,3 @@
|
||||
# The WASM Guide
|
||||
|
||||
This guide will help you build your first app that leverages WASM in the browser to display the user interface.
|
||||
@@ -0,0 +1,3 @@
|
||||
# The Desktop Guide
|
||||
|
||||
This guide will help you build your first app that leverages webview for desktop to display the user interface.
|
||||
+1
-21
@@ -55,28 +55,11 @@ impl<T: Properties> Comp for FC<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// impl<T: Properties, F: Fn(&Context<T>) -> VNode> Comp for F {
|
||||
// type Props = T;
|
||||
|
||||
// fn render(&self, ctx: &mut Context<T>) -> VNode {
|
||||
// let g = self(ctx);
|
||||
// g
|
||||
// }
|
||||
|
||||
// fn builder(&self) -> T {
|
||||
// T::new()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Properties for () {
|
||||
fn new() -> Self {
|
||||
()
|
||||
}
|
||||
}
|
||||
#[allow(unused, non_upper_case_globals)]
|
||||
static MyComp: FC<()> = |ctx| {
|
||||
html! {
|
||||
<div>
|
||||
<p> "hello world" </p>
|
||||
</div>
|
||||
}
|
||||
};
|
||||
@@ -89,7 +72,4 @@ fn test() {
|
||||
let mut ctx = Context { props: &() };
|
||||
let f = MyComp.render(&mut ctx);
|
||||
let props = MyComp.builder();
|
||||
|
||||
// let f = my_comp.render(&mut ctx);
|
||||
// let props = my_comp.builder();
|
||||
}
|
||||
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"rust-analyzer.inlayHints.enable": false
|
||||
"rust-analyzer.inlayHints.enable": true
|
||||
}
|
||||
@@ -28,3 +28,4 @@ owning_ref = "0.4.1"
|
||||
typed-arena = "2.0.1"
|
||||
toolshed = "0.8.1"
|
||||
id-arena = "2.2.1"
|
||||
thiserror = "1.0.23"
|
||||
@@ -12,12 +12,14 @@ static Header: FC<()> = |ctx, props| {
|
||||
|
||||
let handler1 = move || println!("Value is {}", inner.current());
|
||||
|
||||
ctx.view(html! {
|
||||
<div>
|
||||
<h1> "This is the header bar" </h1>
|
||||
<h1> "Idnt it awesome" </h1>
|
||||
<button onclick={move |_| handler1()}> "Click me" </button>
|
||||
</div>
|
||||
ctx.view(|bump| {
|
||||
builder::div(bump)
|
||||
.child(VNode::Component(VComponent::new(
|
||||
Bottom,
|
||||
//
|
||||
(),
|
||||
)))
|
||||
.finish()
|
||||
})
|
||||
};
|
||||
|
||||
@@ -29,12 +31,3 @@ static Bottom: FC<()> = |ctx, props| {
|
||||
</div>
|
||||
})
|
||||
};
|
||||
|
||||
static Example: FC<()> = |ctx, props| {
|
||||
ctx.view(html! {
|
||||
<div>
|
||||
<h1> "BROSKI!" </h1>
|
||||
<h1> "DRO!" </h1>
|
||||
</div>
|
||||
})
|
||||
};
|
||||
@@ -11,7 +11,7 @@ fn main() -> Result<(), ()> {
|
||||
let p1 = Props { name: "bob".into() };
|
||||
|
||||
let mut vdom = VirtualDom::new_with_props(Example, p1);
|
||||
vdom.progress()?;
|
||||
// vdom.progress()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ impl<P: Properties> Component for FC<P> {
|
||||
|
||||
/// The `Properties` trait defines any struct that can be constructed using a combination of default / optional fields.
|
||||
/// Components take a "properties" object
|
||||
pub trait Properties {
|
||||
pub trait Properties // where
|
||||
// Self: Sized,
|
||||
{
|
||||
fn call(&self, ptr: *const ()) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ use crate::{inner::Scope, nodes::VNode};
|
||||
use bumpalo::Bump;
|
||||
use hooks::Hook;
|
||||
use std::{
|
||||
any::TypeId, cell::RefCell, future::Future, marker::PhantomData, sync::atomic::AtomicUsize,
|
||||
any::TypeId, borrow::Borrow, cell::RefCell, future::Future, marker::PhantomData,
|
||||
sync::atomic::AtomicUsize,
|
||||
};
|
||||
|
||||
/// Components in Dioxus use the "Context" object to interact with their lifecycle.
|
||||
@@ -27,15 +28,17 @@ use std::{
|
||||
// todo: force lifetime of source into T as a valid lifetime too
|
||||
// it's definitely possible, just needs some more messing around
|
||||
pub struct Context<'src> {
|
||||
pub idx: AtomicUsize,
|
||||
|
||||
pub(crate) scope: &'src Scope,
|
||||
/// Direct access to the properties used to create this component.
|
||||
// pub props: &'src PropType,
|
||||
pub idx: AtomicUsize,
|
||||
|
||||
// Borrowed from scope
|
||||
pub(crate) arena: &'src typed_arena::Arena<Hook>,
|
||||
pub(crate) hooks: &'src RefCell<Vec<*mut Hook>>,
|
||||
pub(crate) components: &'src generational_arena::Arena<Scope>,
|
||||
pub(crate) bump: &'src Bump,
|
||||
// pub(crate) components: &'src generational_arena::Arena<Scope>,
|
||||
|
||||
// holder for the src lifetime
|
||||
// todo @jon remove this
|
||||
@@ -49,11 +52,6 @@ impl<'a> Context<'a> {
|
||||
todo!("Children API not yet implemented for component Context")
|
||||
}
|
||||
|
||||
/// Access a parent context
|
||||
pub fn parent_context<C>(&self) -> C {
|
||||
todo!("Context API is not ready yet")
|
||||
}
|
||||
|
||||
/// Create a subscription that schedules a future render for the reference component
|
||||
pub fn schedule_update(&self) -> impl Fn() -> () {
|
||||
todo!("Subscription API is not ready yet");
|
||||
@@ -75,8 +73,8 @@ impl<'a> Context<'a> {
|
||||
/// ctx.view(lazy_tree)
|
||||
/// }
|
||||
///```
|
||||
pub fn view(self, v: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> VNode<'a> {
|
||||
todo!()
|
||||
pub fn view(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> VNode<'a> {
|
||||
lazy_nodes(self.bump.borrow())
|
||||
}
|
||||
|
||||
pub fn callback(&self, f: impl Fn(()) + 'static) {}
|
||||
|
||||
@@ -33,7 +33,7 @@ mod tests {
|
||||
ctx.view(html! { <div>"hello world" </div> })
|
||||
});
|
||||
|
||||
dom.progress()?;
|
||||
// dom.progress()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,44 @@
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
#[derive(ThisError, Debug)]
|
||||
pub enum Error {
|
||||
#[error("No event to progress")]
|
||||
NoEvent,
|
||||
|
||||
#[error("Out of compute credits")]
|
||||
OutOfCredits,
|
||||
|
||||
/// Used when errors need to propogate but are too unique to be typed
|
||||
#[error("{0}")]
|
||||
Unique(String),
|
||||
|
||||
#[error("GraphQL error: {0}")]
|
||||
GraphQL(String),
|
||||
|
||||
// TODO(haze): Remove, or make a better error. This is pretty much useless
|
||||
#[error("GraphQL response mismatch. Got {found} but expected {expected}")]
|
||||
GraphQLMisMatch {
|
||||
expected: &'static str,
|
||||
found: String,
|
||||
},
|
||||
|
||||
#[error("Difference detected in SystemTime! {0}")]
|
||||
SystemTime(#[from] std::time::SystemTimeError),
|
||||
|
||||
#[error("Failed to parse Integer")]
|
||||
ParseInt(#[from] std::num::ParseIntError),
|
||||
|
||||
#[error("")]
|
||||
MissingAuthentication,
|
||||
|
||||
#[error("Failed to create experiment run: {0}")]
|
||||
FailedToCreateExperimentRun(String),
|
||||
|
||||
#[error("Could not find shared behavior with ID {0}")]
|
||||
MissingSharedBehavior(String),
|
||||
|
||||
#[error("I/O Error: {0}")]
|
||||
IO(#[from] std::io::Error),
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
//! Useful, foundational hooks that 3rd parties can implement.
|
||||
//! Currently implemented:
|
||||
//! - use_ref
|
||||
//! - use_state
|
||||
//! - [x] use_ref
|
||||
//! - [x] use_state
|
||||
//! - [ ] use_reducer
|
||||
//! - [ ] use_effect
|
||||
|
||||
pub use use_ref_def::use_ref;
|
||||
pub use use_state_def::use_state;
|
||||
|
||||
mod use_state_def {
|
||||
use crate::inner::*;
|
||||
use std::{borrow::BorrowMut, cell::RefCell, ops::DerefMut, rc::Rc};
|
||||
use std::{cell::RefCell, ops::DerefMut, rc::Rc};
|
||||
|
||||
struct UseState<T: 'static> {
|
||||
new_val: Rc<RefCell<Option<T>>>,
|
||||
@@ -79,7 +81,7 @@ mod use_state_def {
|
||||
|
||||
mod use_ref_def {
|
||||
use crate::inner::*;
|
||||
use std::{borrow::BorrowMut, cell::RefCell, ops::DerefMut, rc::Rc};
|
||||
use std::{cell::RefCell, ops::DerefMut};
|
||||
|
||||
pub struct UseRef<T: 'static> {
|
||||
_current: RefCell<T>,
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
pub mod component;
|
||||
pub mod context;
|
||||
pub mod debug_renderer;
|
||||
pub mod diff;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
pub mod hooks;
|
||||
pub mod nodebuilder;
|
||||
@@ -85,6 +87,7 @@ pub(crate) mod inner {
|
||||
pub use crate::component::{Component, Properties};
|
||||
use crate::context::hooks::Hook;
|
||||
pub use crate::context::Context;
|
||||
pub use crate::error::{Error, Result};
|
||||
use crate::nodes;
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::virtual_dom::VirtualDom;
|
||||
|
||||
+18
-33
@@ -232,56 +232,41 @@ mod vtext {
|
||||
|
||||
impl<'a> VText<'a> {
|
||||
// / Create an new `VText` instance with the specified text.
|
||||
// pub fn new<S>(text: S) -> Self
|
||||
pub fn new(text: &'a str) -> Self
|
||||
// pub fn new<S>(text: S) -> Self
|
||||
// where
|
||||
// S: Into<String>,
|
||||
// {
|
||||
// VText { text: text.into() }
|
||||
// }
|
||||
// S: Into<str>,
|
||||
{
|
||||
VText { text: text.into() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Virtual Components for custom user-defined components
|
||||
/// Only supports the functional syntax
|
||||
mod vcomponent {
|
||||
use crate::prelude::Properties;
|
||||
use crate::inner::{Properties, FC};
|
||||
use std::{any::TypeId, fmt, future::Future, marker::PhantomData};
|
||||
|
||||
use super::VNode;
|
||||
|
||||
pub struct VComponent<'src> {
|
||||
_p: PhantomData<&'src ()>,
|
||||
runner: Box<dyn Properties + 'src>,
|
||||
props: Box<dyn Properties>,
|
||||
// props: Box<dyn Properties + 'src>,
|
||||
caller: *const (),
|
||||
props_type: TypeId,
|
||||
}
|
||||
|
||||
// impl<'src> PartialEq for CallerSource<'src> {
|
||||
// fn eq(&self, other: &Self) -> bool {
|
||||
// todo!()
|
||||
// }
|
||||
// }
|
||||
// caller: Box<dyn Fn
|
||||
// should we box the caller?
|
||||
// probably, so we can call it again
|
||||
//
|
||||
// props_id: TypeId,
|
||||
// callerIDs are unsafely coerced to function pointers
|
||||
// This is okay because #1, we store the props_id and verify and 2# the html! macro rejects components not made this way
|
||||
//
|
||||
// Manually constructing the VComponent is not possible from 3rd party crates
|
||||
|
||||
impl<'a> VComponent<'a> {
|
||||
// /// Construct a VComponent directly from a function component
|
||||
// /// This should be *very* fast - we store the function pointer and props type ID. It should also be small on the stack
|
||||
// pub fn from_fn<P: Properties>(f: FC<P>, props: P) -> Self {
|
||||
// // // Props needs to be static
|
||||
// // let props_id = std::any::TypeId::of::<P>();
|
||||
pub fn new<P: Properties + 'static>(caller: FC<P>, props: P) -> Self {
|
||||
let caller = caller as *const ();
|
||||
let props = Box::new(props);
|
||||
|
||||
// // // Cast the caller down
|
||||
|
||||
// // Self { props_id }
|
||||
// Self {}
|
||||
// }
|
||||
Self {
|
||||
_p: PhantomData {},
|
||||
props,
|
||||
caller,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
-41
@@ -1,6 +1,6 @@
|
||||
use crate::context::hooks::Hook;
|
||||
use crate::inner::*;
|
||||
use crate::nodes::VNode;
|
||||
use crate::{context::hooks::Hook, diff::diff};
|
||||
use bumpalo::Bump;
|
||||
use generational_arena::Index;
|
||||
use std::{
|
||||
@@ -15,22 +15,35 @@ use std::{
|
||||
/// 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 {
|
||||
// TODO @Jon
|
||||
// These hooks are actually references into the hook arena
|
||||
// These two could be combined with "OwningRef" to remove unsafe usage
|
||||
// TODO @Jon
|
||||
// could also use ourborous
|
||||
hooks: RefCell<Vec<*mut Hook>>,
|
||||
hook_arena: typed_arena::Arena<Hook>,
|
||||
|
||||
// Map to the parent
|
||||
parent: Option<Index>,
|
||||
|
||||
// todo, do better with the active frame stuff
|
||||
frames: [Bump; 2],
|
||||
|
||||
// somehow build this vnode with a lifetime tied to self
|
||||
cur_node: *mut VNode<'static>,
|
||||
|
||||
active_frame: u8,
|
||||
|
||||
// IE Which listeners need to be woken up?
|
||||
listeners: Vec<Box<dyn Fn()>>,
|
||||
|
||||
//
|
||||
props_type: TypeId,
|
||||
caller: *const i32,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
// create a new scope from a function
|
||||
pub fn new<T: 'static>(f: FC<T>, parent: Option<Index>) -> Self {
|
||||
pub(crate) fn new<T: 'static>(f: FC<T>, parent: Option<Index>) -> Self {
|
||||
// Capture the props type
|
||||
let props_type = TypeId::of::<T>();
|
||||
let hook_arena = typed_arena::Arena::new();
|
||||
@@ -38,56 +51,60 @@ impl Scope {
|
||||
|
||||
let caller = f as *const i32;
|
||||
|
||||
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 _;
|
||||
|
||||
Self {
|
||||
hook_arena,
|
||||
hooks,
|
||||
props_type,
|
||||
caller,
|
||||
active_frame,
|
||||
listeners,
|
||||
parent,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_context<'a, T: Properties>(
|
||||
&'a mut self,
|
||||
components: &'a generational_arena::Arena<Scope>,
|
||||
props: &'a T,
|
||||
) -> Context {
|
||||
|
||||
//
|
||||
Context {
|
||||
scope: &*self,
|
||||
_p: PhantomData {},
|
||||
arena: &self.hook_arena,
|
||||
hooks: &self.hooks,
|
||||
idx: 0.into(),
|
||||
components,
|
||||
frames,
|
||||
cur_node,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
fn run<T: 'static>(&self, f: FC<T>) {}
|
||||
}
|
||||
///
|
||||
/// Props is ?Sized because we borrow the props
|
||||
pub(crate) fn run<'a, P: Properties + ?Sized>(&self, props: &'a P) {
|
||||
let bump = &self.frames[0];
|
||||
|
||||
mod bad_unsafety {
|
||||
// todo
|
||||
// fn call<'a, T: Properties + 'static>(&'a mut self, val: T) {
|
||||
// if self.props_type == TypeId::of::<T>() {
|
||||
// /*
|
||||
// SAFETY ALERT
|
||||
let ctx = Context {
|
||||
scope: &*self,
|
||||
_p: PhantomData {},
|
||||
arena: &self.hook_arena,
|
||||
hooks: &self.hooks,
|
||||
idx: 0.into(),
|
||||
bump,
|
||||
};
|
||||
|
||||
// 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.
|
||||
/*
|
||||
SAFETY ALERT
|
||||
|
||||
// This is safe because we check that the generic type matches before casting.
|
||||
// */
|
||||
// let caller = unsafe { std::mem::transmute::<*const i32, FC<T>>(self.caller) };
|
||||
// // let ctx = self.create_context::<T>();
|
||||
// // // TODO: do something with these nodes
|
||||
// // let nodes = caller(ctx);
|
||||
// } else {
|
||||
// panic!("Do not try to use `call` on Scopes with the wrong props type")
|
||||
// }
|
||||
// }
|
||||
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.
|
||||
|
||||
we could do a better check to make sure that the TypeID is correct
|
||||
--
|
||||
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 };
|
||||
|
||||
// perform the diff, dumping into the change list
|
||||
crate::diff::diff(old_nodes, &new_nodes);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ use std::{
|
||||
pub struct VirtualDom<P: Properties> {
|
||||
/// All mounted components are arena allocated to make additions, removals, and references easy to work with
|
||||
/// A generational arean is used to re-use slots of deleted scopes without having to resize the underlying arena.
|
||||
components: Arena<Scope>,
|
||||
pub(crate) components: Arena<Scope>,
|
||||
|
||||
/// The index of the root component.
|
||||
base_scope: Index,
|
||||
@@ -48,7 +48,8 @@ pub struct VirtualDom<P: Properties> {
|
||||
/// Components generate lifecycle events
|
||||
event_queue: Vec<LifecycleEvent>,
|
||||
|
||||
root_props: P,
|
||||
// mark the root props with P, even though they're held by the root component
|
||||
_p: PhantomData<P>,
|
||||
}
|
||||
|
||||
/// Implement VirtualDom with no props for components that initialize their state internal to the VDom rather than externally.
|
||||
@@ -83,7 +84,7 @@ impl<P: Properties + 'static> VirtualDom<P> {
|
||||
let base_scope = components.insert(Scope::new(root, None));
|
||||
|
||||
// Create a new mount event with no root container
|
||||
let first_event = LifecycleEvent::mount(base_scope, None, 0);
|
||||
let first_event = LifecycleEvent::mount(base_scope, None, 0, root_props);
|
||||
|
||||
// Create an event queue with a mount for the base scope
|
||||
let event_queue = vec![first_event];
|
||||
@@ -92,83 +93,119 @@ impl<P: Properties + 'static> VirtualDom<P> {
|
||||
components,
|
||||
base_scope,
|
||||
event_queue,
|
||||
root_props,
|
||||
_p: PhantomData {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Pop an event off the even queue and process it
|
||||
pub fn progress(&mut self) -> Result<(), ()> {
|
||||
let LifecycleEvent { index, event_type } = self.event_queue.pop().ok_or(())?;
|
||||
/// Pop an event off the event queue and process it
|
||||
pub fn progress(&mut self) -> Result<()> {
|
||||
let event = self.event_queue.pop().ok_or(Error::NoEvent)?;
|
||||
|
||||
let scope = self.components.get(index).ok_or(())?;
|
||||
|
||||
match event_type {
|
||||
// Component needs to be mounted to the virtual dom
|
||||
LifecycleType::Mount { to, under } => {
|
||||
// todo! run the FC with the bump allocator
|
||||
// Run it with its properties
|
||||
if let Some(other) = to {
|
||||
// mount to another component
|
||||
let p = ();
|
||||
} else {
|
||||
// mount to the root
|
||||
}
|
||||
}
|
||||
|
||||
// The parent for this component generated new props and the component needs update
|
||||
LifecycleType::PropsChanged {} => {}
|
||||
|
||||
// Component was successfully mounted to the dom
|
||||
LifecycleType::Mounted {} => {}
|
||||
|
||||
// Component was removed from the DOM
|
||||
// Run any destructors and cleanup for the hooks and the dump the component
|
||||
LifecycleType::Removed {} => {
|
||||
let f = self.components.remove(index);
|
||||
}
|
||||
|
||||
// Component was messaged via the internal subscription service
|
||||
LifecycleType::Messaged => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
process_event(self, event)
|
||||
}
|
||||
|
||||
/// Update the root props, causing a full event cycle
|
||||
pub fn update_props(&mut self, new_props: P) {}
|
||||
}
|
||||
|
||||
/// Run through every event in the event queue until the events are empty.
|
||||
/// Function is asynchronous to allow for async components to finish their work.
|
||||
pub async fn progess_completely() {}
|
||||
/// Using mutable access to the Virtual Dom, progress a given lifecycle event
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
fn process_event<P: Properties>(
|
||||
dom: &mut VirtualDom<P>,
|
||||
LifecycleEvent { index, event_type }: LifecycleEvent,
|
||||
) -> Result<()> {
|
||||
let scope = dom.components.get(index).ok_or(Error::NoEvent)?;
|
||||
|
||||
/// Create a new context object for a given component and scope
|
||||
fn new_context<T: Properties>(&self) -> Context {
|
||||
// fn new_context<T: Properties>(&self) -> Context<T> {
|
||||
todo!()
|
||||
match event_type {
|
||||
// Component needs to be mounted to the virtual dom
|
||||
LifecycleType::Mount { to, under, props } => {
|
||||
if let Some(other) = to {
|
||||
// mount to another component
|
||||
} else {
|
||||
// mount to the root
|
||||
}
|
||||
|
||||
let g = props.as_ref();
|
||||
scope.run(g);
|
||||
// scope.run(runner, props, dom);
|
||||
}
|
||||
|
||||
// The parent for this component generated new props and the component needs update
|
||||
LifecycleType::PropsChanged { props } => {
|
||||
//
|
||||
}
|
||||
|
||||
// Component was successfully mounted to the dom
|
||||
LifecycleType::Mounted {} => {
|
||||
//
|
||||
}
|
||||
|
||||
// Component was removed from the DOM
|
||||
// Run any destructors and cleanup for the hooks and the dump the component
|
||||
LifecycleType::Removed {} => {
|
||||
let f = dom.components.remove(index);
|
||||
}
|
||||
|
||||
// Component was messaged via the internal subscription service
|
||||
LifecycleType::Messaged => {
|
||||
//
|
||||
}
|
||||
|
||||
// Event from renderer was fired with a given listener ID
|
||||
//
|
||||
LifecycleType::Callback { listener_id } => {}
|
||||
}
|
||||
|
||||
/// Stop writing to the current buffer and start writing to the new one.
|
||||
/// This should be done inbetween CallbackEvent handling, but not between lifecycle events.
|
||||
pub fn swap_buffers(&mut self) {}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct LifecycleEvent {
|
||||
pub index: Index,
|
||||
pub event_type: LifecycleType,
|
||||
}
|
||||
impl LifecycleEvent {
|
||||
fn mount(which: Index, to: Option<Index>, under: usize) -> Self {
|
||||
Self {
|
||||
index: which,
|
||||
event_type: LifecycleType::Mount { to, under },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The internal lifecycle event system is managed by these
|
||||
/// Right now, we box the properties and but them in the enum
|
||||
/// Later, we could directly call the chain of children without boxing
|
||||
/// We could try to reuse the boxes somehow
|
||||
pub enum LifecycleType {
|
||||
Mount { to: Option<Index>, under: usize },
|
||||
PropsChanged,
|
||||
Mount {
|
||||
to: Option<Index>,
|
||||
under: usize,
|
||||
props: Box<dyn Properties>,
|
||||
},
|
||||
PropsChanged {
|
||||
props: Box<dyn Properties>,
|
||||
},
|
||||
Mounted,
|
||||
Removed,
|
||||
Messaged,
|
||||
Callback {
|
||||
listener_id: i32,
|
||||
},
|
||||
}
|
||||
|
||||
impl LifecycleEvent {
|
||||
// helper method for shortcutting to the enum type
|
||||
// probably not necessary
|
||||
fn mount<P: Properties + 'static>(
|
||||
which: Index,
|
||||
to: Option<Index>,
|
||||
under: usize,
|
||||
props: P,
|
||||
) -> Self {
|
||||
Self {
|
||||
index: which,
|
||||
event_type: LifecycleType::Mount {
|
||||
to,
|
||||
under,
|
||||
props: Box::new(props),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user