ferroui_controls/
definition_list.rs1use crate::{DefinitionBase, Grid};
2use ferroui_base::collections::{FerroList, NotifyCollectionChangedEventArgs, ResetBehavior};
3use ferroui_base::{ObjectType, Ref, Upcast, WeakRef};
4use std::cell::{Cell, RefCell};
5use std::ops::Deref;
6use std::rc::Rc;
7
8struct DefinitionListData<T: ObjectType + Upcast<DefinitionBase>> {
11 list: FerroList<Ref<T>>,
12 is_dirty: Cell<bool>,
13 parent: RefCell<Option<WeakRef<Grid>>>,
14}
15
16pub struct DefinitionList<T: ObjectType + Upcast<DefinitionBase>> {
20 data: Rc<DefinitionListData<T>>,
21}
22
23impl<T: ObjectType + Upcast<DefinitionBase>> Clone for DefinitionList<T> {
24 fn clone(&self) -> Self {
25 Self { data: self.data.clone() }
26 }
27}
28
29impl<T: ObjectType + Upcast<DefinitionBase>> PartialEq for DefinitionList<T> {
31 fn eq(&self, other: &Self) -> bool {
32 Rc::ptr_eq(&self.data, &other.data)
33 }
34}
35
36impl<T: ObjectType + Upcast<DefinitionBase>> Default for DefinitionList<T> {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl<T: ObjectType + Upcast<DefinitionBase>> DefinitionList<T> {
43 pub fn new() -> Self {
45 let data = Rc::new(DefinitionListData {
46 list: FerroList::new(),
47 is_dirty: Cell::new(true),
48 parent: RefCell::new(None),
49 });
50 data.list.set_reset_behavior(ResetBehavior::Remove);
51 let weak = Rc::downgrade(&data);
52 data.list
53 .add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, Ref<T>>| {
54 if let Some(data) = weak.upgrade() {
55 data.on_collection_changed(e);
56 }
57 }));
58 Self { data }
59 }
60
61 #[inline]
64 pub(crate) fn is_dirty(&self) -> bool {
65 self.data.is_dirty.get()
66 }
67
68 #[inline]
69 pub(crate) fn set_is_dirty(&self, value: bool) {
70 self.data.is_dirty.set(value)
71 }
72
73 #[allow(dead_code)]
75 pub(crate) fn parent(&self) -> Option<Ref<Grid>> {
76 self.data.parent()
77 }
78
79 pub(crate) fn set_parent(&self, value: Option<&Ref<Grid>>) {
80 self.data.set_parent(value)
81 }
82}
83
84impl<T: ObjectType + Upcast<DefinitionBase>> Deref for DefinitionList<T> {
85 type Target = FerroList<Ref<T>>;
86
87 #[inline]
88 fn deref(&self) -> &Self::Target {
89 &self.data.list
90 }
91}
92
93impl<T: ObjectType + Upcast<DefinitionBase>> DefinitionListData<T> {
94 fn parent(&self) -> Option<Ref<Grid>> {
95 self.parent.borrow().as_ref().and_then(WeakRef::upgrade)
96 }
97
98 fn set_parent(&self, value: Option<&Ref<Grid>>) {
99 if self.parent().as_ref() == value {
100 return;
101 }
102
103 *self.parent.borrow_mut() = value.map(Ref::downgrade);
104
105 for (idx, definition) in self.list.snapshot().iter().enumerate() {
109 let definition: &DefinitionBase = (**definition).upcast();
110 Self::set_definition_parent(definition, value);
111 definition.set_index(idx as i32);
112 }
113 }
114
115 fn set_definition_parent(definition: &DefinitionBase, parent: Option<&Ref<Grid>>) {
123 let current = definition.parent();
124 if current.as_ref() == parent {
125 return;
126 }
127
128 if current.is_some() {
129 definition.on_exit_parent_tree();
130 }
131
132 definition.set_parent(parent);
133
134 if parent.is_some() {
135 definition.on_enter_parent_tree();
136 }
137 }
138
139 fn on_collection_changed(&self, e: &NotifyCollectionChangedEventArgs<'_, Ref<T>>) {
140 for (idx, definition) in self.list.snapshot().iter().enumerate() {
141 let definition: &DefinitionBase = (**definition).upcast();
142 definition.set_index(idx as i32);
143 }
144
145 self.update_definition_parent(e.new_items, false);
146 self.update_definition_parent(e.old_items, true);
147
148 self.is_dirty.set(true);
149 }
150
151 fn update_definition_parent(&self, items: &[Ref<T>], was_removed: bool) {
152 if items.is_empty() {
153 return;
154 }
155
156 let parent = if was_removed { None } else { self.parent() };
157
158 for item in items {
159 Self::set_definition_parent((**item).upcast(), parent.as_ref());
160 }
161 }
162}