Skip to main content

gtk/
builder.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::{Builder, Widget, ffi};
4use glib::GString;
5use glib::Object;
6use glib::prelude::*;
7use glib::translate::*;
8use std::path::Path;
9use std::ptr;
10
11impl Builder {
12    #[doc(alias = "gtk_builder_new_from_file")]
13    pub fn from_file<T: AsRef<Path>>(file_path: T) -> Builder {
14        assert_initialized_main_thread!();
15        unsafe {
16            from_glib_full(ffi::gtk_builder_new_from_file(
17                file_path.as_ref().to_glib_none().0,
18            ))
19        }
20    }
21}
22
23mod sealed {
24    pub trait Sealed {}
25    impl<T: glib::object::IsA<crate::Builder>> Sealed for T {}
26}
27
28pub trait BuilderExtManual: IsA<Builder> + sealed::Sealed + 'static {
29    #[doc(alias = "gtk_builder_get_object")]
30    #[doc(alias = "get_object")]
31    fn object<T: IsA<Object>>(&self, name: &str) -> Option<T> {
32        unsafe {
33            Option::<Object>::from_glib_none(ffi::gtk_builder_get_object(
34                self.upcast_ref().to_glib_none().0,
35                name.to_glib_none().0,
36            ))
37            .and_then(|obj| obj.dynamic_cast::<T>().ok())
38        }
39    }
40
41    #[doc(alias = "gtk_builder_add_from_file")]
42    fn add_from_file<T: AsRef<Path>>(&self, file_path: T) -> Result<(), glib::Error> {
43        unsafe {
44            let mut error = ::std::ptr::null_mut();
45            let exit_code = ffi::gtk_builder_add_from_file(
46                self.upcast_ref().to_glib_none().0,
47                file_path.as_ref().to_glib_none().0,
48                &mut error,
49            );
50            assert_eq!(exit_code == 0, !error.is_null());
51            if error.is_null() {
52                Ok(())
53            } else {
54                Err(from_glib_full(error))
55            }
56        }
57    }
58    /// Parses a resource file containing a [GtkBuilder UI definition][BUILDER-UI]
59    /// and merges it with the current contents of `self`.
60    ///
61    /// Most users will probably want to use [`Builder::from_resource()`][crate::Builder::from_resource()].
62    ///
63    /// If an error occurs, 0 will be returned and `error` will be assigned a
64    /// [`glib::Error`][crate::glib::Error] from the `GTK_BUILDER_ERROR`, `G_MARKUP_ERROR` or `G_RESOURCE_ERROR`
65    /// domain.
66    ///
67    /// It’s not really reasonable to attempt to handle failures of this
68    /// call. The only reasonable thing to do when an error is detected is
69    /// to call `g_error()`.
70    /// ## `resource_path`
71    /// the path of the resource file to parse
72    ///
73    /// # Returns
74    ///
75    /// A positive value on success, 0 if an error occurred
76    #[doc(alias = "gtk_builder_add_from_resource")]
77    fn add_from_resource(&self, resource_path: &str) -> Result<(), glib::Error> {
78        unsafe {
79            let mut error = ptr::null_mut();
80            let exit_code = ffi::gtk_builder_add_from_resource(
81                self.as_ref().to_glib_none().0,
82                resource_path.to_glib_none().0,
83                &mut error,
84            );
85            assert_eq!(exit_code == 0, !error.is_null());
86            if error.is_null() {
87                Ok(())
88            } else {
89                Err(from_glib_full(error))
90            }
91        }
92    }
93    /// Parses a string containing a [GtkBuilder UI definition][BUILDER-UI]
94    /// and merges it with the current contents of `self`.
95    ///
96    /// Most users will probably want to use [`Builder::from_string()`][crate::Builder::from_string()].
97    ///
98    /// Upon errors 0 will be returned and `error` will be assigned a
99    /// [`glib::Error`][crate::glib::Error] from the `GTK_BUILDER_ERROR`, `G_MARKUP_ERROR` or
100    /// `G_VARIANT_PARSE_ERROR` domain.
101    ///
102    /// It’s not really reasonable to attempt to handle failures of this
103    /// call. The only reasonable thing to do when an error is detected is
104    /// to call `g_error()`.
105    /// ## `buffer`
106    /// the string to parse
107    /// ## `length`
108    /// the length of `buffer` (may be -1 if `buffer` is nul-terminated)
109    ///
110    /// # Returns
111    ///
112    /// A positive value on success, 0 if an error occurred
113    #[doc(alias = "gtk_builder_add_from_string")]
114    fn add_from_string(&self, buffer: &str) -> Result<(), glib::Error> {
115        let length = buffer.len();
116        unsafe {
117            let mut error = ptr::null_mut();
118            let exit_code = ffi::gtk_builder_add_from_string(
119                self.as_ref().to_glib_none().0,
120                buffer.to_glib_none().0,
121                length,
122                &mut error,
123            );
124            assert_eq!(exit_code == 0, !error.is_null());
125            if error.is_null() {
126                Ok(())
127            } else {
128                Err(from_glib_full(error))
129            }
130        }
131    }
132
133    /// Parses a resource file containing a [GtkBuilder UI definition][BUILDER-UI]
134    /// building only the requested objects and merges
135    /// them with the current contents of `self`.
136    ///
137    /// Upon errors 0 will be returned and `error` will be assigned a
138    /// [`glib::Error`][crate::glib::Error] from the `GTK_BUILDER_ERROR`, `G_MARKUP_ERROR` or `G_RESOURCE_ERROR`
139    /// domain.
140    ///
141    /// If you are adding an object that depends on an object that is not
142    /// its child (for instance a [`TreeView`][crate::TreeView] that depends on its
143    /// [`TreeModel`][crate::TreeModel]), you have to explicitly list all of them in `object_ids`.
144    /// ## `resource_path`
145    /// the path of the resource file to parse
146    /// ## `object_ids`
147    /// nul-terminated array of objects to build
148    ///
149    /// # Returns
150    ///
151    /// A positive value on success, 0 if an error occurred
152    #[doc(alias = "gtk_builder_add_objects_from_resource")]
153    fn add_objects_from_resource(
154        &self,
155        resource_path: &str,
156        object_ids: &[&str],
157    ) -> Result<(), glib::Error> {
158        unsafe {
159            let mut error = ptr::null_mut();
160            let exit_code = ffi::gtk_builder_add_objects_from_resource(
161                self.as_ref().to_glib_none().0,
162                resource_path.to_glib_none().0,
163                object_ids.to_glib_none().0,
164                &mut error,
165            );
166            assert_eq!(exit_code == 0, !error.is_null());
167            if error.is_null() {
168                Ok(())
169            } else {
170                Err(from_glib_full(error))
171            }
172        }
173    }
174    /// Parses a string containing a [GtkBuilder UI definition][BUILDER-UI]
175    /// building only the requested objects and merges
176    /// them with the current contents of `self`.
177    ///
178    /// Upon errors 0 will be returned and `error` will be assigned a
179    /// [`glib::Error`][crate::glib::Error] from the `GTK_BUILDER_ERROR` or `G_MARKUP_ERROR` domain.
180    ///
181    /// If you are adding an object that depends on an object that is not
182    /// its child (for instance a [`TreeView`][crate::TreeView] that depends on its
183    /// [`TreeModel`][crate::TreeModel]), you have to explicitly list all of them in `object_ids`.
184    /// ## `buffer`
185    /// the string to parse
186    /// ## `length`
187    /// the length of `buffer` (may be -1 if `buffer` is nul-terminated)
188    /// ## `object_ids`
189    /// nul-terminated array of objects to build
190    ///
191    /// # Returns
192    ///
193    /// A positive value on success, 0 if an error occurred
194    #[doc(alias = "gtk_builder_add_objects_from_string")]
195    fn add_objects_from_string(
196        &self,
197        buffer: &str,
198        object_ids: &[&str],
199    ) -> Result<(), glib::Error> {
200        let length = buffer.len();
201        unsafe {
202            let mut error = ptr::null_mut();
203            let exit_code = ffi::gtk_builder_add_objects_from_string(
204                self.as_ref().to_glib_none().0,
205                buffer.to_glib_none().0,
206                length,
207                object_ids.to_glib_none().0,
208                &mut error,
209            );
210            assert_eq!(exit_code == 0, !error.is_null());
211            if error.is_null() {
212                Ok(())
213            } else {
214                Err(from_glib_full(error))
215            }
216        }
217    }
218
219    #[doc(alias = "gtk_builder_connect_signals_full")]
220    fn connect_signals<
221        P: FnMut(&Builder, &str) -> Box<dyn Fn(&[glib::Value]) -> Option<glib::Value> + 'static>,
222    >(
223        &self,
224        func: P,
225    ) {
226        let func_data: P = func;
227        unsafe extern "C" fn func_func<
228            P: FnMut(&Builder, &str) -> Box<dyn Fn(&[glib::Value]) -> Option<glib::Value> + 'static>,
229        >(
230            builder: *mut ffi::GtkBuilder,
231            object: *mut glib::gobject_ffi::GObject,
232            signal_name: *const libc::c_char,
233            handler_name: *const libc::c_char,
234            connect_object: *mut glib::gobject_ffi::GObject,
235            flags: glib::gobject_ffi::GConnectFlags,
236            user_data: glib::ffi::gpointer,
237        ) {
238            unsafe {
239                assert!(connect_object.is_null(), "Connect object is not supported");
240                assert!(
241                    flags & glib::gobject_ffi::G_CONNECT_SWAPPED == 0,
242                    "Swapped signal handler is not supported"
243                );
244
245                let builder = from_glib_borrow(builder);
246                let object: Borrowed<glib::Object> = from_glib_borrow(object);
247                let signal_name: Borrowed<GString> = from_glib_borrow(signal_name);
248                let handler_name: Borrowed<GString> = from_glib_borrow(handler_name);
249                let callback: *mut P = user_data as *const _ as usize as *mut P;
250                let func = (*callback)(&builder, handler_name.as_str());
251                object.connect_unsafe(
252                    signal_name.as_str(),
253                    flags & glib::gobject_ffi::G_CONNECT_AFTER != 0,
254                    move |args| func(args),
255                );
256            }
257        }
258        let func = Some(func_func::<P> as _);
259        let super_callback0: &P = &func_data;
260        unsafe {
261            ffi::gtk_builder_connect_signals_full(
262                self.as_ref().to_glib_none().0,
263                func,
264                super_callback0 as *const _ as usize as *mut _,
265            );
266        }
267    }
268
269    /// Main private entry point for building composite container
270    /// components from template XML.
271    ///
272    /// This is exported purely to let gtk-builder-tool validate
273    /// templates, applications have no need to call this function.
274    /// ## `widget`
275    /// the widget that is being extended
276    /// ## `template_type`
277    /// the type that the template is for
278    /// ## `buffer`
279    /// the string to parse
280    /// ## `length`
281    /// the length of `buffer` (may be -1 if `buffer` is nul-terminated)
282    ///
283    /// # Returns
284    ///
285    /// A positive value on success, 0 if an error occurred
286    #[doc(alias = "gtk_builder_extend_with_template")]
287    fn extend_with_template(
288        &self,
289        widget: &impl IsA<Widget>,
290        template_type: glib::types::Type,
291        buffer: &str,
292    ) -> Result<(), glib::Error> {
293        let length = buffer.len();
294        unsafe {
295            let mut error = ptr::null_mut();
296            let exit_code = ffi::gtk_builder_extend_with_template(
297                self.as_ref().to_glib_none().0,
298                widget.as_ref().to_glib_none().0,
299                template_type.into_glib(),
300                buffer.to_glib_none().0,
301                length,
302                &mut error,
303            );
304            assert_eq!(exit_code == 0, !error.is_null());
305            if error.is_null() {
306                Ok(())
307            } else {
308                Err(from_glib_full(error))
309            }
310        }
311    }
312}
313
314impl<O: IsA<Builder>> BuilderExtManual for O {}