ferroui_controls/
item_collection.rs1use crate::items_source::{ItemsChangedEventArgs, ItemsSource};
2use crate::ItemsSourceView;
3use ferroui_base::collections::{FerroList, ResetBehavior};
4use ferroui_base::utilities::HandlerList;
5use ferroui_base::BoxedValue;
6use std::cell::{Cell, RefCell};
7use std::ops::Deref;
8use std::rc::Rc;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11enum Mode {
12 Items,
13 ItemsSource,
14}
15
16type DefaultCollection = FerroList<Option<BoxedValue>>;
17
18#[derive(Clone)]
24pub struct ItemCollection(Rc<ItemCollectionData>);
25
26struct ItemCollectionData {
27 view: Rc<ItemsSourceView>,
28 mode: Cell<Mode>,
29 writable: RefCell<Option<Rc<DefaultCollection>>>,
31 source_changed: HandlerList<dyn Fn()>,
32}
33
34impl PartialEq for ItemCollection {
35 fn eq(&self, other: &Self) -> bool {
36 Rc::ptr_eq(&self.0, &other.0)
37 }
38}
39
40impl std::fmt::Debug for ItemCollection {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 write!(f, "ItemCollection({})", self.0.view.count())
43 }
44}
45
46impl Deref for ItemCollection {
47 type Target = ItemsSourceView;
48
49 fn deref(&self) -> &ItemsSourceView {
50 &self.0.view
51 }
52}
53
54impl ItemCollection {
55 pub(crate) fn new() -> Self {
56 Self(Rc::new(ItemCollectionData {
57 view: ItemsSourceView::new(ItemsSourceView::uninitialized_source()),
58 mode: Cell::new(Mode::Items),
59 writable: RefCell::new(None),
60 source_changed: HandlerList::new(),
61 }))
62 }
63
64 pub fn view(&self) -> &Rc<ItemsSourceView> {
66 &self.0.view
67 }
68
69 pub fn set(&self, index: usize, value: Option<BoxedValue>) {
73 self.writable_source().set(index, value)
74 }
75
76 pub fn is_read_only(&self) -> bool {
78 self.0.mode.get() == Mode::ItemsSource
79 }
80
81 pub(crate) fn add_source_changed(&self, handler: Rc<dyn Fn()>) -> u64 {
83 self.0.source_changed.add(handler)
84 }
85
86 pub fn add(&self, value: Option<BoxedValue>) -> i32 {
91 let source = self.writable_source();
92 source.add(value);
93 source.count() as i32 - 1
94 }
95
96 pub fn clear(&self) {
101 self.writable_source().clear()
102 }
103
104 pub fn insert(&self, index: usize, value: Option<BoxedValue>) {
108 self.writable_source().insert(index, value)
109 }
110
111 pub fn remove_at(&self, index: usize) {
115 self.writable_source().remove_at(index)
116 }
117
118 pub fn remove(&self, value: &Option<BoxedValue>) -> bool {
123 let source = self.writable_source();
124 let index = self.0.view.index_of(value);
125 if index >= 0 {
126 source.remove_at(index as usize);
127 true
128 } else {
129 false
130 }
131 }
132
133 fn writable_source(&self) -> Rc<DefaultCollection> {
134 if self.is_read_only() {
135 Self::throw_is_items_source();
136 }
137 if self.0.view.try_get_initialized_source().is_none() {
138 self.set_source_default();
139 }
140 self.0.writable.borrow().clone().expect("the collection is in items mode")
141 }
142
143 pub(crate) fn set_items_source(&self, value: Option<ItemsSource>) {
144 if self.0.mode.get() != Mode::ItemsSource && self.0.view.count() > 0 {
145 panic!("Items collection must be empty before using ItemsSource.");
146 }
147
148 match value {
149 Some(value) => {
150 self.0.mode.set(Mode::ItemsSource);
151 *self.0.writable.borrow_mut() = None;
152 self.set_source(value);
153 }
154 None => {
155 self.0.mode.set(Mode::Items);
156 self.set_source_default();
157 }
158 }
159 }
160
161 fn set_source_default(&self) {
162 let collection = Self::create_default_collection();
163 *self.0.writable.borrow_mut() = Some(collection.clone());
164 self.set_source(collection.into());
165 }
166
167 fn set_source(&self, source: ItemsSource) {
168 let old_source = self.0.view.source();
169
170 self.0.view.set_source(source.clone());
171
172 if old_source.count() > 0 {
176 self.0.view.raise_collection_changed(&ItemsChangedEventArgs::remove(&old_source, 0));
177 }
178 if source.count() > 0 {
179 self.0.view.raise_collection_changed(&ItemsChangedEventArgs::add(&source, 0));
180 }
181 if !self.0.source_changed.is_empty() {
182 for (_, handler) in self.0.source_changed.snapshot().iter() {
183 handler();
184 }
185 }
186 }
187
188 fn create_default_collection() -> Rc<DefaultCollection> {
189 let collection = DefaultCollection::new();
190 collection.set_reset_behavior(ResetBehavior::Remove);
191 Rc::new(collection)
192 }
193
194 fn throw_is_items_source() -> ! {
195 panic!(
196 "Operation is not valid while ItemsSource is in use.\
197 Access and modify elements with ItemsControl.ItemsSource instead."
198 );
199 }
200}