mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-17 06:08:26 +00:00
wip: cleanup public apis
This commit is contained in:
parent
8b0eb87c72
commit
927b05f358
13 changed files with 178 additions and 371 deletions
|
@ -16,9 +16,6 @@ dioxus-core-macro = { path = "../core-macro", version = "0.1.1" }
|
|||
# Bumpalo is used as a micro heap backing each component
|
||||
bumpalo = { version = "3.6.0", features = ["collections", "boxed"] }
|
||||
|
||||
# custom error type
|
||||
thiserror = "1"
|
||||
|
||||
# faster hashmaps
|
||||
fxhash = "0.2.1"
|
||||
|
||||
|
|
|
@ -40,42 +40,3 @@ We have big goals for Dioxus. The final implementation must:
|
|||
- Be "live". Components should be able to be both server rendered and client rendered without needing frontend APIs.
|
||||
- Be modular. Components and hooks should be work anywhere without worrying about target platform.
|
||||
|
||||
## Optimizations
|
||||
|
||||
- Support a pluggable allocation strategy that makes VNode creation **very** fast
|
||||
- Support lazy VNodes (ie VNodes that are not actually created when the html! macro is used)
|
||||
- Support advanced diffing strategies (patience, Meyers, etc)
|
||||
|
||||
```rust
|
||||
|
||||
rsx!{ "this is a text node" }
|
||||
|
||||
rsx!{
|
||||
div {}
|
||||
"asd"
|
||||
div {}
|
||||
div {}
|
||||
}
|
||||
rsx!{
|
||||
div {
|
||||
a {}
|
||||
b {}
|
||||
c {}
|
||||
Container {
|
||||
Container {
|
||||
Container {
|
||||
Container {
|
||||
Container {
|
||||
div {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
//! Debug virtual doms!
|
||||
//! This renderer comes built in with dioxus core and shows how to implement a basic renderer.
|
||||
//!
|
||||
//! Renderers don't actually need to own the virtual dom (it's up to the implementer).
|
||||
|
||||
use crate::innerlude::RealDom;
|
||||
use crate::{events::EventTrigger, virtual_dom::VirtualDom};
|
||||
use crate::{innerlude::Result, prelude::*};
|
||||
|
||||
pub struct DebugRenderer {
|
||||
internal_dom: VirtualDom,
|
||||
}
|
||||
|
||||
impl DebugRenderer {
|
||||
/// Create a new instance of the Dioxus Virtual Dom with no properties for the root component.
|
||||
///
|
||||
/// This means that the root component must either consumes its own context, or statics are used to generate the page.
|
||||
/// The root component can access things like routing in its context.
|
||||
pub fn new(root: FC<()>) -> Self {
|
||||
Self::new_with_props(root, ())
|
||||
}
|
||||
|
||||
/// Create a new text-renderer instance from a functional component root.
|
||||
/// Automatically progresses the creation of the VNode tree to completion.
|
||||
///
|
||||
/// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
|
||||
pub fn new_with_props<T: Properties + 'static>(root: FC<T>, root_props: T) -> Self {
|
||||
Self::from_vdom(VirtualDom::new_with_props(root, root_props))
|
||||
}
|
||||
|
||||
/// Create a new text renderer from an existing Virtual DOM.
|
||||
pub fn from_vdom(dom: VirtualDom) -> Self {
|
||||
// todo: initialize the event registry properly
|
||||
Self { internal_dom: dom }
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, trigger: EventTrigger) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// pub fn step<Dom: RealDom>(&mut self, machine: &mut DiffMachine<Dom>) -> Result<()> {
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// this does a "holy" compare - if something is missing in the rhs, it doesn't complain.
|
||||
// it only complains if something shows up that's not in the lhs, *or* if a value is different.
|
||||
// This lets you exclude various fields if you just want to drill in to a specific prop
|
||||
// It leverages the internal diffing mechanism.
|
||||
// If you have a list or "nth" child, you do need to list those children, but you don't need to
|
||||
// fill in their children/attrs/etc
|
||||
// Does not handle children or lifecycles and will always fail the test if they show up in the rhs
|
||||
pub fn compare<F>(&self, other: LazyNodes<F>) -> Result<()>
|
||||
where
|
||||
F: for<'b, 'c> FnOnce(&'b NodeFactory<'c>) -> VNode<'c>,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Do a full compare - everything must match
|
||||
// Ignores listeners and children components
|
||||
pub fn compare_full<F>(&self, other: LazyNodes<F>) -> Result<()>
|
||||
where
|
||||
F: for<'b, 'c> FnOnce(&'b NodeFactory<'c>) -> VNode<'c>,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn trigger_listener(&mut self, id: usize) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn render_nodes<F>(&self, other: LazyNodes<F>) -> Result<()>
|
||||
where
|
||||
F: for<'b, 'c> FnOnce(&'b NodeFactory<'c>) -> VNode<'c>,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -105,7 +105,7 @@ use DomEdit::*;
|
|||
///
|
||||
/// Funnily enough, this stack machine's entire job is to create instructions for another stack machine to execute. It's
|
||||
/// stack machines all the way down!
|
||||
pub struct DiffMachine<'bump> {
|
||||
pub(crate) struct DiffMachine<'bump> {
|
||||
pub vdom: &'bump ResourcePool,
|
||||
pub mutations: Mutations<'bump>,
|
||||
pub stack: DiffStack<'bump>,
|
||||
|
@ -115,7 +115,7 @@ pub struct DiffMachine<'bump> {
|
|||
/// a "saved" form of a diff machine
|
||||
/// in regular diff machine, the &'bump reference is a stack borrow, but the
|
||||
/// bump lifetimes are heap borrows.
|
||||
pub struct SavedDiffWork<'bump> {
|
||||
pub(crate) struct SavedDiffWork<'bump> {
|
||||
pub mutations: Mutations<'bump>,
|
||||
pub stack: DiffStack<'bump>,
|
||||
pub seen_scopes: FxHashSet<ScopeId>,
|
||||
|
@ -174,36 +174,23 @@ impl<'bump> DiffMachine<'bump> {
|
|||
///
|
||||
/// We do depth-first to maintain high cache locality (nodes were originally generated recursively).
|
||||
pub async fn work(&mut self) {
|
||||
// defer to individual functions so the compiler produces better code
|
||||
// large functions tend to be difficult for the compiler to work with
|
||||
while let Some(instruction) = self.stack.pop() {
|
||||
// defer to individual functions so the compiler produces better code
|
||||
// large functions tend to be difficult for the compiler to work with
|
||||
match instruction {
|
||||
DiffInstruction::PopScope => {
|
||||
self.stack.pop_scope();
|
||||
}
|
||||
|
||||
DiffInstruction::DiffNode { old, new, .. } => {
|
||||
self.diff_node(old, new);
|
||||
}
|
||||
DiffInstruction::DiffNode { old, new, .. } => self.diff_node(old, new),
|
||||
|
||||
DiffInstruction::DiffChildren { old, new } => {
|
||||
self.diff_children(old, new);
|
||||
}
|
||||
DiffInstruction::DiffChildren { old, new } => self.diff_children(old, new),
|
||||
|
||||
DiffInstruction::Create { node } => {
|
||||
self.create_node(node);
|
||||
}
|
||||
DiffInstruction::Create { node } => self.create_node(node),
|
||||
|
||||
DiffInstruction::Mount { and } => {
|
||||
self.mount(and);
|
||||
}
|
||||
DiffInstruction::Mount { and } => self.mount(and),
|
||||
|
||||
DiffInstruction::PrepareMoveNode { node } => {
|
||||
for el in RealChildIterator::new(node, self.vdom) {
|
||||
self.mutations.push_root(el.direct_id());
|
||||
self.stack.add_child_count(1);
|
||||
}
|
||||
}
|
||||
DiffInstruction::PrepareMoveNode { node } => self.prepare_move_node(node),
|
||||
};
|
||||
|
||||
// todo: call this less frequently, there is a bit of overhead involved
|
||||
|
@ -211,6 +198,13 @@ impl<'bump> DiffMachine<'bump> {
|
|||
}
|
||||
}
|
||||
|
||||
fn prepare_move_node(&mut self, node: &'bump VNode<'bump>) {
|
||||
for el in RealChildIterator::new(node, self.vdom) {
|
||||
self.mutations.push_root(el.direct_id());
|
||||
self.stack.add_child_count(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn mount(&mut self, and: MountType<'bump>) {
|
||||
let nodes_created = self.stack.pop_nodes_created();
|
||||
match and {
|
||||
|
@ -363,22 +357,12 @@ impl<'bump> DiffMachine<'bump> {
|
|||
let new_component = self.vdom.get_scope_mut(new_idx).unwrap();
|
||||
|
||||
// Run the scope for one iteration to initialize it
|
||||
match new_component.run_scope() {
|
||||
Ok(_g) => {
|
||||
// all good, new nodes exist
|
||||
}
|
||||
Err(err) => {
|
||||
// failed to run. this is the first time the component ran, and it failed
|
||||
// we manually set its head node to an empty fragment
|
||||
panic!("failing components not yet implemented");
|
||||
}
|
||||
if new_component.run_scope() {
|
||||
// Take the node that was just generated from running the component
|
||||
let nextnode = new_component.frames.fin_head();
|
||||
self.stack.create_component(new_idx, nextnode);
|
||||
}
|
||||
|
||||
// Take the node that was just generated from running the component
|
||||
let nextnode = new_component.frames.fin_head();
|
||||
|
||||
self.stack.create_component(new_idx, nextnode);
|
||||
|
||||
// Finally, insert this scope as a seen node.
|
||||
self.seen_scopes.insert(new_idx);
|
||||
}
|
||||
|
@ -545,8 +529,9 @@ impl<'bump> DiffMachine<'bump> {
|
|||
}
|
||||
false => {
|
||||
// the props are different...
|
||||
scope.run_scope().unwrap();
|
||||
self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
|
||||
if scope.run_scope() {
|
||||
self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ pub enum MountType<'a> {
|
|||
InsertBefore { other_node: &'a VNode<'a> },
|
||||
}
|
||||
|
||||
pub struct DiffStack<'bump> {
|
||||
pub(crate) struct DiffStack<'bump> {
|
||||
instructions: Vec<DiffInstruction<'bump>>,
|
||||
nodes_created_stack: SmallVec<[usize; 10]>,
|
||||
pub scope_stack: SmallVec<[ScopeId; 5]>,
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
|
||||
use crate::innerlude::ScopeId;
|
||||
|
||||
/// A `DomEdit` represents a serialzied form of the VirtualDom's trait-based API. This allows streaming edits across the
|
||||
/// network or through FFI boundaries.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(
|
||||
feature = "serialize",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(tag = "type")
|
||||
)]
|
||||
pub enum DomEdit<'bump> {
|
||||
PushRoot {
|
||||
id: u64,
|
||||
},
|
||||
PopRoot,
|
||||
|
||||
AppendChildren {
|
||||
many: u32,
|
||||
},
|
||||
|
||||
// "Root" refers to the item direclty
|
||||
// it's a waste of an instruction to push the root directly
|
||||
ReplaceWith {
|
||||
root: u64,
|
||||
m: u32,
|
||||
},
|
||||
InsertAfter {
|
||||
root: u64,
|
||||
n: u32,
|
||||
},
|
||||
InsertBefore {
|
||||
root: u64,
|
||||
n: u32,
|
||||
},
|
||||
Remove {
|
||||
root: u64,
|
||||
},
|
||||
|
||||
RemoveAllChildren,
|
||||
CreateTextNode {
|
||||
text: &'bump str,
|
||||
id: u64,
|
||||
},
|
||||
CreateElement {
|
||||
tag: &'bump str,
|
||||
id: u64,
|
||||
},
|
||||
CreateElementNs {
|
||||
tag: &'bump str,
|
||||
id: u64,
|
||||
ns: &'static str,
|
||||
},
|
||||
CreatePlaceholder {
|
||||
id: u64,
|
||||
},
|
||||
NewEventListener {
|
||||
event_name: &'static str,
|
||||
scope: ScopeId,
|
||||
mounted_node_id: u64,
|
||||
},
|
||||
RemoveEventListener {
|
||||
event: &'static str,
|
||||
},
|
||||
SetText {
|
||||
text: &'bump str,
|
||||
},
|
||||
SetAttribute {
|
||||
field: &'static str,
|
||||
value: &'bump str,
|
||||
ns: Option<&'bump str>,
|
||||
},
|
||||
RemoveAttribute {
|
||||
name: &'static str,
|
||||
},
|
||||
}
|
||||
impl DomEdit<'_> {
|
||||
pub fn is(&self, id: &'static str) -> bool {
|
||||
match self {
|
||||
DomEdit::InsertAfter { .. } => id == "InsertAfter",
|
||||
DomEdit::InsertBefore { .. } => id == "InsertBefore",
|
||||
DomEdit::PushRoot { .. } => id == "PushRoot",
|
||||
DomEdit::PopRoot => id == "PopRoot",
|
||||
DomEdit::AppendChildren { .. } => id == "AppendChildren",
|
||||
DomEdit::ReplaceWith { .. } => id == "ReplaceWith",
|
||||
DomEdit::Remove { .. } => id == "Remove",
|
||||
DomEdit::RemoveAllChildren => id == "RemoveAllChildren",
|
||||
DomEdit::CreateTextNode { .. } => id == "CreateTextNode",
|
||||
DomEdit::CreateElement { .. } => id == "CreateElement",
|
||||
DomEdit::CreateElementNs { .. } => id == "CreateElementNs",
|
||||
DomEdit::CreatePlaceholder { .. } => id == "CreatePlaceholder",
|
||||
DomEdit::NewEventListener { .. } => id == "NewEventListener",
|
||||
DomEdit::RemoveEventListener { .. } => id == "RemoveEventListener",
|
||||
DomEdit::SetText { .. } => id == "SetText",
|
||||
DomEdit::SetAttribute { .. } => id == "SetAttribute",
|
||||
DomEdit::RemoveAttribute { .. } => id == "RemoveAttribute",
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
//! Internal error handling for Dioxus
|
||||
//!
|
||||
//!
|
||||
|
||||
use thiserror::Error as ThisError;
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
#[derive(ThisError, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Fatal Internal Error: {0}")]
|
||||
FatalInternal(&'static str),
|
||||
|
||||
#[error("Context is missing")]
|
||||
MissingSharedContext,
|
||||
|
||||
#[error("No event to progress")]
|
||||
NoEvent,
|
||||
|
||||
#[error("Wrong Properties Type")]
|
||||
WrongProps,
|
||||
|
||||
#[error("The component failed to return VNodes")]
|
||||
ComponentFailed,
|
||||
|
||||
#[error("Base scope has not been mounted yet")]
|
||||
NotMounted,
|
||||
|
||||
#[error("I/O Error: {0}")]
|
||||
IO(#[from] std::io::Error),
|
||||
}
|
|
@ -1,14 +1,5 @@
|
|||
#![allow(non_snake_case)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
//! Dioxus Core
|
||||
//! ----------
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
|
||||
pub use crate::innerlude::{
|
||||
format_args_f, html, rsx, Context, DiffInstruction, DioxusElement, DomEdit, DomTree, ElementId,
|
||||
|
@ -26,7 +17,6 @@ pub mod prelude {
|
|||
pub use dioxus_core_macro::{format_args_f, html, rsx, Props};
|
||||
}
|
||||
|
||||
// types used internally that are important
|
||||
pub(crate) mod innerlude {
|
||||
pub use crate::bumpframe::*;
|
||||
pub use crate::childiter::*;
|
||||
|
@ -34,8 +24,6 @@ pub(crate) mod innerlude {
|
|||
pub use crate::context::*;
|
||||
pub use crate::diff::*;
|
||||
pub use crate::diff_stack::*;
|
||||
pub use crate::dom_edits::*;
|
||||
pub use crate::error::*;
|
||||
pub use crate::events::*;
|
||||
pub use crate::heuristics::*;
|
||||
pub use crate::hooklist::*;
|
||||
|
@ -43,7 +31,6 @@ pub(crate) mod innerlude {
|
|||
pub use crate::mutations::*;
|
||||
pub use crate::nodes::*;
|
||||
pub use crate::scheduler::*;
|
||||
// pub use crate::scheduler::*;
|
||||
pub use crate::scope::*;
|
||||
pub use crate::util::*;
|
||||
pub use crate::virtual_dom::*;
|
||||
|
@ -57,19 +44,29 @@ pub(crate) mod innerlude {
|
|||
|
||||
pub mod exports {
|
||||
//! Important dependencies that are used by the rest of the library
|
||||
|
||||
// the foundation of this library
|
||||
pub use bumpalo;
|
||||
}
|
||||
|
||||
/*
|
||||
Navigating this crate:
|
||||
- virtual_dom: the primary entrypoint for the crate
|
||||
- scheduler: the core interior logic called by virtual_dom
|
||||
- nodes: the definition of VNodes, listeners, etc.
|
||||
-
|
||||
|
||||
|
||||
Some utilities
|
||||
|
||||
|
||||
|
||||
*/
|
||||
pub mod bumpframe;
|
||||
pub mod childiter;
|
||||
pub mod component;
|
||||
pub mod context;
|
||||
pub mod diff;
|
||||
pub mod diff_stack;
|
||||
pub mod dom_edits;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
pub mod heuristics;
|
||||
pub mod hooklist;
|
||||
|
|
|
@ -163,3 +163,100 @@ impl<'a> NodeRefMutation<'a> {
|
|||
.and_then(|f| f.downcast_mut::<T>())
|
||||
}
|
||||
}
|
||||
|
||||
/// A `DomEdit` represents a serialzied form of the VirtualDom's trait-based API. This allows streaming edits across the
|
||||
/// network or through FFI boundaries.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(
|
||||
feature = "serialize",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(tag = "type")
|
||||
)]
|
||||
pub enum DomEdit<'bump> {
|
||||
PushRoot {
|
||||
id: u64,
|
||||
},
|
||||
PopRoot,
|
||||
|
||||
AppendChildren {
|
||||
many: u32,
|
||||
},
|
||||
|
||||
// "Root" refers to the item direclty
|
||||
// it's a waste of an instruction to push the root directly
|
||||
ReplaceWith {
|
||||
root: u64,
|
||||
m: u32,
|
||||
},
|
||||
InsertAfter {
|
||||
root: u64,
|
||||
n: u32,
|
||||
},
|
||||
InsertBefore {
|
||||
root: u64,
|
||||
n: u32,
|
||||
},
|
||||
Remove {
|
||||
root: u64,
|
||||
},
|
||||
|
||||
RemoveAllChildren,
|
||||
CreateTextNode {
|
||||
text: &'bump str,
|
||||
id: u64,
|
||||
},
|
||||
CreateElement {
|
||||
tag: &'bump str,
|
||||
id: u64,
|
||||
},
|
||||
CreateElementNs {
|
||||
tag: &'bump str,
|
||||
id: u64,
|
||||
ns: &'static str,
|
||||
},
|
||||
CreatePlaceholder {
|
||||
id: u64,
|
||||
},
|
||||
NewEventListener {
|
||||
event_name: &'static str,
|
||||
scope: ScopeId,
|
||||
mounted_node_id: u64,
|
||||
},
|
||||
RemoveEventListener {
|
||||
event: &'static str,
|
||||
},
|
||||
SetText {
|
||||
text: &'bump str,
|
||||
},
|
||||
SetAttribute {
|
||||
field: &'static str,
|
||||
value: &'bump str,
|
||||
ns: Option<&'bump str>,
|
||||
},
|
||||
RemoveAttribute {
|
||||
name: &'static str,
|
||||
},
|
||||
}
|
||||
impl DomEdit<'_> {
|
||||
pub fn is(&self, id: &'static str) -> bool {
|
||||
match self {
|
||||
DomEdit::InsertAfter { .. } => id == "InsertAfter",
|
||||
DomEdit::InsertBefore { .. } => id == "InsertBefore",
|
||||
DomEdit::PushRoot { .. } => id == "PushRoot",
|
||||
DomEdit::PopRoot => id == "PopRoot",
|
||||
DomEdit::AppendChildren { .. } => id == "AppendChildren",
|
||||
DomEdit::ReplaceWith { .. } => id == "ReplaceWith",
|
||||
DomEdit::Remove { .. } => id == "Remove",
|
||||
DomEdit::RemoveAllChildren => id == "RemoveAllChildren",
|
||||
DomEdit::CreateTextNode { .. } => id == "CreateTextNode",
|
||||
DomEdit::CreateElement { .. } => id == "CreateElement",
|
||||
DomEdit::CreateElementNs { .. } => id == "CreateElementNs",
|
||||
DomEdit::CreatePlaceholder { .. } => id == "CreatePlaceholder",
|
||||
DomEdit::NewEventListener { .. } => id == "NewEventListener",
|
||||
DomEdit::RemoveEventListener { .. } => id == "RemoveEventListener",
|
||||
DomEdit::SetText { .. } => id == "SetText",
|
||||
DomEdit::SetAttribute { .. } => id == "SetAttribute",
|
||||
DomEdit::RemoveAttribute { .. } => id == "RemoveAttribute",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,7 +128,12 @@ pub enum SchedulerMsg {
|
|||
/// We can prevent this safety issue from occurring if we track which scopes are invalidated when starting a new task.
|
||||
///
|
||||
///
|
||||
pub struct Scheduler {
|
||||
pub(crate) struct Scheduler {
|
||||
/// 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.
|
||||
///
|
||||
/// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
|
||||
/// and rusts's guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
|
||||
pub pool: ResourcePool,
|
||||
|
||||
pub heuristics: HeuristicsEngine,
|
||||
|
@ -523,7 +528,7 @@ impl Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct PriorityLane {
|
||||
pub(crate) struct PriorityLane {
|
||||
pub dirty_scopes: IndexSet<ScopeId>,
|
||||
pub saved_state: Option<SavedDiffWork<'static>>,
|
||||
pub in_progress: bool,
|
||||
|
@ -682,7 +687,7 @@ impl ResourcePool {
|
|||
&'b self,
|
||||
_id: ScopeId,
|
||||
_f: impl FnOnce(&'b mut Scope) -> O,
|
||||
) -> Result<O> {
|
||||
) -> Option<O> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
@ -692,13 +697,13 @@ impl ResourcePool {
|
|||
&self,
|
||||
_id: ScopeId,
|
||||
_f: impl FnOnce(&mut Scope) -> &VNode<'b>,
|
||||
) -> Result<&VNode<'b>> {
|
||||
) -> Option<&VNode<'b>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn try_remove(&self, id: ScopeId) -> Result<Scope> {
|
||||
pub fn try_remove(&self, id: ScopeId) -> Option<Scope> {
|
||||
let inner = unsafe { &mut *self.components.get() };
|
||||
Ok(inner.remove(id.0))
|
||||
Some(inner.remove(id.0))
|
||||
// .try_remove(id.0)
|
||||
// .ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||
}
|
||||
|
|
|
@ -113,7 +113,9 @@ impl Scope {
|
|||
self.child_nodes = child_nodes;
|
||||
}
|
||||
|
||||
pub(crate) fn run_scope<'sel>(&'sel mut self) -> Result<()> {
|
||||
/// Returns true if the scope completed successfully
|
||||
///
|
||||
pub(crate) fn run_scope<'sel>(&'sel mut self) -> bool {
|
||||
// Cycle to the next frame and then reset it
|
||||
// This breaks any latent references, invalidating every pointer referencing into it.
|
||||
// Remove all the outdated listeners
|
||||
|
@ -133,17 +135,12 @@ impl Scope {
|
|||
let render: &WrappedCaller = self.caller.as_ref();
|
||||
|
||||
match render(self) {
|
||||
None => {
|
||||
// the user's component failed. We avoid cycling to the next frame
|
||||
log::error!("Running your component failed! It will no longer receive events.");
|
||||
Err(Error::ComponentFailed)
|
||||
}
|
||||
None => false,
|
||||
Some(new_head) => {
|
||||
// the user's component succeeded. We can safely cycle to the next frame
|
||||
self.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
|
||||
self.frames.cycle_frame();
|
||||
log::debug!("Successfully rendered component");
|
||||
Ok(())
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,7 +199,7 @@ impl Scope {
|
|||
&mut self,
|
||||
event: SyntheticEvent,
|
||||
element: ElementId,
|
||||
) -> Result<()> {
|
||||
) -> Option<()> {
|
||||
let listners = self.listeners.borrow_mut();
|
||||
|
||||
let raw_listener = listners.iter().find(|lis| {
|
||||
|
@ -226,7 +223,7 @@ impl Scope {
|
|||
log::warn!("An event was triggered but there was no listener to handle it");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn root(&self) -> &VNode {
|
||||
|
|
|
@ -11,22 +11,3 @@ pub fn empty_cell() -> Cell<Option<ElementId>> {
|
|||
pub fn type_name_of<T>(_: T) -> &'static str {
|
||||
std::any::type_name::<T>()
|
||||
}
|
||||
|
||||
// /// A helper type that lets scopes be ordered by their height
|
||||
// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
// pub struct HeightMarker {
|
||||
// pub idx: ScopeId,
|
||||
// pub height: u32,
|
||||
// }
|
||||
|
||||
// impl Ord for HeightMarker {
|
||||
// fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
// self.height.cmp(&other.height)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl PartialOrd for HeightMarker {
|
||||
// fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
// Some(self.cmp(other))
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
//!
|
||||
//! This module includes just the barebones for a complete VirtualDOM API.
|
||||
//! Additional functionality is defined in the respective files.
|
||||
|
||||
use crate::innerlude::*;
|
||||
use futures_util::{pin_mut, Future, FutureExt};
|
||||
use std::{
|
||||
|
@ -35,23 +36,13 @@ use std::{
|
|||
///
|
||||
///
|
||||
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.
|
||||
///
|
||||
/// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
|
||||
/// and rusts's guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
|
||||
pub scheduler: Scheduler,
|
||||
scheduler: Scheduler,
|
||||
|
||||
/// The index of the root component
|
||||
/// Should always be the first (gen=0, id=0)
|
||||
base_scope: ScopeId,
|
||||
|
||||
// for managing the props that were used to create the dom
|
||||
#[doc(hidden)]
|
||||
_root_prop_type: std::any::TypeId,
|
||||
root_prop_type: std::any::TypeId,
|
||||
|
||||
#[doc(hidden)]
|
||||
_root_props: std::pin::Pin<Box<dyn std::any::Any>>,
|
||||
root_props: Pin<Box<dyn std::any::Any>>,
|
||||
}
|
||||
|
||||
impl VirtualDom {
|
||||
|
@ -67,8 +58,8 @@ impl VirtualDom {
|
|||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// fn Example(cx: Context<SomeProps>) -> VNode {
|
||||
/// cx.render(rsx!{ div{"hello world"} })
|
||||
/// fn Example(cx: Context<()>) -> DomTree {
|
||||
/// cx.render(rsx!( div { "hello world" } ))
|
||||
/// }
|
||||
///
|
||||
/// let dom = VirtualDom::new(Example);
|
||||
|
@ -91,25 +82,34 @@ impl VirtualDom {
|
|||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// fn Example(cx: Context<SomeProps>) -> VNode {
|
||||
/// cx.render(rsx!{ div{"hello world"} })
|
||||
/// #[derive(PartialEq, Props)]
|
||||
/// struct SomeProps {
|
||||
/// name: &'static str
|
||||
/// }
|
||||
///
|
||||
/// fn Example(cx: Context<SomeProps>) -> DomTree {
|
||||
/// cx.render(rsx!{ div{ "hello {cx.name}" } })
|
||||
/// }
|
||||
///
|
||||
/// let dom = VirtualDom::new(Example);
|
||||
/// ```
|
||||
///
|
||||
/// Note: the VirtualDOM is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
|
||||
/// Note: the VirtualDOM is not progressed on creation. You must either "run_with_deadline" or use "rebuild" to progress it.
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
|
||||
/// let mutations = dom.rebuild();
|
||||
/// ```
|
||||
pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
|
||||
let scheduler = Scheduler::new();
|
||||
|
||||
let _root_props: Pin<Box<dyn Any>> = Box::pin(root_props);
|
||||
let _root_prop_type = TypeId::of::<P>();
|
||||
|
||||
let props_ptr = _root_props.as_ref().downcast_ref::<P>().unwrap() as *const P;
|
||||
let props_ptr = _root_props.downcast_ref::<P>().unwrap() as *const P;
|
||||
|
||||
let base_scope = scheduler.pool.insert_scope_with_key(|myidx| {
|
||||
let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
|
||||
let name = type_name_of(root);
|
||||
Scope::new(
|
||||
caller,
|
||||
myidx,
|
||||
|
@ -122,9 +122,9 @@ impl VirtualDom {
|
|||
|
||||
Self {
|
||||
base_scope,
|
||||
_root_props,
|
||||
scheduler,
|
||||
_root_prop_type,
|
||||
root_props: _root_props,
|
||||
root_prop_type: _root_prop_type,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,7 +168,7 @@ impl VirtualDom {
|
|||
.expect("The base scope should never be moved");
|
||||
|
||||
// // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
|
||||
if cur_component.run_scope().is_ok() {
|
||||
if cur_component.run_scope() {
|
||||
diff_machine
|
||||
.stack
|
||||
.create_node(cur_component.frames.fin_head(), MountType::Append);
|
||||
|
@ -204,9 +204,9 @@ impl VirtualDom {
|
|||
.get_scope_mut(self.base_scope)
|
||||
.expect("The base scope should never be moved");
|
||||
|
||||
cur_component.run_scope().unwrap();
|
||||
|
||||
diff_machine.diff_scope(self.base_scope).await;
|
||||
if cur_component.run_scope() {
|
||||
diff_machine.diff_scope(self.base_scope).await;
|
||||
}
|
||||
|
||||
diff_machine.mutations
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue