cosmic/widget/table/model/
mod.rs1pub mod category;
2pub mod entity;
3pub mod selection;
4
5use std::{
6 any::{Any, TypeId},
7 collections::{HashMap, VecDeque},
8};
9
10use category::{ItemCategory, ItemInterface};
11use entity::EntityMut;
12use selection::Selectable;
13use slotmap::{SecondaryMap, SlotMap};
14
15slotmap::new_key_type! {
16 pub struct Entity;
18}
19
20#[derive(Debug, Default)]
22pub(super) struct Storage(HashMap<TypeId, SecondaryMap<Entity, Box<dyn Any>>>);
23
24pub struct Model<SelectionMode: Default, Item: ItemInterface<Category>, Category: ItemCategory>
25where
26 Category: ItemCategory,
27{
28 pub(super) categories: Vec<Category>,
29
30 pub(super) items: SlotMap<Entity, Item>,
32
33 pub(super) active: SecondaryMap<Entity, bool>,
35
36 pub(super) indents: SecondaryMap<Entity, u16>,
38
39 pub(super) order: VecDeque<Entity>,
41
42 pub(super) selection: SelectionMode,
44
45 pub(super) sort: Option<(Category, bool)>,
47
48 pub(super) storage: Storage,
50}
51
52impl<SelectionMode: Default, Item: ItemInterface<Category>, Category: ItemCategory>
53 Model<SelectionMode, Item, Category>
54where
55 Self: Selectable,
56{
57 pub fn new(categories: Vec<Category>) -> Self {
58 Self {
59 categories,
60 items: SlotMap::default(),
61 active: SecondaryMap::default(),
62 indents: SecondaryMap::default(),
63 order: VecDeque::new(),
64 selection: SelectionMode::default(),
65 sort: None,
66 storage: Storage::default(),
67 }
68 }
69
70 pub fn categories(&mut self, cats: Vec<Category>) {
71 self.categories = cats;
72 }
73
74 pub fn activate(&mut self, id: Entity) {
80 Selectable::activate(self, id);
81 }
82
83 pub fn activate_position(&mut self, position: u16) -> bool {
85 if let Some(entity) = self.entity_at(position) {
86 self.activate(entity);
87 return true;
88 }
89
90 false
91 }
92
93 pub fn clear(&mut self) {
103 for entity in self.order.clone() {
104 self.remove(entity);
105 }
106 }
107
108 pub fn contains_item(&self, id: Entity) -> bool {
116 self.items.contains_key(id)
117 }
118
119 pub fn item(&self, id: Entity) -> Option<&Item> {
127 self.items.get(id)
128 }
129
130 pub fn item_mut(&mut self, id: Entity) -> Option<&mut Item> {
132 self.items.get_mut(id)
133 }
134
135 pub fn item_set(&mut self, id: Entity, data: Item) {
143 if let Some(item) = self.items.get_mut(id) {
144 *item = data;
145 }
146 }
147
148 pub fn data<Data: 'static>(&self, id: Entity) -> Option<&Data> {
156 self.storage
157 .0
158 .get(&TypeId::of::<Data>())
159 .and_then(|storage| storage.get(id))
160 .and_then(|data| data.downcast_ref())
161 }
162
163 pub fn data_mut<Data: 'static>(&mut self, id: Entity) -> Option<&mut Data> {
165 self.storage
166 .0
167 .get_mut(&TypeId::of::<Data>())
168 .and_then(|storage| storage.get_mut(id))
169 .and_then(|data| data.downcast_mut())
170 }
171
172 pub fn data_set<Data: 'static>(&mut self, id: Entity, data: Data) {
180 if self.contains_item(id) {
181 self.storage
182 .0
183 .entry(TypeId::of::<Data>())
184 .or_default()
185 .insert(id, Box::new(data));
186 }
187 }
188
189 pub fn data_remove<Data: 'static>(&mut self, id: Entity) {
195 self.storage
196 .0
197 .get_mut(&TypeId::of::<Data>())
198 .and_then(|storage| storage.remove(id));
199 }
200
201 pub fn enable(&mut self, id: Entity, enable: bool) {
207 if let Some(e) = self.active.get_mut(id) {
208 *e = enable;
209 }
210 }
211
212 #[must_use]
214 pub fn entity_at(&mut self, position: u16) -> Option<Entity> {
215 self.order.get(position as usize).copied()
216 }
217
218 #[must_use]
224 pub fn insert(&mut self, item: Item) -> EntityMut<SelectionMode, Item, Category> {
225 let id = self.items.insert(item);
226 self.order.push_back(id);
227 EntityMut { model: self, id }
228 }
229
230 #[must_use]
232 pub fn is_active(&self, id: Entity) -> bool {
233 <Self as Selectable>::is_active(self, id)
234 }
235
236 #[must_use]
246 pub fn is_enabled(&self, id: Entity) -> bool {
247 self.active.get(id).map_or(false, |e| *e)
248 }
249
250 pub fn iter(&self) -> impl Iterator<Item = Entity> + '_ {
252 self.order.iter().copied()
253 }
254
255 pub fn indent(&self, id: Entity) -> Option<u16> {
256 self.indents.get(id).copied()
257 }
258
259 pub fn indent_set(&mut self, id: Entity, indent: u16) -> Option<u16> {
260 if !self.contains_item(id) {
261 return None;
262 }
263
264 self.indents.insert(id, indent)
265 }
266
267 pub fn indent_remove(&mut self, id: Entity) -> Option<u16> {
268 self.indents.remove(id)
269 }
270
271 #[must_use]
278 pub fn position(&self, id: Entity) -> Option<u16> {
279 #[allow(clippy::cast_possible_truncation)]
280 self.order.iter().position(|k| *k == id).map(|v| v as u16)
281 }
282
283 pub fn position_set(&mut self, id: Entity, position: u16) -> Option<usize> {
291 let Some(index) = self.position(id) else {
292 return None;
293 };
294
295 self.order.remove(index as usize);
296
297 let position = self.order.len().min(position as usize);
298
299 self.order.insert(position, id);
300 Some(position)
301 }
302
303 pub fn position_swap(&mut self, first: Entity, second: Entity) -> bool {
313 let Some(first_index) = self.position(first) else {
314 return false;
315 };
316
317 let Some(second_index) = self.position(second) else {
318 return false;
319 };
320
321 self.order.swap(first_index as usize, second_index as usize);
322 true
323 }
324
325 pub fn remove(&mut self, id: Entity) {
331 self.items.remove(id);
332 self.deactivate(id);
333
334 for storage in self.storage.0.values_mut() {
335 storage.remove(id);
336 }
337
338 if let Some(index) = self.position(id) {
339 self.order.remove(index as usize);
340 }
341 }
342
343 pub fn get_sort(&self) -> Option<(Category, bool)> {
345 self.sort
346 }
347
348 pub fn sort(&mut self, category: Category, ascending: bool) {
350 match self.sort {
351 Some((cat, asc)) if cat == category && asc == ascending => return,
352 Some((cat, _)) if cat == category => self.order.make_contiguous().reverse(),
353 _ => {
354 self.order.make_contiguous().sort_by(|entity_a, entity_b| {
355 let cmp = self
356 .items
357 .get(*entity_a)
358 .unwrap()
359 .compare(self.items.get(*entity_b).unwrap(), category);
360 if ascending { cmp } else { cmp.reverse() }
361 });
362 }
363 }
364 self.sort = Some((category, ascending));
365 }
366}