Merge branch 'master' into map-signal

This commit is contained in:
Evan Almloff committed 2023-12-23 13:43:54 -06:00
commit 0c7729da05
152 files changed
+3705 -778

No files matched your search

+91 -22
View File
@@ -1,6 +1,6 @@
use crate::SignalMap;
use std::{
cell::{Ref, RefCell, RefMut},
cell::RefCell,
mem::MaybeUninit,
ops::{Deref, DerefMut},
rc::Rc,
@@ -11,8 +11,9 @@ use dioxus_core::{
prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
ScopeId, ScopeState,
};
use generational_box::{GenerationalRef, GenerationalRefMut};
use crate::{CopyValue, Effect};
use crate::{get_effect_stack, CopyValue, Effect, EffectStack};
/// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
///
@@ -45,9 +46,19 @@ use crate::{CopyValue, Effect};
/// }
/// }
/// ```
#[track_caller]
#[must_use]
pub fn use_signal<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal<T> {
*cx.use_hook(|| Signal::new(f()))
#[cfg(debug_assertions)]
let caller = std::panic::Location::caller();
*cx.use_hook(|| {
Signal::new_with_caller(
f(),
#[cfg(debug_assertions)]
caller,
)
})
}
#[derive(Clone)]
@@ -83,6 +94,7 @@ pub(crate) struct SignalData<T> {
pub(crate) subscribers: Rc<RefCell<Vec<ScopeId>>>,
pub(crate) effect_subscribers: Rc<RefCell<Vec<Effect>>>,
pub(crate) update_any: Arc<dyn Fn(ScopeId)>,
pub(crate) effect_stack: EffectStack,
pub(crate) value: T,
}
@@ -138,6 +150,7 @@ impl<'de, T: serde::Deserialize<'de> + 'static> serde::Deserialize<'de> for Sign
impl<T: 'static> Signal<T> {
/// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
#[track_caller]
pub fn new(value: T) -> Self {
Self {
inner: CopyValue::new(SignalData {
@@ -145,10 +158,31 @@ impl<T: 'static> Signal<T> {
effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"),
value,
effect_stack: get_effect_stack(),
}),
}
}
/// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
fn new_with_caller(
value: T,
#[cfg(debug_assertions)] caller: &'static std::panic::Location<'static>,
) -> Self {
Self {
inner: CopyValue::new_with_caller(
SignalData {
subscribers: Default::default(),
effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"),
value,
effect_stack: get_effect_stack(),
},
#[cfg(debug_assertions)]
caller,
),
}
}
/// Create a new signal with a custom owner scope. The signal will be dropped when the owner scope is dropped instead of the current scope.
pub fn new_in_scope(value: T, owner: ScopeId) -> Self {
Self {
@@ -158,6 +192,7 @@ impl<T: 'static> Signal<T> {
effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"),
value,
effect_stack: get_effect_stack(),
},
owner,
),
@@ -169,11 +204,13 @@ impl<T: 'static> Signal<T> {
self.inner.origin_scope()
}
/// Get the current value of the signal. This will subscribe the current scope to the signal.
/// Get the current value of the signal. This will subscribe the current scope to the signal. If you would like to read the signal without subscribing to it, you can use [`Self::peek`] instead.
///
/// If the signal has been dropped, this will panic.
pub fn read(&self) -> Ref<'static, T> {
#[track_caller]
pub fn read(&self) -> GenerationalRef<T> {
let inner = self.inner.read();
if let Some(effect) = Effect::current() {
if let Some(effect) = inner.effect_stack.current() {
let mut effect_subscribers = inner.effect_subscribers.borrow_mut();
if !effect_subscribers.contains(&effect) {
effect_subscribers.push(effect);
@@ -195,14 +232,24 @@ impl<T: 'static> Signal<T> {
}
}
}
Ref::map(inner, |v| &v.value)
GenerationalRef::map(inner, |v| &v.value)
}
/// Get the current value of the signal. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
///
/// If the signal has been dropped, this will panic.
pub fn peek(&self) -> GenerationalRef<T> {
let inner = self.inner.read();
GenerationalRef::map(inner, |v| &v.value)
}
/// Get a mutable reference to the signal's value.
///
/// If the signal has been dropped, this will panic.
pub fn write(&self) -> Write<'static, T> {
#[track_caller]
pub fn write(&self) -> Write<T> {
let inner = self.inner.write();
let borrow = RefMut::map(inner, |v| &mut v.value);
let borrow = GenerationalRefMut::map(inner, |v| &mut v.value);
Write {
write: borrow,
signal: SignalSubscriberDrop { signal: *self },
@@ -238,12 +285,14 @@ impl<T: 'static> Signal<T> {
}
/// Set the value of the signal. This will trigger an update on all subscribers.
#[track_caller]
pub fn set(&self, value: T) {
*self.write() = value;
}
/// Run a closure with a reference to the signal's value.
/// If the signal has been dropped, this will panic.
#[track_caller]
pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
let write = self.read();
f(&*write)
@@ -251,6 +300,7 @@ impl<T: 'static> Signal<T> {
/// Run a closure with a mutable reference to the signal's value.
/// If the signal has been dropped, this will panic.
#[track_caller]
pub fn with_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
let mut write = self.write();
f(&mut *write)
@@ -265,11 +315,19 @@ impl<T: 'static> Signal<T> {
impl<T: Clone + 'static> Signal<T> {
/// Get the current value of the signal. This will subscribe the current scope to the signal.
/// If the signal has been dropped, this will panic.
#[track_caller]
pub fn value(&self) -> T {
self.read().clone()
}
}
impl Signal<bool> {
/// Invert the boolean value of the signal. This will trigger an update on all subscribers.
pub fn toggle(&self) {
self.set(!self.value());
}
}
impl<T: 'static> PartialEq for Signal<T> {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
@@ -277,7 +335,7 @@ impl<T: 'static> PartialEq for Signal<T> {
}
impl<T> Deref for Signal<T> {
type Target = dyn Fn() -> Ref<'static, T>;
type Target = dyn Fn() -> GenerationalRef<T>;
fn deref(&self) -> &Self::Target {
// https://github.com/dtolnay/case-studies/tree/master/callable-types
@@ -320,17 +378,17 @@ impl<T: 'static> Drop for SignalSubscriberDrop<T> {
}
/// A mutable reference to a signal's value.
pub struct Write<'a, T: 'static, I: 'static = T> {
write: RefMut<'a, T>,
pub struct Write<T: 'static, I: 'static = T> {
write: GenerationalRefMut<T>,
signal: SignalSubscriberDrop<I>,
}
impl<'a, T: 'static, I: 'static> Write<'a, T, I> {
impl<T: 'static, I: 'static> Write<T, I> {
/// Map the mutable reference to the signal's value to a new type.
pub fn map<O>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, I> {
pub fn map<O>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<O, I> {
let Self { write, signal } = myself;
Write {
write: RefMut::map(write, f),
write: GenerationalRefMut::map(write, f),
signal,
}
}
@@ -339,14 +397,14 @@ impl<'a, T: 'static, I: 'static> Write<'a, T, I> {
pub fn filter_map<O>(
myself: Self,
f: impl FnOnce(&mut T) -> Option<&mut O>,
) -> Option<Write<'a, O, I>> {
) -> Option<Write<O, I>> {
let Self { write, signal } = myself;
let write = RefMut::filter_map(write, f).ok();
let write = GenerationalRefMut::filter_map(write, f);
write.map(|write| Write { write, signal })
}
}
impl<'a, T: 'static, I: 'static> Deref for Write<'a, T, I> {
impl<T: 'static, I: 'static> Deref for Write<T, I> {
type Target = T;
fn deref(&self) -> &Self::Target {
@@ -354,7 +412,7 @@ impl<'a, T: 'static, I: 'static> Deref for Write<'a, T, I> {
}
}
impl<T, I> DerefMut for Write<'_, T, I> {
impl<T, I> DerefMut for Write<T, I> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.write
}
@@ -376,12 +434,23 @@ impl<T: 'static> ReadOnlySignal<T> {
self.inner.origin_scope()
}
/// Get the current value of the signal. This will subscribe the current scope to the signal.
pub fn read(&self) -> Ref<T> {
/// Get the current value of the signal. This will subscribe the current scope to the signal. If you would like to read the signal without subscribing to it, you can use [`Self::peek`] instead.
///
/// If the signal has been dropped, this will panic.
#[track_caller]
pub fn read(&self) -> GenerationalRef<T> {
self.inner.read()
}
/// Get the current value of the signal. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
///
/// If the signal has been dropped, this will panic.
pub fn peek(&self) -> GenerationalRef<T> {
self.inner.peek()
}
/// Run a closure with a reference to the signal's value.
#[track_caller]
pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
self.inner.with(f)
}
@@ -401,7 +470,7 @@ impl<T: 'static> PartialEq for ReadOnlySignal<T> {
}
impl<T> Deref for ReadOnlySignal<T> {
type Target = dyn Fn() -> Ref<'static, T>;
type Target = dyn Fn() -> GenerationalRef<T>;
fn deref(&self) -> &Self::Target {
// https://github.com/dtolnay/case-studies/tree/master/callable-types