cosmic/widget/segmented_button/model/
builder.rs

1// Copyright 2023 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4use slotmap::{SecondaryMap, SparseSecondaryMap};
5
6use super::{Entity, Model, Selectable};
7use crate::widget::icon::Icon;
8use std::borrow::Cow;
9
10/// A builder for a [`Model`].
11#[derive(Default)]
12pub struct ModelBuilder<SelectionMode: Default>(Model<SelectionMode>);
13
14/// Constructs a new item for the [`ModelBuilder`].
15pub struct BuilderEntity<SelectionMode: Default> {
16    model: ModelBuilder<SelectionMode>,
17    id: Entity,
18}
19
20impl<SelectionMode: Default> ModelBuilder<SelectionMode>
21where
22    Model<SelectionMode>: Selectable,
23{
24    /// Inserts a new item and its associated data into the model.
25    #[must_use]
26    pub fn insert(
27        mut self,
28        builder: impl Fn(BuilderEntity<SelectionMode>) -> BuilderEntity<SelectionMode>,
29    ) -> Self {
30        let id = self.0.insert().id();
31        builder(BuilderEntity { model: self, id }).model
32    }
33
34    /// Consumes the builder and returns the model.
35    #[inline]
36    pub fn build(self) -> Model<SelectionMode> {
37        self.0
38    }
39}
40
41impl<SelectionMode: Default> BuilderEntity<SelectionMode>
42where
43    Model<SelectionMode>: Selectable,
44{
45    /// Activates the newly-inserted item.
46    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
47    #[inline]
48    pub fn activate(mut self) -> Self {
49        self.model.0.activate(self.id);
50        self
51    }
52
53    /// Defines that the close button should appear
54    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
55    #[inline]
56    pub fn closable(mut self) -> Self {
57        self.model.0.closable_set(self.id, true);
58        self
59    }
60
61    /// Associates extra data with an external secondary map.
62    ///
63    /// The secondary map internally uses a `Vec`, so should only be used for data that
64    /// is commonly associated.
65    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
66    #[inline]
67    pub fn secondary<Data>(self, map: &mut SecondaryMap<Entity, Data>, data: Data) -> Self {
68        map.insert(self.id, data);
69        self
70    }
71
72    /// Associates extra data with an external sparse secondary map.
73    ///
74    /// Sparse maps internally use a `HashMap`, for data that is sparsely associated.
75    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
76    #[inline]
77    pub fn secondary_sparse<Data>(
78        self,
79        map: &mut SparseSecondaryMap<Entity, Data>,
80        data: Data,
81    ) -> Self {
82        map.insert(self.id, data);
83        self
84    }
85
86    /// Assigns extra data to the item.
87    ///
88    /// There can only be one data component per Rust type.
89    ///
90    /// ```ignore
91    /// enum ViewItem { A }
92    ///
93    /// segmented_button::Model::builder()
94    ///     .insert(|b| b.text("Item A").data(ViewItem::A))
95    ///     .build()
96    /// ```
97    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
98    #[inline]
99    pub fn data<Data: 'static>(mut self, data: Data) -> Self {
100        self.model.0.data_set(self.id, data);
101        self
102    }
103
104    #[inline]
105    pub fn divider_above(mut self) -> Self {
106        self.model.0.divider_above_set(self.id, true);
107        self
108    }
109
110    /// Defines an icon for the item.
111    ///
112    /// ```ignore
113    /// segmented_button::Model::builder()
114    ///     .insert(|b| b.text("Item A").icon("custom-icon"))
115    ///     .build()
116    /// ```
117    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
118    pub fn icon(mut self, icon: impl Into<Icon>) -> Self {
119        self.model.0.icon_set(self.id, icon.into());
120        self
121    }
122
123    /// Define the position of the newly-inserted item.
124    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
125    #[inline]
126    pub fn position(mut self, position: u16) -> Self {
127        self.model.0.position_set(self.id, position);
128        self
129    }
130
131    /// Swap the position with another item in the model.
132    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
133    #[inline]
134    pub fn position_swap(mut self, other: Entity) -> Self {
135        self.model.0.position_swap(self.id, other);
136        self
137    }
138
139    /// Defines the text for the item.
140    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
141    pub fn text(mut self, text: impl Into<Cow<'static, str>>) -> Self {
142        self.model.0.text_set(self.id, text);
143        self
144    }
145
146    /// Calls a function with the ID
147    #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
148    pub fn with_id(self, func: impl FnOnce(Entity)) -> Self {
149        func(self.id);
150        self
151    }
152}