diff --git a/examples/button.rs b/examples/button.rs
new file mode 100644
index 000000000..9d32819e8
--- /dev/null
+++ b/examples/button.rs
@@ -0,0 +1,11 @@
+use dioxus::prelude::*;
+
+fn main() {
+ dioxus_desktop::launch(app);
+}
+
+fn app(cx: Scope) -> Element {
+ cx.render(rsx! {
+ button { "hello, desktop!" }
+ })
+}
diff --git a/packages/core/src/any_props.rs b/packages/core/src/any_props.rs
index c3ea4dc2c..7014a2771 100644
--- a/packages/core/src/any_props.rs
+++ b/packages/core/src/any_props.rs
@@ -1,4 +1,4 @@
-use std::marker::PhantomData;
+use std::{marker::PhantomData, panic::AssertUnwindSafe};
use crate::{
innerlude::Scoped,
@@ -62,12 +62,19 @@ where
}
fn render(&'a self, cx: &'a ScopeState) -> RenderReturn<'a> {
- let scope: &mut Scoped
= cx.bump().alloc(Scoped {
- props: &self.props,
- scope: cx,
- });
+ let res = std::panic::catch_unwind(AssertUnwindSafe(move || {
+ // Call the render function directly
+ let scope: &mut Scoped
= cx.bump().alloc(Scoped {
+ props: &self.props,
+ scope: cx,
+ });
- // Call the render function directly
- (self.render_fn)(scope).into_return(cx)
+ (self.render_fn)(scope).into_return(cx)
+ }));
+
+ match res {
+ Ok(e) => e,
+ Err(_) => RenderReturn::default(),
+ }
}
}
diff --git a/packages/core/src/arena.rs b/packages/core/src/arena.rs
index 9909e2fb4..36323730a 100644
--- a/packages/core/src/arena.rs
+++ b/packages/core/src/arena.rs
@@ -81,12 +81,12 @@ impl VirtualDom {
self.ensure_drop_safety(id);
if let Some(root) = self.scopes[id.0].as_ref().try_root_node() {
- if let RenderReturn::Sync(Some(node)) = unsafe { root.extend_lifetime_ref() } {
+ if let RenderReturn::Ready(node) = unsafe { root.extend_lifetime_ref() } {
self.drop_scope_inner(node)
}
}
if let Some(root) = unsafe { self.scopes[id.0].as_ref().previous_frame().try_load_node() } {
- if let RenderReturn::Sync(Some(node)) = unsafe { root.extend_lifetime_ref() } {
+ if let RenderReturn::Ready(node) = unsafe { root.extend_lifetime_ref() } {
self.drop_scope_inner(node)
}
}
diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs
index b0bca26d5..f54684410 100644
--- a/packages/core/src/create.rs
+++ b/packages/core/src/create.rs
@@ -433,9 +433,9 @@ impl<'b> VirtualDom {
use DynamicNode::*;
match node {
Text(text) => self.create_dynamic_text(template, text, idx),
- Fragment(frag) => self.create_fragment(frag),
- Placeholder(frag) => self.create_placeholder(frag, template, idx),
+ Placeholder(place) => self.create_placeholder(place, template, idx),
Component(component) => self.create_component_node(template, component, idx),
+ Fragment(frag) => frag.iter().map(|child| self.create(child)).sum(),
}
}
@@ -487,40 +487,38 @@ impl<'b> VirtualDom {
0
}
- pub(crate) fn create_fragment(&mut self, nodes: &'b [VNode<'b>]) -> usize {
- nodes.iter().map(|child| self.create(child)).sum()
- }
-
pub(super) fn create_component_node(
&mut self,
template: &'b VNode<'b>,
component: &'b VComponent<'b>,
idx: usize,
) -> usize {
- let scope = match component.props.take() {
- Some(props) => {
- let unbounded_props: Box = unsafe { std::mem::transmute(props) };
- let scope = self.new_scope(unbounded_props, component.name);
- scope.id
- }
+ use RenderReturn::*;
- // Component is coming back, it probably still exists, right?
- None => component.scope.get().unwrap(),
- };
+ // Load up a ScopeId for this vcomponent
+ let scope = self.load_scope_from_vcomponent(component);
component.scope.set(Some(scope));
- let return_nodes = unsafe { self.run_scope(scope).extend_lifetime_ref() };
-
- use RenderReturn::*;
-
- match return_nodes {
- Sync(Some(t)) => self.mount_component(scope, template, t, idx),
- Sync(None) => todo!("Propogate error upwards"),
- Async(_) => self.mount_component_placeholder(template, idx, scope),
+ match unsafe { self.run_scope(scope).extend_lifetime_ref() } {
+ Ready(t) => self.mount_component(scope, template, t, idx),
+ Aborted(t) => self.mount_aborted(template, t),
+ Pending(_) => self.mount_async(template, idx, scope),
}
}
+ /// Load a scope from a vcomponent. If the props don't exist, that means the component is currently "live"
+ fn load_scope_from_vcomponent(&mut self, component: &VComponent) -> ScopeId {
+ component
+ .props
+ .take()
+ .map(|props| {
+ let unbounded_props: Box = unsafe { std::mem::transmute(props) };
+ self.new_scope(unbounded_props, component.name).id
+ })
+ .unwrap_or_else(|| component.scope.get().unwrap())
+ }
+
fn mount_component(
&mut self,
scope: ScopeId,
@@ -575,15 +573,17 @@ impl<'b> VirtualDom {
0
}
+ fn mount_aborted(&mut self, parent: &'b VNode<'b>, placeholder: &VPlaceholder) -> usize {
+ let id = self.next_element(parent, &[]);
+ self.mutations.push(Mutation::CreatePlaceholder { id });
+ placeholder.id.set(Some(id));
+ 1
+ }
+
/// Take the rendered nodes from a component and handle them if they were async
///
/// IE simply assign an ID to the placeholder
- fn mount_component_placeholder(
- &mut self,
- template: &VNode,
- idx: usize,
- scope: ScopeId,
- ) -> usize {
+ fn mount_async(&mut self, template: &VNode, idx: usize, scope: ScopeId) -> usize {
let new_id = self.next_element(template, template.template.get().node_paths[idx]);
// Set the placeholder of the scope
diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs
index fa11a6e2f..efd534fe7 100644
--- a/packages/core/src/diff.rs
+++ b/packages/core/src/diff.rs
@@ -30,29 +30,44 @@ impl<'b> VirtualDom {
.try_load_node()
.expect("Call rebuild before diffing");
- use RenderReturn::{Async, Sync};
+ use RenderReturn::{Aborted, Pending, Ready};
match (old, new) {
- (Sync(Some(l)), Sync(Some(r))) => self.diff_node(l, r),
+ // Normal pathway
+ (Ready(l), Ready(r)) => self.diff_node(l, r),
- // Err cases
- (Sync(Some(l)), Sync(None)) => self.diff_ok_to_err(l),
- (Sync(None), Sync(Some(r))) => self.diff_err_to_ok(r),
- (Sync(None), Sync(None)) => { /* nothing */ }
+ // Unwind the mutations if need be
+ (Ready(l), Aborted(p)) => self.diff_ok_to_err(l, p),
- // Async
- (Sync(Some(_l)), Async(_)) => todo!(),
- (Sync(None), Async(_)) => todo!(),
- (Async(_), Sync(Some(_r))) => todo!(),
- (Async(_), Sync(None)) => { /* nothing */ }
- (Async(_), Async(_)) => { /* nothing */ }
+ // Just move over the placeholder
+ (Aborted(l), Aborted(r)) => r.id.set(l.id.get()),
+
+ // Becomes async, do nothing while we ait
+ (Ready(_nodes), Pending(_fut)) => self.diff_ok_to_async(_nodes, scope),
+
+ // Placeholder becomes something
+ // We should also clear the error now
+ (Aborted(l), Ready(r)) => self.replace_placeholder(l, [r]),
+
+ (Aborted(_), Pending(_)) => todo!("async should not resolve here"),
+ (Pending(_), Ready(_)) => todo!("async should not resolve here"),
+ (Pending(_), Aborted(_)) => todo!("async should not resolve here"),
+ (Pending(_), Pending(_)) => {
+ // All suspense should resolve before we diff it again
+ panic!("Should not roll from suspense to suspense.");
+ }
};
}
self.scope_stack.pop();
}
- fn diff_ok_to_err(&mut self, _l: &'b VNode<'b>) {}
- fn diff_err_to_ok(&mut self, _l: &'b VNode<'b>) {}
+ fn diff_ok_to_async(&mut self, _new: &'b VNode<'b>, _scope: ScopeId) {
+ //
+ }
+
+ fn diff_ok_to_err(&mut self, _l: &'b VNode<'b>, _p: &'b VPlaceholder) {
+ todo!()
+ }
fn diff_node(&mut self, left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) {
// If hot reloading is enabled, we need to make sure we're using the latest template
@@ -129,7 +144,7 @@ impl<'b> VirtualDom {
(Fragment(left), Fragment(right)) => self.diff_non_empty_fragment(left, right),
(Placeholder(left), Placeholder(right)) => right.id.set(left.id.get()),
(Component(left), Component(right)) => self.diff_vcomponent(left, right, node, idx),
- (Placeholder(left), Fragment(right)) => self.replace_placeholder(left, right),
+ (Placeholder(left), Fragment(right)) => self.replace_placeholder(left, *right),
(Fragment(left), Placeholder(right)) => self.node_to_placeholder(left, right),
_ => todo!("This is an usual custom case for dynamic nodes. We don't know how to handle it yet."),
};
@@ -208,8 +223,12 @@ impl<'b> VirtualDom {
) {
let m = self.create_component_node(right_template, right, idx);
+ let pre_edits = self.mutations.edits.len();
+
self.remove_component_node(left, true);
+ assert!(self.mutations.edits.len() > pre_edits);
+
// We want to optimize the replace case to use one less mutation if possible
// Since mutations are done in reverse, the last node removed will be the first in the stack
// Instead of *just* removing it, we can use the replace mutation
@@ -691,7 +710,8 @@ impl<'b> VirtualDom {
Component(comp) => {
let scope = comp.scope.get().unwrap();
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
- RenderReturn::Sync(Some(node)) => self.push_all_real_nodes(node),
+ RenderReturn::Ready(node) => self.push_all_real_nodes(node),
+ RenderReturn::Aborted(_node) => todo!(),
_ => todo!(),
}
}
@@ -719,7 +739,11 @@ impl<'b> VirtualDom {
}
/// Simply replace a placeholder with a list of nodes
- fn replace_placeholder(&mut self, l: &'b VPlaceholder, r: &'b [VNode<'b>]) {
+ fn replace_placeholder(
+ &mut self,
+ l: &'b VPlaceholder,
+ r: impl IntoIterator- >,
+ ) {
let m = self.create_children(r);
let id = l.id.get().unwrap();
self.mutations.push(Mutation::ReplaceWith { id, m });
@@ -816,8 +840,11 @@ impl<'b> VirtualDom {
.map(|path| path.len());
// if the path is 1 the attribute is in the root, so we don't need to clean it up
// if the path is 0, the attribute is a not attached at all, so we don't need to clean it up
- if let Some(..=1) = path_len {
- continue;
+
+ if let Some(len) = path_len {
+ if (..=1).contains(&len) {
+ continue;
+ }
}
let next_id = attr.mounted_element.get();
@@ -877,14 +904,20 @@ impl<'b> VirtualDom {
}
fn remove_component_node(&mut self, comp: &VComponent, gen_muts: bool) {
- let scope = comp.scope.take().unwrap();
+ let scope = comp
+ .scope
+ .take()
+ .expect("VComponents to always have a scope");
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
- RenderReturn::Sync(Some(t)) => {
- println!("Removing component node sync {:?}", gen_muts);
- self.remove_node(t, gen_muts)
+ RenderReturn::Ready(t) => self.remove_node(t, gen_muts),
+ RenderReturn::Aborted(t) => {
+ if let Some(id) = t.id.get() {
+ self.try_reclaim(id);
+ }
+ return;
}
- _ => todo!("cannot handle nonstandard nodes"),
+ _ => todo!(),
};
let props = self.scopes[scope.0].props.take();
@@ -910,7 +943,7 @@ impl<'b> VirtualDom {
Some(Component(comp)) => {
let scope = comp.scope.get().unwrap();
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
- RenderReturn::Sync(Some(t)) => self.find_first_element(t),
+ RenderReturn::Ready(t) => self.find_first_element(t),
_ => todo!("cannot handle nonstandard nodes"),
}
}
@@ -926,7 +959,7 @@ impl<'b> VirtualDom {
Some(Component(comp)) => {
let scope = comp.scope.get().unwrap();
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
- RenderReturn::Sync(Some(t)) => self.find_last_element(t),
+ RenderReturn::Ready(t) => self.find_last_element(t),
_ => todo!("cannot handle nonstandard nodes"),
}
}
diff --git a/packages/core/src/error_boundary.rs b/packages/core/src/error_boundary.rs
index fd8810f1a..071fbe48b 100644
--- a/packages/core/src/error_boundary.rs
+++ b/packages/core/src/error_boundary.rs
@@ -1,19 +1,203 @@
-use std::cell::RefCell;
-
-use crate::ScopeId;
+use crate::{ScopeId, ScopeState};
+use std::{
+ any::{Any, TypeId},
+ cell::RefCell,
+ fmt::Debug,
+};
/// A boundary that will capture any errors from child components
-#[allow(dead_code)]
pub struct ErrorBoundary {
- error: RefCell