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