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