relm4/factory/dynamic_index.rs
1use std::sync::Arc;
2use std::sync::atomic::{AtomicUsize, Ordering};
3
4/// A dynamic index that updates automatically when items are shifted inside a factory container.
5///
6/// For example a [`FactoryVecDeque`](super::FactoryVecDeque) has an [`insert`](super::FactoryVecDequeGuard::insert)
7/// method that allows users to insert data at arbitrary positions.
8/// If we insert at the front all following widgets will be moved by one which would
9/// invalidate their indices.
10/// To allow widgets in a factory container to send messages with valid indices
11/// this type ensures that the indices is always up to date.
12///
13/// Never send an index as [`usize`] but always as [`DynamicIndex`]
14/// to the update function because messages can be queued up and stale by the time they are handled.
15///
16/// [`DynamicIndex`] is a smart pointer so cloning will work similar to [`std::rc::Rc`] and will create
17/// a pointer to the same data.
18///
19/// In short: only call [`current_index`](DynamicIndex::current_index) from the update function
20/// where you actually need the index as [`usize`].
21#[derive(Debug)]
22pub struct DynamicIndex {
23 inner: Arc<AtomicUsize>,
24}
25
26impl PartialEq for DynamicIndex {
27 fn eq(&self, other: &Self) -> bool {
28 self.current_index().eq(&other.current_index())
29 }
30}
31
32impl Eq for DynamicIndex {}
33
34impl Clone for DynamicIndex {
35 fn clone(&self) -> Self {
36 Self {
37 inner: self.inner.clone(),
38 }
39 }
40}
41
42impl DynamicIndex {
43 /// Get the current index number.
44 ///
45 /// This value is updated by the factory container and might change after each update function.
46 #[must_use]
47 pub fn current_index(&self) -> usize {
48 self.inner.load(Ordering::Relaxed)
49 }
50
51 // Creates a [`WeakDynamicIndex`] for sending in messages.
52 // pub fn downgrade(&self) -> WeakDynamicIndex {
53 // WeakDynamicIndex {
54 // inner: Arc::downgrade(&self.inner),
55 // }
56 // }
57
58 pub(super) fn increment(&self) {
59 self.inner.fetch_add(1, Ordering::Relaxed);
60 }
61
62 pub(super) fn decrement(&self) {
63 self.inner.fetch_sub(1, Ordering::Relaxed);
64 }
65
66 pub(super) fn set_value(&self, new_value: usize) {
67 self.inner.store(new_value, Ordering::Relaxed);
68 }
69
70 pub(super) fn new(index: usize) -> Self {
71 Self {
72 inner: Arc::new(AtomicUsize::new(index)),
73 }
74 }
75}
76
77// A weak version of [`DynamicIndex`].
78//
79// Use this to send messages to the update function and call [`upgrade`](WeakDynamicIndex::upgrade)
80// to receive the actual [`DynamicIndex`].
81//
82// A weak index is preferred for sending in messages because messages can be stale by the time they
83// are handled and the element already deleted. A weak reference doesn't keep the index alive
84// if the element was deleted which allows you to properly handle invalid indices.
85//
86// # Panics
87//
88// Sending a [`WeakDynamicIndex`] to a different thread and accessing it will panic.
89// #[derive(Debug)]
90// pub struct WeakDynamicIndex {
91// inner: Weak<Mutex<usize>>,
92// }
93
94// impl Clone for WeakDynamicIndex {
95// fn clone(&self) -> Self {
96// WeakDynamicIndex {
97// inner: self.inner.clone(),
98// }
99// }
100// }
101
102// impl WeakDynamicIndex {
103// Attempts to upgrade the [`WeakDynamicIndex`] to a [`DynamicIndex`].
104//
105// Returns [`None`] if the index has since been dropped.
106// pub fn upgrade(&self) -> Option<DynamicIndex> {
107// Weak::upgrade(&self.inner).map(|inner| {
108// DynamicIndex {
109// inner,
110// }
111// })
112// }
113// }