1use std::cell::RefCell;
2use std::rc::Rc;
3use std::sync::atomic::{AtomicUsize, Ordering};
4
5use crate::reactive;
6
7pub type SubId = usize;
8
9static NEXT_SIGNAL_ID: AtomicUsize = AtomicUsize::new(1);
10
11pub struct Signal<T: 'static>(Rc<RefCell<Inner<T>>>);
12
13impl<T> Clone for Signal<T> {
14 fn clone(&self) -> Self {
15 Self(self.0.clone())
16 }
17}
18
19struct Inner<T> {
20 id: usize,
21 value: T,
22 subs: Vec<Option<Box<dyn Fn(&T)>>>,
23}
24
25impl<T> Signal<T> {
26 pub fn new(value: T) -> Self {
27 let id = NEXT_SIGNAL_ID.fetch_add(1, Ordering::Relaxed);
28 Self(Rc::new(RefCell::new(Inner {
29 id,
30 value,
31 subs: Vec::new(),
32 })))
33 }
34 pub fn id(&self) -> usize {
35 self.0.borrow().id
36 }
37 pub fn get(&self) -> T
38 where
39 T: Clone,
40 {
41 let inner = self.0.borrow();
42 reactive::register_signal_read(inner.id);
43 inner.value.clone()
44 }
45 pub fn set(&self, v: T) {
46 let mut inner = self.0.borrow_mut();
47 inner.value = v;
48 let vref = &inner.value;
49 for s in &inner.subs {
50 if let Some(cb) = s.as_ref() {
51 cb(vref);
52 }
53 }
54 reactive::signal_changed(inner.id);
56 }
57 pub fn update<F: FnOnce(&mut T)>(&self, f: F) {
58 let mut inner = self.0.borrow_mut();
59 f(&mut inner.value);
60 let vref = &inner.value;
61 for s in &inner.subs {
62 if let Some(cb) = s.as_ref() {
63 cb(vref);
64 }
65 }
66 reactive::signal_changed(inner.id);
67 }
68 pub fn subscribe(&self, f: impl Fn(&T) + 'static) -> SubId {
69 self.0.borrow_mut().subs.push(Some(Box::new(f)));
70 self.0.borrow().subs.len() - 1
71 }
72 pub fn unsubscribe(&self, id: SubId) -> bool {
74 let mut inner = self.0.borrow_mut();
75 if id < inner.subs.len() {
76 inner.subs[id] = None;
77 true
78 } else {
79 false
80 }
81 }
82 pub fn subscribe_guard(&self, f: impl Fn(&T) + 'static) -> SubGuard<T> {
84 let id = self.subscribe(f);
85 let sig = self.clone();
86 SubGuard {
87 sig,
88 id,
89 active: true,
90 }
91 }
92}
93
94pub fn signal<T>(t: T) -> Signal<T> {
95 Signal::new(t)
96}
97
98pub struct SubGuard<T: 'static> {
100 sig: crate::Signal<T>,
101 id: SubId,
102 active: bool,
103}
104impl<T> Drop for SubGuard<T> {
105 fn drop(&mut self) {
106 if self.active {
107 let _ = self.sig.unsubscribe(self.id);
108 self.active = false;
109 }
110 }
111}