gtk4/subclass/
buildable.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Traits intended for implementing the [`Buildable`](crate::Buildable)
5//! interface.
6use std::sync::OnceLock;
7
8use glib::{translate::*, GString, Object, Quark, Value};
9
10use super::PtrHolder;
11use crate::{ffi, prelude::*, subclass::prelude::*, Buildable, Builder};
12
13pub trait BuildableImpl: ObjectImpl {
14    fn set_id(&self, id: &str) {
15        self.parent_set_id(id)
16    }
17    fn id(&self) -> Option<GString> {
18        self.parent_id()
19    }
20    fn add_child(&self, builder: &Builder, child: &Object, type_: Option<&str>) {
21        self.parent_add_child(builder, child, type_)
22    }
23    fn set_buildable_property(&self, builder: &Builder, name: &str, value: &Value) {
24        self.parent_set_buildable_property(builder, name, value)
25    }
26    fn parser_finished(&self, builder: &Builder) {
27        self.parent_parser_finished(builder)
28    }
29    fn internal_child(&self, builder: &Builder, name: &str) -> Option<Object> {
30        self.parent_internal_child(builder, name)
31    }
32    fn construct_child(&self, builder: &Builder, name: &str) -> Object {
33        self.parent_construct_child(builder, name)
34    }
35    // Only useful for custom tags, not something the application developer has to
36    // often implement and needs more thinking in terms of how to handle both
37    // BuildableParser & the various ptr you're supposed to pass around
38    // fn custom_tag_start(
39    // &self,
40    // builder: &Builder,
41    // child: Option<&Object>,
42    // tagname: &str,
43    // parser: BuildableParser,
44    // data: ptr,
45    // );
46    // fn custom_tag_end(
47    // &self,
48    // builder: &Builder,
49    // child: Option<&Object>,
50    // tagname: &str,
51    // data: ptr,
52    // );
53    // fn custom_finished(
54    // &self,
55    // builder: &Builder,
56    // child: Option<&Object>,
57    // tagname: &str,
58    // data: ptr,
59    // );
60}
61
62mod sealed {
63    pub trait Sealed {}
64    impl<T: super::BuildableImplExt> Sealed for T {}
65}
66
67pub trait BuildableImplExt: sealed::Sealed + ObjectSubclass {
68    fn parent_set_id(&self, id: &str) {
69        unsafe {
70            let type_data = Self::type_data();
71            let parent_iface =
72                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
73
74            let func = (*parent_iface)
75                .set_id
76                .expect("no parent \"set_id\" implementation");
77
78            func(
79                self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
80                id.to_glib_none().0,
81            )
82        }
83    }
84
85    fn parent_id(&self) -> Option<GString> {
86        unsafe {
87            let type_data = Self::type_data();
88            let parent_iface =
89                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
90
91            let func = (*parent_iface)
92                .get_id
93                .expect("no parent \"get_id\" implementation");
94
95            from_glib_none(func(
96                self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
97            ))
98        }
99    }
100
101    fn parent_add_child(&self, builder: &Builder, child: &Object, type_: Option<&str>) {
102        unsafe {
103            let type_data = Self::type_data();
104            let parent_iface =
105                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
106
107            let func = (*parent_iface)
108                .add_child
109                .expect("no parent \"add_child\" implementation");
110
111            func(
112                self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
113                builder.to_glib_none().0,
114                child.to_glib_none().0,
115                type_.to_glib_none().0,
116            )
117        }
118    }
119
120    fn parent_set_buildable_property(&self, builder: &Builder, name: &str, value: &Value) {
121        unsafe {
122            let type_data = Self::type_data();
123            let parent_iface =
124                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
125
126            // gtk::Builder falls back to using ObjectExt::set_property if the method is not
127            // implemented
128            if let Some(func) = (*parent_iface).set_buildable_property {
129                func(
130                    self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
131                    builder.to_glib_none().0,
132                    name.to_glib_none().0,
133                    value.to_glib_none().0,
134                )
135            } else {
136                self.obj().set_property_from_value(name, value);
137            }
138        }
139    }
140
141    fn parent_parser_finished(&self, builder: &Builder) {
142        unsafe {
143            let type_data = Self::type_data();
144            let parent_iface =
145                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
146
147            if let Some(func) = (*parent_iface).parser_finished {
148                func(
149                    self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
150                    builder.to_glib_none().0,
151                )
152            }
153        }
154    }
155
156    fn parent_internal_child(&self, builder: &Builder, name: &str) -> Option<Object> {
157        unsafe {
158            let type_data = Self::type_data();
159            let parent_iface =
160                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
161
162            if let Some(func) = (*parent_iface).get_internal_child {
163                from_glib_none(func(
164                    self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
165                    builder.to_glib_none().0,
166                    name.to_glib_none().0,
167                ))
168            } else {
169                None
170            }
171        }
172    }
173
174    fn parent_construct_child(&self, builder: &Builder, name: &str) -> Object {
175        unsafe {
176            let type_data = Self::type_data();
177            let parent_iface =
178                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
179
180            let func = (*parent_iface)
181                .construct_child
182                .expect("no parent \"construct_child\" implementation");
183
184            from_glib_full(func(
185                self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
186                builder.to_glib_none().0,
187                name.to_glib_none().0,
188            ))
189        }
190    }
191}
192
193impl<T: BuildableImpl> BuildableImplExt for T {}
194
195unsafe impl<T: BuildableImpl> IsImplementable<T> for Buildable {
196    fn interface_init(iface: &mut glib::Interface<Self>) {
197        let iface = iface.as_mut();
198
199        iface.set_id = Some(buildable_set_id::<T>);
200        iface.get_id = Some(buildable_get_id::<T>);
201        iface.add_child = Some(buildable_add_child::<T>);
202        iface.set_buildable_property = Some(buildable_set_buildable_property::<T>);
203        iface.construct_child = Some(buildable_construct_child::<T>);
204        iface.parser_finished = Some(buildable_parser_finished::<T>);
205        iface.get_internal_child = Some(buildable_get_internal_child::<T>);
206        // for the future
207        // iface.custom_tag_start = Some(buildable_custom_tag_start::<T>);
208        // iface.custom_tag_end = Some(buildable_custom_tag_end::<T>);
209        // iface.custom_finished = Some(buildable_custom_finished::<T>);
210    }
211}
212
213unsafe extern "C" fn buildable_set_id<T: BuildableImpl>(
214    buildable: *mut ffi::GtkBuildable,
215    id: *const libc::c_char,
216) {
217    let instance = &*(buildable as *mut T::Instance);
218    let imp = instance.imp();
219    let id = from_glib_borrow::<_, GString>(id);
220
221    imp.set_id(&id)
222}
223
224unsafe extern "C" fn buildable_get_id<T: BuildableImpl>(
225    buildable: *mut ffi::GtkBuildable,
226) -> *const libc::c_char {
227    let instance = &*(buildable as *mut T::Instance);
228    let imp = instance.imp();
229
230    imp.id().into_glib_ptr()
231}
232
233unsafe extern "C" fn buildable_add_child<T: BuildableImpl>(
234    buildable: *mut ffi::GtkBuildable,
235    builderptr: *mut ffi::GtkBuilder,
236    objectptr: *mut glib::gobject_ffi::GObject,
237    typeptr: *const libc::c_char,
238) {
239    let instance = &*(buildable as *mut T::Instance);
240    let imp = instance.imp();
241    let type_ = from_glib_borrow::<_, Option<GString>>(typeptr);
242
243    imp.add_child(
244        &from_glib_borrow(builderptr),
245        &from_glib_borrow(objectptr),
246        type_.as_ref().as_ref().map(|s| s.as_ref()),
247    )
248}
249
250unsafe extern "C" fn buildable_set_buildable_property<T: BuildableImpl>(
251    buildable: *mut ffi::GtkBuildable,
252    builderptr: *mut ffi::GtkBuilder,
253    nameptr: *const libc::c_char,
254    valueptr: *const glib::gobject_ffi::GValue,
255) {
256    let instance = &*(buildable as *mut T::Instance);
257    let imp = instance.imp();
258    let name = from_glib_borrow::<_, GString>(nameptr);
259
260    imp.set_buildable_property(
261        &from_glib_borrow(builderptr),
262        &name,
263        &from_glib_none(valueptr),
264    )
265}
266
267unsafe extern "C" fn buildable_construct_child<T: BuildableImpl>(
268    buildable: *mut ffi::GtkBuildable,
269    builderptr: *mut ffi::GtkBuilder,
270    nameptr: *const libc::c_char,
271) -> *mut glib::gobject_ffi::GObject {
272    let instance = &*(buildable as *mut T::Instance);
273    let imp = instance.imp();
274    let name = from_glib_borrow::<_, GString>(nameptr);
275
276    imp.construct_child(&from_glib_borrow(builderptr), &name)
277        .into_glib_ptr()
278}
279
280unsafe extern "C" fn buildable_parser_finished<T: BuildableImpl>(
281    buildable: *mut ffi::GtkBuildable,
282    builderptr: *mut ffi::GtkBuilder,
283) {
284    let instance = &*(buildable as *mut T::Instance);
285    let imp = instance.imp();
286
287    imp.parser_finished(&from_glib_borrow(builderptr))
288}
289
290unsafe extern "C" fn buildable_get_internal_child<T: BuildableImpl>(
291    buildable: *mut ffi::GtkBuildable,
292    builderptr: *mut ffi::GtkBuilder,
293    nameptr: *const libc::c_char,
294) -> *mut glib::gobject_ffi::GObject {
295    let instance = &*(buildable as *mut T::Instance);
296    let imp = instance.imp();
297    let name = from_glib_borrow::<_, GString>(nameptr);
298
299    let ret = imp.internal_child(&from_glib_borrow(builderptr), &name);
300
301    static QUARK: OnceLock<Quark> = OnceLock::new();
302    let quark =
303        *QUARK.get_or_init(|| Quark::from_str("gtk4-rs-subclass-buildable-get-internal-child"));
304
305    // transfer none: ensure the internal child stays alive for as long as the
306    // object building it
307    let ret = ret.into_glib_ptr();
308    imp.obj().set_qdata(
309        quark,
310        PtrHolder(ret, |ptr| {
311            glib::gobject_ffi::g_object_unref(ptr as *mut _);
312        }),
313    );
314    ret
315}