fix subscriptions in events
This commit is contained in:
1 parent
07ed3e67dd
commit
a6f611eccf
12 files changed
+445
-103
No files matched your search
+48
-14
@@ -1,26 +1,60 @@
|
|||||||
//! Example: README.md showcase
|
#![allow(non_snake_case)]
|
||||||
//!
|
|
||||||
//! The example from the README.md.
|
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_signals::use_signal;
|
use dioxus_signals::{use_signal, Effect, Signal};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus_desktop::launch(app);
|
dioxus_desktop::launch(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let mut count = use_signal(cx, || 0);
|
let counts = use_signal(cx, || (0..100).map(Signal::new).collect::<Vec<_>>());
|
||||||
|
|
||||||
use_future!(cx, || async move {
|
cx.use_hook(|| {
|
||||||
loop {
|
Effect::new(move || {
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
println!("Counts: {:?}", counts);
|
||||||
count += 1;
|
})
|
||||||
println!("current: {count}");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.render(rsx! {
|
render! {
|
||||||
div { "High-Five counter: {count}" }
|
for count in counts {
|
||||||
})
|
Child {
|
||||||
|
count: count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Props, PartialEq)]
|
||||||
|
struct ChildProps {
|
||||||
|
count: Signal<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Child(cx: Scope<ChildProps>) -> Element {
|
||||||
|
let count = cx.props.count;
|
||||||
|
|
||||||
|
// use_future!(cx, || async move {
|
||||||
|
// loop {
|
||||||
|
// tokio::time::sleep(std::time::Duration::from_secs(count.value())).await;
|
||||||
|
// *count.write() += 1;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
render! {
|
||||||
|
div {
|
||||||
|
"Child: {count}"
|
||||||
|
button {
|
||||||
|
onclick: move |_| {
|
||||||
|
*count.write() += 1;
|
||||||
|
},
|
||||||
|
"Increase"
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
onclick: move |_| {
|
||||||
|
*count.write() -= 1;
|
||||||
|
},
|
||||||
|
"Decrease"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,4 +14,4 @@ rand = "0.8.5"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["check_generation"]
|
default = ["check_generation"]
|
||||||
check_generation = []
|
check_generation = []
|
||||||
@@ -193,6 +193,17 @@ impl<T: 'static> CopyHandle<T> {
|
|||||||
pub fn write(&self) -> RefMut<'_, T> {
|
pub fn write(&self) -> RefMut<'_, T> {
|
||||||
self.try_write().unwrap()
|
self.try_write().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ptr_eq(&self, other: &Self) -> bool {
|
||||||
|
#[cfg(any(debug_assertions, feature = "check_generation"))]
|
||||||
|
{
|
||||||
|
self.raw.data.as_ptr() == other.raw.data.as_ptr() && self.generation == other.generation
|
||||||
|
}
|
||||||
|
#[cfg(not(any(debug_assertions, feature = "check_generation")))]
|
||||||
|
{
|
||||||
|
self.raw.data.as_ptr() == other.raw.data.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Copy for CopyHandle<T> {}
|
impl<T> Copy for CopyHandle<T> {}
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ impl VirtualDom {
|
|||||||
let listener = unsafe { &*listener };
|
let listener = unsafe { &*listener };
|
||||||
match &listener.value {
|
match &listener.value {
|
||||||
AttributeValue::Listener(l) => {
|
AttributeValue::Listener(l) => {
|
||||||
_ = l.take();
|
_ = l.callback.take();
|
||||||
}
|
}
|
||||||
AttributeValue::Any(a) => {
|
AttributeValue::Any(a) => {
|
||||||
_ = a.take();
|
_ = a.take();
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
use crate::ScopeId;
|
||||||
|
|
||||||
/// A wrapper around some generic data that handles the event's state
|
/// A wrapper around some generic data that handles the event's state
|
||||||
///
|
///
|
||||||
@@ -134,13 +136,15 @@ impl<T: std::fmt::Debug> std::fmt::Debug for Event<T> {
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
pub struct EventHandler<'bump, T = ()> {
|
pub struct EventHandler<'bump, T:?Sized = ()> {
|
||||||
|
pub(crate) origin: ScopeId,
|
||||||
pub(super) callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
|
pub(super) callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for EventHandler<'_, T> {
|
impl<T> Default for EventHandler<'_, T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
origin: ScopeId(0),
|
||||||
callback: Default::default(),
|
callback: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{
|
use crate::prelude::EventHandler;use crate::{
|
||||||
any_props::AnyProps, arena::ElementId, Element, Event, LazyNodes, ScopeId, ScopeState,
|
any_props::AnyProps, arena::ElementId, Element, Event, LazyNodes, ScopeId, ScopeState,
|
||||||
};
|
};
|
||||||
use bumpalo::boxed::Box as BumpBox;
|
use bumpalo::boxed::Box as BumpBox;
|
||||||
@@ -476,7 +476,7 @@ pub enum AttributeValue<'a> {
|
|||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
|
||||||
/// A listener, like "onclick"
|
/// A listener, like "onclick"
|
||||||
Listener(RefCell<Option<ListenerCb<'a>>>),
|
Listener(EventHandler<'a, Event<dyn Any>>),
|
||||||
|
|
||||||
/// An arbitrary value that implements PartialEq and is static
|
/// An arbitrary value that implements PartialEq and is static
|
||||||
Any(RefCell<Option<BumpBox<'a, dyn AnyValue>>>),
|
Any(RefCell<Option<BumpBox<'a, dyn AnyValue>>>),
|
||||||
@@ -485,8 +485,6 @@ pub enum AttributeValue<'a> {
|
|||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
|
|
||||||
|
|
||||||
/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements that are borrowed
|
/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements that are borrowed
|
||||||
///
|
///
|
||||||
/// These varients are used to communicate what the value of an attribute is that needs to be updated
|
/// These varients are used to communicate what the value of an attribute is that needs to be updated
|
||||||
|
|||||||
+10
-16
@@ -446,7 +446,7 @@ impl<'src> ScopeState {
|
|||||||
let handler: &mut dyn FnMut(T) = self.bump().alloc(f);
|
let handler: &mut dyn FnMut(T) = self.bump().alloc(f);
|
||||||
let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
|
let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
|
||||||
let callback = RefCell::new(Some(caller));
|
let callback = RefCell::new(Some(caller));
|
||||||
EventHandler { callback }
|
EventHandler { callback, origin: self.context().id }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new [`AttributeValue`] with the listener variant from a callback
|
/// Create a new [`AttributeValue`] with the listener variant from a callback
|
||||||
@@ -456,22 +456,16 @@ impl<'src> ScopeState {
|
|||||||
&'src self,
|
&'src self,
|
||||||
mut callback: impl FnMut(Event<T>) + 'src,
|
mut callback: impl FnMut(Event<T>) + 'src,
|
||||||
) -> AttributeValue<'src> {
|
) -> AttributeValue<'src> {
|
||||||
// safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
|
|
||||||
// This is the suggested way to build a bumpbox
|
|
||||||
//
|
|
||||||
// In theory, we could just use regular boxes
|
|
||||||
let boxed: BumpBox<'src, dyn FnMut(_) + 'src> = unsafe {
|
|
||||||
BumpBox::from_raw(self.bump().alloc(move |event: Event<dyn Any>| {
|
|
||||||
if let Ok(data) = event.data.downcast::<T>() {
|
|
||||||
callback(Event {
|
|
||||||
propagates: event.propagates,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
|
|
||||||
AttributeValue::Listener(RefCell::new(Some(boxed)))
|
AttributeValue::Listener(self.event_handler(move |event: Event<dyn Any>| {
|
||||||
|
if let Ok(data) = event.data.downcast::<T>() {
|
||||||
|
callback(Event {
|
||||||
|
propagates: event.propagates,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
|
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
|
||||||
|
|||||||
@@ -391,9 +391,12 @@ impl VirtualDom {
|
|||||||
// We check the bubble state between each call to see if the event has been stopped from bubbling
|
// We check the bubble state between each call to see if the event has been stopped from bubbling
|
||||||
for listener in listeners.drain(..).rev() {
|
for listener in listeners.drain(..).rev() {
|
||||||
if let AttributeValue::Listener(listener) = listener {
|
if let AttributeValue::Listener(listener) = listener {
|
||||||
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
|
let origin = listener.origin;
|
||||||
|
self.runtime.scope_stack.borrow_mut().push(origin);
|
||||||
|
if let Some(cb) = listener.callback.borrow_mut().as_deref_mut() {
|
||||||
cb(uievent.clone());
|
cb(uievent.clone());
|
||||||
}
|
}
|
||||||
|
self.runtime.scope_stack.borrow_mut().pop();
|
||||||
|
|
||||||
if !uievent.propagates.get() {
|
if !uievent.propagates.get() {
|
||||||
return;
|
return;
|
||||||
@@ -422,9 +425,12 @@ impl VirtualDom {
|
|||||||
// Only call the listener if this is the exact target element.
|
// Only call the listener if this is the exact target element.
|
||||||
if attr.name.trim_start_matches("on") == name && target_path == this_path {
|
if attr.name.trim_start_matches("on") == name && target_path == this_path {
|
||||||
if let AttributeValue::Listener(listener) = &attr.value {
|
if let AttributeValue::Listener(listener) = &attr.value {
|
||||||
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
|
let origin = listener.origin;
|
||||||
|
self.runtime.scope_stack.borrow_mut().push(origin);
|
||||||
|
if let Some(cb) = listener.callback.borrow_mut().as_deref_mut() {
|
||||||
cb(uievent.clone());
|
cb(uievent.clone());
|
||||||
}
|
}
|
||||||
|
self.runtime.scope_stack.borrow_mut().pop();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
use dioxus_core::prelude::*;
|
||||||
|
|
||||||
|
use crate::CopyValue;
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Copy)]
|
||||||
|
pub(crate) struct EffectStack {
|
||||||
|
pub(crate) effects: CopyValue<Vec<Effect>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_effect_stack() -> EffectStack {
|
||||||
|
match consume_context() {
|
||||||
|
Some(rt) => rt,
|
||||||
|
None => {
|
||||||
|
let store = EffectStack::default();
|
||||||
|
provide_root_context(store).expect("in a virtual dom")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
|
pub struct Effect {
|
||||||
|
callback: CopyValue<Box<dyn FnMut()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Effect {
|
||||||
|
pub(crate) fn current() -> Option<Self> {
|
||||||
|
get_effect_stack().effects.read().last().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(callback: impl FnMut() + 'static) -> Self {
|
||||||
|
let myself = Self {
|
||||||
|
callback: CopyValue::new(Box::new(callback)),
|
||||||
|
};
|
||||||
|
|
||||||
|
myself.try_run();
|
||||||
|
|
||||||
|
myself
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the effect callback immediately. Returns `true` if the effect was run. Returns `false` is the effect is dead.
|
||||||
|
pub fn try_run(&self) {
|
||||||
|
if let Some(mut callback) = self.callback.try_write() {
|
||||||
|
{
|
||||||
|
get_effect_stack().effects.write().push(*self);
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
{
|
||||||
|
get_effect_stack().effects.write().pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
use crate::rt::CopyValue;
|
||||||
|
use crate::Signal;
|
||||||
|
|
||||||
|
use std::cell::{Ref, RefMut};
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
fmt::{Debug, Display},
|
||||||
|
ops::{Add, Div, Mul, Sub},
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! impls {
|
||||||
|
($ty:ident) => {
|
||||||
|
impl<T: Default + 'static> Default for $ty<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(T::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::clone::Clone for $ty<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Copy for $ty<T> {}
|
||||||
|
|
||||||
|
impl<T: Display + 'static> Display for $ty<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.with(|v| Display::fmt(v, f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug + 'static> Debug for $ty<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.with(|v| Debug::fmt(v, f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Add<Output = T> + Copy + 'static> std::ops::AddAssign<T> for $ty<T> {
|
||||||
|
fn add_assign(&mut self, rhs: T) {
|
||||||
|
self.set(self.value() + rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Sub<Output = T> + Copy + 'static> std::ops::SubAssign<T> for $ty<T> {
|
||||||
|
fn sub_assign(&mut self, rhs: T) {
|
||||||
|
self.set(self.value() - rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Mul<Output = T> + Copy + 'static> std::ops::MulAssign<T> for $ty<T> {
|
||||||
|
fn mul_assign(&mut self, rhs: T) {
|
||||||
|
self.set(self.value() * rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Div<Output = T> + Copy + 'static> std::ops::DivAssign<T> for $ty<T> {
|
||||||
|
fn div_assign(&mut self, rhs: T) {
|
||||||
|
self.set(self.value() / rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> $ty<Vec<T>> {
|
||||||
|
pub fn push(&self, value: T) {
|
||||||
|
self.with_mut(|v| v.push(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&self) -> Option<T> {
|
||||||
|
self.with_mut(|v| v.pop())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&self, index: usize, value: T) {
|
||||||
|
self.with_mut(|v| v.insert(index, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&self, index: usize) -> T {
|
||||||
|
self.with_mut(|v| v.remove(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.with_mut(|v| v.clear())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend(&self, iter: impl IntoIterator<Item = T>) {
|
||||||
|
self.with_mut(|v| v.extend(iter))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn truncate(&self, len: usize) {
|
||||||
|
self.with_mut(|v| v.truncate(len))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap_remove(&self, index: usize) -> T {
|
||||||
|
self.with_mut(|v| v.swap_remove(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn retain(&self, f: impl FnMut(&T) -> bool) {
|
||||||
|
self.with_mut(|v| v.retain(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn split_off(&self, at: usize) -> Vec<T> {
|
||||||
|
self.with_mut(|v| v.split_off(at))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, index: usize) -> Option<Ref<'_, T>> {
|
||||||
|
Ref::filter_map(self.read(), |v| v.get(index)).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&self, index: usize) -> Option<RefMut<'_, T>> {
|
||||||
|
RefMut::filter_map(self.write(), |v| v.get_mut(index)).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> $ty<Option<T>> {
|
||||||
|
pub fn take(&self) -> Option<T> {
|
||||||
|
self.with_mut(|v| v.take())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn replace(&self, value: T) -> Option<T> {
|
||||||
|
self.with_mut(|v| v.replace(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap(&self) -> T
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
self.with(|v| v.clone()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ref(&self) -> Option<Ref<'_, T>> {
|
||||||
|
Ref::filter_map(self.read(), |v| v.as_ref()).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_mut(&self) -> Option<RefMut<'_, T>> {
|
||||||
|
RefMut::filter_map(self.write(), |v| v.as_mut()).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_insert(&self, default: T) -> Ref<'_, T> {
|
||||||
|
self.get_or_insert_with(|| default)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_insert_with(&self, default: impl FnOnce() -> T) -> Ref<'_, T> {
|
||||||
|
let borrow = self.read();
|
||||||
|
if borrow.is_none() {
|
||||||
|
drop(borrow);
|
||||||
|
self.with_mut(|v| *v = Some(default()));
|
||||||
|
Ref::map(self.read(), |v| v.as_ref().unwrap())
|
||||||
|
} else {
|
||||||
|
Ref::map(borrow, |v| v.as_ref().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impls!(CopyValue);
|
||||||
|
impls!(Signal);
|
||||||
|
|
||||||
|
pub struct CopyValueIterator<T: 'static> {
|
||||||
|
index: usize,
|
||||||
|
value: CopyValue<Vec<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> Iterator for CopyValueIterator<T> {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let index = self.index;
|
||||||
|
self.index += 1;
|
||||||
|
self.value.get(index).map(|v| v.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone + 'static> IntoIterator for CopyValue<Vec<T>> {
|
||||||
|
type IntoIter = CopyValueIterator<T>;
|
||||||
|
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
CopyValueIterator {
|
||||||
|
index: 0,
|
||||||
|
value: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CopySignalIterator<T: 'static> {
|
||||||
|
index: usize,
|
||||||
|
value: Signal<Vec<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> Iterator for CopySignalIterator<T> {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let index = self.index;
|
||||||
|
self.index += 1;
|
||||||
|
self.value.get(index).map(|v| v.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone + 'static> IntoIterator for Signal<Vec<T>> {
|
||||||
|
type IntoIter = CopySignalIterator<T>;
|
||||||
|
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
CopySignalIterator {
|
||||||
|
index: 0,
|
||||||
|
value: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+69
-56
@@ -1,15 +1,18 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefMut},
|
cell::{Ref, RefCell, RefMut},
|
||||||
fmt::{Debug, Display},
|
rc::Rc,
|
||||||
ops::{Add, Div, Mul, Sub},
|
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod rt;
|
mod rt;
|
||||||
pub use rt::*;
|
pub use rt::*;
|
||||||
|
mod effect;
|
||||||
|
pub use effect::*;
|
||||||
|
#[macro_use]
|
||||||
|
mod impls;
|
||||||
|
|
||||||
use dioxus_core::{
|
use dioxus_core::{
|
||||||
prelude::{current_scope_id, schedule_update_any},
|
prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
|
||||||
ScopeId, ScopeState,
|
ScopeId, ScopeState,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -17,8 +20,36 @@ pub fn use_signal<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal<
|
|||||||
*cx.use_hook(|| Signal::new(f()))
|
*cx.use_hook(|| Signal::new(f()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Unsubscriber {
|
||||||
|
scope: ScopeId,
|
||||||
|
subscribers: Rc<RefCell<Vec<Rc<RefCell<Vec<ScopeId>>>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Unsubscriber {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
for subscribers in self.subscribers.borrow().iter() {
|
||||||
|
subscribers.borrow_mut().retain(|s| *s != self.scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_unsubscriber() -> Unsubscriber {
|
||||||
|
match has_context() {
|
||||||
|
Some(rt) => rt,
|
||||||
|
None => {
|
||||||
|
let owner = Unsubscriber {
|
||||||
|
scope: current_scope_id().expect("in a virtual dom"),
|
||||||
|
subscribers: Default::default(),
|
||||||
|
};
|
||||||
|
provide_context(owner).expect("in a virtual dom")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SignalData<T> {
|
struct SignalData<T> {
|
||||||
subscribers: Vec<ScopeId>,
|
subscribers: Rc<RefCell<Vec<ScopeId>>>,
|
||||||
|
effect_subscribers: Rc<RefCell<Vec<Effect>>>,
|
||||||
update_any: Arc<dyn Fn(ScopeId)>,
|
update_any: Arc<dyn Fn(ScopeId)>,
|
||||||
value: T,
|
value: T,
|
||||||
}
|
}
|
||||||
@@ -31,7 +62,8 @@ impl<T: 'static> Signal<T> {
|
|||||||
pub fn new(value: T) -> Self {
|
pub fn new(value: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: CopyValue::new(SignalData {
|
inner: CopyValue::new(SignalData {
|
||||||
subscribers: Vec::new(),
|
subscribers: Default::default(),
|
||||||
|
effect_subscribers: Default::default(),
|
||||||
update_any: schedule_update_any().expect("in a virtual dom"),
|
update_any: schedule_update_any().expect("in a virtual dom"),
|
||||||
value,
|
value,
|
||||||
}),
|
}),
|
||||||
@@ -39,21 +71,40 @@ impl<T: 'static> Signal<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&self) -> Ref<T> {
|
pub fn read(&self) -> Ref<T> {
|
||||||
|
let inner = self.inner.read();
|
||||||
if let Some(current_scope_id) = current_scope_id() {
|
if let Some(current_scope_id) = current_scope_id() {
|
||||||
let mut inner = self.inner.write();
|
let mut subscribers = inner.subscribers.borrow_mut();
|
||||||
if !inner.subscribers.contains(¤t_scope_id) {
|
if !subscribers.contains(¤t_scope_id) {
|
||||||
inner.subscribers.push(current_scope_id);
|
subscribers.push(current_scope_id);
|
||||||
|
drop(subscribers);
|
||||||
|
let unsubscriber = current_unsubscriber();
|
||||||
|
inner.subscribers.borrow_mut().push(unsubscriber.scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ref::map(self.inner.read(), |v| &v.value)
|
if let Some(effect) = Effect::current() {
|
||||||
|
let mut effect_subscribers = inner.effect_subscribers.borrow_mut();
|
||||||
|
if !effect_subscribers.contains(&effect) {
|
||||||
|
effect_subscribers.push(effect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ref::map(inner, |v| &v.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&self) -> RefMut<T> {
|
pub fn write(&self) -> RefMut<T> {
|
||||||
let inner = self.inner.write();
|
{
|
||||||
for &scope_id in &inner.subscribers {
|
let inner = self.inner.read();
|
||||||
(inner.update_any)(scope_id);
|
for &scope_id in &*inner.subscribers.borrow() {
|
||||||
|
(inner.update_any)(scope_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let subscribers =
|
||||||
|
{ std::mem::take(&mut *self.inner.read().effect_subscribers.borrow_mut()) };
|
||||||
|
for effect in subscribers {
|
||||||
|
effect.try_run();
|
||||||
|
}
|
||||||
|
|
||||||
|
let inner = self.inner.write();
|
||||||
RefMut::map(inner, |v| &mut v.value)
|
RefMut::map(inner, |v| &mut v.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,58 +117,20 @@ impl<T: 'static> Signal<T> {
|
|||||||
f(&*write)
|
f(&*write)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
|
pub fn with_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
|
||||||
let mut write = self.write();
|
let mut write = self.write();
|
||||||
f(&mut *write)
|
f(&mut *write)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + 'static> Signal<T> {
|
impl<T: Clone + 'static> Signal<T> {
|
||||||
pub fn get(&self) -> T {
|
pub fn value(&self) -> T {
|
||||||
self.read().clone()
|
self.read().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> std::clone::Clone for Signal<T> {
|
impl<T: 'static> PartialEq for Signal<T> {
|
||||||
fn clone(&self) -> Self {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
*self
|
self.inner == other.inner
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Copy for Signal<T> {}
|
|
||||||
|
|
||||||
impl<T: Display + 'static> Display for Signal<T> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.with(|v| Display::fmt(v, f))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Debug + 'static> Debug for Signal<T> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.with(|v| Debug::fmt(v, f))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Add<Output = T> + Copy + 'static> std::ops::AddAssign<T> for Signal<T> {
|
|
||||||
fn add_assign(&mut self, rhs: T) {
|
|
||||||
self.set(self.get() + rhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Sub<Output = T> + Copy + 'static> std::ops::SubAssign<T> for Signal<T> {
|
|
||||||
fn sub_assign(&mut self, rhs: T) {
|
|
||||||
self.set(self.get() - rhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Mul<Output = T> + Copy + 'static> std::ops::MulAssign<T> for Signal<T> {
|
|
||||||
fn mul_assign(&mut self, rhs: T) {
|
|
||||||
self.set(self.get() * rhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Div<Output = T> + Copy + 'static> std::ops::DivAssign<T> for Signal<T> {
|
|
||||||
fn div_assign(&mut self, rhs: T) {
|
|
||||||
self.set(self.get() / rhs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,14 +26,6 @@ fn current_owner() -> Rc<Owner> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Copy for CopyValue<T> {}
|
|
||||||
|
|
||||||
impl<T> Clone for CopyValue<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CopyValue<T: 'static> {
|
pub struct CopyValue<T: 'static> {
|
||||||
pub value: CopyHandle<T>,
|
pub value: CopyHandle<T>,
|
||||||
}
|
}
|
||||||
@@ -62,4 +54,30 @@ impl<T: 'static> CopyValue<T> {
|
|||||||
pub fn write(&self) -> RefMut<'_, T> {
|
pub fn write(&self) -> RefMut<'_, T> {
|
||||||
self.value.write()
|
self.value.write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, value: T) {
|
||||||
|
*self.write() = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
|
||||||
|
let write = self.read();
|
||||||
|
f(&*write)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
|
||||||
|
let mut write = self.write();
|
||||||
|
f(&mut *write)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone + 'static> CopyValue<T> {
|
||||||
|
pub fn value(&self) -> T {
|
||||||
|
self.read().clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> PartialEq for CopyValue<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.value.ptr_eq(&other.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user