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