cosmic/widget/table/model/
mod.rs1pub mod category;
2pub mod entity;
3pub mod selection;
4
5use std::any::{Any, TypeId};
6use std::collections::{HashMap, VecDeque};
7
8use category::{ItemCategory, ItemInterface};
9use entity::EntityMut;
10use selection::Selectable;
11use slotmap::{SecondaryMap, SlotMap};
12
13slotmap::new_key_type! {
14 pub struct Entity;
16}
17
18#[derive(Debug, Default)]
20pub(super) struct Storage(HashMap<TypeId, SecondaryMap<Entity, Box<dyn Any>>>);
21
22pub struct Model<SelectionMode: Default, Item: ItemInterface<Category>, Category: ItemCategory>
23where
24 Category: ItemCategory,
25{
26 pub(super) categories: Vec<Category>,
27
28 pub(super) items: SlotMap<Entity, Item>,
30
31 pub(super) active: SecondaryMap<Entity, bool>,
33
34 pub(super) indents: SecondaryMap<Entity, u16>,
36
37 pub(super) order: VecDeque<Entity>,
39
40 pub(super) selection: SelectionMode,
42
43 pub(super) sort: Option<(Category, bool)>,
45
46 pub(super) storage: Storage,
48}
49
50impl<SelectionMode: Default, Item: ItemInterface<Category>, Category: ItemCategory>
51 Model<SelectionMode, Item, Category>
52where
53 Self: Selectable,
54{
55 pub fn new(categories: Vec<Category>) -> Self {
56 Self {
57 categories,
58 items: SlotMap::default(),
59 active: SecondaryMap::default(),
60 indents: SecondaryMap::default(),
61 order: VecDeque::new(),
62 selection: SelectionMode::default(),
63 sort: None,
64 storage: Storage::default(),
65 }
66 }
67
68 pub fn categories(&mut self, cats: Vec<Category>) {
69 self.categories = cats;
70 }
71
72 pub fn activate(&mut self, id: Entity) {
78 Selectable::activate(self, id);
79 }
80
81 pub fn activate_position(&mut self, position: u16) -> bool {
83 if let Some(entity) = self.entity_at(position) {
84 self.activate(entity);
85 return true;
86 }
87
88 false
89 }
90
91 pub fn clear(&mut self) {
101 for entity in self.order.clone() {
102 self.remove(entity);
103 }
104 }
105
106 pub fn contains_item(&self, id: Entity) -> bool {
114 self.items.contains_key(id)
115 }
116
117 pub fn item(&self, id: Entity) -> Option<&Item> {
125 self.items.get(id)
126 }
127
128 pub fn item_mut(&mut self, id: Entity) -> Option<&mut Item> {
130 self.items.get_mut(id)
131 }
132
133 pub fn item_set(&mut self, id: Entity, data: Item) {
141 if let Some(item) = self.items.get_mut(id) {
142 *item = data;
143 }
144 }
145
146 pub fn data<Data: 'static>(&self, id: Entity) -> Option<&Data> {
154 self.storage
155 .0
156 .get(&TypeId::of::<Data>())
157 .and_then(|storage| storage.get(id))
158 .and_then(|data| data.downcast_ref())
159 }
160
161 pub fn data_mut<Data: 'static>(&mut self, id: Entity) -> Option<&mut Data> {
163 self.storage
164 .0
165 .get_mut(&TypeId::of::<Data>())
166 .and_then(|storage| storage.get_mut(id))
167 .and_then(|data| data.downcast_mut())
168 }
169
170 pub fn data_set<Data: 'static>(&mut self, id: Entity, data: Data) {
178 if self.contains_item(id) {
179 self.storage
180 .0
181 .entry(TypeId::of::<Data>())
182 .or_default()
183 .insert(id, Box::new(data));
184 }
185 }
186
187 pub fn data_remove<Data: 'static>(&mut self, id: Entity) {
193 self.storage
194 .0
195 .get_mut(&TypeId::of::<Data>())
196 .and_then(|storage| storage.remove(id));
197 }
198
199 pub fn enable(&mut self, id: Entity, enable: bool) {
205 if let Some(e) = self.active.get_mut(id) {
206 *e = enable;
207 }
208 }
209
210 #[must_use]
212 pub fn entity_at(&mut self, position: u16) -> Option<Entity> {
213 self.order.get(position as usize).copied()
214 }
215
216 #[must_use]
222 pub fn insert(&mut self, item: Item) -> EntityMut<'_, SelectionMode, Item, Category> {
223 let id = self.items.insert(item);
224 self.order.push_back(id);
225 EntityMut { model: self, id }
226 }
227
228 #[must_use]
230 pub fn is_active(&self, id: Entity) -> bool {
231 <Self as Selectable>::is_active(self, id)
232 }
233
234 #[must_use]
244 pub fn is_enabled(&self, id: Entity) -> bool {
245 self.active.get(id).is_some_and(|e| *e)
246 }
247
248 pub fn iter(&self) -> impl Iterator<Item = Entity> + '_ {
250 self.order.iter().copied()
251 }
252
253 pub fn indent(&self, id: Entity) -> Option<u16> {
254 self.indents.get(id).copied()
255 }
256
257 pub fn indent_set(&mut self, id: Entity, indent: u16) -> Option<u16> {
258 if !self.contains_item(id) {
259 return None;
260 }
261
262 self.indents.insert(id, indent)
263 }
264
265 pub fn indent_remove(&mut self, id: Entity) -> Option<u16> {
266 self.indents.remove(id)
267 }
268
269 #[must_use]
276 pub fn position(&self, id: Entity) -> Option<u16> {
277 #[allow(clippy::cast_possible_truncation)]
278 self.order.iter().position(|k| *k == id).map(|v| v as u16)
279 }
280
281 pub fn position_set(&mut self, id: Entity, position: u16) -> Option<usize> {
289 let index = self.position(id)?;
290
291 self.order.remove(index as usize);
292
293 let position = self.order.len().min(position as usize);
294
295 self.order.insert(position, id);
296 Some(position)
297 }
298
299 pub fn position_swap(&mut self, first: Entity, second: Entity) -> bool {
309 let Some(first_index) = self.position(first) else {
310 return false;
311 };
312
313 let Some(second_index) = self.position(second) else {
314 return false;
315 };
316
317 self.order.swap(first_index as usize, second_index as usize);
318 true
319 }
320
321 pub fn remove(&mut self, id: Entity) {
327 self.items.remove(id);
328 self.deactivate(id);
329
330 for storage in self.storage.0.values_mut() {
331 storage.remove(id);
332 }
333
334 if let Some(index) = self.position(id) {
335 self.order.remove(index as usize);
336 }
337 }
338
339 pub fn get_sort(&self) -> Option<(Category, bool)> {
341 self.sort
342 }
343
344 pub fn sort(&mut self, category: Category, ascending: bool) {
346 match self.sort {
347 Some((cat, asc)) if cat == category && asc == ascending => return,
348 Some((cat, _)) if cat == category => self.order.make_contiguous().reverse(),
349 _ => {
350 self.order.make_contiguous().sort_by(|entity_a, entity_b| {
351 let cmp = self
352 .items
353 .get(*entity_a)
354 .unwrap()
355 .compare(self.items.get(*entity_b).unwrap(), category);
356 if ascending { cmp } else { cmp.reverse() }
357 });
358 }
359 }
360 self.sort = Some((category, ascending));
361 }
362}