cosmic/widget/segmented_button/model/
mod.rs
1mod builder;
5pub use self::builder::{BuilderEntity, ModelBuilder};
6
7mod entity;
8pub use self::entity::EntityMut;
9
10mod selection;
11pub use self::selection::{MultiSelect, Selectable, SingleSelect};
12
13use crate::widget::Icon;
14use slotmap::{SecondaryMap, SlotMap};
15use std::any::{Any, TypeId};
16use std::borrow::Cow;
17use std::collections::{HashMap, VecDeque};
18
19slotmap::new_key_type! {
20 pub struct Entity;
22}
23
24#[derive(Clone, Debug)]
25pub struct Settings {
26 pub enabled: bool,
27 pub closable: bool,
28}
29
30impl Default for Settings {
31 fn default() -> Self {
32 Self {
33 enabled: true,
34 closable: false,
35 }
36 }
37}
38
39pub type SingleSelectModel = Model<SingleSelect>;
41
42pub type SingleSelectEntityMut<'a> = EntityMut<'a, SingleSelect>;
44
45pub type MultiSelectModel = Model<MultiSelect>;
47
48pub type MultiSelectEntityMut<'a> = EntityMut<'a, MultiSelect>;
50
51#[derive(Debug, Default)]
53pub(super) struct Storage(HashMap<TypeId, SecondaryMap<Entity, Box<dyn Any>>>);
54
55#[derive(Default)]
57pub struct Model<SelectionMode: Default> {
58 pub(super) items: SlotMap<Entity, Settings>,
60
61 pub(super) divider_aboves: SecondaryMap<Entity, bool>,
63
64 pub(super) icons: SecondaryMap<Entity, Icon>,
66
67 pub(super) indents: SecondaryMap<Entity, u16>,
69
70 pub(super) text: SecondaryMap<Entity, Cow<'static, str>>,
72
73 pub(super) order: VecDeque<Entity>,
75
76 pub(super) selection: SelectionMode,
78
79 pub(super) storage: Storage,
81}
82
83impl<SelectionMode: Default> Model<SelectionMode>
84where
85 Self: Selectable,
86{
87 #[inline]
93 pub fn activate(&mut self, id: Entity) {
94 Selectable::activate(self, id);
95 }
96
97 #[inline]
99 pub fn activate_position(&mut self, position: u16) -> bool {
100 if let Some(entity) = self.entity_at(position) {
101 self.activate(entity);
102 return true;
103 }
104
105 false
106 }
107
108 #[must_use]
118 #[inline]
119 pub fn builder() -> ModelBuilder<SelectionMode> {
120 ModelBuilder::default()
121 }
122
123 #[inline]
133 pub fn clear(&mut self) {
134 for entity in self.order.clone() {
135 self.remove(entity);
136 }
137 }
138
139 #[inline]
141 pub fn closable_set(&mut self, id: Entity, closable: bool) {
142 if let Some(settings) = self.items.get_mut(id) {
143 settings.closable = closable;
144 }
145 }
146
147 #[inline]
155 pub fn contains_item(&self, id: Entity) -> bool {
156 self.items.contains_key(id)
157 }
158
159 pub fn data<Data: 'static>(&self, id: Entity) -> Option<&Data> {
167 self.storage
168 .0
169 .get(&TypeId::of::<Data>())
170 .and_then(|storage| storage.get(id))
171 .and_then(|data| data.downcast_ref())
172 }
173
174 pub fn data_mut<Data: 'static>(&mut self, id: Entity) -> Option<&mut Data> {
176 self.storage
177 .0
178 .get_mut(&TypeId::of::<Data>())
179 .and_then(|storage| storage.get_mut(id))
180 .and_then(|data| data.downcast_mut())
181 }
182
183 pub fn data_set<Data: 'static>(&mut self, id: Entity, data: Data) {
191 if self.contains_item(id) {
192 self.storage
193 .0
194 .entry(TypeId::of::<Data>())
195 .or_default()
196 .insert(id, Box::new(data));
197 }
198 }
199
200 pub fn data_remove<Data: 'static>(&mut self, id: Entity) {
206 self.storage
207 .0
208 .get_mut(&TypeId::of::<Data>())
209 .and_then(|storage| storage.remove(id));
210 }
211
212 #[inline]
213 pub fn divider_above(&self, id: Entity) -> Option<bool> {
214 self.divider_aboves.get(id).copied()
215 }
216
217 pub fn divider_above_set(&mut self, id: Entity, divider_above: bool) -> Option<bool> {
218 if !self.contains_item(id) {
219 return None;
220 }
221
222 self.divider_aboves.insert(id, divider_above)
223 }
224
225 #[inline]
226 pub fn divider_above_remove(&mut self, id: Entity) -> Option<bool> {
227 self.divider_aboves.remove(id)
228 }
229
230 #[inline]
236 pub fn enable(&mut self, id: Entity, enable: bool) {
237 if let Some(e) = self.items.get_mut(id) {
238 e.enabled = enable;
239 }
240 }
241
242 #[must_use]
244 #[inline]
245 pub fn entity_at(&mut self, position: u16) -> Option<Entity> {
246 self.order.get(position as usize).copied()
247 }
248
249 #[inline]
257 pub fn icon(&self, id: Entity) -> Option<&Icon> {
258 self.icons.get(id)
259 }
260
261 #[inline]
269 pub fn icon_set(&mut self, id: Entity, icon: Icon) -> Option<Icon> {
270 if !self.contains_item(id) {
271 return None;
272 }
273
274 self.icons.insert(id, icon)
275 }
276
277 #[inline]
284 pub fn icon_remove(&mut self, id: Entity) -> Option<Icon> {
285 self.icons.remove(id)
286 }
287
288 #[must_use]
294 #[inline]
295 pub fn insert(&mut self) -> EntityMut<SelectionMode> {
296 let id = self.items.insert(Settings::default());
297 self.order.push_back(id);
298 EntityMut { model: self, id }
299 }
300
301 #[must_use]
303 #[inline]
304 pub fn is_active(&self, id: Entity) -> bool {
305 <Self as Selectable>::is_active(self, id)
306 }
307
308 #[must_use]
310 #[inline]
311 pub fn is_closable(&self, id: Entity) -> bool {
312 self.items.get(id).map(|e| e.closable).unwrap_or_default()
313 }
314
315 #[must_use]
325 #[inline]
326 pub fn is_enabled(&self, id: Entity) -> bool {
327 self.items.get(id).map(|e| e.enabled).unwrap_or_default()
328 }
329
330 #[inline]
332 pub fn len(&self) -> usize {
333 self.order.len()
334 }
335
336 pub fn iter(&self) -> impl Iterator<Item = Entity> + '_ {
338 self.order.iter().copied()
339 }
340
341 #[inline]
342 pub fn indent(&self, id: Entity) -> Option<u16> {
343 self.indents.get(id).copied()
344 }
345
346 #[inline]
347 pub fn indent_set(&mut self, id: Entity, indent: u16) -> Option<u16> {
348 if !self.contains_item(id) {
349 return None;
350 }
351
352 self.indents.insert(id, indent)
353 }
354
355 #[inline]
356 pub fn indent_remove(&mut self, id: Entity) -> Option<u16> {
357 self.indents.remove(id)
358 }
359
360 #[must_use]
367 #[inline]
368 pub fn position(&self, id: Entity) -> Option<u16> {
369 #[allow(clippy::cast_possible_truncation)]
370 self.order.iter().position(|k| *k == id).map(|v| v as u16)
371 }
372
373 pub fn position_set(&mut self, id: Entity, position: u16) -> Option<usize> {
381 let index = self.position(id)?;
382
383 self.order.remove(index as usize);
384
385 let position = self.order.len().min(position as usize);
386
387 self.order.insert(position, id);
388 Some(position)
389 }
390
391 pub fn position_swap(&mut self, first: Entity, second: Entity) -> bool {
401 let Some(first_index) = self.position(first) else {
402 return false;
403 };
404
405 let Some(second_index) = self.position(second) else {
406 return false;
407 };
408
409 self.order.swap(first_index as usize, second_index as usize);
410 true
411 }
412
413 pub fn remove(&mut self, id: Entity) {
419 self.items.remove(id);
420 self.deactivate(id);
421
422 for storage in self.storage.0.values_mut() {
423 storage.remove(id);
424 }
425
426 if let Some(index) = self.position(id) {
427 self.order.remove(index as usize);
428 }
429 }
430
431 #[inline]
439 pub fn text(&self, id: Entity) -> Option<&str> {
440 self.text.get(id).map(Cow::as_ref)
441 }
442
443 pub fn text_set(&mut self, id: Entity, text: impl Into<Cow<'static, str>>) -> Option<Cow<str>> {
451 if !self.contains_item(id) {
452 return None;
453 }
454
455 self.text.insert(id, text.into())
456 }
457
458 #[inline]
464 pub fn text_remove(&mut self, id: Entity) -> Option<Cow<'static, str>> {
465 self.text.remove(id)
466 }
467}