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};
4use glib::prelude::*;
5use glib::translate::*;
6use glib::GString;
7use glib::Object;
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::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 assert!(connect_object.is_null(), "Connect object is not supported");
239 assert!(
240 flags & glib::gobject_ffi::G_CONNECT_SWAPPED == 0,
241 "Swapped signal handler is not supported"
242 );
243
244 let builder = from_glib_borrow(builder);
245 let object: Borrowed<glib::Object> = from_glib_borrow(object);
246 let signal_name: Borrowed<GString> = from_glib_borrow(signal_name);
247 let handler_name: Borrowed<GString> = from_glib_borrow(handler_name);
248 let callback: *mut P = user_data as *const _ as usize as *mut P;
249 let func = (*callback)(&builder, handler_name.as_str());
250 object.connect_unsafe(
251 signal_name.as_str(),
252 flags & glib::gobject_ffi::G_CONNECT_AFTER != 0,
253 move |args| func(args),
254 );
255 }
256 let func = Some(func_func::<P> as _);
257 let super_callback0: &P = &func_data;
258 unsafe {
259 ffi::gtk_builder_connect_signals_full(
260 self.as_ref().to_glib_none().0,
261 func,
262 super_callback0 as *const _ as usize as *mut _,
263 );
264 }
265 }
266
267 /// Main private entry point for building composite container
268 /// components from template XML.
269 ///
270 /// This is exported purely to let gtk-builder-tool validate
271 /// templates, applications have no need to call this function.
272 /// ## `widget`
273 /// the widget that is being extended
274 /// ## `template_type`
275 /// the type that the template is for
276 /// ## `buffer`
277 /// the string to parse
278 /// ## `length`
279 /// the length of `buffer` (may be -1 if `buffer` is nul-terminated)
280 ///
281 /// # Returns
282 ///
283 /// A positive value on success, 0 if an error occurred
284 #[doc(alias = "gtk_builder_extend_with_template")]
285 fn extend_with_template(
286 &self,
287 widget: &impl IsA<Widget>,
288 template_type: glib::types::Type,
289 buffer: &str,
290 ) -> Result<(), glib::Error> {
291 let length = buffer.len();
292 unsafe {
293 let mut error = ptr::null_mut();
294 let exit_code = ffi::gtk_builder_extend_with_template(
295 self.as_ref().to_glib_none().0,
296 widget.as_ref().to_glib_none().0,
297 template_type.into_glib(),
298 buffer.to_glib_none().0,
299 length,
300 &mut error,
301 );
302 assert_eq!(exit_code == 0, !error.is_null());
303 if error.is_null() {
304 Ok(())
305 } else {
306 Err(from_glib_full(error))
307 }
308 }
309 }
310}
311
312impl<O: IsA<Builder>> BuilderExtManual for O {}