1 // Copyright 2017-2018, The Gtk-rs Project Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution.
3 // Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5 //! Module that contains all types needed for creating a direct subclass of `GObject`
6 //! or implementing virtual methods of it.
7
8 use super::prelude::*;
9 use super::types;
10 use glib_sys;
11 use gobject_sys;
12 use std::borrow::Borrow;
13 use std::fmt;
14 use std::mem;
15 use std::ptr;
16 use translate::*;
17 use {Object, ObjectClass, ObjectType, SignalFlags, Type, Value};
18
19 #[macro_export]
20 /// Macro for boilerplate of [`ObjectImpl`] implementations.
21 ///
22 /// [`ObjectImpl`]: subclass/object/trait.ObjectImpl.html
23 macro_rules! glib_object_impl {
24 () => {
25 fn get_type_data(&self) -> ::std::ptr::NonNull<$crate::subclass::TypeData> {
26 Self::type_data()
27 }
28 };
29 }
30
31 /// Trait for implementors of `glib::Object` subclasses.
32 ///
33 /// This allows overriding the virtual methods of `glib::Object`.
34 pub trait ObjectImpl: ObjectImplExt + 'static {
35 /// Storage for the type-specific data used during registration.
36 ///
37 /// This is usually generated by the [`glib_object_impl!`] macro.
38 ///
39 /// [`glib_object_impl!`]: ../../macro.glib_object_impl.html
get_type_data(&self) -> ptr::NonNull<types::TypeData>40 fn get_type_data(&self) -> ptr::NonNull<types::TypeData>;
41
42 /// Property setter.
43 ///
44 /// This is called whenever the property of this specific subclass with the
45 /// given index is set. The new value is passed as `glib::Value`.
set_property(&self, _obj: &Object, _id: usize, _value: &Value)46 fn set_property(&self, _obj: &Object, _id: usize, _value: &Value) {
47 unimplemented!()
48 }
49
50 /// Property getter.
51 ///
52 /// This is called whenever the property value of the specific subclass with the
53 /// given index should be returned.
get_property(&self, _obj: &Object, _id: usize) -> Result<Value, ()>54 fn get_property(&self, _obj: &Object, _id: usize) -> Result<Value, ()> {
55 unimplemented!()
56 }
57
58 /// Constructed.
59 ///
60 /// This is called once construction of the instance is finished.
61 ///
62 /// Should chain up to the parent class' implementation.
constructed(&self, obj: &Object)63 fn constructed(&self, obj: &Object) {
64 self.parent_constructed(obj);
65 }
66 }
67
get_property<T: ObjectSubclass>( obj: *mut gobject_sys::GObject, id: u32, value: *mut gobject_sys::GValue, _pspec: *mut gobject_sys::GParamSpec, )68 unsafe extern "C" fn get_property<T: ObjectSubclass>(
69 obj: *mut gobject_sys::GObject,
70 id: u32,
71 value: *mut gobject_sys::GValue,
72 _pspec: *mut gobject_sys::GParamSpec,
73 ) {
74 let instance = &*(obj as *mut T::Instance);
75 let imp = instance.get_impl();
76
77 match imp.get_property(&from_glib_borrow(obj), (id - 1) as usize) {
78 Ok(v) => {
79 // We first unset the value we get passed in, in case it contained
80 // any previous data. Then we directly overwrite it with our new
81 // value, and pass ownership of the contained data to the C GValue
82 // by forgetting it on the Rust side.
83 //
84 // Without this, by using the GValue API, we would have to create
85 // a copy of the value when setting it on the destination just to
86 // immediately free the original value afterwards.
87 gobject_sys::g_value_unset(value);
88 ptr::write(value, ptr::read(v.to_glib_none().0));
89 mem::forget(v);
90 }
91 Err(()) => eprintln!("Failed to get property"),
92 }
93 }
94
set_property<T: ObjectSubclass>( obj: *mut gobject_sys::GObject, id: u32, value: *mut gobject_sys::GValue, _pspec: *mut gobject_sys::GParamSpec, )95 unsafe extern "C" fn set_property<T: ObjectSubclass>(
96 obj: *mut gobject_sys::GObject,
97 id: u32,
98 value: *mut gobject_sys::GValue,
99 _pspec: *mut gobject_sys::GParamSpec,
100 ) {
101 let instance = &*(obj as *mut T::Instance);
102 let imp = instance.get_impl();
103 imp.set_property(
104 &from_glib_borrow(obj),
105 (id - 1) as usize,
106 &*(value as *mut Value),
107 );
108 }
109
constructed<T: ObjectSubclass>(obj: *mut gobject_sys::GObject)110 unsafe extern "C" fn constructed<T: ObjectSubclass>(obj: *mut gobject_sys::GObject) {
111 let instance = &*(obj as *mut T::Instance);
112 let imp = instance.get_impl();
113
114 imp.constructed(&from_glib_borrow(obj));
115 }
116
117 /// Definition of a property.
118 #[derive(Clone)]
119 pub struct Property<'a>(pub &'a str, pub fn(&str) -> ::ParamSpec);
120
121 impl<'a> fmt::Debug for Property<'a> {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>122 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
123 f.debug_tuple("Property").field(&self.0).finish()
124 }
125 }
126
127 /// Extension trait for `glib::Object`'s class struct.
128 ///
129 /// This contains various class methods and allows subclasses to override the virtual methods.
130 pub unsafe trait ObjectClassSubclassExt: Sized + 'static {
131 /// Install properties on the subclass.
132 ///
133 /// The index in the properties array is going to be the index passed to the
134 /// property setters and getters.
install_properties<'a, T: Borrow<Property<'a>>>(&mut self, properties: &[T])135 fn install_properties<'a, T: Borrow<Property<'a>>>(&mut self, properties: &[T]) {
136 if properties.is_empty() {
137 return;
138 }
139
140 let mut pspecs = Vec::with_capacity(properties.len());
141
142 for property in properties {
143 let property = property.borrow();
144 let pspec = (property.1)(property.0);
145 pspecs.push(pspec);
146 }
147
148 unsafe {
149 let mut pspecs_ptrs = Vec::with_capacity(properties.len());
150
151 pspecs_ptrs.push(ptr::null_mut());
152
153 for pspec in &pspecs {
154 pspecs_ptrs.push(pspec.to_glib_none().0);
155 }
156
157 gobject_sys::g_object_class_install_properties(
158 self as *mut _ as *mut gobject_sys::GObjectClass,
159 pspecs_ptrs.len() as u32,
160 pspecs_ptrs.as_mut_ptr(),
161 );
162 }
163 }
164
165 /// Add a new signal to the subclass.
166 ///
167 /// This can be emitted later by `glib::Object::emit` and external code
168 /// can connect to the signal to get notified about emissions.
add_signal(&mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type)169 fn add_signal(&mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type) {
170 unsafe {
171 super::types::add_signal(
172 *(self as *mut _ as *mut glib_sys::GType),
173 name,
174 flags,
175 arg_types,
176 ret_type,
177 );
178 }
179 }
180
181 /// Add a new signal with class handler to the subclass.
182 ///
183 /// This can be emitted later by `glib::Object::emit` and external code
184 /// can connect to the signal to get notified about emissions.
185 ///
186 /// The class handler will be called during the signal emission at the corresponding stage.
add_signal_with_class_handler<F>( &mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type, class_handler: F, ) where F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,187 fn add_signal_with_class_handler<F>(
188 &mut self,
189 name: &str,
190 flags: SignalFlags,
191 arg_types: &[Type],
192 ret_type: Type,
193 class_handler: F,
194 ) where
195 F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
196 {
197 unsafe {
198 super::types::add_signal_with_class_handler(
199 *(self as *mut _ as *mut glib_sys::GType),
200 name,
201 flags,
202 arg_types,
203 ret_type,
204 class_handler,
205 );
206 }
207 }
208
209 /// Add a new signal with accumulator to the subclass.
210 ///
211 /// This can be emitted later by `glib::Object::emit` and external code
212 /// can connect to the signal to get notified about emissions.
213 ///
214 /// The accumulator function is used for accumulating the return values of
215 /// multiple signal handlers. The new value is passed as second argument and
216 /// should be combined with the old value in the first argument. If no further
217 /// signal handlers should be called, `false` should be returned.
add_signal_with_accumulator<F>( &mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type, accumulator: F, ) where F: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,218 fn add_signal_with_accumulator<F>(
219 &mut self,
220 name: &str,
221 flags: SignalFlags,
222 arg_types: &[Type],
223 ret_type: Type,
224 accumulator: F,
225 ) where
226 F: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
227 {
228 unsafe {
229 super::types::add_signal_with_accumulator(
230 *(self as *mut _ as *mut glib_sys::GType),
231 name,
232 flags,
233 arg_types,
234 ret_type,
235 accumulator,
236 );
237 }
238 }
239
240 /// Add a new signal with accumulator and class handler to the subclass.
241 ///
242 /// This can be emitted later by `glib::Object::emit` and external code
243 /// can connect to the signal to get notified about emissions.
244 ///
245 /// The accumulator function is used for accumulating the return values of
246 /// multiple signal handlers. The new value is passed as second argument and
247 /// should be combined with the old value in the first argument. If no further
248 /// signal handlers should be called, `false` should be returned.
249 ///
250 /// The class handler will be called during the signal emission at the corresponding stage.
add_signal_with_class_handler_and_accumulator<F, G>( &mut self, name: &str, flags: SignalFlags, arg_types: &[Type], ret_type: Type, class_handler: F, accumulator: G, ) where F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static, G: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,251 fn add_signal_with_class_handler_and_accumulator<F, G>(
252 &mut self,
253 name: &str,
254 flags: SignalFlags,
255 arg_types: &[Type],
256 ret_type: Type,
257 class_handler: F,
258 accumulator: G,
259 ) where
260 F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
261 G: Fn(&super::SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
262 {
263 unsafe {
264 super::types::add_signal_with_class_handler_and_accumulator(
265 *(self as *mut _ as *mut glib_sys::GType),
266 name,
267 flags,
268 arg_types,
269 ret_type,
270 class_handler,
271 accumulator,
272 );
273 }
274 }
275
override_signal_class_handler<F>(&mut self, name: &str, class_handler: F) where F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,276 fn override_signal_class_handler<F>(&mut self, name: &str, class_handler: F)
277 where
278 F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
279 {
280 unsafe {
281 super::types::signal_override_class_handler(
282 name,
283 *(self as *mut _ as *mut glib_sys::GType),
284 class_handler,
285 );
286 }
287 }
288 }
289
290 unsafe impl ObjectClassSubclassExt for ObjectClass {}
291
292 unsafe impl<T: ObjectSubclass> IsSubclassable<T> for ObjectClass {
override_vfuncs(&mut self)293 fn override_vfuncs(&mut self) {
294 unsafe {
295 let klass = &mut *(self as *mut Self as *mut gobject_sys::GObjectClass);
296 klass.set_property = Some(set_property::<T>);
297 klass.get_property = Some(get_property::<T>);
298 klass.constructed = Some(constructed::<T>);
299 }
300 }
301 }
302
303 pub trait ObjectImplExt {
304 /// Chain up to the parent class' implementation of `glib::Object::constructed()`.
parent_constructed(&self, obj: &Object)305 fn parent_constructed(&self, obj: &Object);
306
signal_chain_from_overridden( &self, token: &super::SignalClassHandlerToken, values: &[Value], ) -> Option<Value>307 fn signal_chain_from_overridden(
308 &self,
309 token: &super::SignalClassHandlerToken,
310 values: &[Value],
311 ) -> Option<Value>;
312 }
313
314 impl<T: ObjectImpl + ObjectSubclass> ObjectImplExt for T {
parent_constructed(&self, obj: &Object)315 fn parent_constructed(&self, obj: &Object) {
316 unsafe {
317 let data = self.get_type_data();
318 let parent_class = data.as_ref().get_parent_class() as *mut gobject_sys::GObjectClass;
319
320 if let Some(ref func) = (*parent_class).constructed {
321 func(obj.to_glib_none().0);
322 }
323 }
324 }
325
signal_chain_from_overridden( &self, token: &super::SignalClassHandlerToken, values: &[Value], ) -> Option<Value>326 fn signal_chain_from_overridden(
327 &self,
328 token: &super::SignalClassHandlerToken,
329 values: &[Value],
330 ) -> Option<Value> {
331 unsafe {
332 super::types::signal_chain_from_overridden(
333 self.get_instance().as_ptr() as *mut _,
334 token,
335 values,
336 )
337 }
338 }
339 }
340
341 #[cfg(test)]
342 mod test {
343 use super::super::super::object::ObjectExt;
344 use super::super::super::subclass;
345 use super::super::super::value::{ToValue, Value};
346 use super::*;
347 use prelude::*;
348
349 use std::{cell::RefCell, error::Error};
350
351 // A dummy `Object` to test setting an `Object` property and returning an `Object` in signals
352 pub struct ChildObject;
353 impl ObjectSubclass for ChildObject {
354 const NAME: &'static str = "ChildObject";
355 type ParentType = Object;
356 type Instance = subclass::simple::InstanceStruct<Self>;
357 type Class = subclass::simple::ClassStruct<Self>;
358
359 glib_object_subclass!();
360
new() -> Self361 fn new() -> Self {
362 ChildObject
363 }
364 }
365 impl ObjectImpl for ChildObject {
366 glib_object_impl!();
367 }
368 impl StaticType for ChildObject {
static_type() -> Type369 fn static_type() -> Type {
370 ChildObject::get_type()
371 }
372 }
373
374 static PROPERTIES: [Property; 3] = [
375 Property("name", |name| {
376 ::ParamSpec::string(
377 name,
378 "Name",
379 "Name of this object",
380 None,
381 ::ParamFlags::READWRITE,
382 )
383 }),
384 Property("constructed", |name| {
385 ::ParamSpec::boolean(
386 name,
387 "Constructed",
388 "True if the constructed() virtual method was called",
389 false,
390 ::ParamFlags::READABLE,
391 )
392 }),
393 Property("child", |name| {
394 ::ParamSpec::object(
395 name,
396 "Child",
397 "Child object",
398 ChildObject::static_type(),
399 ::ParamFlags::READWRITE,
400 )
401 }),
402 ];
403
404 pub struct SimpleObject {
405 name: RefCell<Option<String>>,
406 constructed: RefCell<bool>,
407 }
408
409 impl ObjectSubclass for SimpleObject {
410 const NAME: &'static str = "SimpleObject";
411 type ParentType = Object;
412 type Instance = subclass::simple::InstanceStruct<Self>;
413 type Class = subclass::simple::ClassStruct<Self>;
414
415 glib_object_subclass!();
416
type_init(type_: &mut subclass::InitializingType<Self>)417 fn type_init(type_: &mut subclass::InitializingType<Self>) {
418 type_.add_interface::<DummyInterface>();
419 }
420
class_init(klass: &mut subclass::simple::ClassStruct<Self>)421 fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
422 klass.install_properties(&PROPERTIES);
423
424 klass.add_signal(
425 "name-changed",
426 SignalFlags::RUN_LAST,
427 &[String::static_type()],
428 ::Type::Unit,
429 );
430
431 klass.add_signal_with_class_handler(
432 "change-name",
433 SignalFlags::RUN_LAST | SignalFlags::ACTION,
434 &[String::static_type()],
435 String::static_type(),
436 |_, args| {
437 let obj = args[0]
438 .get::<Object>()
439 .expect("Failed to get args[0]")
440 .expect("Failed to get Object from args[0]");
441 let new_name = args[1]
442 .get::<String>()
443 .expect("Failed to get args[1]")
444 .expect("Failed to get Object from args[1]");
445 let imp = Self::from_instance(&obj);
446
447 let old_name = imp.name.borrow_mut().take();
448 *imp.name.borrow_mut() = Some(new_name);
449
450 obj.emit("name-changed", &[&*imp.name.borrow()])
451 .expect("Failed to borrow name");
452
453 Some(old_name.to_value())
454 },
455 );
456
457 klass.add_signal(
458 "create-string",
459 SignalFlags::RUN_LAST,
460 &[],
461 String::static_type(),
462 );
463
464 klass.add_signal(
465 "create-child-object",
466 SignalFlags::RUN_LAST,
467 &[],
468 ChildObject::static_type(),
469 );
470 }
471
new() -> Self472 fn new() -> Self {
473 Self {
474 name: RefCell::new(None),
475 constructed: RefCell::new(false),
476 }
477 }
478 }
479
480 impl ObjectImpl for SimpleObject {
481 glib_object_impl!();
482
set_property(&self, obj: &Object, id: usize, value: &Value)483 fn set_property(&self, obj: &Object, id: usize, value: &Value) {
484 let prop = &PROPERTIES[id];
485
486 match *prop {
487 Property("name", ..) => {
488 let name = value
489 .get()
490 .expect("type conformity checked by 'Object::set_property'");
491 self.name.replace(name);
492 obj.emit("name-changed", &[&*self.name.borrow()])
493 .expect("Failed to borrow name");
494 }
495 Property("child", ..) => {
496 // not stored, only used to test `set_property` with `Objects`
497 }
498 _ => unimplemented!(),
499 }
500 }
501
get_property(&self, _obj: &Object, id: usize) -> Result<Value, ()>502 fn get_property(&self, _obj: &Object, id: usize) -> Result<Value, ()> {
503 let prop = &PROPERTIES[id];
504
505 match *prop {
506 Property("name", ..) => Ok(self.name.borrow().to_value()),
507 Property("constructed", ..) => Ok(self.constructed.borrow().to_value()),
508 _ => unimplemented!(),
509 }
510 }
511
constructed(&self, obj: &Object)512 fn constructed(&self, obj: &Object) {
513 self.parent_constructed(obj);
514
515 assert_eq!(obj, &self.get_instance());
516 assert_eq!(self as *const _, Self::from_instance(obj) as *const _);
517
518 *self.constructed.borrow_mut() = true;
519 }
520 }
521
522 #[repr(C)]
523 pub struct DummyInterface {
524 parent: gobject_sys::GTypeInterface,
525 }
526
527 impl ObjectInterface for DummyInterface {
528 const NAME: &'static str = "DummyInterface";
529
530 glib_object_interface!();
531
type_init(type_: &mut subclass::InitializingType<Self>)532 fn type_init(type_: &mut subclass::InitializingType<Self>) {
533 type_.add_prerequisite::<Object>();
534 }
535 }
536
537 // Usually this would be implemented on a Rust wrapper type defined
538 // with glib_wrapper!() but for the test the following is susyscient
539 impl StaticType for DummyInterface {
static_type() -> Type540 fn static_type() -> Type {
541 DummyInterface::get_type()
542 }
543 }
544
545 // Usually this would be implemented on a Rust wrapper type defined
546 // with glib_wrapper!() but for the test the following is susyscient
547 unsafe impl<T: ObjectSubclass> IsImplementable<T> for DummyInterface {
interface_init( _iface: glib_sys::gpointer, _iface_data: glib_sys::gpointer, )548 unsafe extern "C" fn interface_init(
549 _iface: glib_sys::gpointer,
550 _iface_data: glib_sys::gpointer,
551 ) {
552 }
553 }
554
555 #[test]
test_create()556 fn test_create() {
557 let type_ = SimpleObject::get_type();
558 let obj = Object::new(type_, &[]).expect("Object::new failed");
559
560 assert!(obj.get_type().is_a(&DummyInterface::static_type()));
561
562 assert_eq!(
563 obj.get_property("constructed")
564 .expect("Failed to get 'constructed' property")
565 .get_some::<bool>()
566 .expect("Failed to get bool from 'constructed' property"),
567 true
568 );
569
570 let weak = obj.downgrade();
571 drop(obj);
572 assert!(weak.upgrade().is_none());
573 }
574
575 #[test]
test_create_child_object()576 fn test_create_child_object() {
577 let type_ = ChildObject::get_type();
578 let obj = Object::new(type_, &[]).expect("Object::new failed");
579
580 // ChildObject is a zero-sized type and we map that to the same pointer as the object
581 // itself. No private/impl data is allocated for zero-sized types.
582 let imp = ChildObject::from_instance(&obj);
583 assert_eq!(imp as *const _ as *const (), obj.as_ptr() as *const _);
584 }
585
586 #[test]
test_set_properties()587 fn test_set_properties() {
588 let obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
589
590 assert!(obj
591 .get_property("name")
592 .expect("Failed to get 'name' property")
593 .get::<&str>()
594 .expect("Failed to get str from 'name' property")
595 .is_none());
596 assert!(obj.set_property("name", &"test").is_ok());
597 assert_eq!(
598 obj.get_property("name")
599 .expect("Failed to get 'name' property")
600 .get::<&str>()
601 .expect("Failed to get str from 'name' property"),
602 Some("test")
603 );
604
605 assert_eq!(
606 obj.set_property("test", &true)
607 .err()
608 .expect("set_property failed")
609 .description(),
610 "property not found",
611 );
612
613 assert_eq!(
614 obj.set_property("constructed", &false)
615 .err()
616 .expect("Failed to set 'constructed' property")
617 .description(),
618 "property is not writable",
619 );
620
621 assert_eq!(
622 obj.set_property("name", &false)
623 .err()
624 .expect("Failed to set 'name' property")
625 .description(),
626 "property can't be set from the given type (expected: gchararray, got: gboolean)",
627 );
628
629 let other_obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
630 assert_eq!(
631 obj.set_property("child", &other_obj)
632 .err()
633 .expect("Failed to set 'child' property")
634 .description(),
635 "property can't be set from the given object type (expected: ChildObject, got: SimpleObject)",
636 );
637
638 let child = Object::new(ChildObject::get_type(), &[]).expect("Object::new failed");
639 assert!(obj.set_property("child", &child).is_ok());
640 }
641
642 #[test]
test_signals()643 fn test_signals() {
644 use std::sync::{Arc, Mutex};
645
646 let type_ = SimpleObject::get_type();
647 let obj = Object::new(type_, &[("name", &"old-name")]).expect("Object::new failed");
648
649 let name_changed_triggered = Arc::new(Mutex::new(false));
650 let name_changed_clone = name_changed_triggered.clone();
651 obj.connect("name-changed", false, move |args| {
652 let _obj = args[0]
653 .get::<Object>()
654 .expect("Failed to get args[0]")
655 .expect("Failed to get str from args[0]");
656 let name = args[1]
657 .get::<&str>()
658 .expect("Failed to get args[1]")
659 .expect("Failed to get str from args[1]");
660
661 assert_eq!(name, "new-name");
662 *name_changed_clone.lock().expect("Failed to lock") = true;
663
664 None
665 })
666 .expect("Failed to connect on 'name-changed'");
667
668 assert_eq!(
669 obj.get_property("name")
670 .expect("Failed to get 'name' property")
671 .get::<&str>()
672 .expect("Failed to get str from 'name' property"),
673 Some("old-name")
674 );
675 assert!(!*name_changed_triggered.lock().expect("Failed to lock"));
676
677 let old_name = obj
678 .emit("change-name", &[&"new-name"])
679 .expect("Failed to emit")
680 .expect("Failed to get value from emit")
681 .get::<String>()
682 .expect("Failed to get str from emit");
683 assert_eq!(old_name, Some("old-name".to_string()));
684 assert!(*name_changed_triggered.lock().expect("Failed to lock"));
685 }
686
687 #[test]
test_signal_return_expected_type()688 fn test_signal_return_expected_type() {
689 let obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
690
691 obj.connect("create-string", false, move |_args| {
692 Some("return value".to_value())
693 })
694 .expect("Failed to connect on 'create-string'");
695
696 let value = obj
697 .emit("create-string", &[])
698 .expect("Failed to emit")
699 .expect("Failed to get value from emit");
700 assert_eq!(value.get::<String>(), Ok(Some("return value".to_string())));
701 }
702
703 // Note: can't test type mismatch in signals since panics accross FFI boundaries
704 // are UB. See https://github.com/gtk-rs/glib/issues/518
705
706 #[test]
test_signal_return_expected_object_type()707 fn test_signal_return_expected_object_type() {
708 let obj = Object::new(SimpleObject::get_type(), &[]).expect("Object::new failed");
709
710 obj.connect("create-child-object", false, move |_args| {
711 Some(
712 Object::new(ChildObject::get_type(), &[])
713 .expect("Object::new failed")
714 .to_value(),
715 )
716 })
717 .expect("Failed to connect on 'create-child-object'");
718
719 let value = obj
720 .emit("create-child-object", &[])
721 .expect("Failed to emit")
722 .expect("Failed to get value from emit");
723 assert!(value.type_().is_a(&ChildObject::static_type()));
724 }
725 }
726