Skip to main content

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`] interface.
5use std::sync::OnceLock;
6
7use glib::{GString, Object, Quark, Value, translate::*};
8
9use crate::{Buildable, Builder, ffi, prelude::*, subclass::prelude::*};
10
11use super::{BuildableParser, PtrHolder};
12
13pub trait BuildableImpl: ObjectImpl + ObjectSubclass<Type: IsA<Buildable>> {
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    fn custom_tag_start(
36        &self,
37        builder: &Builder,
38        child: Option<&Object>,
39        tag_name: &str,
40    ) -> Option<BuildableParser> {
41        self.parent_custom_tag_start(builder, child, tag_name)
42    }
43    // rustdoc-stripper-ignore-next
44    /// # Safety
45    ///
46    /// `data` must be the pointer originally returned by
47    /// [`BuildableParser::new()`](BuildableParser::new) via
48    /// [`custom_tag_start()`](BuildableImpl::custom_tag_start).
49    unsafe fn custom_tag_end(
50        &self,
51        builder: &Builder,
52        child: Option<&Object>,
53        tag_name: &str,
54        data: glib::ffi::gpointer,
55    ) {
56        unsafe { self.parent_custom_tag_end(builder, child, tag_name, data) }
57    }
58    // rustdoc-stripper-ignore-next
59    /// # Safety
60    ///
61    /// `data` must be the pointer originally returned by
62    /// [`BuildableParser::new()`](BuildableParser::new) via
63    /// [`custom_tag_start()`](BuildableImpl::custom_tag_start).
64    unsafe fn custom_finished(
65        &self,
66        builder: &Builder,
67        child: Option<&Object>,
68        tag_name: &str,
69        data: glib::ffi::gpointer,
70    ) {
71        unsafe { self.parent_custom_finished(builder, child, tag_name, data) }
72    }
73}
74
75pub trait BuildableImplExt: BuildableImpl {
76    fn parent_set_id(&self, id: &str);
77    fn parent_id(&self) -> Option<GString>;
78    fn parent_add_child(&self, builder: &Builder, child: &Object, type_: Option<&str>);
79    fn parent_set_buildable_property(&self, builder: &Builder, name: &str, value: &Value);
80    fn parent_parser_finished(&self, builder: &Builder);
81    fn parent_internal_child(&self, builder: &Builder, name: &str) -> Option<Object>;
82    fn parent_construct_child(&self, builder: &Builder, name: &str) -> Object;
83    fn parent_custom_tag_start(
84        &self,
85        builder: &Builder,
86        child: Option<&Object>,
87        tag_name: &str,
88    ) -> Option<BuildableParser>;
89    // rustdoc-stripper-ignore-next
90    /// # Safety
91    ///
92    /// `data` must be the pointer originally returned by
93    /// [`BuildableParser::new()`](BuildableParser::new) via
94    /// [`custom_tag_start()`](BuildableImpl::custom_tag_start).
95    unsafe fn parent_custom_tag_end(
96        &self,
97        builder: &Builder,
98        child: Option<&Object>,
99        tag_name: &str,
100        data: glib::ffi::gpointer,
101    );
102    // rustdoc-stripper-ignore-next
103    /// # Safety
104    ///
105    /// `data` must be the pointer originally returned by
106    /// [`BuildableParser::new()`](BuildableParser::new) via
107    /// [`custom_tag_start()`](BuildableImpl::custom_tag_start).
108    unsafe fn parent_custom_finished(
109        &self,
110        builder: &Builder,
111        child: Option<&Object>,
112        tag_name: &str,
113        data: glib::ffi::gpointer,
114    );
115}
116
117impl<T: BuildableImpl> BuildableImplExt for T {
118    fn parent_set_id(&self, id: &str) {
119        unsafe {
120            let type_data = Self::type_data();
121            let parent_iface =
122                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
123
124            let func = (*parent_iface)
125                .set_id
126                .expect("no parent \"set_id\" implementation");
127
128            func(
129                self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
130                id.to_glib_none().0,
131            )
132        }
133    }
134
135    fn parent_id(&self) -> Option<GString> {
136        unsafe {
137            let type_data = Self::type_data();
138            let parent_iface =
139                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
140
141            let func = (*parent_iface)
142                .get_id
143                .expect("no parent \"get_id\" implementation");
144
145            from_glib_none(func(
146                self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
147            ))
148        }
149    }
150
151    fn parent_add_child(&self, builder: &Builder, child: &Object, type_: Option<&str>) {
152        unsafe {
153            let type_data = Self::type_data();
154            let parent_iface =
155                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
156
157            let func = (*parent_iface)
158                .add_child
159                .expect("no parent \"add_child\" implementation");
160
161            func(
162                self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
163                builder.to_glib_none().0,
164                child.to_glib_none().0,
165                type_.to_glib_none().0,
166            )
167        }
168    }
169
170    fn parent_set_buildable_property(&self, builder: &Builder, name: &str, value: &Value) {
171        unsafe {
172            let type_data = Self::type_data();
173            let parent_iface =
174                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
175
176            // gtk::Builder falls back to using ObjectExt::set_property if the method is not
177            // implemented
178            if let Some(func) = (*parent_iface).set_buildable_property {
179                func(
180                    self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
181                    builder.to_glib_none().0,
182                    name.to_glib_none().0,
183                    value.to_glib_none().0,
184                )
185            } else {
186                self.obj().set_property_from_value(name, value);
187            }
188        }
189    }
190
191    fn parent_parser_finished(&self, builder: &Builder) {
192        unsafe {
193            let type_data = Self::type_data();
194            let parent_iface =
195                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
196
197            if let Some(func) = (*parent_iface).parser_finished {
198                func(
199                    self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
200                    builder.to_glib_none().0,
201                )
202            }
203        }
204    }
205
206    fn parent_internal_child(&self, builder: &Builder, name: &str) -> Option<Object> {
207        unsafe {
208            let type_data = Self::type_data();
209            let parent_iface =
210                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
211
212            if let Some(func) = (*parent_iface).get_internal_child {
213                from_glib_none(func(
214                    self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
215                    builder.to_glib_none().0,
216                    name.to_glib_none().0,
217                ))
218            } else {
219                None
220            }
221        }
222    }
223
224    fn parent_construct_child(&self, builder: &Builder, name: &str) -> Object {
225        unsafe {
226            let type_data = Self::type_data();
227            let parent_iface =
228                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
229
230            let func = (*parent_iface)
231                .construct_child
232                .expect("no parent \"construct_child\" implementation");
233
234            from_glib_full(func(
235                self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
236                builder.to_glib_none().0,
237                name.to_glib_none().0,
238            ))
239        }
240    }
241
242    fn parent_custom_tag_start(
243        &self,
244        builder: &Builder,
245        child: Option<&Object>,
246        tag_name: &str,
247    ) -> Option<BuildableParser> {
248        unsafe {
249            let type_data = Self::type_data();
250            let parent_iface =
251                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
252
253            let func = (*parent_iface)
254                .custom_tag_start
255                .expect("no parent \"custom_tag_start\" implementation");
256
257            let mut parser = std::mem::MaybeUninit::<ffi::GtkBuildableParser>::uninit();
258            let mut data: glib::ffi::gpointer = std::ptr::null_mut();
259
260            let ret: bool = from_glib(func(
261                self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
262                builder.to_glib_none().0,
263                child.to_glib_none().0,
264                tag_name.to_glib_none().0,
265                parser.as_mut_ptr(),
266                &mut data,
267            ));
268            if ret {
269                Some(BuildableParser::from_raw_parts(parser.assume_init(), data))
270            } else {
271                None
272            }
273        }
274    }
275
276    unsafe fn parent_custom_tag_end(
277        &self,
278        builder: &Builder,
279        child: Option<&Object>,
280        tag_name: &str,
281        data: glib::ffi::gpointer,
282    ) {
283        unsafe {
284            let type_data = Self::type_data();
285            let parent_iface =
286                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
287
288            let func = (*parent_iface)
289                .custom_tag_end
290                .expect("no parent \"custom_tag_end\" implementation");
291
292            func(
293                self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
294                builder.to_glib_none().0,
295                child.to_glib_none().0,
296                tag_name.to_glib_none().0,
297                data,
298            )
299        }
300    }
301
302    unsafe fn parent_custom_finished(
303        &self,
304        builder: &Builder,
305        child: Option<&Object>,
306        tag_name: &str,
307        data: glib::ffi::gpointer,
308    ) {
309        unsafe {
310            let type_data = Self::type_data();
311            let parent_iface =
312                type_data.as_ref().parent_interface::<Buildable>() as *const ffi::GtkBuildableIface;
313
314            let func = (*parent_iface)
315                .custom_finished
316                .expect("no parent \"custom_finished\" implementation");
317
318            func(
319                self.obj().unsafe_cast_ref::<Buildable>().to_glib_none().0,
320                builder.to_glib_none().0,
321                child.to_glib_none().0,
322                tag_name.to_glib_none().0,
323                data,
324            )
325        }
326    }
327}
328
329unsafe impl<T: BuildableImpl> IsImplementable<T> for Buildable {
330    fn interface_init(iface: &mut glib::Interface<Self>) {
331        let iface = iface.as_mut();
332
333        iface.set_id = Some(buildable_set_id::<T>);
334        iface.get_id = Some(buildable_get_id::<T>);
335        iface.add_child = Some(buildable_add_child::<T>);
336        iface.set_buildable_property = Some(buildable_set_buildable_property::<T>);
337        iface.construct_child = Some(buildable_construct_child::<T>);
338        iface.parser_finished = Some(buildable_parser_finished::<T>);
339        iface.get_internal_child = Some(buildable_get_internal_child::<T>);
340        iface.custom_tag_start = Some(buildable_custom_tag_start::<T>);
341        iface.custom_tag_end = Some(buildable_custom_tag_end::<T>);
342        iface.custom_finished = Some(buildable_custom_finished::<T>);
343    }
344}
345
346unsafe extern "C" fn buildable_set_id<T: BuildableImpl>(
347    buildable: *mut ffi::GtkBuildable,
348    id: *const libc::c_char,
349) {
350    unsafe {
351        let instance = &*(buildable as *mut T::Instance);
352        let imp = instance.imp();
353        let id = from_glib_borrow::<_, GString>(id);
354
355        imp.set_id(&id)
356    }
357}
358
359unsafe extern "C" fn buildable_get_id<T: BuildableImpl>(
360    buildable: *mut ffi::GtkBuildable,
361) -> *const libc::c_char {
362    unsafe {
363        let instance = &*(buildable as *mut T::Instance);
364        let imp = instance.imp();
365
366        imp.id().into_glib_ptr()
367    }
368}
369
370unsafe extern "C" fn buildable_add_child<T: BuildableImpl>(
371    buildable: *mut ffi::GtkBuildable,
372    builderptr: *mut ffi::GtkBuilder,
373    objectptr: *mut glib::gobject_ffi::GObject,
374    typeptr: *const libc::c_char,
375) {
376    unsafe {
377        let instance = &*(buildable as *mut T::Instance);
378        let imp = instance.imp();
379        let type_ = from_glib_borrow::<_, Option<GString>>(typeptr);
380
381        imp.add_child(
382            &from_glib_borrow(builderptr),
383            &from_glib_borrow(objectptr),
384            type_.as_ref().as_ref().map(|s| s.as_ref()),
385        )
386    }
387}
388
389unsafe extern "C" fn buildable_set_buildable_property<T: BuildableImpl>(
390    buildable: *mut ffi::GtkBuildable,
391    builderptr: *mut ffi::GtkBuilder,
392    nameptr: *const libc::c_char,
393    valueptr: *const glib::gobject_ffi::GValue,
394) {
395    unsafe {
396        let instance = &*(buildable as *mut T::Instance);
397        let imp = instance.imp();
398        let name = from_glib_borrow::<_, GString>(nameptr);
399
400        imp.set_buildable_property(
401            &from_glib_borrow(builderptr),
402            &name,
403            &from_glib_none(valueptr),
404        )
405    }
406}
407
408unsafe extern "C" fn buildable_construct_child<T: BuildableImpl>(
409    buildable: *mut ffi::GtkBuildable,
410    builderptr: *mut ffi::GtkBuilder,
411    nameptr: *const libc::c_char,
412) -> *mut glib::gobject_ffi::GObject {
413    unsafe {
414        let instance = &*(buildable as *mut T::Instance);
415        let imp = instance.imp();
416        let name = from_glib_borrow::<_, GString>(nameptr);
417
418        imp.construct_child(&from_glib_borrow(builderptr), &name)
419            .into_glib_ptr()
420    }
421}
422
423unsafe extern "C" fn buildable_parser_finished<T: BuildableImpl>(
424    buildable: *mut ffi::GtkBuildable,
425    builderptr: *mut ffi::GtkBuilder,
426) {
427    unsafe {
428        let instance = &*(buildable as *mut T::Instance);
429        let imp = instance.imp();
430
431        imp.parser_finished(&from_glib_borrow(builderptr))
432    }
433}
434
435unsafe extern "C" fn buildable_get_internal_child<T: BuildableImpl>(
436    buildable: *mut ffi::GtkBuildable,
437    builderptr: *mut ffi::GtkBuilder,
438    nameptr: *const libc::c_char,
439) -> *mut glib::gobject_ffi::GObject {
440    unsafe {
441        let instance = &*(buildable as *mut T::Instance);
442        let imp = instance.imp();
443        let name = from_glib_borrow::<_, GString>(nameptr);
444
445        let ret = imp.internal_child(&from_glib_borrow(builderptr), &name);
446
447        static QUARK: OnceLock<Quark> = OnceLock::new();
448        let quark =
449            *QUARK.get_or_init(|| Quark::from_str("gtk4-rs-subclass-buildable-get-internal-child"));
450
451        // transfer none: ensure the internal child stays alive for as long as the
452        // object building it
453        let ret = ret.into_glib_ptr();
454        imp.obj().set_qdata(
455            quark,
456            PtrHolder(ret, |ptr| {
457                glib::gobject_ffi::g_object_unref(ptr as *mut _);
458            }),
459        );
460        ret
461    }
462}
463
464unsafe extern "C" fn buildable_custom_tag_start<T: BuildableImpl>(
465    buildable: *mut ffi::GtkBuildable,
466    builderptr: *mut ffi::GtkBuilder,
467    child: *mut glib::gobject_ffi::GObject,
468    nameptr: *const libc::c_char,
469    parserptr: *mut ffi::GtkBuildableParser,
470    data: *mut glib::ffi::gpointer,
471) -> glib::ffi::gboolean {
472    unsafe {
473        let instance = &*(buildable as *mut T::Instance);
474        let imp = instance.imp();
475        let name = from_glib_borrow::<_, GString>(nameptr);
476        let child = from_glib_borrow::<_, Option<Object>>(child);
477
478        if let Some(parser) = imp.custom_tag_start(
479            &from_glib_borrow(builderptr),
480            child.as_ref().as_ref(),
481            &name,
482        ) {
483            let (raw_parser, user_data) = parser.into_raw_parts();
484            std::ptr::write(parserptr, raw_parser);
485            *data = user_data;
486            true.into_glib()
487        } else {
488            false.into_glib()
489        }
490    }
491}
492
493unsafe extern "C" fn buildable_custom_tag_end<T: BuildableImpl>(
494    buildable: *mut ffi::GtkBuildable,
495    builderptr: *mut ffi::GtkBuilder,
496    child: *mut glib::gobject_ffi::GObject,
497    nameptr: *const libc::c_char,
498    data: glib::ffi::gpointer,
499) {
500    unsafe {
501        let instance = &*(buildable as *mut T::Instance);
502        let imp = instance.imp();
503        let name = from_glib_borrow::<_, GString>(nameptr);
504        let child = from_glib_borrow::<_, Option<Object>>(child);
505
506        imp.custom_tag_end(
507            &from_glib_borrow(builderptr),
508            child.as_ref().as_ref(),
509            &name,
510            data,
511        )
512    }
513}
514
515unsafe extern "C" fn buildable_custom_finished<T: BuildableImpl>(
516    buildable: *mut ffi::GtkBuildable,
517    builderptr: *mut ffi::GtkBuilder,
518    child: *mut glib::gobject_ffi::GObject,
519    nameptr: *const libc::c_char,
520    data: glib::ffi::gpointer,
521) {
522    unsafe {
523        let instance = &*(buildable as *mut T::Instance);
524        let imp = instance.imp();
525        let name = from_glib_borrow::<_, GString>(nameptr);
526        let child = from_glib_borrow::<_, Option<Object>>(child);
527
528        imp.custom_finished(
529            &from_glib_borrow(builderptr),
530            child.as_ref().as_ref(),
531            &name,
532            data,
533        )
534    }
535}