use crate::*;
impl<T> Signal<T>
where
T: Clone + PartialEq + 'static,
{
pub fn create(value: T) -> Self {
let boxed: Box<SignalInner<T>> = Box::new(SignalInner::new(value, Vec::new(), true));
let ptr: *mut SignalInner<T> = Box::into_raw(boxed);
let addr: usize = ptr as usize;
signal_inner_registry_mut().insert(addr);
let mut signal: Self = Self::new(0, std::marker::PhantomData);
signal.set_inner(addr);
signal
}
pub fn get(&self) -> T {
let inner: &mut SignalInner<T> = get_signal_inner_ref::<T>(self.get_inner());
if !inner.get_alive() {
return inner.get_value().clone();
}
let tracking_id: usize = CURRENT_TRACKING_DYNAMIC_ID.load(Ordering::Relaxed);
if tracking_id != usize::MAX {
self.add_dependent(tracking_id);
}
inner.get_value().clone()
}
pub fn subscribe<F>(&self, callback: F)
where
F: FnMut() + 'static,
{
get_signal_inner_ref::<T>(self.get_inner())
.get_mut_listeners()
.push(Box::new(callback));
}
pub(crate) fn replace_subscribe<F>(&self, callback: F)
where
F: FnMut() + 'static,
{
let listeners: &mut Vec<Box<dyn FnMut()>> =
get_signal_inner_ref::<T>(self.get_inner()).get_mut_listeners();
listeners.clear();
listeners.push(Box::new(callback));
}
pub(crate) fn deactivate(&self) {
let inner: &mut SignalInner<T> = get_signal_inner_ref::<T>(self.get_inner());
inner.set_alive(false);
inner.get_mut_listeners().clear();
inner.get_mut_dependents().clear();
}
fn update_and_notify(&self, value: T) -> bool {
let inner: &mut SignalInner<T> = get_signal_inner_ref::<T>(self.get_inner());
if !inner.get_alive() {
return false;
}
if *inner.get_value() == value {
return false;
}
inner.set_value(value);
let mut listeners: Vec<Box<dyn FnMut()>> = Vec::new();
swap(inner.get_mut_listeners(), &mut listeners);
for listener in listeners.iter_mut() {
listener();
}
if !is_signal_inner_alive(self.get_inner()) {
return true;
}
let inner: &mut SignalInner<T> = get_signal_inner_ref::<T>(self.get_inner());
if inner.get_alive() {
let new_listeners: &mut Vec<Box<dyn FnMut()>> = inner.get_mut_listeners();
if new_listeners.is_empty() {
swap(new_listeners, &mut listeners);
} else {
listeners.append(new_listeners);
swap(new_listeners, &mut listeners);
}
}
true
}
pub(crate) fn add_dependent(&self, dynamic_id: usize) {
let deps: &mut Vec<usize> =
get_signal_inner_ref::<T>(self.get_inner()).get_mut_dependents();
if !deps.contains(&dynamic_id) {
deps.push(dynamic_id);
}
}
#[allow(dead_code)]
pub(crate) fn remove_dependent(&self, dynamic_id: usize) {
get_signal_inner_ref::<T>(self.get_inner())
.get_mut_dependents()
.retain(|id| *id != dynamic_id);
}
pub(crate) fn get_dependents(&self) -> Vec<usize> {
get_signal_inner_ref::<T>(self.get_inner())
.get_dependents()
.clone()
}
pub fn set(&self, value: T) {
if self.update_and_notify(value) {
let dependents: Vec<usize> = self.get_dependents();
schedule_signal_update_targeted(&dependents);
}
}
pub fn set_silent(&self, value: T) {
self.update_and_notify(value);
}
pub fn set_untracked(&self, value: T) {
let inner: &mut SignalInner<T> = get_signal_inner_ref::<T>(self.get_inner());
if !inner.get_alive() {
return;
}
inner.set_value(value);
}
}
impl<T> Default for Signal<T>
where
T: Clone + Default + PartialEq + 'static,
{
fn default() -> Self {
Self::create(T::default())
}
}
impl<T> Clone for Signal<T>
where
T: Clone + PartialEq + 'static,
{
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Signal<T> where T: Clone + PartialEq + 'static {}
unsafe impl<T> Sync for SignalCell<T> where T: Clone + PartialEq + 'static {}
impl<T> SignalCell<T>
where
T: Clone + PartialEq + 'static,
{
pub const fn none() -> Self {
Self {
inner: UnsafeCell::new(None),
}
}
pub fn set(&self, signal: Signal<T>) {
unsafe {
let ptr: &mut Option<Signal<T>> = &mut *self.get_inner().get();
if ptr.is_some() {
panic!("SignalCell::set called on an already-initialized cell");
}
*ptr = Some(signal);
}
}
pub fn get(&self) -> Signal<T> {
unsafe {
let ptr: &Option<Signal<T>> = &*self.get_inner().get();
match ptr {
Some(signal) => *signal,
None => panic!("SignalCell::get called on an uninitialized cell"),
}
}
}
}
impl<T> Default for SignalCell<T>
where
T: Clone + PartialEq + 'static,
{
fn default() -> Self {
Self {
inner: UnsafeCell::new(None),
}
}
}
unsafe impl Sync for SignalInnerRegistryCell {}