Skip to main content

fractal/utils/
single_item_list_model.rs

1use gtk::{gio, glib, prelude::*, subclass::prelude::*};
2
3mod imp {
4    use std::cell::{Cell, RefCell};
5
6    use super::*;
7
8    #[derive(Debug, Default, glib::Properties)]
9    #[properties(wrapper_type = super::SingleItemListModel)]
10    pub struct SingleItemListModel {
11        /// The item contained by this model.
12        #[property(get, set = Self::set_item, explicit_notify, nullable)]
13        item: RefCell<Option<glib::Object>>,
14        /// Whether the item is hidden.
15        #[property(get, set = Self::set_is_hidden, explicit_notify)]
16        is_hidden: Cell<bool>,
17    }
18
19    #[glib::object_subclass]
20    impl ObjectSubclass for SingleItemListModel {
21        const NAME: &'static str = "SingleItemListModel";
22        type Type = super::SingleItemListModel;
23        type Interfaces = (gio::ListModel,);
24    }
25
26    #[glib::derived_properties]
27    impl ObjectImpl for SingleItemListModel {}
28
29    impl ListModelImpl for SingleItemListModel {
30        fn item_type(&self) -> glib::Type {
31            self.item
32                .borrow()
33                .as_ref()
34                .map_or_else(glib::Object::static_type, glib::Object::type_)
35        }
36
37        fn n_items(&self) -> u32 {
38            (!self.is_empty()).into()
39        }
40
41        fn item(&self, position: u32) -> Option<glib::Object> {
42            if self.is_hidden.get() || position != 0 {
43                return None;
44            }
45
46            self.item.borrow().clone().and_upcast()
47        }
48    }
49
50    impl SingleItemListModel {
51        /// Set the item contained by this model.
52        fn set_item(&self, item: Option<glib::Object>) {
53            if *self.item.borrow() == item {
54                return;
55            }
56
57            let was_empty = self.is_empty();
58
59            self.item.replace(item);
60            self.obj().notify_item();
61
62            self.notify_items_changed(was_empty);
63        }
64
65        /// Set whether the item is hidden.
66        fn set_is_hidden(&self, hidden: bool) {
67            if self.is_hidden.get() == hidden {
68                return;
69            }
70
71            let was_empty = self.is_empty();
72
73            self.is_hidden.set(hidden);
74            self.obj().notify_is_hidden();
75
76            if was_empty != self.is_empty() {
77                self.notify_items_changed(was_empty);
78            }
79        }
80
81        /// Whether this model is empty.
82        fn is_empty(&self) -> bool {
83            self.is_hidden.get() || self.item.borrow().is_none()
84        }
85
86        /// Notify that the number of items changed.
87        fn notify_items_changed(&self, was_empty: bool) {
88            let is_empty = self.is_empty();
89
90            let removed = (!was_empty).into();
91            let added = (!is_empty).into();
92            self.obj().items_changed(0, removed, added);
93        }
94    }
95}
96
97glib::wrapper! {
98    /// A list model that can contain at most a single item.
99    pub struct SingleItemListModel(ObjectSubclass<imp::SingleItemListModel>)
100        @implements gio::ListModel;
101}
102
103impl SingleItemListModel {
104    /// Construct a new `SingleItemListModel` for the given item.
105    pub fn new(item: Option<&impl IsA<glib::Object>>) -> Self {
106        glib::Object::builder().property("item", item).build()
107    }
108}
109
110impl Default for SingleItemListModel {
111    fn default() -> Self {
112        Self::new(None::<&glib::Object>)
113    }
114}