Skip to main content

ferroui_system/events/
handler_list.rs

1use std::cell::{Cell, RefCell};
2use smallvec::SmallVec;
3use std::rc::Rc;
4use std::sync::atomic::{AtomicU64, Ordering};
5
6/// A list of event handlers that tolerates being modified while it is being
7/// invoked: invocation works on a snapshot, and the snapshot is only copied
8/// when the list actually changes.
9pub struct HandlerList<F: ?Sized> {
10    /// `None` until the first handler is added: most lists of an object never get one, and an
11    /// empty list then costs no allocation.
12    handlers: RefCell<Option<Rc<Handlers<F>>>>,
13    next_id: HandlerIds,
14}
15
16/// The handlers of a list: most lists hold one or two, which are stored with the list's shared
17/// allocation.
18type Handlers<F> = SmallVec<[(u64, Rc<F>); 2]>;
19
20/// The handlers of a [`HandlerList`] at the time of [`HandlerList::snapshot`]: later
21/// modifications of the list do not affect it.
22pub struct HandlerSnapshot<F: ?Sized>(Option<Rc<Handlers<F>>>);
23
24impl<F: ?Sized> Clone for HandlerSnapshot<F> {
25    fn clone(&self) -> Self {
26        Self(self.0.clone())
27    }
28}
29
30impl<F: ?Sized> std::ops::Deref for HandlerSnapshot<F> {
31    type Target = [(u64, Rc<F>)];
32
33    #[inline]
34    fn deref(&self) -> &[(u64, Rc<F>)] {
35        match &self.0 {
36            Some(handlers) => handlers,
37            None => &[],
38        }
39    }
40}
41
42/// Where the tokens of a handler list come from.
43enum HandlerIds {
44    Own(Cell<u64>),
45    /// A counter shared with other lists, so that tokens across them are in
46    /// subscription order (the typed and untyped forms of one event).
47    Shared(Rc<Cell<u64>>),
48    /// The counter of the process, which every list made with
49    /// [`HandlerList::with_ordered_ids`] shares: tokens across all of them are
50    /// in subscription order, and a list needs no counter of its own.
51    Ordered,
52}
53
54/// The counter of [`HandlerIds::Ordered`].
55static ORDERED_IDS: AtomicU64 = AtomicU64::new(1);
56
57impl HandlerIds {
58    fn next(&self) -> u64 {
59        let cell = match self {
60            Self::Own(cell) => cell,
61            Self::Shared(cell) => cell,
62            Self::Ordered => return ORDERED_IDS.fetch_add(1, Ordering::Relaxed),
63        };
64        let id = cell.get();
65        cell.set(id + 1);
66        id
67    }
68}
69
70impl<F: ?Sized> Default for HandlerList<F> {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76impl<F: ?Sized> HandlerList<F> {
77    pub fn new() -> Self {
78        Self { handlers: RefCell::new(None), next_id: HandlerIds::Own(Cell::new(1)) }
79    }
80
81    /// A list whose tokens come from `ids`, a counter shared with other
82    /// lists: tokens across all of them increase in subscription order.
83    pub fn with_shared_ids(ids: Rc<Cell<u64>>) -> Self {
84        Self { handlers: RefCell::new(None), next_id: HandlerIds::Shared(ids) }
85    }
86
87    /// A list whose tokens come from the counter of the process, which every
88    /// list made this way shares: tokens across all of them increase in
89    /// subscription order, as with [`with_shared_ids`](Self::with_shared_ids),
90    /// without a counter to allocate.
91    pub fn with_ordered_ids() -> Self {
92        Self { handlers: RefCell::new(None), next_id: HandlerIds::Ordered }
93    }
94
95    #[inline]
96    pub fn is_empty(&self) -> bool {
97        self.handlers.borrow().as_ref().is_none_or(|handlers| handlers.is_empty())
98    }
99
100    pub fn len(&self) -> usize {
101        self.handlers.borrow().as_ref().map_or(0, |handlers| handlers.len())
102    }
103
104    /// Adds a handler and returns a token that removes it again.
105    pub fn add(&self, handler: Rc<F>) -> u64 {
106        let id = self.next_id.next();
107        let mut handlers = self.handlers.borrow_mut();
108        let handlers = handlers.get_or_insert_with(|| Rc::new(SmallVec::new()));
109        Rc::make_mut_or_clone(handlers).push((id, handler));
110        id
111    }
112
113    /// Removes the handler registered under `token`.
114    pub fn remove(&self, token: u64) -> bool {
115        let mut handlers = self.handlers.borrow_mut();
116        let Some(handlers) = handlers.as_mut() else {
117            return false;
118        };
119        match handlers.iter().position(|(id, _)| *id == token) {
120            Some(index) => {
121                Rc::make_mut_or_clone(handlers).remove(index);
122                true
123            }
124            None => false,
125        }
126    }
127
128    /// Returns the current handlers; later modifications do not affect it.
129    #[inline]
130    pub fn snapshot(&self) -> HandlerSnapshot<F> {
131        HandlerSnapshot(self.handlers.borrow().clone())
132    }
133}
134
135trait MakeMutOrClone<T> {
136    fn make_mut_or_clone(this: &mut Self) -> &mut T;
137}
138
139impl<F: ?Sized> MakeMutOrClone<Handlers<F>> for Rc<Handlers<F>> {
140    fn make_mut_or_clone(this: &mut Self) -> &mut Handlers<F> {
141        if Rc::get_mut(this).is_none() {
142            let copy: Handlers<F> = this.iter().map(|(id, f)| (*id, f.clone())).collect();
143            *this = Rc::new(copy);
144        }
145        Rc::get_mut(this).expect("uniquely owned after copy")
146    }
147}