gio/subclass/
list_model.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::sync::OnceLock;
4
5use glib::{prelude::*, subclass::prelude::*, translate::*};
6
7use crate::{ffi, ListModel};
8
9pub trait ListModelImpl: ObjectImpl {
10    #[doc(alias = "get_item_type")]
11    fn item_type(&self) -> glib::Type;
12    #[doc(alias = "get_n_items")]
13    fn n_items(&self) -> u32;
14    #[doc(alias = "get_item")]
15    fn item(&self, position: u32) -> Option<glib::Object>;
16}
17
18mod sealed {
19    pub trait Sealed {}
20    impl<T: super::ListModelImplExt> Sealed for T {}
21}
22
23pub trait ListModelImplExt: sealed::Sealed + ObjectSubclass {
24    fn parent_item_type(&self) -> glib::Type {
25        unsafe {
26            let type_data = Self::type_data();
27            let parent_iface = type_data.as_ref().parent_interface::<ListModel>()
28                as *const ffi::GListModelInterface;
29
30            let func = (*parent_iface)
31                .get_item_type
32                .expect("no parent \"item_type\" implementation");
33            let ret = func(self.obj().unsafe_cast_ref::<ListModel>().to_glib_none().0);
34            from_glib(ret)
35        }
36    }
37
38    fn parent_n_items(&self) -> u32 {
39        unsafe {
40            let type_data = Self::type_data();
41            let parent_iface = type_data.as_ref().parent_interface::<ListModel>()
42                as *const ffi::GListModelInterface;
43
44            let func = (*parent_iface)
45                .get_n_items
46                .expect("no parent \"n_items\" implementation");
47            func(self.obj().unsafe_cast_ref::<ListModel>().to_glib_none().0)
48        }
49    }
50
51    fn parent_item(&self, position: u32) -> Option<glib::Object> {
52        unsafe {
53            let type_data = Self::type_data();
54            let parent_iface = type_data.as_ref().parent_interface::<ListModel>()
55                as *const ffi::GListModelInterface;
56
57            let func = (*parent_iface)
58                .get_item
59                .expect("no parent \"get_item\" implementation");
60            let ret = func(
61                self.obj().unsafe_cast_ref::<ListModel>().to_glib_none().0,
62                position,
63            );
64            from_glib_full(ret)
65        }
66    }
67}
68
69impl<T: ListModelImpl> ListModelImplExt for T {}
70
71unsafe impl<T: ListModelImpl> IsImplementable<T> for ListModel
72where
73    <T as ObjectSubclass>::Type: IsA<glib::Object>,
74{
75    fn interface_init(iface: &mut glib::Interface<Self>) {
76        let iface = iface.as_mut();
77
78        iface.get_item_type = Some(list_model_get_item_type::<T>);
79        iface.get_n_items = Some(list_model_get_n_items::<T>);
80        iface.get_item = Some(list_model_get_item::<T>);
81    }
82}
83
84unsafe extern "C" fn list_model_get_item_type<T: ListModelImpl>(
85    list_model: *mut ffi::GListModel,
86) -> glib::ffi::GType
87where
88    <T as ObjectSubclass>::Type: IsA<glib::Object>,
89{
90    let instance = &*(list_model as *mut T::Instance);
91    let imp = instance.imp();
92
93    let type_ = imp.item_type().into_glib();
94
95    // Store the type so we can enforce that it doesn't change.
96    let instance = imp.obj();
97    let type_quark = {
98        static QUARK: OnceLock<glib::Quark> = OnceLock::new();
99        *QUARK.get_or_init(|| glib::Quark::from_str("gtk-rs-subclass-list-model-item-type"))
100    };
101    match instance.qdata(type_quark) {
102        Some(old_type) => {
103            assert_eq!(
104                type_,
105                *old_type.as_ref(),
106                "ListModel's get_item_type cannot be changed"
107            );
108        }
109        None => {
110            instance.set_qdata(type_quark, type_);
111        }
112    }
113    type_
114}
115
116unsafe extern "C" fn list_model_get_n_items<T: ListModelImpl>(
117    list_model: *mut ffi::GListModel,
118) -> u32
119where
120    <T as ObjectSubclass>::Type: IsA<glib::Object>,
121{
122    let instance = &*(list_model as *mut T::Instance);
123    let imp = instance.imp();
124
125    imp.n_items()
126}
127
128unsafe extern "C" fn list_model_get_item<T: ListModelImpl>(
129    list_model: *mut ffi::GListModel,
130    position: u32,
131) -> *mut glib::gobject_ffi::GObject
132where
133    <T as ObjectSubclass>::Type: IsA<glib::Object>,
134{
135    let instance = &*(list_model as *mut T::Instance);
136    let imp = instance.imp();
137
138    let item = imp.item(position);
139
140    if let Some(ref i) = item {
141        let type_ = imp.item_type();
142        assert!(
143            i.type_().is_a(type_),
144            "All ListModel items need to be of type {} or a subtype of it",
145            type_.name()
146        );
147    };
148    item.into_glib_ptr()
149}