mirror of
https://github.com/Zenithsiz/dynatos.git
synced 2026-02-05 03:07:58 +00:00
Added new reactivity primitive Trigger.
`Signal<T>` now uses `T` underneath.
This commit is contained in:
parent
d5c96a117f
commit
bee58c76a2
@ -3,12 +3,14 @@
|
||||
// Modules
|
||||
pub mod effect;
|
||||
pub mod signal;
|
||||
pub mod trigger;
|
||||
pub mod with_default;
|
||||
|
||||
// Exports
|
||||
pub use self::{
|
||||
effect::{Effect, WeakEffect},
|
||||
signal::Signal,
|
||||
trigger::Trigger,
|
||||
with_default::{SignalWithDefault, WithDefault},
|
||||
};
|
||||
|
||||
|
||||
@ -5,53 +5,27 @@
|
||||
|
||||
// Imports
|
||||
use {
|
||||
crate::{Effect, SignalGet, SignalReplace, SignalSet, SignalUpdate, SignalWith, WeakEffect},
|
||||
std::{cell::RefCell, collections::HashSet, fmt, mem, rc::Rc},
|
||||
crate::{Effect, SignalGet, SignalReplace, SignalSet, SignalUpdate, SignalWith, Trigger, WeakEffect},
|
||||
std::{cell::RefCell, fmt, mem, rc::Rc},
|
||||
};
|
||||
|
||||
/// Signal inner
|
||||
struct Inner<T> {
|
||||
/// Value
|
||||
value: T,
|
||||
|
||||
/// Subscribers
|
||||
subscribers: HashSet<WeakEffect>,
|
||||
}
|
||||
|
||||
/// Signal
|
||||
pub struct Signal<T> {
|
||||
/// Inner
|
||||
inner: Rc<RefCell<Inner<T>>>,
|
||||
/// Value
|
||||
value: Rc<RefCell<T>>,
|
||||
|
||||
/// Trigger
|
||||
trigger: Trigger,
|
||||
}
|
||||
|
||||
impl<T> Signal<T> {
|
||||
/// Creates a new signal
|
||||
pub fn new(value: T) -> Self {
|
||||
let inner = Inner {
|
||||
value,
|
||||
subscribers: HashSet::new(),
|
||||
};
|
||||
Self {
|
||||
inner: Rc::new(RefCell::new(inner)),
|
||||
value: Rc::new(RefCell::new(value)),
|
||||
trigger: Trigger::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Explicitly adds a subscriber to this signal.
|
||||
///
|
||||
/// Returns if the subscriber already existed.
|
||||
pub fn add_subscriber<S: IntoSubscriber>(&self, subscriber: S) -> bool {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let new_effect = inner.subscribers.insert(subscriber.into_subscriber());
|
||||
!new_effect
|
||||
}
|
||||
|
||||
/// Removes a subscriber from this signal.
|
||||
///
|
||||
/// Returns if the subscriber existed
|
||||
pub fn remove_subscriber<S: IntoSubscriber>(&self, subscriber: S) -> bool {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
inner.subscribers.remove(&subscriber.into_subscriber())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SignalGet for Signal<T>
|
||||
@ -73,11 +47,11 @@ impl<T> SignalWith for Signal<T> {
|
||||
F: FnOnce(&Self::Value) -> O,
|
||||
{
|
||||
if let Some(effect) = Effect::running() {
|
||||
self.add_subscriber(effect);
|
||||
self.trigger.add_subscriber(effect);
|
||||
}
|
||||
|
||||
let inner = self.inner.try_borrow().expect("Cannot use signal value while updating");
|
||||
f(&inner.value)
|
||||
let value = self.value.try_borrow().expect("Cannot use signal value while updating");
|
||||
f(&value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,24 +76,15 @@ impl<T> SignalUpdate for Signal<T> {
|
||||
{
|
||||
// Update the value and get the output
|
||||
let output = {
|
||||
let mut inner = self
|
||||
.inner
|
||||
let mut value = self
|
||||
.value
|
||||
.try_borrow_mut()
|
||||
.expect("Cannot update signal value while using it");
|
||||
f(&mut inner.value)
|
||||
f(&mut value)
|
||||
};
|
||||
|
||||
// Then update all subscribers, removing any stale ones.
|
||||
// Note: Since running the effect will add subscribers, we can't keep
|
||||
// the inner borrow active, so we gather all dependencies before-hand.
|
||||
// However, we can remove subscribers in between running effects, so we
|
||||
// don't need to wait for that.
|
||||
let subscribers = self.inner.borrow().subscribers.iter().cloned().collect::<Vec<_>>();
|
||||
for subscriber in subscribers {
|
||||
if !subscriber.try_run() {
|
||||
self.remove_subscriber(subscriber);
|
||||
}
|
||||
}
|
||||
// Then trigger our trigger
|
||||
self.trigger.trigger();
|
||||
|
||||
output
|
||||
}
|
||||
@ -128,16 +93,15 @@ impl<T> SignalUpdate for Signal<T> {
|
||||
impl<T> Clone for Signal<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: Rc::clone(&self.inner),
|
||||
value: Rc::clone(&self.value),
|
||||
trigger: self.trigger.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for Signal<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Signal")
|
||||
.field("value", &self.inner.borrow().value)
|
||||
.finish()
|
||||
f.debug_struct("Signal").field("value", &*self.value.borrow()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
108
dynatos-reactive/src/trigger.rs
Normal file
108
dynatos-reactive/src/trigger.rs
Normal file
@ -0,0 +1,108 @@
|
||||
//! Trigger
|
||||
//!
|
||||
//! A reactivity primitive that allows re-running
|
||||
//! any subscribers.
|
||||
|
||||
// Imports
|
||||
use {
|
||||
crate::{Effect, WeakEffect},
|
||||
std::{cell::RefCell, collections::HashSet, fmt, rc::Rc},
|
||||
};
|
||||
|
||||
/// Trigger inner
|
||||
struct Inner {
|
||||
/// Subscribers
|
||||
subscribers: HashSet<WeakEffect>,
|
||||
}
|
||||
|
||||
/// Trigger
|
||||
pub struct Trigger {
|
||||
/// Inner
|
||||
inner: Rc<RefCell<Inner>>,
|
||||
}
|
||||
|
||||
impl Trigger {
|
||||
/// Creates a new trigger
|
||||
pub fn new() -> Self {
|
||||
let inner = Inner {
|
||||
subscribers: HashSet::new(),
|
||||
};
|
||||
Self {
|
||||
inner: Rc::new(RefCell::new(inner)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a subscriber to this trigger.
|
||||
///
|
||||
/// Returns if the subscriber already existed.
|
||||
pub fn add_subscriber<S: IntoSubscriber>(&self, subscriber: S) -> bool {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let new_effect = inner.subscribers.insert(subscriber.into_subscriber());
|
||||
!new_effect
|
||||
}
|
||||
|
||||
/// Removes a subscriber from this trigger.
|
||||
///
|
||||
/// Returns if the subscriber existed
|
||||
pub fn remove_subscriber<S: IntoSubscriber>(&self, subscriber: S) -> bool {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
inner.subscribers.remove(&subscriber.into_subscriber())
|
||||
}
|
||||
|
||||
/// Triggers this trigger.
|
||||
///
|
||||
/// Re-runs all subscribers.
|
||||
pub fn trigger(&self) {
|
||||
// Run all subscribers, and remove any empty ones
|
||||
// Note: Since running the subscriber might add subscribers, we can't keep
|
||||
// the inner borrow active, so we gather all dependencies before-hand.
|
||||
// However, we can remove subscribers in between running effects, so we
|
||||
// don't need to wait for that.
|
||||
// TODO: Have a 2nd field `to_add_subscribers` where subscribers are added if
|
||||
// the main field is locked, and after this loop move any subscribers from
|
||||
// it to the main field?
|
||||
let subscribers = self.inner.borrow().subscribers.iter().cloned().collect::<Vec<_>>();
|
||||
for subscriber in subscribers {
|
||||
if !subscriber.try_run() {
|
||||
self.remove_subscriber(subscriber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Trigger {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Trigger {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: Rc::clone(&self.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Trigger {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Trigger").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
/// Types that may be converted into a subscriber
|
||||
pub trait IntoSubscriber {
|
||||
fn into_subscriber(self) -> WeakEffect;
|
||||
}
|
||||
|
||||
#[duplicate::duplicate_item(
|
||||
T body;
|
||||
[ Effect ] [ self.downgrade() ];
|
||||
[ &'_ Effect ] [ self.downgrade() ];
|
||||
[ WeakEffect ] [ self ];
|
||||
)]
|
||||
impl IntoSubscriber for T {
|
||||
fn into_subscriber(self) -> WeakEffect {
|
||||
body
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user