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// }