make desktop compile

This commit is contained in:
Evan Almloff 2024-01-05 14:39:45 -06:00
parent 21b71e992f
commit 3865f44081
27 changed files with 357 additions and 422 deletions

View file

@ -1,5 +1,5 @@
use crate::{
scope_context::{consume_context, current_scope_id, schedule_update_any},
global_context::{consume_context, current_scope_id},
Element, IntoDynNode, Properties, ScopeId, Template, TemplateAttribute, TemplateNode, VNode,
};
use std::{
@ -9,7 +9,6 @@ use std::{
error::Error,
fmt::{Debug, Display},
rc::Rc,
sync::Arc,
};
/// Provide an error boundary to catch errors from child components
@ -28,7 +27,6 @@ pub struct ErrorBoundary {
pub struct ErrorBoundaryInner {
error: RefCell<Option<CapturedError>>,
_id: ScopeId,
rerun_boundary: Arc<dyn Fn(ScopeId) + Send + Sync>,
}
impl Debug for ErrorBoundaryInner {
@ -81,7 +79,6 @@ impl Default for ErrorBoundaryInner {
error: RefCell::new(None),
_id: current_scope_id()
.expect("Cannot create an error boundary outside of a component's scope."),
rerun_boundary: schedule_update_any().unwrap(),
}
}
}
@ -93,15 +90,11 @@ impl ErrorBoundary {
}
/// Create a new error boundary in the current scope
pub(crate) fn new_in_scope(
scope: ScopeId,
rerun_boundary: Arc<dyn Fn(ScopeId) + Send + Sync>,
) -> Self {
pub(crate) fn new_in_scope(scope: ScopeId) -> Self {
Self {
inner: Rc::new(ErrorBoundaryInner {
error: RefCell::new(None),
_id: scope,
rerun_boundary,
}),
}
}
@ -113,13 +106,14 @@ impl ErrorBoundary {
error: Box<dyn Debug + 'static>,
backtrace: Backtrace,
) {
println!("{:?} {:?}", error, self.inner._id);
self.inner.error.replace(Some(CapturedError {
error,
scope,
backtrace,
}));
(self.inner.rerun_boundary)(self.inner._id);
if self.inner._id != ScopeId::ROOT {
self.inner._id.needs_update();
}
}
/// Take any error that has been captured by this error boundary

View file

@ -1,4 +1,4 @@
use crate::{runtime::with_runtime, scope_context::current_scope_id, ScopeId};
use crate::{global_context::current_scope_id, runtime::with_runtime, ScopeId};
use std::{
cell::{Cell, RefCell},
rc::Rc,

View file

@ -0,0 +1,115 @@
use futures_util::Future;
use crate::{
runtime::{with_current_scope, with_runtime},
Element, ScopeId, Task,
};
/// Get the current scope id
pub fn current_scope_id() -> Option<ScopeId> {
with_runtime(|rt| rt.current_scope_id()).flatten()
}
#[doc(hidden)]
/// Check if the virtual dom is currently inside of the body of a component
pub fn vdom_is_rendering() -> bool {
with_runtime(|rt| rt.rendering.get()).unwrap_or_default()
}
/// Consume context from the current scope
pub fn consume_context<T: 'static + Clone>() -> Option<T> {
with_current_scope(|cx| cx.consume_context::<T>()).flatten()
}
/// Consume context from the current scope
pub fn consume_context_from_scope<T: 'static + Clone>(scope_id: ScopeId) -> Option<T> {
with_runtime(|rt| {
rt.get_context(scope_id)
.and_then(|cx| cx.consume_context::<T>())
})
.flatten()
}
/// Check if the current scope has a context
pub fn has_context<T: 'static + Clone>() -> Option<T> {
with_current_scope(|cx| cx.has_context::<T>()).flatten()
}
/// Provide context to the current scope
pub fn provide_context<T: 'static + Clone>(value: T) -> Option<T> {
with_current_scope(|cx| cx.provide_context(value))
}
/// Provide a context to the root scope
pub fn provide_root_context<T: 'static + Clone>(value: T) -> Option<T> {
with_current_scope(|cx| cx.provide_root_context(value))
}
/// Suspends the current component
pub fn suspend() -> Option<Element> {
with_current_scope(|cx| {
cx.suspend();
});
None
}
/// Pushes the future onto the poll queue to be polled after the component renders.
pub fn push_future(fut: impl Future<Output = ()> + 'static) -> Option<Task> {
with_current_scope(|cx| cx.push_future(fut))
}
/// Spawns the future but does not return the [`TaskId`]
pub fn spawn(fut: impl Future<Output = ()> + 'static) {
with_current_scope(|cx| cx.spawn(fut));
}
/// Spawn a future that Dioxus won't clean up when this component is unmounted
///
/// This is good for tasks that need to be run after the component has been dropped.
pub fn spawn_forever(fut: impl Future<Output = ()> + 'static) -> Option<Task> {
with_current_scope(|cx| cx.spawn_forever(fut))
}
/// Informs the scheduler that this task is no longer needed and should be removed.
///
/// This drops the task immediately.
pub fn remove_future(id: Task) {
with_current_scope(|cx| cx.remove_future(id));
}
/// Store a value between renders. The foundational hook for all other hooks.
///
/// Accepts an `initializer` closure, which is run on the first use of the hook (typically the initial render). The return value of this closure is stored for the lifetime of the component, and a mutable reference to it is provided on every render as the return value of `use_hook`.
///
/// When the component is unmounted (removed from the UI), the value is dropped. This means you can return a custom type and provide cleanup code by implementing the [`Drop`] trait
///
/// # Example
///
/// ```
/// use dioxus_core::ScopeState;
///
/// // prints a greeting on the initial render
/// pub fn use_hello_world() {
/// once(|| println!("Hello, world!"));
/// }
/// ```
pub fn once<State: Clone + 'static>(initializer: impl FnOnce() -> State) -> State {
with_current_scope(|cx| cx.use_hook(initializer)).expect("to be in a dioxus runtime")
}
/// Get the current render since the inception of this component
///
/// This can be used as a helpful diagnostic when debugging hooks/renders, etc
pub fn generation() -> Option<usize> {
with_current_scope(|cx| Some(cx.generation())).expect("to be in a dioxus runtime")
}
/// Get the parent of the current scope if it exists
pub fn parent_scope() -> Option<ScopeId> {
with_current_scope(|cx| cx.parent_id()).flatten()
}
/// Mark the current scope as dirty, causing it to re-render
pub fn needs_update() {
with_current_scope(|cx| cx.needs_update());
}

