Skip to main content

ferroui_base/media/
geometry_collection.rs

1use crate::media::{Geometry, GeometryGroup, MediaCollection, ResetBehavior};
2use crate::reactive::IDisposable;
3use crate::{Ref, WeakRef};
4use std::cell::RefCell;
5use std::ops::Deref;
6use std::rc::Rc;
7
8type ChildSubscriptions = Rc<RefCell<Vec<(Ref<Geometry>, Rc<dyn IDisposable>)>>>;
9
10/// A collection of [`Geometry`] objects that notifies its parent
11/// [`GeometryGroup`] of changes.
12///
13/// Cloning the handle shares the collection; equality is by identity.
14#[derive(Clone)]
15pub struct GeometryCollection {
16    list: MediaCollection<Ref<Geometry>>,
17    parent: Rc<RefCell<Option<WeakRef<GeometryGroup>>>>,
18}
19
20impl PartialEq for GeometryCollection {
21    #[inline]
22    fn eq(&self, other: &Self) -> bool {
23        self.list == other.list
24    }
25}
26
27impl Default for GeometryCollection {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33impl Deref for GeometryCollection {
34    type Target = MediaCollection<Ref<Geometry>>;
35    #[inline]
36    fn deref(&self) -> &Self::Target {
37        &self.list
38    }
39}
40
41impl FromIterator<Ref<Geometry>> for GeometryCollection {
42    fn from_iter<I: IntoIterator<Item = Ref<Geometry>>>(iter: I) -> Self {
43        Self::from_items(iter)
44    }
45}
46
47impl GeometryCollection {
48    /// Creates an empty collection.
49    pub fn new() -> Self {
50        Self::from_items([])
51    }
52
53    /// Creates a collection holding `items`.
54    pub fn from_items(items: impl IntoIterator<Item = Ref<Geometry>>) -> Self {
55        let list = MediaCollection::from_items(items);
56        list.set_reset_behavior(ResetBehavior::Remove);
57        let parent: Rc<RefCell<Option<WeakRef<GeometryGroup>>>> = Rc::new(RefCell::new(None));
58        let subscriptions: ChildSubscriptions = Rc::new(RefCell::new(Vec::new()));
59
60        let invalidate_parent = {
61            let parent = parent.clone();
62            move || {
63                let parent = parent.borrow().as_ref().and_then(WeakRef::upgrade);
64                if let Some(parent) = parent {
65                    parent.invalidate();
66                }
67            }
68        };
69
70        let subscriptions_added = subscriptions.clone();
71        let (invalidate_added, invalidate_removed) = (invalidate_parent.clone(), invalidate_parent);
72        // The subscription lives as long as the list.
73        let _ = list.for_each_item(
74            move |_, x: &Ref<Geometry>| {
75                let child_changed = invalidate_added.clone();
76                let subscription = x.changed(child_changed);
77                subscriptions_added.borrow_mut().push((x.clone(), subscription));
78                invalidate_added();
79            },
80            move |_, x: &Ref<Geometry>| {
81                let position = subscriptions.borrow().iter().position(|(g, _)| g.ptr_eq(x));
82                if let Some(position) = position {
83                    let (_, subscription) = subscriptions.borrow_mut().remove(position);
84                    subscription.dispose();
85                }
86                invalidate_removed();
87            },
88            || panic!("Collection reset is not supported."),
89        );
90
91        Self { list, parent }
92    }
93
94    /// The group that owns the collection.
95    pub fn parent(&self) -> Option<Ref<GeometryGroup>> {
96        self.parent.borrow().as_ref().and_then(WeakRef::upgrade)
97    }
98
99    /// Sets the group that owns the collection.
100    pub fn set_parent(&self, value: Option<&Ref<GeometryGroup>>) {
101        *self.parent.borrow_mut() = value.map(Ref::downgrade);
102    }
103}