cosmic/widget/table/widget/
compact.rs1use derive_setters::Setters;
2
3use crate::widget::table::model::{
4 Entity, Model,
5 category::{ItemCategory, ItemInterface},
6 selection::Selectable,
7};
8use crate::{
9 Apply, Element, theme,
10 widget::{self, container, menu},
11};
12use iced::{Alignment, Border, Padding};
13
14#[derive(Setters)]
15#[must_use]
16pub struct CompactTableView<'a, SelectionMode, Item, Category, Message>
17where
18 Category: ItemCategory,
19 Item: ItemInterface<Category>,
20 Model<SelectionMode, Item, Category>: Selectable,
21 SelectionMode: Default,
22 Message: Clone + 'static,
23{
24 pub(super) model: &'a Model<SelectionMode, Item, Category>,
25
26 #[setters(into)]
27 pub(super) element_padding: Padding,
28
29 #[setters(into)]
30 pub(super) item_padding: Padding,
31 pub(super) item_spacing: u16,
32 pub(super) icon_size: u16,
33
34 #[setters(into)]
35 pub(super) divider_padding: Padding,
36
37 #[setters(skip)]
39 pub(super) on_item_mb_left: Option<Box<dyn Fn(Entity) -> Message + 'static>>,
40 #[setters(skip)]
41 pub(super) on_item_mb_double: Option<Box<dyn Fn(Entity) -> Message + 'static>>,
42 #[setters(skip)]
43 pub(super) on_item_mb_mid: Option<Box<dyn Fn(Entity) -> Message + 'static>>,
44 #[setters(skip)]
45 pub(super) on_item_mb_right: Option<Box<dyn Fn(Entity) -> Message + 'static>>,
46 #[setters(skip)]
47 pub(super) item_context_builder: Box<dyn Fn(&Item) -> Option<Vec<menu::Tree<Message>>>>,
48}
49
50impl<'a, SelectionMode, Item, Category, Message>
51 From<CompactTableView<'a, SelectionMode, Item, Category, Message>> for Element<'a, Message>
52where
53 Category: ItemCategory,
54 Item: ItemInterface<Category>,
55 Model<SelectionMode, Item, Category>: Selectable,
56 SelectionMode: Default,
57 Message: Clone + 'static,
58{
59 fn from(val: CompactTableView<'a, SelectionMode, Item, Category, Message>) -> Self {
60 let cosmic_theme::Spacing { space_xxxs, .. } = theme::spacing();
61 val.model
62 .iter()
63 .map(|entity| {
64 let item = val.model.item(entity).unwrap();
65 let selected = val.model.is_active(entity);
66 let context_menu = (val.item_context_builder)(item);
67
68 widget::column::with_capacity(2)
69 .spacing(val.item_spacing)
70 .push(
71 widget::divider::horizontal::default()
72 .apply(container)
73 .padding(val.divider_padding),
74 )
75 .push(
76 widget::row::with_capacity(2)
77 .spacing(space_xxxs)
78 .align_y(Alignment::Center)
79 .push_maybe(
80 item.get_icon(Category::default())
81 .map(|icon| icon.size(val.icon_size)),
82 )
83 .push(
84 widget::column::with_capacity(2)
85 .push(widget::text::body(item.get_text(Category::default())))
86 .push({
87 let mut elements = val
88 .model
89 .categories
90 .iter()
91 .skip_while(|cat| **cat != Category::default())
92 .flat_map(|category| {
93 [
94 widget::text::caption(item.get_text(*category))
95 .apply(Element::from),
96 widget::text::caption("-").apply(Element::from),
97 ]
98 })
99 .collect::<Vec<Element<'static, Message>>>();
100 elements.pop();
101 elements
102 .apply(widget::row::with_children)
103 .spacing(space_xxxs)
104 .wrap()
105 }),
106 )
107 .apply(container)
108 .padding(val.item_padding)
109 .width(iced::Length::Fill)
110 .class(theme::Container::custom(move |theme| {
111 widget::container::Style {
112 icon_color: if selected {
113 Some(theme.cosmic().on_accent_color().into())
114 } else {
115 None
116 },
117 text_color: if selected {
118 Some(theme.cosmic().on_accent_color().into())
119 } else {
120 None
121 },
122 background: if selected {
123 Some(iced::Background::Color(
124 theme.cosmic().accent_color().into(),
125 ))
126 } else {
127 None
128 },
129 border: Border {
130 radius: theme.cosmic().radius_xs().into(),
131 ..Default::default()
132 },
133 shadow: Default::default(),
134 snap: true,
135 }
136 }))
137 .apply(widget::mouse_area)
138 .apply(|mouse_area| {
140 if let Some(ref on_item_mb) = val.on_item_mb_left {
141 mouse_area.on_press((on_item_mb)(entity))
142 } else {
143 mouse_area
144 }
145 })
146 .apply(|mouse_area| {
148 if let Some(ref on_item_mb) = val.on_item_mb_double {
149 mouse_area.on_double_click((on_item_mb)(entity))
150 } else {
151 mouse_area
152 }
153 })
154 .apply(|mouse_area| {
156 if let Some(ref on_item_mb) = val.on_item_mb_mid {
157 mouse_area.on_middle_press((on_item_mb)(entity))
158 } else {
159 mouse_area
160 }
161 })
162 .apply(|mouse_area| {
164 if let Some(ref on_item_mb) = val.on_item_mb_right {
165 mouse_area.on_right_press((on_item_mb)(entity))
166 } else {
167 mouse_area
168 }
169 })
170 .apply(|ma| widget::context_menu(ma, context_menu)),
171 )
172 .apply(Element::from)
173 })
174 .apply(widget::column::with_children)
175 .spacing(val.item_spacing)
176 .padding(val.element_padding)
177 .apply(Element::from)
178 }
179}
180
181impl<'a, SelectionMode, Item, Category, Message>
182 CompactTableView<'a, SelectionMode, Item, Category, Message>
183where
184 SelectionMode: Default,
185 Model<SelectionMode, Item, Category>: Selectable,
186 Category: ItemCategory,
187 Item: ItemInterface<Category>,
188 Message: Clone + 'static,
189{
190 pub fn new(model: &'a Model<SelectionMode, Item, Category>) -> Self {
191 let cosmic_theme::Spacing {
192 space_xxxs,
193 space_xxs,
194 ..
195 } = theme::spacing();
196
197 Self {
198 model,
199 element_padding: Padding::from(0),
200
201 divider_padding: Padding::from(0).left(space_xxxs).right(space_xxxs),
202
203 item_padding: Padding::from(space_xxs),
204 item_spacing: 0,
205 icon_size: 48,
206
207 on_item_mb_left: None,
208 on_item_mb_double: None,
209 on_item_mb_mid: None,
210 on_item_mb_right: None,
211 item_context_builder: Box::new(|_| None),
212 }
213 }
214
215 pub fn on_item_left_click<F>(mut self, on_click: F) -> Self
216 where
217 F: Fn(Entity) -> Message + 'static,
218 {
219 self.on_item_mb_left = Some(Box::new(on_click));
220 self
221 }
222
223 pub fn on_item_double_click<F>(mut self, on_click: F) -> Self
224 where
225 F: Fn(Entity) -> Message + 'static,
226 {
227 self.on_item_mb_double = Some(Box::new(on_click));
228 self
229 }
230
231 pub fn on_item_middle_click<F>(mut self, on_click: F) -> Self
232 where
233 F: Fn(Entity) -> Message + 'static,
234 {
235 self.on_item_mb_mid = Some(Box::new(on_click));
236 self
237 }
238
239 pub fn on_item_right_click<F>(mut self, on_click: F) -> Self
240 where
241 F: Fn(Entity) -> Message + 'static,
242 {
243 self.on_item_mb_right = Some(Box::new(on_click));
244 self
245 }
246
247 pub fn item_context<F>(mut self, context_menu_builder: F) -> Self
248 where
249 F: Fn(&Item) -> Option<Vec<menu::Tree<Message>>> + 'static,
250 Message: 'static,
251 {
252 self.item_context_builder = Box::new(context_menu_builder);
253 self
254 }
255}