1use super::{
4 Filter, OrdFn, RelmSelectionExt, TypedListItem, get_mut_value, get_value,
5 iterator::TypedIterator,
6};
7use gtk::{
8 gio, glib,
9 prelude::{Cast, CastNone, FilterExt, IsA, ListItemExt, ListModelExt, ObjectExt},
10};
11use std::{any::Any, cmp::Ordering, marker::PhantomData};
12
13pub trait RelmGridItem: Any {
15 type Root: IsA<gtk::Widget>;
17
18 type Widgets;
20
21 fn setup(grid_item: >k::ListItem) -> (Self::Root, Self::Widgets);
23
24 fn bind(&mut self, _widgets: &mut Self::Widgets, _root: &mut Self::Root) {}
26
27 fn unbind(&mut self, _widgets: &mut Self::Widgets, _root: &mut Self::Root) {}
29
30 fn teardown(_grid_item: >k::ListItem) {}
32}
33
34pub struct TypedGridView<T, S> {
41 pub view: gtk::GridView,
43 pub selection_model: S,
45 store: gio::ListStore,
46 filters: Vec<Filter>,
47 active_model: gio::ListModel,
48 base_model: gio::ListModel,
49 _ty: PhantomData<*const T>,
50}
51
52impl<T: std::fmt::Debug, S: std::fmt::Debug> std::fmt::Debug for TypedGridView<T, S> {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 f.debug_struct("TypedGridView")
55 .field("store", &self.store)
56 .field("view", &self.view)
57 .field("filters", &"<Vec<gtk::Filter>>")
58 .field("active_model", &self.active_model)
59 .field("base_model", &self.base_model)
60 .field("selection_model", &self.selection_model)
61 .finish()
62 }
63}
64
65impl<T, S> TypedGridView<T, S>
66where
67 T: RelmGridItem + Ord,
68 S: RelmSelectionExt,
69{
70 #[must_use]
73 pub fn with_sorting() -> Self {
74 Self::init(Some(Box::new(T::cmp)))
75 }
76}
77
78impl<T, S> Default for TypedGridView<T, S>
79where
80 T: RelmGridItem,
81 S: RelmSelectionExt,
82{
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88impl<T, S> TypedGridView<T, S>
89where
90 T: RelmGridItem,
91 S: RelmSelectionExt,
92{
93 #[must_use]
95 pub fn new() -> Self {
96 Self::init(None)
97 }
98
99 fn init(sort_fn: OrdFn<T>) -> Self {
100 let store = gio::ListStore::new::<glib::BoxedAnyObject>();
101
102 let factory = gtk::SignalListItemFactory::new();
103 factory.connect_setup(move |_, list_item| {
104 let list_item = list_item
105 .downcast_ref::<gtk::ListItem>()
106 .expect("Needs to be ListItem");
107
108 let (root, widgets) = T::setup(list_item);
109 unsafe { root.set_data("widgets", widgets) };
110 list_item.set_child(Some(&root));
111 });
112
113 factory.connect_bind(move |_, list_item| {
114 let list_item = list_item
115 .downcast_ref::<gtk::ListItem>()
116 .expect("Needs to be ListItem");
117
118 let widget = list_item
119 .downcast_ref::<gtk::ListItem>()
120 .expect("Needs to be ListItem")
121 .child();
122
123 let obj = list_item.item().unwrap();
124 let mut obj = get_mut_value::<T>(&obj);
125
126 let mut root = widget.and_downcast::<T::Root>().unwrap();
127
128 let mut widgets = unsafe { root.steal_data("widgets") }.unwrap();
129 obj.bind(&mut widgets, &mut root);
130 unsafe { root.set_data("widgets", widgets) };
131 });
132
133 factory.connect_unbind(move |_, list_item| {
134 let list_item = list_item
135 .downcast_ref::<gtk::ListItem>()
136 .expect("Needs to be ListItem");
137
138 let widget = list_item
139 .downcast_ref::<gtk::ListItem>()
140 .expect("Needs to be ListItem")
141 .child();
142
143 let obj = list_item.item().unwrap();
144 let mut obj = get_mut_value::<T>(&obj);
145
146 let mut root = widget.and_downcast::<T::Root>().unwrap();
147
148 let mut widgets = unsafe { root.steal_data("widgets") }.unwrap();
149 obj.unbind(&mut widgets, &mut root);
150 unsafe { root.set_data("widgets", widgets) };
151 });
152
153 factory.connect_teardown(move |_, list_item| {
154 let list_item = list_item
155 .downcast_ref::<gtk::ListItem>()
156 .expect("Needs to be ListItem");
157
158 T::teardown(list_item);
159 });
160
161 let model: gio::ListModel = store.clone().upcast();
162
163 let base_model = if let Some(sort_fn) = sort_fn {
164 let sorter = gtk::CustomSorter::new(move |first, second| {
165 let first = get_value::<T>(first);
166 let second = get_value::<T>(second);
167 match sort_fn(&first, &second) {
168 Ordering::Less => gtk::Ordering::Smaller,
169 Ordering::Equal => gtk::Ordering::Equal,
170 Ordering::Greater => gtk::Ordering::Larger,
171 }
172 });
173
174 gtk::SortListModel::new(Some(model), Some(sorter)).upcast()
175 } else {
176 model
177 };
178
179 let selection_model = S::new_model(base_model.clone());
180 let view = gtk::GridView::new(Some(selection_model.clone()), Some(factory));
181
182 Self {
183 store,
184 view,
185 filters: Vec::new(),
186 active_model: base_model.clone(),
187 base_model,
188 _ty: PhantomData,
189 selection_model,
190 }
191 }
192
193 pub fn add_filter<F: Fn(&T) -> bool + 'static>(&mut self, f: F) {
198 let filter = gtk::CustomFilter::new(move |obj| {
199 let value = get_value::<T>(obj);
200 f(&value)
201 });
202 let filter_model =
203 gtk::FilterListModel::new(Some(self.active_model.clone()), Some(filter.clone()));
204 self.active_model = filter_model.clone().upcast();
205 self.selection_model.set_list_model(&self.active_model);
206 self.filters.push(Filter {
207 filter,
208 model: filter_model,
209 });
210 }
211
212 pub fn filters_len(&self) -> usize {
214 self.filters.len()
215 }
216
217 pub fn set_filter_status(&mut self, idx: usize, active: bool) -> bool {
219 if let Some(filter) = self.filters.get(idx) {
220 if active {
221 filter.model.set_filter(Some(&filter.filter));
222 } else {
223 filter.model.set_filter(None::<>k::CustomFilter>);
224 }
225 true
226 } else {
227 false
228 }
229 }
230
231 pub fn notify_filter_changed(&self, idx: usize) -> bool {
236 if let Some(filter) = self.filters.get(idx) {
237 filter.filter.changed(gtk::FilterChange::Different);
238 true
239 } else {
240 false
241 }
242 }
243
244 pub fn pop_filter(&mut self) {
246 let filter = self.filters.pop();
247 if let Some(filter) = filter {
248 self.active_model = filter.model.model().unwrap();
249 self.selection_model.set_list_model(&self.active_model);
250 }
251 }
252
253 pub fn clear_filters(&mut self) {
255 self.filters.clear();
256 self.active_model = self.base_model.clone();
257 self.selection_model.set_list_model(&self.active_model);
258 }
259
260 pub fn append(&mut self, value: T) {
262 self.store.append(&glib::BoxedAnyObject::new(value));
263 }
264
265 pub fn extend_from_iter<I: IntoIterator<Item = T>>(&mut self, init: I) {
267 let objects: Vec<glib::BoxedAnyObject> =
268 init.into_iter().map(glib::BoxedAnyObject::new).collect();
269 self.store.extend_from_slice(&objects);
270 }
271
272 #[cfg(feature = "gnome_43")]
273 #[cfg_attr(docsrs, doc(cfg(feature = "gnome_43")))]
274 pub fn find<F: FnMut(&T) -> bool>(&self, mut equal_func: F) -> Option<u32> {
276 self.store.find_with_equal_func(move |obj| {
277 let value = get_value::<T>(obj);
278 equal_func(&value)
279 })
280 }
281
282 pub fn is_empty(&self) -> bool {
284 self.len() == 0
285 }
286
287 pub fn len(&self) -> u32 {
289 self.store.n_items()
290 }
291
292 pub fn get(&self, position: u32) -> Option<TypedListItem<T>> {
296 if let Some(obj) = self.store.item(position) {
297 let wrapper = obj.downcast::<glib::BoxedAnyObject>().unwrap();
298 Some(TypedListItem::new(wrapper))
299 } else {
300 None
301 }
302 }
303
304 pub fn get_visible(&self, position: u32) -> Option<TypedListItem<T>> {
309 if let Some(obj) = self.active_model.item(position) {
310 let wrapper = obj.downcast::<glib::BoxedAnyObject>().unwrap();
311 Some(TypedListItem::new(wrapper))
312 } else {
313 None
314 }
315 }
316
317 pub fn insert(&mut self, position: u32, value: T) {
319 self.store
320 .insert(position, &glib::BoxedAnyObject::new(value));
321 }
322
323 pub fn insert_sorted<F: FnMut(&T, &T) -> Ordering>(
326 &self,
327 value: T,
328 mut compare_func: F,
329 ) -> u32 {
330 let item = glib::BoxedAnyObject::new(value);
331
332 let compare = move |first: &glib::Object, second: &glib::Object| -> Ordering {
333 let first = get_value::<T>(first);
334 let second = get_value::<T>(second);
335 compare_func(&first, &second)
336 };
337
338 self.store.insert_sorted(&item, compare)
339 }
340
341 pub fn remove(&mut self, position: u32) {
343 self.store.remove(position);
344 }
345
346 pub fn clear(&mut self) {
348 self.store.remove_all();
349 }
350
351 pub fn iter(&self) -> TypedIterator<'_, TypedGridView<T, S>> {
353 TypedIterator {
354 list: self,
355 index: 0,
356 }
357 }
358}