View file

@ -11,6 +11,7 @@ mod dirty_scope;
mod error_boundary;
mod events;
mod fragment;
mod global_context;
mod mutations;
mod nodes;
mod properties;
@ -27,13 +28,13 @@ pub(crate) mod innerlude {
pub use crate::error_boundary::*;
pub use crate::events::*;
pub use crate::fragment::*;
pub use crate::global_context::*;
pub use crate::mutations::*;
pub use crate::nodes::RenderReturn;
pub use crate::nodes::*;
pub use crate::properties::*;
pub use crate::runtime::{Runtime, RuntimeGuard};
pub use crate::scheduler::*;
pub use crate::scope_context::*;
pub use crate::scopes::*;
pub use crate::virtual_dom::*;
@ -75,8 +76,8 @@ pub(crate) mod innerlude {
pub use crate::innerlude::{
fc_to_builder, generation, once, vdom_is_rendering, AnyValue, Attribute, AttributeValue,
CapturedError, Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode,
Mutation, MutationsVec, Properties, RenderReturn, ScopeId, ScopeState, TaskId, Template,
TemplateAttribute, TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom,
Mutation, MutationsVec, Properties, RenderReturn, ScopeId, Task, Template, TemplateAttribute,
TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom, WriteMutations,
};
/// The purpose of this module is to alleviate imports of many common types
@ -85,10 +86,10 @@ pub use crate::innerlude::{
pub mod prelude {
pub use crate::innerlude::{
consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, generation,
has_context, once, provide_context, provide_context_to_scope, provide_root_context,
push_future, remove_future, schedule_update_any, spawn, spawn_forever, suspend,
use_error_boundary, AnyValue, Component, Element, ErrorBoundary, Event, EventHandler,
Fragment, IntoAttributeValue, IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId,
TaskId, Template, TemplateAttribute, TemplateNode, Throw, VNode, VirtualDom,
has_context, needs_update, once, parent_scope, provide_context, provide_root_context,
push_future, remove_future, spawn, spawn_forever, suspend, use_error_boundary, AnyValue,
Component, Element, ErrorBoundary, Event, EventHandler, Fragment, IntoAttributeValue,
IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, Task, Template, TemplateAttribute,
TemplateNode, Throw, VNode, VirtualDom,
};
}

View file

@ -518,8 +518,7 @@ impl Attribute {
///
/// "Volatile" referes to whether or not Dioxus should always override the value. This helps prevent the UI in
/// some renderers stay in sync with the VirtualDom's understanding of the world
pub fn attr(
&self,
pub fn new(
name: &'static str,
value: impl IntoAttributeValue,
namespace: Option<&'static str>,

View file

@ -41,6 +41,14 @@ where
.flatten()
}
/// Runs a function with the current scope
pub(crate) fn with_scope<F, R>(scope: ScopeId, f: F) -> Option<R>
where
F: FnOnce(&ScopeContext) -> R,
{
with_runtime(|runtime| runtime.get_context(scope).map(|sc| f(&sc))).flatten()
}
/// A global runtime that is shared across all scopes that provides the async runtime and context API
pub struct Runtime {
pub(crate) scope_contexts: RefCell<Vec<Option<ScopeContext>>>,
@ -98,7 +106,7 @@ impl Runtime {
}
}
/// A gaurd for a new runtime. This must be used to override the current runtime when importing components from a dynamic library that has it's own runtime.
/// A guard for a new runtime. This must be used to override the current runtime when importing components from a dynamic library that has it's own runtime.
///
/// ```rust
/// use dioxus::prelude::*;

View file

@ -15,7 +15,7 @@ pub(crate) enum SchedulerMsg {
Immediate(ScopeId),
/// A task has woken and needs to be progressed
TaskNotified(TaskId),
TaskNotified(Task),
}
use std::{cell::RefCell, rc::Rc};

View file

@ -1,6 +1,7 @@
use futures_util::task::ArcWake;
use super::{Scheduler, SchedulerMsg};
use crate::innerlude::{push_future, remove_future};
use crate::ScopeId;
use std::cell::RefCell;
use std::future::Future;
@ -14,7 +15,29 @@ use std::task::Waker;
/// once a Task has been completed.
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct TaskId(pub usize);
pub struct Task(pub(crate) usize);
impl Task {
/// Start a new future on the same thread as the rest of the VirtualDom.
///
/// This future will not contribute to suspense resolving, so you should primarily use this for reacting to changes
/// and long running tasks.
///
/// Whenever the component that owns this future is dropped, the future will be dropped as well.
///
/// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which
/// will only occur when the VirtualDom itself has been dropped.
pub fn new(task: impl Future<Output = ()> + 'static) -> Self {
push_future(task).expect("to be in a dioxus runtime")
}
/// Drop the task immediately.
///
/// This does not abort the task, so you'll want to wrap it in an abort handle if that's important to you
pub fn stop(self) {
remove_future(self);
}
}
/// the task itself is the waker
pub(crate) struct LocalTask {
@ -32,12 +55,12 @@ impl Scheduler {
/// Whenever the component that owns this future is dropped, the future will be dropped as well.
///
/// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which
/// will only occur when the VirtuaalDom itself has been dropped.
pub fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> TaskId {
/// will only occur when the VirtualDom itself has been dropped.
pub fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> Task {
let mut tasks = self.tasks.borrow_mut();
let entry = tasks.vacant_entry();
let task_id = TaskId(entry.key());
let task_id = Task(entry.key());
let task = LocalTask {
task: RefCell::new(Box::pin(task)),
@ -63,14 +86,14 @@ impl Scheduler {
/// Drop the future with the given TaskId
///
/// This does not abort the task, so you'll want to wrap it in an aborthandle if that's important to you
pub fn remove(&self, id: TaskId) -> Option<LocalTask> {
/// This does not abort the task, so you'll want to wrap it in an abort handle if that's important to you
pub fn remove(&self, id: Task) -> Option<LocalTask> {
self.tasks.borrow_mut().try_remove(id.0)
}
}
pub struct LocalTaskHandle {
id: TaskId,
id: Task,
tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
}

View file

@ -1,4 +1,4 @@
use crate::{runtime::RuntimeGuard, TaskId, VirtualDom};
use crate::{runtime::RuntimeGuard, Task, VirtualDom};
use std::task::Context;
impl VirtualDom {
@ -6,7 +6,7 @@ impl VirtualDom {
///
/// This is precise, meaning we won't poll every task, just tasks that have woken up as notified to use by the
/// queue
pub(crate) fn handle_task_wakeup(&mut self, id: TaskId) {
pub(crate) fn handle_task_wakeup(&mut self, id: Task) {
let _runtime = RuntimeGuard::new(self.runtime.clone());
let mut tasks = self.runtime.scheduler.tasks.borrow_mut();

View file

@ -11,7 +11,7 @@ impl VirtualDom {
pub(super) fn new_scope(&mut self, props: BoxedAnyProps, name: &'static str) -> &ScopeState {
let parent_id = self.runtime.current_scope_id();
let height = parent_id
.and_then(|parent_id| self.get_scope(parent_id).map(|f| f.context().height + 1))
.and_then(|parent_id| self.runtime.get_context(parent_id).map(|f| f.height + 1))
.unwrap_or(0);
let entry = self.scopes.vacant_entry();
let id = ScopeId(entry.key());

View file

@ -1,7 +1,7 @@
use crate::{
innerlude::{Scheduler, SchedulerMsg},
runtime::{with_current_scope, with_runtime},
Element, ScopeId, TaskId,
runtime::{with_runtime, with_scope},
Element, ScopeId, Task,
};
use rustc_hash::FxHashSet;
use std::{
@ -9,7 +9,6 @@ use std::{
cell::{Cell, RefCell},
future::Future,
rc::Rc,
sync::Arc,
};
/// A component's state separate from its props.
@ -32,7 +31,7 @@ pub(crate) struct ScopeContext {
pub(crate) hook_index: Cell<usize>,
pub(crate) tasks: Rc<Scheduler>,
pub(crate) spawned_tasks: RefCell<FxHashSet<TaskId>>,
pub(crate) spawned_tasks: RefCell<FxHashSet<Task>>,
}
impl ScopeContext {
@ -62,42 +61,11 @@ impl ScopeContext {
self.parent_id
}
pub fn scope_id(&self) -> ScopeId {
self.id
}
/// Create a subscription that schedules a future render for the reference component
///
/// ## Notice: you should prefer using [`Self::schedule_update_any`] and [`Self::scope_id`]
pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
let (chan, id) = (self.tasks.sender.clone(), self.scope_id());
Arc::new(move || drop(chan.unbounded_send(SchedulerMsg::Immediate(id))))
}
/// Schedule an update for any component given its [`ScopeId`].
///
/// A component's [`ScopeId`] can be obtained from `use_hook` or the [`ScopeState::scope_id`] method.
///
/// This method should be used when you want to schedule an update for a component
pub fn schedule_update_any(&self) -> Arc<dyn Fn(ScopeId) + Send + Sync> {
let chan = self.tasks.sender.clone();
Arc::new(move |id| {
chan.unbounded_send(SchedulerMsg::Immediate(id)).unwrap();
})
}
/// Mark this scope as dirty, and schedule a render for it.
pub fn needs_update(&self) {
self.needs_update_any(self.scope_id());
}
/// Get the [`ScopeId`] of a mounted component.
///
/// `ScopeId` is not unique for the lifetime of the [`crate::VirtualDom`] - a [`ScopeId`] will be reused if a component is unmounted.
pub fn needs_update_any(&self, id: ScopeId) {
self.tasks
.sender
.unbounded_send(SchedulerMsg::Immediate(id))
.unbounded_send(SchedulerMsg::Immediate(self.id))
.expect("Scheduler to exist if scope exists");
}
@ -221,7 +189,7 @@ impl ScopeContext {
}
/// Pushes the future onto the poll queue to be polled after the component renders.
pub fn push_future(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
pub fn push_future(&self, fut: impl Future<Output = ()> + 'static) -> Task {
let id = self.tasks.spawn(self.id, fut);
self.spawned_tasks.borrow_mut().insert(id);
id
@ -235,7 +203,7 @@ impl ScopeContext {
/// Spawn a future that Dioxus won't clean up when this component is unmounted
///
/// This is good for tasks that need to be run after the component has been dropped.
pub fn spawn_forever(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
pub fn spawn_forever(&self, fut: impl Future<Output = ()> + 'static) -> Task {
// The root scope will never be unmounted so we can just add the task at the top of the app
self.tasks.spawn(ScopeId::ROOT, fut)
}
@ -243,7 +211,7 @@ impl ScopeContext {
/// Informs the scheduler that this task is no longer needed and should be removed.
///
/// This drops the task immediately.
pub fn remove_future(&self, id: TaskId) {
pub fn remove_future(&self, id: Task) {
self.tasks.remove(id);
}
@ -317,115 +285,74 @@ impl Drop for ScopeContext {
}
}
/// Schedule an update for any component given its [`ScopeId`].
///
/// A component's [`ScopeId`] can be obtained from `use_hook` or the [`crate::scopes::ScopeState::scope_id`] method.
///
/// This method should be used when you want to schedule an update for a component
pub fn schedule_update_any() -> Option<Arc<dyn Fn(ScopeId) + Send + Sync>> {
with_current_scope(|cx| cx.schedule_update_any())
}
impl ScopeId {
/// Get the current scope id
pub fn current_scope_id(self) -> Option<ScopeId> {
with_runtime(|rt| rt.current_scope_id()).flatten()
}
/// Get the current scope id
pub fn current_scope_id() -> Option<ScopeId> {
with_runtime(|rt| rt.current_scope_id()).flatten()
}
#[doc(hidden)]
/// Check if the virtual dom is currently inside of the body of a component
pub fn vdom_is_rendering(self) -> bool {
with_runtime(|rt| rt.rendering.get()).unwrap_or_default()
}
#[doc(hidden)]
/// Check if the virtual dom is currently inside of the body of a component
pub fn vdom_is_rendering() -> bool {
with_runtime(|rt| rt.rendering.get()).unwrap_or_default()
}
/// Consume context from the current scope
pub fn consume_context<T: 'static + Clone>(self) -> Option<T> {
with_scope(self, |cx| cx.consume_context::<T>()).flatten()
}
/// Consume context from the current scope
pub fn consume_context<T: 'static + Clone>() -> Option<T> {
with_current_scope(|cx| cx.consume_context::<T>()).flatten()
}
/// Consume context from the current scope
pub fn consume_context_from_scope<T: 'static + Clone>(self, scope_id: ScopeId) -> Option<T> {
with_runtime(|rt| {
rt.get_context(scope_id)
.and_then(|cx| cx.consume_context::<T>())
})
.flatten()
}
/// Consume context from the current scope
pub fn consume_context_from_scope<T: 'static + Clone>(scope_id: ScopeId) -> Option<T> {
with_runtime(|rt| {
rt.get_context(scope_id)
.and_then(|cx| cx.consume_context::<T>())
})
.flatten()
}
/// Check if the current scope has a context
pub fn has_context<T: 'static + Clone>(self) -> Option<T> {
with_scope(self, |cx| cx.has_context::<T>()).flatten()
}
/// Check if the current scope has a context
pub fn has_context<T: 'static + Clone>() -> Option<T> {
with_current_scope(|cx| cx.has_context::<T>()).flatten()
}
/// Provide context to the current scope
pub fn provide_context<T: 'static + Clone>(self, value: T) -> Option<T> {
with_scope(self, |cx| cx.provide_context(value))
}
/// Provide context to the current scope
pub fn provide_context<T: 'static + Clone>(value: T) -> Option<T> {
with_current_scope(|cx| cx.provide_context(value))
}
/// Suspends the current component
pub fn suspend(self) -> Option<Element> {
with_scope(self, |cx| {
cx.suspend();
});
None
}
/// Provide context to the the given scope
pub fn provide_context_to_scope<T: 'static + Clone>(scope_id: ScopeId, value: T) -> Option<T> {
with_runtime(|rt| rt.get_context(scope_id).map(|cx| cx.provide_context(value))).flatten()
}
/// Pushes the future onto the poll queue to be polled after the component renders.
pub fn push_future(self, fut: impl Future<Output = ()> + 'static) -> Option<Task> {
with_scope(self, |cx| cx.push_future(fut))
}
/// Provide a context to the root scope
pub fn provide_root_context<T: 'static + Clone>(value: T) -> Option<T> {
with_current_scope(|cx| cx.provide_root_context(value))
}
/// Spawns the future but does not return the [`TaskId`]
pub fn spawn(self, fut: impl Future<Output = ()> + 'static) {
with_scope(self, |cx| cx.spawn(fut));
}
/// Suspends the current component
pub fn suspend() -> Option<Element> {
with_current_scope(|cx| {
cx.suspend();
});
None
}
/// Get the current render since the inception of this component
///
/// This can be used as a helpful diagnostic when debugging hooks/renders, etc
pub fn generation(self) -> Option<usize> {
with_scope(self, |cx| Some(cx.generation())).expect("to be in a dioxus runtime")
}
/// Pushes the future onto the poll queue to be polled after the component renders.
pub fn push_future(fut: impl Future<Output = ()> + 'static) -> Option<TaskId> {
with_current_scope(|cx| cx.push_future(fut))
}
/// Get the parent of the current scope if it exists
pub fn parent_scope(self) -> Option<ScopeId> {
with_scope(self, |cx| cx.parent_id()).flatten()
}
/// Spawns the future but does not return the [`TaskId`]
pub fn spawn(fut: impl Future<Output = ()> + 'static) {
with_current_scope(|cx| cx.spawn(fut));
}
/// Spawn a future that Dioxus won't clean up when this component is unmounted
///
/// This is good for tasks that need to be run after the component has been dropped.
pub fn spawn_forever(fut: impl Future<Output = ()> + 'static) -> Option<TaskId> {
with_current_scope(|cx| cx.spawn_forever(fut))
}
/// Informs the scheduler that this task is no longer needed and should be removed.
///
/// This drops the task immediately.
pub fn remove_future(id: TaskId) {
with_current_scope(|cx| cx.remove_future(id));
}
/// Store a value between renders. The foundational hook for all other hooks.
///
/// Accepts an `initializer` closure, which is run on the first use of the hook (typically the initial render). The return value of this closure is stored for the lifetime of the component, and a mutable reference to it is provided on every render as the return value of `use_hook`.
///
/// When the component is unmounted (removed from the UI), the value is dropped. This means you can return a custom type and provide cleanup code by implementing the [`Drop`] trait
///
/// # Example
///
/// ```
/// use dioxus_core::ScopeState;
///
/// // prints a greeting on the initial render
/// pub fn use_hello_world() {
/// once(|| println!("Hello, world!"));
/// }
/// ```
pub fn once<State: Clone + 'static>(initializer: impl FnOnce() -> State) -> State {
with_current_scope(|cx| cx.use_hook(initializer)).expect("to be in a dioxus runtime")
}
/// Get the current render since the inception of this component
///
/// This can be used as a helpful diagnostic when debugging hooks/renders, etc
pub fn generation() -> Option<usize> {
with_current_scope(|cx| Some(cx.generation())).expect("to be in a dioxus runtime")
/// Mark the current scope as dirty, causing it to re-render
pub fn needs_update(self) {
with_scope(self, |cx| cx.needs_update());
}
}

View file

@ -10,7 +10,7 @@ use std::{cell::Ref, fmt::Debug, rc::Rc};
/// time for any logic that relies on these IDs to properly update.
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct ScopeId(pub usize);
pub struct ScopeId(pub(crate) usize);
impl ScopeId {
/// The root ScopeId.
@ -30,7 +30,7 @@ impl ScopeId {
/// A component's state separate from its props.
///
/// This struct exists to provide a common interface for all scopes without relying on generics.
pub struct ScopeState {
pub(crate) struct ScopeState {
pub(crate) runtime: Rc<Runtime>,
pub(crate) context_id: ScopeId,

View file

@ -18,7 +18,7 @@ use crate::{
use futures_util::{pin_mut, StreamExt};
use rustc_hash::{FxHashMap, FxHashSet};
use slab::Slab;
use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc, sync::Arc};
use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc};
/// A virtual node system that progresses user events and diffs UI trees.
///
@ -277,10 +277,7 @@ impl VirtualDom {
// Unlike react, we provide a default error boundary that just renders the error as a string
root.context()
.provide_context(Rc::new(ErrorBoundary::new_in_scope(
ScopeId::ROOT,
Arc::new(|_| {}),
)));
.provide_context(Rc::new(ErrorBoundary::new_in_scope(ScopeId::ROOT)));
// the root element is always given element ID 0 since it's the container for the entire tree
dom.elements.insert(None);
@ -291,17 +288,23 @@ impl VirtualDom {
/// Get the state for any scope given its ID
///
/// This is useful for inserting or removing contexts from a scope, or rendering out its root node
pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeState> {
pub(crate) fn get_scope(&self, id: ScopeId) -> Option<&ScopeState> {
self.scopes.get(id.0).map(|s| &**s)
}
/// Get the single scope at the top of the VirtualDom tree that will always be around
///
/// This scope has a ScopeId of 0 and is the root of the tree
pub fn base_scope(&self) -> &ScopeState {
pub(crate) fn base_scope(&self) -> &ScopeState {
self.get_scope(ScopeId::ROOT).unwrap()
}
/// Run a closure inside the dioxus runtime
pub fn in_runtime<O>(&self, f: impl FnOnce() -> O) -> O {
let _runtime = RuntimeGuard::new(self.runtime.clone());
f()
}
/// Build the virtualdom with a global context inserted into the base scope
///
/// This is useful for what is essentially dependency injection when building the app

View file

@ -7,11 +7,13 @@ use crate::shortcut::{HotKey, ShortcutId, ShortcutRegistry, ShortcutRegistryErro
use crate::AssetHandler;
use crate::Config;
use crate::WebviewHandler;
use dioxus_core::ScopeState;
use dioxus_core::once;
use dioxus_core::ScopeId;
use dioxus_core::VirtualDom;
#[cfg(all(feature = "hot-reload", debug_assertions))]
use dioxus_hot_reload::HotReloadMsg;
use dioxus_interpreter_js::binary_protocol::Channel;
use dioxus_interpreter_js::MutationState;
use rustc_hash::FxHashMap;
use slab::Slab;
use std::cell::RefCell;
@ -45,10 +47,8 @@ pub fn window() -> DesktopContext {
/// Get an imperative handle to the current window
#[deprecated = "Prefer the using the `window` function directly for cleaner code"]
pub fn use_window(cx: &ScopeState) -> &DesktopContext {
cx.use_hook(|| cx.consume_context::<DesktopContext>())
.as_ref()
.unwrap()
pub fn use_window() -> DesktopContext {
once(window)
}
/// This handles communication between the requests that the webview makes and the interpreter. The interpreter constantly makes long running requests to the webview to get any edits that should be made to the DOM almost like server side events.
@ -123,10 +123,8 @@ pub struct DesktopService {
pub(crate) shortcut_manager: ShortcutRegistry,
pub(crate) edit_queue: EditQueue,
pub(crate) templates: RefCell<FxHashMap<String, u16>>,
pub(crate) max_template_count: AtomicU16,
pub(crate) mutation_state: RefCell<MutationState>,
pub(crate) channel: RefCell<Channel>,
pub(crate) asset_handlers: AssetHandlerRegistry,
#[cfg(target_os = "ios")]
@ -165,9 +163,7 @@ impl DesktopService {
event_handlers,
shortcut_manager,
edit_queue,
templates: Default::default(),
max_template_count: Default::default(),
channel: Default::default(),
mutation_state: Default::default(),
asset_handlers,
#[cfg(target_os = "ios")]
views: Default::default(),
@ -192,11 +188,11 @@ impl DesktopService {
self.shortcut_manager.clone(),
);
let desktop_context = window
.dom
.base_scope()
.consume_context::<Rc<DesktopService>>()
.unwrap();
let desktop_context = window.dom.in_runtime(|| {
ScopeId::ROOT
.consume_context::<Rc<DesktopService>>()
.unwrap()
});
let id = window.desktop_context.webview.window().id();
@ -463,10 +459,9 @@ impl WryWindowEventHandlerInner {
/// Get a closure that executes any JavaScript in the WebView context.
pub fn use_wry_event_handler(
cx: &ScopeState,
handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
) -> &WryEventHandler {
cx.use_hook(move || {
) -> WryEventHandler {
once(move || {
let desktop = window();
let id = desktop.create_wry_event_handler(handler);
@ -481,6 +476,7 @@ pub fn use_wry_event_handler(
/// A wry event handler that is scoped to the current component and window. The event handler will only receive events for the window it was created for and global events.
///
/// This will automatically be removed when the component is unmounted.
#[derive(Clone)]
pub struct WryEventHandler {
handlers: WindowEventHandlers,
/// The unique identifier of the event handler.

View file

@ -1,16 +1,17 @@
#![allow(clippy::await_holding_refcell_ref)]
use async_trait::async_trait;
use dioxus_core::ScopeState;
use dioxus_core::prelude::consume_context;
use dioxus_core::prelude::provide_context;
use dioxus_html::prelude::{EvalError, EvalProvider, Evaluator};
use std::{cell::RefCell, rc::Rc};
use crate::{query::Query, DesktopContext};
/// Provides the DesktopEvalProvider through [`cx.provide_context`].
pub fn init_eval(cx: &ScopeState) {
let desktop_ctx = cx.consume_context::<DesktopContext>().unwrap();
pub fn init_eval() {
let desktop_ctx = consume_context::<DesktopContext>().unwrap();
let provider: Rc<dyn EvalProvider> = Rc::new(DesktopEvalProvider { desktop_ctx });
cx.provide_context(provider);
provide_context(provider);
}
/// Reprents the desktop-target's provider of evaluators.

View file

@ -126,7 +126,7 @@ pub fn launch_cfg(root: Component, config_builder: Config) {
/// })
/// }
/// ```
pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config) {
pub fn launch_with_props<P: Clone + 'static>(root: Component<P>, props: P, cfg: Config) {
let event_loop = EventLoopBuilder::<UserWindowEvent>::with_user_event().build();
let proxy = event_loop.create_proxy();
@ -301,13 +301,13 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
// check for a mounted event placeholder and replace it with a desktop specific element
let as_any = if let dioxus_html::EventData::Mounted = &data {
let query = view
.dom
.base_scope()
.consume_context::<DesktopContext>()
.unwrap()
.query
.clone();
let query = view.dom.in_runtime(|| {
ScopeId::ROOT
.consume_context::<DesktopContext>()
.unwrap()
.query
.clone()
});
let element =
DesktopElement::new(element, view.desktop_context.clone(), query);
@ -319,7 +319,9 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
view.dom.handle_event(&name, as_any, element, bubbles);
send_edits(view.dom.render_immediate(), &view.desktop_context);
view.dom
.render_immediate(&mut *view.desktop_context.mutation_state.borrow_mut());
send_edits(&view.desktop_context);
}
// When the webview sends a query, we need to send it to the query manager which handles dispatching the data to the correct pending query
@ -328,13 +330,13 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
if let Ok(result) = serde_json::from_value::<QueryResult>(params) {
let view = webviews.get(&event.1).unwrap();
let query = view
.dom
.base_scope()
.consume_context::<DesktopContext>()
.unwrap()
.query
.clone();
let query = view.dom.in_runtime(|| {
ScopeId::ROOT
.consume_context::<DesktopContext>()
.unwrap()
.query
.clone()
});
query.send(result);
}
@ -342,7 +344,9 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
EventData::Ipc(msg) if msg.method() == "initialize" => {
let view = webviews.get_mut(&event.1).unwrap();
send_edits(view.dom.rebuild(), &view.desktop_context);
view.dom
.rebuild(&mut *view.desktop_context.mutation_state.borrow_mut());
send_edits(&view.desktop_context);
view.desktop_context
.webview
.window()
@ -384,7 +388,10 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
view.dom.handle_event(event_name, data, id, event_bubbles);
}
send_edits(view.dom.render_immediate(), &view.desktop_context);
view.dom.render_immediate(
&mut *view.desktop_context.mutation_state.borrow_mut(),
);
send_edits(&view.desktop_context);
}
}
@ -417,11 +424,12 @@ fn create_new_window(
asset_handlers,
));
let cx = dom.base_scope();
cx.provide_context(desktop_context.clone());
// Init eval
init_eval(cx);
let query = dom.in_runtime(|| {
let query = ScopeId::ROOT.provide_context(desktop_context.clone());
// Init eval
init_eval();
query
});
WebviewHandler {
// We want to poll the virtualdom and the event loop at the same time, so the waker will be connected to both
@ -461,149 +469,17 @@ fn poll_vdom(view: &mut WebviewHandler) {
}
}
send_edits(view.dom.render_immediate(), &view.desktop_context);
view.dom
.render_immediate(&mut *view.desktop_context.mutation_state.borrow_mut());
send_edits(&view.desktop_context);
}
}
/// Send a list of mutations to the webview
fn send_edits(edits: Mutations, desktop_context: &DesktopContext) {
let mut channel = desktop_context.channel.borrow_mut();
let mut templates = desktop_context.templates.borrow_mut();
if let Some(bytes) = apply_edits(
edits,
&mut channel,
&mut templates,
&desktop_context.max_template_count,
) {
desktop_context.edit_queue.add_edits(bytes)
}
}
fn apply_edits(
mutations: Mutations,
channel: &mut Channel,
templates: &mut FxHashMap<String, u16>,
max_template_count: &AtomicU16,
) -> Option<Vec<u8>> {
use dioxus_core::Mutation::*;
if mutations.templates.is_empty() && mutations.edits.is_empty() {
return None;
}
for template in mutations.templates {
add_template(&template, channel, templates, max_template_count);
}
for edit in mutations.edits {
match edit {
AppendChildren { id, m } => channel.append_children(id.0 as u32, m as u16),
AssignId { path, id } => channel.assign_id(path, id.0 as u32),
CreatePlaceholder { id } => channel.create_placeholder(id.0 as u32),
CreateTextNode { value, id } => channel.create_text_node(value, id.0 as u32),
HydrateText { path, value, id } => channel.hydrate_text(path, value, id.0 as u32),
LoadTemplate { name, index, id } => {
if let Some(tmpl_id) = templates.get(name) {
channel.load_template(*tmpl_id, index as u16, id.0 as u32)
}
}
ReplaceWith { id, m } => channel.replace_with(id.0 as u32, m as u16),
ReplacePlaceholder { path, m } => channel.replace_placeholder(path, m as u16),
InsertAfter { id, m } => channel.insert_after(id.0 as u32, m as u16),
InsertBefore { id, m } => channel.insert_before(id.0 as u32, m as u16),
SetAttribute {
name,
value,
id,
ns,
} => match value {
BorrowedAttributeValue::Text(txt) => {
channel.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
}
BorrowedAttributeValue::Float(f) => {
channel.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default())
}
BorrowedAttributeValue::Int(n) => {
channel.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default())
}
BorrowedAttributeValue::Bool(b) => channel.set_attribute(
id.0 as u32,
name,
if b { "true" } else { "false" },
ns.unwrap_or_default(),
),
BorrowedAttributeValue::None => {
channel.remove_attribute(id.0 as u32, name, ns.unwrap_or_default())
}
_ => unreachable!(),
},
SetText { value, id } => channel.set_text(id.0 as u32, value),
NewEventListener { name, id, .. } => {
channel.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8)
}
RemoveEventListener { name, id } => {
channel.remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8)
}
Remove { id } => channel.remove(id.0 as u32),
PushRoot { id } => channel.push_root(id.0 as u32),
}
}
let bytes: Vec<_> = channel.export_memory().collect();
channel.reset();
Some(bytes)
}
fn add_template(
template: &Template<'static>,
channel: &mut Channel,
templates: &mut FxHashMap<String, u16>,
max_template_count: &AtomicU16,
) {
let current_max_template_count = max_template_count.load(std::sync::atomic::Ordering::Relaxed);
for root in template.roots.iter() {
create_template_node(channel, root);
templates.insert(template.name.to_owned(), current_max_template_count);
}
channel.add_templates(current_max_template_count, template.roots.len() as u16);
max_template_count.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
fn create_template_node(channel: &mut Channel, v: &'static TemplateNode<'static>) {
use TemplateNode::*;
match v {
Element {
tag,
namespace,
attrs,
children,
..
} => {
// Push the current node onto the stack
match namespace {
Some(ns) => channel.create_element_ns(tag, ns),
None => channel.create_element(tag),
}
// Set attributes on the current node
for attr in *attrs {
if let TemplateAttribute::Static {
name,
value,
namespace,
} = attr
{
channel.set_top_attribute(name, value, namespace.unwrap_or_default())
}
}
// Add each child to the stack
for child in *children {
create_template_node(channel, child);
}
// Add all children to the parent
channel.append_children_to_top(children.len() as u16);
}
Text { text } => channel.create_raw_text(text),
DynamicText { .. } => channel.create_raw_text("p"),
Dynamic { .. } => channel.add_placeholder(),
}
fn send_edits(desktop_context: &DesktopContext) {
let mut mutations = desktop_context.mutation_state.borrow_mut();
let serialized_edits = mutations.export_memory();
desktop_context.edit_queue.add_edits(serialized_edits);
}
/// Different hide implementations per platform

View file

@ -1,5 +1,5 @@
use crate::{window, DesktopContext};
use dioxus_core::ScopeState;
use dioxus_core::prelude::{once, spawn};
use dioxus_interpreter_js::INTERPRETER_JS;
use slab::Slab;
use std::{
@ -159,6 +159,7 @@ impl AssetHandlerRegistry {
}
/// A handle to a registered asset handler.
#[derive(Clone)]
pub struct AssetHandlerHandle {
desktop: DesktopContext,
handler_id: Rc<OnceCell<usize>>,
@ -192,16 +193,13 @@ impl Drop for AssetHandlerHandle {
///
/// The callback takes a path as requested by the web view, and it should return `Some(response)`
/// if you want to load the asset, and `None` if you want to fallback on the default behavior.
pub fn use_asset_handler<F: AssetFuture>(
cx: &ScopeState,
handler: impl AssetHandler<F>,
) -> &AssetHandlerHandle {
cx.use_hook(|| {
pub fn use_asset_handler<F: AssetFuture>(handler: impl AssetHandler<F>) -> AssetHandlerHandle {
once(|| {
let desktop = window();
let handler_id = Rc::new(OnceCell::new());
let handler_id_ref = Rc::clone(&handler_id);
let desktop_ref = Rc::clone(&desktop);
cx.push_future(async move {
spawn(async move {
let id = desktop.asset_handlers.register_handler(handler).await;
handler_id.set(id).unwrap();
});

View file

@ -1,6 +1,5 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc, str::FromStr};
use dioxus_core::ScopeState;
use dioxus_html::input_data::keyboard_types::Modifiers;
use slab::Slab;
use wry::application::keyboard::ModifiersState;
@ -100,7 +99,7 @@ impl ShortcutRegistry {
Err(HotkeyError::HotKeyParseError(shortcut)) => {
return Err(ShortcutRegistryError::InvalidShortcut(shortcut))
}
Err(err) => return Err(ShortcutRegistryError::Other(Box::new(err))),
Err(err) => return Err(ShortcutRegistryError::Other(Rc::new(err))),
}
},
)
@ -125,24 +124,25 @@ impl ShortcutRegistry {
}
}
#[non_exhaustive]
#[derive(Debug)]
/// An error that can occur when registering a shortcut.
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum ShortcutRegistryError {
/// The shortcut is invalid.
InvalidShortcut(String),
/// An unknown error occurred.
Other(Box<dyn std::error::Error>),
Other(Rc<dyn std::error::Error>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
/// An global id for a shortcut.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ShortcutId {
id: u32,
number: usize,
}
/// A global shortcut. This will be automatically removed when it is dropped.
#[derive(Clone)]
pub struct ShortcutHandle {
desktop: DesktopContext,
/// The id of the shortcut
@ -179,11 +179,10 @@ impl IntoAccelerator for &str {
/// Get a closure that executes any JavaScript in the WebView context.
pub fn use_global_shortcut(
cx: &ScopeState,
accelerator: impl IntoAccelerator,
handler: impl FnMut() + 'static,
) -> &Result<ShortcutHandle, ShortcutRegistryError> {
cx.use_hook(move || {
) -> Result<ShortcutHandle, ShortcutRegistryError> {
dioxus_core::once(move || {
let desktop = window();
let id = desktop.create_shortcut(accelerator.accelerator(), handler);

View file

@ -112,7 +112,7 @@ where
pub struct UseServerFuture<T> {
update: Arc<dyn Fn()>,
needs_regen: Cell<bool>,
task: Cell<Option<TaskId>>,
task: Cell<Option<Task>>,
dependencies: Vec<Box<dyn Any>>,
value: Rc<RefCell<Option<Box<T>>>>,
}
@ -142,7 +142,7 @@ impl<T> UseServerFuture<T> {
}
/// Get the ID of the future in Dioxus' internal scheduler
pub fn task(&self) -> Option<TaskId> {
pub fn task(&self) -> Option<Task> {
self.task.get()
}

View file

@ -1,4 +1,4 @@
use dioxus_core::{ScopeState, TaskId};
use dioxus_core::{ScopeState, Task};
pub use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
use std::future::Future;
@ -87,7 +87,7 @@ pub fn use_coroutine_handle<M: 'static>(cx: &ScopeState) -> Option<&Coroutine<M>
pub struct Coroutine<T> {
tx: UnboundedSender<T>,
task: TaskId,
task: Task,
}
// for use in futures
@ -103,7 +103,7 @@ impl<T> Clone for Coroutine<T> {
impl<T> Coroutine<T> {
/// Get the ID of this coroutine
#[must_use]
pub fn task_id(&self) -> TaskId {
pub fn task_id(&self) -> Task {
self.task
}

View file

@ -1,4 +1,4 @@
use dioxus_core::{ScopeState, TaskId};
use dioxus_core::{ScopeState, Task};
use std::{
any::Any,
cell::{Cell, RefCell},
@ -67,7 +67,7 @@ where
{
struct UseEffect {
needs_regen: bool,
task: Cell<Option<TaskId>>,
task: Cell<Option<Task>>,
dependencies: Vec<Box<dyn Any>>,
cleanup: UseEffectCleanup,
}
@ -108,14 +108,14 @@ type UseEffectCleanup = Rc<RefCell<Option<Box<dyn FnOnce()>>>>;
/// Something that can be returned from a `use_effect` hook.
pub trait UseEffectReturn<T> {
fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> TaskId;
fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> Task;
}
impl<T> UseEffectReturn<()> for T
where
T: Future<Output = ()> + 'static,
{
fn apply(self, _: UseEffectCleanup, cx: &ScopeState) -> TaskId {
fn apply(self, _: UseEffectCleanup, cx: &ScopeState) -> Task {
cx.push_future(self)
}
}
@ -127,7 +127,7 @@ where
T: Future<Output = F> + 'static,
F: FnOnce() + 'static,
{
fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> TaskId {
fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> Task {
cx.push_future(async move {
let cleanup = self.await;
*oncleanup.borrow_mut() = Some(Box::new(cleanup) as Box<dyn FnOnce()>);

View file

@ -1,5 +1,5 @@
#![allow(missing_docs)]
use dioxus_core::{ScopeState, TaskId};
use dioxus_core::{ScopeState, Task};
use std::{any::Any, cell::Cell, future::Future, rc::Rc, sync::Arc};
use crate::{use_state, UseState};
@ -74,7 +74,7 @@ pub enum FutureState<'a, T> {
pub struct UseFuture<T: 'static> {
update: Arc<dyn Fn()>,
needs_regen: Rc<Cell<bool>>,
task: Rc<Cell<Option<TaskId>>>,
task: Rc<Cell<Option<Task>>>,
state: UseState<Option<T>>,
}
@ -114,7 +114,7 @@ impl<T> UseFuture<T> {
}
/// Get the ID of the future in Dioxus' internal scheduler
pub fn task(&self) -> Option<TaskId> {
pub fn task(&self) -> Option<Task> {
self.task.get()
}

View file

@ -16,10 +16,10 @@ pub use file_watcher::*;
/// A message the hot reloading server sends to the client
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
#[serde(bound(deserialize = "'de: 'static"))]
pub enum HotReloadMsg {
/// A template has been updated
#[serde(borrow = "'static")]
UpdateTemplate(Template<'static>),
UpdateTemplate(Template),
/// The program needs to be recompiled, and the client should shut down
Shutdown,
}

View file

@ -1,7 +1,7 @@
#![allow(clippy::await_holding_refcell_ref)]
use async_trait::async_trait;
use dioxus_core::ScopeState;
use dioxus_core::prelude::*;
use std::future::{Future, IntoFuture};
use std::pin::Pin;
use std::rc::Rc;
@ -34,18 +34,14 @@ type EvalCreator = Rc<dyn Fn(&str) -> Result<UseEval, EvalError>>;
/// it. **This applies especially to web targets, where the JavaScript context
/// has access to most, if not all of your application data.**
#[must_use]
pub fn use_eval(cx: &ScopeState) -> &EvalCreator {
&*cx.use_hook(|| {
let eval_provider = cx
.consume_context::<Rc<dyn EvalProvider>>()
.expect("evaluator not provided");
pub fn eval_provider() -> EvalCreator {
let eval_provider = consume_context::<Rc<dyn EvalProvider>>().expect("evaluator not provided");
Rc::new(move |script: &str| {
eval_provider
.new_evaluator(script.to_string())
.map(UseEval::new)
}) as Rc<dyn Fn(&str) -> Result<UseEval, EvalError>>
})
Rc::new(move |script: &str| {
eval_provider
.new_evaluator(script.to_string())
.map(UseEval::new)
}) as Rc<dyn Fn(&str) -> Result<UseEval, EvalError>>
}
pub fn eval(script: &str) -> Result<UseEval, EvalError> {

View file

@ -9,11 +9,11 @@ macro_rules! impl_event {
$(
$( #[$attr] )*
#[inline]
pub fn $name<'a, E: crate::EventReturn<T>, T>(_cx: &'a ::dioxus_core::ScopeState, mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'a) -> ::dioxus_core::Attribute {
pub fn $name<E: crate::EventReturn<T>, T>(mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'static) -> ::dioxus_core::Attribute {
::dioxus_core::Attribute::new(
stringify!($name),
_cx.listener(move |e: ::dioxus_core::Event<$data>| {
_f(e).spawn(_cx);
::dioxus_core::AttributeValue::listener(move |e: ::dioxus_core::Event<$data>| {
_f(e).spawn();
}),
None,
false,
@ -155,7 +155,7 @@ use std::future::Future;
#[doc(hidden)]
pub trait EventReturn<P>: Sized {
fn spawn(self, _cx: &dioxus_core::ScopeState) {}
fn spawn(self) {}
}
impl EventReturn<()> for () {}
@ -167,7 +167,7 @@ where
T: Future<Output = ()> + 'static,
{
#[inline]
fn spawn(self, cx: &dioxus_core::ScopeState) {
cx.spawn(self);
fn spawn(self) {
dioxus_core::prelude::spawn(self);
}
}

View file

@ -148,14 +148,13 @@ impl_event! {
/// ondoubleclick
#[inline]
pub fn ondoubleclick<'a, E: crate::EventReturn<T>, T>(
_cx: &'a ::dioxus_core::ScopeState,
mut _f: impl FnMut(::dioxus_core::Event<MouseData>) -> E + 'a,
pub fn ondoubleclick<E: crate::EventReturn<T>, T>(
mut _f: impl FnMut(::dioxus_core::Event<MouseData>) -> E + 'static,
) -> ::dioxus_core::Attribute {
::dioxus_core::Attribute::new(
"ondblclick",
_cx.listener(move |e: ::dioxus_core::Event<MouseData>| {
_f(e).spawn(_cx);
dioxus_core::AttributeValue::listener(move |e: ::dioxus_core::Event<MouseData>| {
_f(e).spawn();
}),
None,
false,