1use std::{mem, ptr};
8
9use crate::{
10 ffi, gobject_ffi,
11 prelude::*,
12 subclass::{prelude::*, Signal},
13 translate::*,
14 Object, ParamSpec, Slice, Value,
15};
16
17pub trait ObjectImpl: ObjectSubclass + ObjectImplExt {
23 fn properties() -> &'static [ParamSpec] {
26 &[]
27 }
28
29 fn signals() -> &'static [Signal] {
32 &[]
33 }
34
35 fn set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
55 unimplemented!()
56 }
57
58 #[doc(alias = "get_property")]
72 fn property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
73 unimplemented!()
74 }
75
76 fn constructed(&self) {
99 self.parent_constructed();
100 }
101
102 fn dispose(&self) {}
122
123 fn notify(&self, pspec: &ParamSpec) {
149 self.parent_notify(pspec)
150 }
151
152 fn dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
160 self.parent_dispatch_properties_changed(pspecs)
161 }
162}
163
164#[doc(alias = "get_property")]
165unsafe extern "C" fn property<T: ObjectImpl>(
166 obj: *mut gobject_ffi::GObject,
167 id: u32,
168 value: *mut gobject_ffi::GValue,
169 pspec: *mut gobject_ffi::GParamSpec,
170) {
171 let instance = &*(obj as *mut T::Instance);
172 let imp = instance.imp();
173
174 let v = imp.property(id as usize, &from_glib_borrow(pspec));
175
176 gobject_ffi::g_value_unset(value);
185 let v = mem::ManuallyDrop::new(v);
186 ptr::write(value, ptr::read(v.to_glib_none().0));
187}
188
189unsafe extern "C" fn set_property<T: ObjectImpl>(
190 obj: *mut gobject_ffi::GObject,
191 id: u32,
192 value: *mut gobject_ffi::GValue,
193 pspec: *mut gobject_ffi::GParamSpec,
194) {
195 let instance = &*(obj as *mut T::Instance);
196 let imp = instance.imp();
197 imp.set_property(
198 id as usize,
199 &*(value as *mut Value),
200 &from_glib_borrow(pspec),
201 );
202}
203
204unsafe extern "C" fn constructed<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
205 let instance = &*(obj as *mut T::Instance);
206 let imp = instance.imp();
207
208 imp.constructed();
209}
210
211unsafe extern "C" fn notify<T: ObjectImpl>(
212 obj: *mut gobject_ffi::GObject,
213 pspec: *mut gobject_ffi::GParamSpec,
214) {
215 let instance = &*(obj as *mut T::Instance);
216 let imp = instance.imp();
217 imp.notify(&from_glib_borrow(pspec));
218}
219
220unsafe extern "C" fn dispatch_properties_changed<T: ObjectImpl>(
221 obj: *mut gobject_ffi::GObject,
222 n_pspecs: u32,
223 pspecs: *mut *mut gobject_ffi::GParamSpec,
224) {
225 let instance = &*(obj as *mut T::Instance);
226 let imp = instance.imp();
227 imp.dispatch_properties_changed(Slice::from_glib_borrow_num(pspecs, n_pspecs as _));
228}
229
230unsafe extern "C" fn dispose<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
231 let instance = &*(obj as *mut T::Instance);
232 let imp = instance.imp();
233
234 imp.dispose();
235
236 let data = T::type_data();
238 let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
239 if let Some(ref func) = (*parent_class).dispose {
240 func(obj);
241 }
242}
243
244pub trait DerivedObjectProperties: ObjectSubclass {
249 fn derived_properties() -> &'static [ParamSpec] {
252 &[]
253 }
254
255 fn derived_set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
259 unimplemented!()
260 }
261
262 fn derived_property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
266 unimplemented!()
267 }
268}
269
270pub unsafe trait ObjectClassSubclassExt: Sized + 'static {
275 fn override_signal_class_handler<F>(&mut self, name: &str, class_handler: F)
276 where
277 F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
278 {
279 unsafe {
280 super::types::signal_override_class_handler(
281 name,
282 *(self as *mut _ as *mut ffi::GType),
283 class_handler,
284 );
285 }
286 }
287}
288
289unsafe impl ObjectClassSubclassExt for crate::Class<Object> {}
290
291unsafe impl<T: ObjectImpl> IsSubclassable<T> for Object {
292 fn class_init(class: &mut crate::Class<Self>) {
293 let klass = class.as_mut();
294 klass.set_property = Some(set_property::<T>);
295 klass.get_property = Some(property::<T>);
296 klass.constructed = Some(constructed::<T>);
297 klass.notify = Some(notify::<T>);
298 klass.dispatch_properties_changed = Some(dispatch_properties_changed::<T>);
299 klass.dispose = Some(dispose::<T>);
300
301 let pspecs = <T as ObjectImpl>::properties();
302 if !pspecs.is_empty() {
303 unsafe {
304 let mut pspecs_ptrs = Vec::with_capacity(pspecs.len() + 1);
305
306 pspecs_ptrs.push(ptr::null_mut());
307
308 for pspec in pspecs {
309 pspecs_ptrs.push(pspec.to_glib_none().0);
310 }
311
312 gobject_ffi::g_object_class_install_properties(
313 klass,
314 pspecs_ptrs.len() as u32,
315 pspecs_ptrs.as_mut_ptr(),
316 );
317 }
318 }
319
320 let type_ = T::type_();
321 let signals = <T as ObjectImpl>::signals();
322 for signal in signals {
323 signal.register(type_);
324 }
325 }
326
327 #[inline]
328 fn instance_init(_instance: &mut super::InitializingObject<T>) {}
329}
330
331mod sealed {
332 pub trait Sealed {}
333 impl<T: super::ObjectImplExt> Sealed for T {}
334}
335
336pub trait ObjectImplExt: sealed::Sealed + ObjectSubclass {
337 #[inline]
340 fn parent_constructed(&self) {
341 unsafe {
342 let data = Self::type_data();
343 let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
344
345 if let Some(ref func) = (*parent_class).constructed {
346 func(self.obj().unsafe_cast_ref::<Object>().to_glib_none().0);
347 }
348 }
349 }
350
351 #[inline]
354 fn parent_notify(&self, pspec: &ParamSpec) {
355 unsafe {
356 let data = Self::type_data();
357 let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
358
359 if let Some(ref func) = (*parent_class).notify {
360 func(
361 self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
362 pspec.to_glib_none().0,
363 );
364 }
365 }
366 }
367
368 #[inline]
371 fn parent_dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
372 unsafe {
373 let data = Self::type_data();
374 let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
375
376 if let Some(ref func) = (*parent_class).dispatch_properties_changed {
377 func(
378 self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
379 pspecs.len() as _,
380 pspecs.as_ptr() as *mut _,
381 );
382 }
383 }
384 }
385
386 fn signal_chain_from_overridden(
389 &self,
390 token: &super::SignalClassHandlerToken,
391 values: &[Value],
392 ) -> Option<Value> {
393 unsafe {
394 super::types::signal_chain_from_overridden(self.obj().as_ptr() as *mut _, token, values)
395 }
396 }
397}
398
399impl<T: ObjectImpl> ObjectImplExt for T {}
400
401#[cfg(test)]
402mod test {
403 use std::cell::RefCell;
404
405 use super::*;
406 use crate as glib;
410
411 mod imp {
412 use std::sync::OnceLock;
413
414 use super::*;
415
416 #[derive(Default)]
418 pub struct ChildObject;
419
420 #[glib::object_subclass]
421 impl ObjectSubclass for ChildObject {
422 const NAME: &'static str = "ChildObject";
423 type Type = super::ChildObject;
424 }
425
426 impl ObjectImpl for ChildObject {}
427
428 pub struct SimpleObject {
429 name: RefCell<Option<String>>,
430 construct_name: RefCell<Option<String>>,
431 constructed: RefCell<bool>,
432 answer: RefCell<i32>,
433 array: RefCell<Vec<String>>,
434 }
435
436 impl Default for SimpleObject {
437 fn default() -> Self {
438 SimpleObject {
439 name: Default::default(),
440 construct_name: Default::default(),
441 constructed: Default::default(),
442 answer: RefCell::new(42i32),
443 array: RefCell::new(vec!["default0".to_string(), "default1".to_string()]),
444 }
445 }
446 }
447
448 #[glib::object_subclass]
449 impl ObjectSubclass for SimpleObject {
450 const NAME: &'static str = "SimpleObject";
451 type Type = super::SimpleObject;
452 type Interfaces = (super::Dummy,);
453 }
454
455 impl ObjectImpl for SimpleObject {
456 fn properties() -> &'static [ParamSpec] {
457 static PROPERTIES: OnceLock<Vec<ParamSpec>> = OnceLock::new();
458 PROPERTIES.get_or_init(|| {
459 vec![
460 crate::ParamSpecString::builder("name").build(),
461 crate::ParamSpecString::builder("construct-name")
462 .construct_only()
463 .build(),
464 crate::ParamSpecBoolean::builder("constructed")
465 .read_only()
466 .build(),
467 crate::ParamSpecObject::builder::<super::ChildObject>("child").build(),
468 crate::ParamSpecInt::builder("answer")
469 .default_value(42i32)
470 .build(),
471 crate::ParamSpecValueArray::builder("array").build(),
472 ]
473 })
474 }
475
476 fn signals() -> &'static [super::Signal] {
477 static SIGNALS: OnceLock<Vec<super::Signal>> = OnceLock::new();
478 SIGNALS.get_or_init(|| {
479 vec![
480 super::Signal::builder("name-changed")
481 .param_types([String::static_type()])
482 .build(),
483 super::Signal::builder("change-name")
484 .param_types([String::static_type()])
485 .return_type::<String>()
486 .action()
487 .class_handler(|_, args| {
488 let obj = args[0]
489 .get::<super::SimpleObject>()
490 .expect("Failed to get Object from args[0]");
491 let new_name = args[1]
492 .get::<String>()
493 .expect("Failed to get Object from args[1]");
494 let imp = obj.imp();
495
496 let old_name = imp.name.replace(Some(new_name));
497
498 obj.emit_by_name::<()>("name-changed", &[&*imp.name.borrow()]);
499
500 Some(old_name.to_value())
501 })
502 .build(),
503 super::Signal::builder("create-string")
504 .return_type::<String>()
505 .build(),
506 super::Signal::builder("create-child-object")
507 .return_type::<super::ChildObject>()
508 .build(),
509 ]
510 })
511 }
512
513 fn set_property(&self, _id: usize, value: &Value, pspec: &crate::ParamSpec) {
514 match pspec.name() {
515 "name" => {
516 let name = value
517 .get()
518 .expect("type conformity checked by 'Object::set_property'");
519 self.name.replace(name);
520 self.obj()
521 .emit_by_name::<()>("name-changed", &[&*self.name.borrow()]);
522 }
523 "construct-name" => {
524 let name = value
525 .get()
526 .expect("type conformity checked by 'Object::set_property'");
527 self.construct_name.replace(name);
528 }
529 "child" => {
530 }
532 "answer" => {
533 let answer = value
534 .get()
535 .expect("type conformity checked by 'Object::set_property'");
536 self.answer.replace(answer);
537 }
538 "array" => {
539 let value = value
540 .get::<crate::ValueArray>()
541 .expect("type conformity checked by 'Object::set_property'");
542 let mut array = self.array.borrow_mut();
543 array.clear();
544 array.extend(value.iter().map(|v| v.get().unwrap()));
545 }
546 _ => unimplemented!(),
547 }
548 }
549
550 fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> Value {
551 match pspec.name() {
552 "name" => self.name.borrow().to_value(),
553 "construct-name" => self.construct_name.borrow().to_value(),
554 "constructed" => self.constructed.borrow().to_value(),
555 "answer" => self.answer.borrow().to_value(),
556 "array" => crate::ValueArray::new(self.array.borrow().iter()).to_value(),
557 _ => unimplemented!(),
558 }
559 }
560
561 fn constructed(&self) {
562 self.parent_constructed();
563
564 debug_assert_eq!(self as *const _, self.obj().imp() as *const _);
565
566 *self.constructed.borrow_mut() = true;
567 }
568 }
569
570 #[derive(Clone, Copy)]
571 #[repr(C)]
572 pub struct DummyInterface {
573 parent: gobject_ffi::GTypeInterface,
574 }
575
576 unsafe impl InterfaceStruct for DummyInterface {
577 type Type = Dummy;
578 }
579
580 pub enum Dummy {}
581
582 #[glib::object_interface]
583 impl ObjectInterface for Dummy {
584 const NAME: &'static str = "Dummy";
585 type Interface = DummyInterface;
586 }
587 }
588
589 wrapper! {
590 pub struct ChildObject(ObjectSubclass<imp::ChildObject>);
591 }
592
593 wrapper! {
594 pub struct SimpleObject(ObjectSubclass<imp::SimpleObject>);
595 }
596
597 wrapper! {
598 pub struct Dummy(ObjectInterface<imp::Dummy>);
599 }
600
601 unsafe impl<T: ObjectSubclass> IsImplementable<T> for Dummy {}
602
603 #[test]
604 fn test_create() {
605 let type_ = SimpleObject::static_type();
606 let obj = Object::with_type(type_);
607
608 assert!(obj.type_().is_a(Dummy::static_type()));
609
610 assert_eq!(
612 mem::size_of::<SimpleObject>(),
613 mem::size_of::<ffi::gpointer>()
614 );
615 assert_eq!(obj.as_ptr() as ffi::gpointer, unsafe {
616 *(&obj as *const _ as *const ffi::gpointer)
617 });
618
619 assert!(obj.property::<bool>("constructed"));
620
621 let weak = obj.downgrade();
622 drop(obj);
623 assert!(weak.upgrade().is_none());
624 }
625
626 #[test]
627 fn test_properties() {
628 let type_ = SimpleObject::static_type();
629 let obj = Object::with_type(type_);
630
631 assert!(obj.type_().is_a(Dummy::static_type()));
632
633 let properties = obj.list_properties();
634 assert_eq!(properties.len(), 6);
635 assert_eq!(properties[0].name(), "name");
636 assert_eq!(properties[1].name(), "construct-name");
637 assert_eq!(properties[2].name(), "constructed");
638 assert_eq!(properties[3].name(), "child");
639 assert_eq!(properties[4].name(), "answer");
640 assert_eq!(properties[5].name(), "array");
641 }
642
643 #[test]
644 fn test_create_child_object() {
645 let obj: ChildObject = Object::new();
646
647 assert_eq!(&obj, obj.imp().obj().as_ref());
648 }
649
650 #[test]
651 fn test_builder() {
652 let obj = Object::builder::<SimpleObject>()
653 .property("construct-name", "meh")
654 .property("name", "initial")
655 .build();
656
657 assert_eq!(
658 obj.property::<String>("construct-name"),
659 String::from("meh")
660 );
661
662 assert_eq!(obj.property::<String>("name"), String::from("initial"));
663 }
664
665 #[test]
666 fn test_set_property() {
667 let obj = Object::builder::<SimpleObject>()
668 .property("construct-name", "meh")
669 .property("name", "initial")
670 .build();
671
672 assert_eq!(
673 obj.property::<String>("construct-name"),
674 String::from("meh")
675 );
676
677 assert_eq!(
678 obj.property::<String>("construct-name"),
679 String::from("meh")
680 );
681
682 assert_eq!(obj.property::<String>("name"), String::from("initial"));
683 obj.set_property("name", "test");
684 assert_eq!(obj.property::<String>("name"), String::from("test"));
685
686 let child = Object::with_type(ChildObject::static_type());
687 obj.set_property("child", &child);
688 }
689
690 #[test]
691 fn builder_property_if() {
692 use crate::ValueArray;
693
694 let array = ["val0", "val1"];
695 let obj = Object::builder::<SimpleObject>()
696 .property_if("name", "some name", true)
697 .property_if("answer", 21i32, 6 != 9)
698 .property_if("array", ValueArray::new(["val0", "val1"]), array.len() == 2)
699 .build();
700
701 assert_eq!(obj.property::<String>("name").as_str(), "some name");
702 assert_eq!(
703 obj.property::<Option<String>>("name").as_deref(),
704 Some("some name")
705 );
706 assert_eq!(obj.property::<i32>("answer"), 21);
707 assert!(obj
708 .property::<ValueArray>("array")
709 .iter()
710 .map(|val| val.get::<&str>().unwrap())
711 .eq(array));
712
713 let obj = Object::builder::<SimpleObject>()
714 .property_if("name", "some name", false)
715 .property_if("answer", 21i32, 6 == 9)
716 .property_if("array", ValueArray::new(array), array.len() == 4)
717 .build();
718
719 assert!(obj.property::<Option<String>>("name").is_none());
720 assert_eq!(obj.property::<i32>("answer"), 42);
721 assert!(obj
722 .property::<ValueArray>("array")
723 .iter()
724 .map(|val| val.get::<&str>().unwrap())
725 .eq(["default0", "default1"]));
726 }
727
728 #[test]
729 fn builder_property_if_some() {
730 use crate::ValueArray;
731
732 let array = ["val0", "val1"];
733 let obj = Object::builder::<SimpleObject>()
734 .property_if_some("name", Some("some name"))
735 .property_if_some("answer", Some(21i32))
736 .property_if_some("array", Some(ValueArray::new(array)))
737 .build();
738
739 assert_eq!(obj.property::<String>("name").as_str(), "some name");
740 assert_eq!(
741 obj.property::<Option<String>>("name").as_deref(),
742 Some("some name")
743 );
744 assert_eq!(obj.property::<i32>("answer"), 21);
745 assert!(obj
746 .property::<ValueArray>("array")
747 .iter()
748 .map(|val| val.get::<&str>().unwrap())
749 .eq(array));
750
751 let obj = Object::builder::<SimpleObject>()
752 .property_if_some("name", Option::<&str>::None)
753 .property_if_some("answer", Option::<i32>::None)
754 .property_if_some("array", Option::<ValueArray>::None)
755 .build();
756
757 assert!(obj.property::<Option<String>>("name").is_none());
758 assert_eq!(obj.property::<i32>("answer"), 42);
759 assert!(obj
760 .property::<ValueArray>("array")
761 .iter()
762 .map(|val| val.get::<&str>().unwrap())
763 .eq(["default0", "default1"]));
764 }
765
766 #[test]
767 fn builder_property_from_iter() {
768 use crate::ValueArray;
769
770 let array = ["val0", "val1"];
771 let obj = Object::builder::<SimpleObject>()
772 .property_from_iter::<ValueArray>("array", &array)
773 .build();
774
775 assert!(obj
776 .property::<ValueArray>("array")
777 .iter()
778 .map(|val| val.get::<&str>().unwrap())
779 .eq(array));
780
781 let obj = Object::builder::<SimpleObject>()
782 .property_from_iter::<ValueArray>("array", Vec::<&str>::new())
783 .build();
784
785 assert!(obj.property::<ValueArray>("array").is_empty());
786 }
787
788 #[test]
789 fn builder_property_if_not_empty() {
790 use crate::ValueArray;
791
792 let array = ["val0", "val1"];
793 let obj = Object::builder::<SimpleObject>()
794 .property_if_not_empty::<ValueArray>("array", &array)
795 .build();
796
797 assert!(obj
798 .property::<ValueArray>("array")
799 .iter()
800 .map(|val| val.get::<&str>().unwrap())
801 .eq(array));
802
803 let empty_vec = Vec::<String>::new();
804 let obj = Object::builder::<SimpleObject>()
805 .property_if_not_empty::<ValueArray>("array", &empty_vec)
806 .build();
807
808 assert!(obj
809 .property::<ValueArray>("array")
810 .iter()
811 .map(|val| val.get::<&str>().unwrap())
812 .eq(["default0", "default1"]));
813 }
814
815 #[test]
816 #[should_panic = "property 'construct-name' of type 'SimpleObject' is not writable"]
817 fn test_set_property_non_writable() {
818 let obj = Object::builder::<SimpleObject>()
819 .property("construct-name", "meh")
820 .property("name", "initial")
821 .build();
822
823 obj.set_property("construct-name", "test");
824 }
825
826 #[test]
827 #[should_panic = "property 'test' of type 'SimpleObject' not found"]
828 fn test_set_property_not_found() {
829 let obj = Object::builder::<SimpleObject>()
830 .property("construct-name", "meh")
831 .property("name", "initial")
832 .build();
833
834 obj.set_property("test", true);
835 }
836
837 #[test]
838 #[should_panic = "property 'constructed' of type 'SimpleObject' is not writable"]
839 fn test_set_property_not_writable() {
840 let obj = Object::builder::<SimpleObject>()
841 .property("construct-name", "meh")
842 .property("name", "initial")
843 .build();
844
845 obj.set_property("constructed", false);
846 }
847
848 #[test]
849 #[should_panic = "property 'name' of type 'SimpleObject' can't be set from the given type (expected: 'gchararray', got: 'gboolean')"]
850 fn test_set_property_wrong_type() {
851 let obj = Object::builder::<SimpleObject>()
852 .property("construct-name", "meh")
853 .property("name", "initial")
854 .build();
855
856 obj.set_property("name", false);
857 }
858
859 #[test]
860 #[should_panic = "property 'child' of type 'SimpleObject' can't be set from the given type (expected: 'ChildObject', got: 'SimpleObject')"]
861 fn test_set_property_wrong_type_2() {
862 let obj = Object::builder::<SimpleObject>()
863 .property("construct-name", "meh")
864 .property("name", "initial")
865 .build();
866
867 let other_obj = Object::with_type(SimpleObject::static_type());
868
869 obj.set_property("child", &other_obj);
870 }
871
872 #[test]
873 #[should_panic = "Can't set construct property 'construct-name' for type 'SimpleObject' twice"]
874 fn test_construct_property_set_twice() {
875 let _obj = Object::builder::<SimpleObject>()
876 .property("construct-name", "meh")
877 .property("construct-name", "meh2")
878 .build();
879 }
880
881 #[test]
882 fn test_signals() {
883 use std::sync::{
884 atomic::{AtomicBool, Ordering},
885 Arc,
886 };
887
888 let obj = Object::builder::<SimpleObject>()
889 .property("name", "old-name")
890 .build();
891
892 let name_changed_triggered = Arc::new(AtomicBool::new(false));
893 let name_changed_clone = name_changed_triggered.clone();
894 obj.connect("name-changed", false, move |args| {
895 let _obj = args[0].get::<Object>().expect("Failed to get args[0]");
896 let name = args[1].get::<&str>().expect("Failed to get args[1]");
897
898 assert_eq!(name, "new-name");
899 name_changed_clone.store(true, Ordering::Relaxed);
900
901 None
902 });
903
904 assert_eq!(obj.property::<String>("name"), String::from("old-name"));
905 assert!(!name_changed_triggered.load(Ordering::Relaxed));
906
907 assert_eq!(
908 obj.emit_by_name::<String>("change-name", &[&"new-name"]),
909 "old-name"
910 );
911 assert!(name_changed_triggered.load(Ordering::Relaxed));
912 }
913
914 #[test]
915 fn test_signal_return_expected_type() {
916 let obj = Object::with_type(SimpleObject::static_type());
917
918 obj.connect("create-string", false, move |_args| {
919 Some("return value".to_value())
920 });
921
922 let signal_id = imp::SimpleObject::signals()[2].signal_id();
923
924 let value = obj.emit::<String>(signal_id, &[]);
925 assert_eq!(value, "return value");
926 }
927
928 #[test]
929 fn test_callback_validity() {
930 use std::sync::{
931 atomic::{AtomicBool, Ordering},
932 Arc,
933 };
934
935 let obj = Object::builder::<SimpleObject>()
936 .property("name", "old-name")
937 .build();
938
939 let name_changed_triggered = Arc::new(AtomicBool::new(false));
940 let name_changed_clone = name_changed_triggered.clone();
941
942 obj.connect_notify(Some("name"), move |_, _| {
943 name_changed_clone.store(true, Ordering::Relaxed);
944 });
945 obj.notify("name");
946 assert!(name_changed_triggered.load(Ordering::Relaxed));
947 }
948
949 #[test]
953 fn test_signal_return_expected_object_type() {
954 let obj = Object::with_type(SimpleObject::static_type());
955
956 obj.connect("create-child-object", false, move |_args| {
957 Some(Object::with_type(ChildObject::static_type()).to_value())
958 });
959 let value: glib::Object = obj.emit_by_name("create-child-object", &[]);
960 assert!(value.type_().is_a(ChildObject::static_type()));
961 }
962}