relm4/factory/sync/collections/
hashmap.rs

1use crate::{Receiver, Sender};
2
3use crate::factory::sync::builder::FactoryBuilder;
4use crate::factory::sync::handle::FactoryHandle;
5use crate::factory::{CloneableFactoryComponent, FactoryComponent, FactoryView};
6
7use std::collections::HashMap;
8use std::collections::hash_map::RandomState;
9use std::hash::{BuildHasher, Hash, Hasher};
10use std::iter::FusedIterator;
11use std::marker::PhantomData;
12use std::ops;
13
14use gtk::prelude::IsA;
15
16#[derive(Debug)]
17#[must_use]
18pub struct FactoryElementGuard<'a, C>
19where
20    C: FactoryComponent,
21{
22    inner: &'a mut FactoryHandle<C>,
23}
24
25impl<C> ops::Deref for FactoryElementGuard<'_, C>
26where
27    C: FactoryComponent,
28{
29    type Target = C;
30
31    fn deref(&self) -> &Self::Target {
32        self.inner.data.get()
33    }
34}
35
36impl<C> ops::DerefMut for FactoryElementGuard<'_, C>
37where
38    C: FactoryComponent,
39{
40    fn deref_mut(&mut self) -> &mut Self::Target {
41        self.inner.data.get_mut()
42    }
43}
44
45impl<C> Drop for FactoryElementGuard<'_, C>
46where
47    C: FactoryComponent,
48{
49    fn drop(&mut self) {
50        self.inner.notifier.send(()).unwrap()
51    }
52}
53
54#[derive(Debug)]
55/// A builder-pattern struct for building a [`FactoryHashMap`].
56pub struct FactoryHashMapBuilder<K, C: FactoryComponent, S = RandomState> {
57    hasher: S,
58    component: PhantomData<C>,
59    key: PhantomData<K>,
60}
61
62impl<K, C> Default for FactoryHashMapBuilder<K, C>
63where
64    C: FactoryComponent,
65{
66    fn default() -> Self {
67        Self::new()
68    }
69}
70
71impl<K, C> FactoryHashMapBuilder<K, C>
72where
73    C: FactoryComponent,
74    C::ParentWidget: Default,
75{
76    #[must_use]
77    /// Launch the factory with a default parent widget.
78    pub fn launch_default(self) -> FactoryHashMapConnector<K, C> {
79        self.launch(Default::default())
80    }
81}
82
83impl<K, C> FactoryHashMapBuilder<K, C>
84where
85    C: FactoryComponent,
86{
87    /// Creates a new [`FactoryHashMapBuilder`].
88    #[must_use]
89    pub fn new() -> Self {
90        Self {
91            hasher: RandomState::default(),
92            component: PhantomData,
93            key: PhantomData,
94        }
95    }
96
97    /// Sets a different hasher.
98    pub fn hasher<H: Hasher>(self, hasher: H) -> FactoryHashMapBuilder<K, C, H> {
99        let Self { component, key, .. } = self;
100
101        FactoryHashMapBuilder {
102            hasher,
103            component,
104            key,
105        }
106    }
107
108    /// Launch the factory.
109    /// This is similar to [`Connector::launch`](crate::component::ComponentBuilder::launch).
110    pub fn launch(self, widget: C::ParentWidget) -> FactoryHashMapConnector<K, C> {
111        let Self { hasher, key, .. } = self;
112
113        let (output_sender, output_receiver) = crate::channel();
114
115        FactoryHashMapConnector {
116            widget,
117            output_sender,
118            output_receiver,
119            hasher,
120            _key: key,
121        }
122    }
123}
124
125#[derive(Debug)]
126/// Second stage of the builder-pattern for building a [`FactoryHashMap`].
127pub struct FactoryHashMapConnector<K, C, S = RandomState>
128where
129    C: FactoryComponent,
130{
131    widget: C::ParentWidget,
132    output_sender: Sender<C::Output>,
133    output_receiver: Receiver<C::Output>,
134    hasher: S,
135    _key: PhantomData<K>,
136}
137
138impl<K, C> FactoryHashMapConnector<K, C>
139where
140    C: FactoryComponent,
141{
142    /// Forwards output events to the designated sender.
143    pub fn forward<F, Msg>(self, sender_: &Sender<Msg>, f: F) -> FactoryHashMap<K, C>
144    where
145        F: Fn(C::Output) -> Msg + Send + 'static,
146        C::Output: Send,
147        Msg: Send + 'static,
148    {
149        let Self {
150            widget,
151            output_sender,
152            output_receiver,
153            hasher,
154            ..
155        } = self;
156
157        let sender_clone = sender_.clone();
158
159        crate::spawn(async move {
160            while let Some(msg) = output_receiver.recv().await {
161                if sender_clone.send(f(msg)).is_err() {
162                    break;
163                }
164            }
165        });
166
167        FactoryHashMap {
168            widget,
169            output_sender,
170            inner: HashMap::with_hasher(hasher),
171        }
172    }
173
174    /// Ignore outputs from the component and finish the builder.
175    pub fn detach(self) -> FactoryHashMap<K, C> {
176        let Self {
177            widget,
178            output_sender,
179            hasher,
180            ..
181        } = self;
182
183        FactoryHashMap {
184            widget,
185            output_sender,
186            inner: HashMap::with_hasher(hasher),
187        }
188    }
189}
190
191/// A container similar to [`HashMap`] that can be used to store
192/// values of type [`FactoryComponent`].
193#[derive(Debug)]
194pub struct FactoryHashMap<K, C: FactoryComponent, S = RandomState> {
195    widget: C::ParentWidget,
196    output_sender: Sender<C::Output>,
197    inner: HashMap<K, FactoryHandle<C>, S>,
198}
199
200impl<K, C, S> Drop for FactoryHashMap<K, C, S>
201where
202    C: FactoryComponent,
203{
204    fn drop(&mut self) {
205        self.clear();
206    }
207}
208
209impl<K, C, S> ops::Index<&K> for FactoryHashMap<K, C, S>
210where
211    C: FactoryComponent<Index = K>,
212    K: Hash + Eq,
213    S: BuildHasher,
214{
215    type Output = C;
216
217    fn index(&self, key: &K) -> &Self::Output {
218        self.get(key).expect("Called `get` on an invalid key")
219    }
220}
221
222impl<K, C> FactoryHashMap<K, C, RandomState>
223where
224    C: FactoryComponent,
225{
226    /// Creates a new [`FactoryHashMap`].
227    #[must_use]
228    pub fn builder() -> FactoryHashMapBuilder<K, C> {
229        FactoryHashMapBuilder::new()
230    }
231}
232
233impl<K, C, S> FactoryHashMap<K, C, S>
234where
235    C: FactoryComponent,
236{
237    /// Returns the number of elements in the [`FactoryHashMap`].
238    pub fn len(&self) -> usize {
239        self.inner.len()
240    }
241
242    /// Returns true if the [`FactoryHashMap`] is empty.
243    pub fn is_empty(&self) -> bool {
244        self.inner.is_empty()
245    }
246
247    /// Send clone of a message to all of the elements.
248    pub fn broadcast(&self, msg: C::Input)
249    where
250        C::Input: Clone,
251    {
252        self.inner.values().for_each(|c| c.input.emit(msg.clone()));
253    }
254
255    /// Returns the widget all components are attached to.
256    pub const fn widget(&self) -> &C::ParentWidget {
257        &self.widget
258    }
259
260    /// An iterator visiting all key-value pairs in arbitrary order.
261    pub fn iter(&self) -> impl ExactSizeIterator<Item = (&K, &C)> + FusedIterator {
262        self.inner.iter().map(|(k, c)| (k, c.data.get()))
263    }
264
265    /// Returns an iterator over the factory components.
266    pub fn values(&self) -> impl ExactSizeIterator<Item = &C> + FusedIterator {
267        self.inner.values().map(|c| c.data.get())
268    }
269
270    /// Returns an iterator over the keys of the hash map.
271    pub fn keys(&self) -> impl ExactSizeIterator<Item = &K> + FusedIterator {
272        self.inner.keys()
273    }
274
275    /// Clears the map, removing all factory components.
276    pub fn clear(&mut self) {
277        for (_, handle) in self.inner.drain() {
278            self.widget.factory_remove(&handle.returned_widget);
279        }
280    }
281}
282
283impl<K, C> FactoryHashMap<K, C, RandomState>
284where
285    C: FactoryComponent<Index = K>,
286    K: Hash + Eq,
287{
288    /// Creates a [`FactoryHashMap`] from a [`Vec`].
289    pub fn from_vec(component_vec: Vec<(K, C::Init)>, widget: C::ParentWidget) -> Self {
290        let mut output = Self::builder().launch(widget).detach();
291        for (key, init) in component_vec {
292            output.insert(key, init);
293        }
294        output
295    }
296}
297
298impl<K, C, S> FactoryHashMap<K, C, S>
299where
300    C: FactoryComponent<Index = K>,
301    K: Hash + Eq,
302    S: BuildHasher,
303{
304    /// Send a mage to one of the elements.
305    pub fn send(&self, key: &K, msg: C::Input) {
306        self.inner[key].input.emit(msg);
307    }
308
309    /// Tries to get an immutable reference to
310    /// the model of one element.
311    ///
312    /// Returns [`None`] if `key` is invalid.
313    pub fn get(&self, key: &K) -> Option<&C> {
314        self.inner.get(key).map(|c| c.data.get())
315    }
316
317    /// Tries to get a mutable reference to
318    /// the model of one element.
319    ///
320    /// Returns [`None`] if `key` is invalid.
321    pub fn get_mut(&mut self, key: &K) -> Option<FactoryElementGuard<'_, C>> {
322        self.inner
323            .get_mut(key)
324            .map(|c| FactoryElementGuard { inner: c })
325    }
326
327    /// Inserts a new factory component into the map.
328    ///
329    /// If the map did not have this key present, None is returned.
330    ///
331    /// If the map did have this key present, the value is updated, and the old value is returned.
332    /// The key is not updated, though; this matters for types that can be == without being identical. See the module-level documentation for more.
333    pub fn insert(&mut self, key: K, init: C::Init) -> Option<C> {
334        let existing = self.remove(&key);
335
336        let builder = FactoryBuilder::new(&key, init, self.output_sender.clone());
337
338        let position = C::position(&builder.data, &key);
339        let returned_widget = self
340            .widget
341            .factory_append(builder.root_widget.clone(), &position);
342
343        let component = builder.launch(&key, returned_widget);
344
345        assert!(self.inner.insert(key, component).is_none());
346
347        existing
348    }
349
350    /// Removes a key from the map, returning the factory component at the key if the key was previously in the map.
351    pub fn remove(&mut self, key: &K) -> Option<C> {
352        if let Some(handle) = self.inner.remove(key) {
353            self.widget.factory_remove(&handle.returned_widget);
354            Some(handle.data.into_inner())
355        } else {
356            None
357        }
358    }
359}
360
361/// Implements the Clone Trait for [`FactoryHashMap`] if the component implements [`CloneableFactoryComponent`].
362impl<K, C> Clone for FactoryHashMap<K, C, RandomState>
363where
364    C: CloneableFactoryComponent,
365    K: Clone + Hash + Eq,
366    C: FactoryComponent<Index = K>,
367{
368    fn clone(&self) -> Self {
369        // Create a new, empty FactoryHashMap.
370        let mut clone = FactoryHashMap::builder()
371            .launch(self.widget.clone())
372            .detach();
373        // Iterate over the items in the original FactoryHashMap.
374        for (k, item) in self.iter() {
375            // Clone each item and push it onto the new FactoryHashMap.
376            let init = C::get_init(item);
377            clone.insert(k.clone(), init);
378        }
379        // Return the new, cloned FactoryHashMap.
380        clone
381    }
382}
383
384impl<K, C> FactoryHashMap<K, C, RandomState>
385where
386    C: FactoryComponent,
387    K: Clone + Hash + Eq,
388    C::ParentWidget: IsA<gtk::Stack>,
389    C::Root: IsA<gtk::Widget>,
390{
391    /// Makes the element at a given key visible in a [`gtk::Stack`].
392    /// Returns [`true`] on success, otherwise [`false`].
393    pub fn set_visible(&self, key: &K) -> bool {
394        if let Some(handle) = self.inner.get(key) {
395            self.widget
396                .as_ref()
397                .set_visible_child(handle.root_widget.as_ref());
398            true
399        } else {
400            false
401        }
402    }
403}