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