fix subscriptions in events
This commit is contained in:
parent
07ed3e67dd
commit
a6f611eccf
|
@ -1,26 +1,60 @@
|
|||
//! Example: README.md showcase
|
||||
//!
|
||||
//! The example from the README.md.
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_signals::use_signal;
|
||||
use dioxus_signals::{use_signal, Effect, Signal};
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
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 {
|
||||
loop {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
count += 1;
|
||||
println!("current: {count}");
|
||||
}
|
||||
cx.use_hook(|| {
|
||||
Effect::new(move || {
|
||||
println!("Counts: {:?}", counts);
|
||||
})
|
||||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
div { "High-Five counter: {count}" }
|
||||
})
|
||||
render! {
|
||||
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]
|
||||
default = ["check_generation"]
|
||||
check_generation = []
|
||||
check_generation = []
|
||||
|
|
|
@ -193,6 +193,17 @@ impl<T: 'static> CopyHandle<T> {
|
|||
pub fn write(&self) -> RefMut<'_, T> {
|
||||
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> {}
|
||||
|
|
|
@ -163,7 +163,7 @@ impl VirtualDom {
|
|||
let listener = unsafe { &*listener };
|
||||
match &listener.value {
|
||||
AttributeValue::Listener(l) => {
|
||||
_ = l.take();
|
||||
_ = l.callback.take();
|
||||
}
|
||||
AttributeValue::Any(a) => {
|
||||
_ = a.take();
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
rc::Rc,
|
||||
|
||||
};
|
||||
use crate::ScopeId;
|
||||
|
||||
/// 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>>>,
|
||||
}
|
||||
|
||||
impl<T> Default for EventHandler<'_, T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
origin: ScopeId(0),
|
||||
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,
|
||||
};
|
||||
use bumpalo::boxed::Box as BumpBox;
|
||||
|
@ -476,7 +476,7 @@ pub enum AttributeValue<'a> {
|
|||
Bool(bool),
|
||||
|
||||
/// A listener, like "onclick"
|
||||
Listener(RefCell<Option<ListenerCb<'a>>>),
|
||||
Listener(EventHandler<'a, Event<dyn Any>>),
|
||||
|
||||
/// An arbitrary value that implements PartialEq and is static
|
||||
Any(RefCell<Option<BumpBox<'a, dyn AnyValue>>>),
|
||||
|
@ -485,8 +485,6 @@ pub enum AttributeValue<'a> {
|
|||
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
|
||||
///
|
||||
/// These varients are used to communicate what the value of an attribute is that needs to be updated
|
||||
|
|
|
@ -446,7 +446,7 @@ impl<'src> ScopeState {
|
|||
let handler: &mut dyn FnMut(T) = self.bump().alloc(f);
|
||||
let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
|
||||
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
|
||||
|
@ -456,22 +456,16 @@ impl<'src> ScopeState {
|
|||
&'src self,
|
||||
mut callback: impl FnMut(Event<T>) + '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`]
|
||||
|
|
|
@ -391,9 +391,12 @@ impl VirtualDom {
|
|||
// We check the bubble state between each call to see if the event has been stopped from bubbling
|
||||
for listener in listeners.drain(..).rev() {
|
||||
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());
|
||||
}
|
||||
self.runtime.scope_stack.borrow_mut().pop();
|
||||
|
||||
if !uievent.propagates.get() {
|
||||
return;
|
||||
|
@ -422,9 +425,12 @@ impl VirtualDom {
|
|||
// Only call the listener if this is the exact target element.
|
||||
if attr.name.trim_start_matches("on") == name && target_path == this_path {
|
||||
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());
|
||||
}
|
||||
self.runtime.scope_stack.borrow_mut().pop();
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,18 @@
|
|||
use std::{
|
||||
cell::{Ref, RefMut},
|
||||
fmt::{Debug, Display},
|
||||
ops::{Add, Div, Mul, Sub},
|
||||
cell::{Ref, RefCell, RefMut},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
mod rt;
|
||||
pub use rt::*;
|
||||
mod effect;
|
||||
pub use effect::*;
|
||||
#[macro_use]
|
||||
mod impls;
|
||||
|
||||
use dioxus_core::{
|
||||
prelude::{current_scope_id, schedule_update_any},
|
||||
prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
|
||||
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()))
|
||||
}
|
||||
|
||||
#[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> {
|
||||
subscribers: Vec<ScopeId>,
|
||||
subscribers: Rc<RefCell<Vec<ScopeId>>>,
|
||||
effect_subscribers: Rc<RefCell<Vec<Effect>>>,
|
||||
update_any: Arc<dyn Fn(ScopeId)>,
|
||||
value: T,
|
||||
}
|
||||
|
@ -31,7 +62,8 @@ impl<T: 'static> Signal<T> {
|
|||
pub fn new(value: T) -> Self {
|
||||
Self {
|
||||
inner: CopyValue::new(SignalData {
|
||||
subscribers: Vec::new(),
|
||||
subscribers: Default::default(),
|
||||
effect_subscribers: Default::default(),
|
||||
update_any: schedule_update_any().expect("in a virtual dom"),
|
||||
value,
|
||||
}),
|
||||
|
@ -39,21 +71,40 @@ impl<T: 'static> Signal<T> {
|
|||
}
|
||||
|
||||
pub fn read(&self) -> Ref<T> {
|
||||
let inner = self.inner.read();
|
||||
if let Some(current_scope_id) = current_scope_id() {
|
||||
let mut inner = self.inner.write();
|
||||
if !inner.subscribers.contains(¤t_scope_id) {
|
||||
inner.subscribers.push(current_scope_id);
|
||||
let mut subscribers = inner.subscribers.borrow_mut();
|
||||
if !subscribers.contains(¤t_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> {
|
||||
let inner = self.inner.write();
|
||||
for &scope_id in &inner.subscribers {
|
||||
(inner.update_any)(scope_id);
|
||||
{
|
||||
let inner = self.inner.read();
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -66,58 +117,20 @@ impl<T: 'static> Signal<T> {
|
|||
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();
|
||||
f(&mut *write)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + 'static> Signal<T> {
|
||||
pub fn get(&self) -> T {
|
||||
pub fn value(&self) -> T {
|
||||
self.read().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::clone::Clone for Signal<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
impl<T: 'static> PartialEq for Signal<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 value: CopyHandle<T>,
|
||||
}
|
||||
|
@ -62,4 +54,30 @@ impl<T: 'static> CopyValue<T> {
|
|||
pub fn write(&self) -> RefMut<'_, T> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue