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<Type: IsA<Object>> {
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
331pub trait ObjectImplExt: ObjectImpl {
332 #[inline]
335 fn parent_constructed(&self) {
336 unsafe {
337 let data = Self::type_data();
338 let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
339
340 if let Some(ref func) = (*parent_class).constructed {
341 func(self.obj().unsafe_cast_ref::<Object>().to_glib_none().0);
342 }
343 }
344 }
345
346 #[inline]
349 fn parent_notify(&self, pspec: &ParamSpec) {
350 unsafe {
351 let data = Self::type_data();
352 let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
353
354 if let Some(ref func) = (*parent_class).notify {
355 func(
356 self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
357 pspec.to_glib_none().0,
358 );
359 }
360 }
361 }
362
363 #[inline]
366 fn parent_dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
367 unsafe {
368 let data = Self::type_data();
369 let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
370
371 if let Some(ref func) = (*parent_class).dispatch_properties_changed {
372 func(
373 self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
374 pspecs.len() as _,
375 pspecs.as_ptr() as *mut _,
376 );
377 }
378 }
379 }
380
381 fn signal_chain_from_overridden(
384 &self,
385 token: &super::SignalClassHandlerToken,
386 values: &[Value],
387 ) -> Option<Value> {
388 unsafe {
389 super::types::signal_chain_from_overridden(self.obj().as_ptr() as *mut _, token, values)
390 }
391 }
392}
393
394impl<T: ObjectImpl> ObjectImplExt for T {}
395
396#[cfg(test)]
397mod test {
398 use std::cell::RefCell;
399
400 use super::*;
401 use crate as glib;
405
406 mod imp {
407 use std::sync::OnceLock;
408
409 use super::*;
410
411 #[derive(Default)]
413 pub struct ChildObject;
414
415 #[glib::object_subclass]
416 impl ObjectSubclass for ChildObject {
417 const NAME: &'static str = "ChildObject";
418 type Type = super::ChildObject;
419 }
420
421 impl ObjectImpl for ChildObject {}
422
423 pub struct SimpleObject {
424 name: RefCell<Option<String>>,
425 construct_name: RefCell<Option<String>>,
426 constructed: RefCell<bool>,
427 answer: RefCell<i32>,
428 array: RefCell<Vec<String>>,
429 }
430
431 impl Default for SimpleObject {
432 fn default() -> Self {
433 SimpleObject {
434 name: Default::default(),
435 construct_name: Default::default(),
436 constructed: Default::default(),
437 answer: RefCell::new(42i32),
438 array: RefCell::new(vec!["default0".to_string(), "default1".to_string()]),
439 }
440 }
441 }
442
443 #[glib::object_subclass]
444 impl ObjectSubclass for SimpleObject {
445 const NAME: &'static str = "SimpleObject";
446 type Type = super::SimpleObject;
447 type Interfaces = (super::Dummy,);
448 }
449
450 impl ObjectImpl for SimpleObject {
451 fn properties() -> &'static [ParamSpec] {
452 static PROPERTIES: OnceLock<Vec<ParamSpec>> = OnceLock::new();
453 PROPERTIES.get_or_init(|| {
454 vec![
455 crate::ParamSpecString::builder("name").build(),
456 crate::ParamSpecString::builder("construct-name")
457 .construct_only()
458 .build(),
459 crate::ParamSpecBoolean::builder("constructed")
460 .read_only()
461 .build(),
462 crate::ParamSpecObject::builder::<super::ChildObject>("child").build(),
463 crate::ParamSpecInt::builder("answer")
464 .default_value(42i32)
465 .build(),
466 crate::ParamSpecValueArray::builder("array").build(),
467 ]
468 })
469 }
470
471 fn signals() -> &'static [super::Signal] {
472 static SIGNALS: OnceLock<Vec<super::Signal>> = OnceLock::new();
473 SIGNALS.get_or_init(|| {
474 vec![
475 super::Signal::builder("name-changed")
476 .param_types([String::static_type()])
477 .build(),
478 super::Signal::builder("change-name")
479 .param_types([String::static_type()])
480 .return_type::<String>()
481 .action()
482 .class_handler(|args| {
483 let obj = args[0]
484 .get::<super::SimpleObject>()
485 .expect("Failed to get Object from args[0]");
486 let new_name = args[1]
487 .get::<String>()
488 .expect("Failed to get Object from args[1]");
489 let imp = obj.imp();
490
491 let old_name = imp.name.replace(Some(new_name));
492
493 obj.emit_by_name::<()>("name-changed", &[&*imp.name.borrow()]);
494
495 Some(old_name.to_value())
496 })
497 .build(),
498 super::Signal::builder("create-string")
499 .return_type::<String>()
500 .accumulator(|_hint, acc, val| {
501 let mut acc = acc
503 .get_owned::<Option<String>>()
504 .unwrap()
505 .map(|mut acc| {
506 acc.push('\n');
507 acc
508 })
509 .unwrap_or_default();
510 acc.push_str(val.get::<&str>().unwrap());
511 std::ops::ControlFlow::Continue(acc.to_value())
512 })
513 .build(),
514 super::Signal::builder("create-child-object")
515 .return_type::<super::ChildObject>()
516 .build(),
517 super::Signal::builder("return-string")
518 .return_type::<String>()
519 .action()
520 .class_handler(|args| {
521 let _obj = args[0]
522 .get::<super::SimpleObject>()
523 .expect("Failed to get Object from args[0]");
524 Some("base".to_value())
525 })
526 .build(),
527 ]
528 })
529 }
530
531 fn set_property(&self, _id: usize, value: &Value, pspec: &crate::ParamSpec) {
532 match pspec.name() {
533 "name" => {
534 let name = value
535 .get()
536 .expect("type conformity checked by 'Object::set_property'");
537 self.name.replace(name);
538 self.obj()
539 .emit_by_name::<()>("name-changed", &[&*self.name.borrow()]);
540 }
541 "construct-name" => {
542 let name = value
543 .get()
544 .expect("type conformity checked by 'Object::set_property'");
545 self.construct_name.replace(name);
546 }
547 "child" => {
548 }
550 "answer" => {
551 let answer = value
552 .get()
553 .expect("type conformity checked by 'Object::set_property'");
554 self.answer.replace(answer);
555 }
556 "array" => {
557 let value = value
558 .get::<crate::ValueArray>()
559 .expect("type conformity checked by 'Object::set_property'");
560 let mut array = self.array.borrow_mut();
561 array.clear();
562 array.extend(value.iter().map(|v| v.get().unwrap()));
563 }
564 _ => unimplemented!(),
565 }
566 }
567
568 fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> Value {
569 match pspec.name() {
570 "name" => self.name.borrow().to_value(),
571 "construct-name" => self.construct_name.borrow().to_value(),
572 "constructed" => self.constructed.borrow().to_value(),
573 "answer" => self.answer.borrow().to_value(),
574 "array" => crate::ValueArray::new(self.array.borrow().iter()).to_value(),
575 _ => unimplemented!(),
576 }
577 }
578
579 fn constructed(&self) {
580 self.parent_constructed();
581
582 debug_assert_eq!(self as *const _, self.obj().imp() as *const _);
583
584 *self.constructed.borrow_mut() = true;
585 }
586 }
587
588 #[derive(Default)]
589 pub struct SimpleSubObject;
590
591 #[glib::object_subclass]
592 impl ObjectSubclass for SimpleSubObject {
593 const NAME: &'static str = "SimpleSubObject";
594 type Type = super::SimpleSubObject;
595 type ParentType = super::SimpleObject;
596
597 fn class_init(class: &mut Self::Class) {
598 class.override_signal_class_handler("return-string", |token, args| {
599 let obj = args[0]
600 .get::<super::SimpleSubObject>()
601 .expect("Failed to get Object from args[0]");
602
603 let res = obj.imp().signal_chain_from_overridden(token, args);
604 assert_eq!(res.unwrap().get::<&str>().unwrap(), "base");
605
606 Some("sub".to_value())
607 });
608 }
609 }
610
611 impl ObjectImpl for SimpleSubObject {}
612
613 impl SimpleObjectImpl for SimpleSubObject {}
614
615 #[derive(Clone, Copy)]
616 #[repr(C)]
617 pub struct DummyInterface {
618 parent: gobject_ffi::GTypeInterface,
619 }
620
621 unsafe impl InterfaceStruct for DummyInterface {
622 type Type = Dummy;
623 }
624
625 pub enum Dummy {}
626
627 #[glib::object_interface]
628 impl ObjectInterface for Dummy {
629 const NAME: &'static str = "Dummy";
630 type Interface = DummyInterface;
631 }
632 }
633
634 wrapper! {
635 pub struct ChildObject(ObjectSubclass<imp::ChildObject>);
636 }
637
638 wrapper! {
639 pub struct SimpleObject(ObjectSubclass<imp::SimpleObject>);
640 }
641
642 pub trait SimpleObjectImpl: ObjectImpl {}
643
644 unsafe impl<Obj: SimpleObjectImpl> IsSubclassable<Obj> for SimpleObject {}
645
646 wrapper! {
647 pub struct SimpleSubObject(ObjectSubclass<imp::SimpleSubObject>) @extends SimpleObject;
648 }
649
650 wrapper! {
651 pub struct Dummy(ObjectInterface<imp::Dummy>);
652 }
653
654 unsafe impl<T: ObjectSubclass> IsImplementable<T> for Dummy {}
655
656 #[test]
657 fn test_create() {
658 let type_ = SimpleObject::static_type();
659 let obj = Object::with_type(type_);
660
661 assert!(obj.type_().is_a(Dummy::static_type()));
662
663 assert_eq!(
665 mem::size_of::<SimpleObject>(),
666 mem::size_of::<ffi::gpointer>()
667 );
668 assert_eq!(obj.as_ptr() as ffi::gpointer, unsafe {
669 *(&obj as *const _ as *const ffi::gpointer)
670 });
671
672 assert!(obj.property::<bool>("constructed"));
673
674 let weak = obj.downgrade();
675 drop(obj);
676 assert!(weak.upgrade().is_none());
677 }
678
679 #[test]
680 fn test_sub_create() {
681 let obj = Object::builder::<SimpleSubObject>().build();
682 assert!(obj.type_().is_a(SimpleObject::static_type()));
683 assert_eq!(obj.type_(), SimpleSubObject::static_type());
684 assert!(obj.property::<bool>("constructed"));
685 }
686
687 #[test]
688 fn test_properties() {
689 let type_ = SimpleObject::static_type();
690 let obj = Object::with_type(type_);
691
692 assert!(obj.type_().is_a(Dummy::static_type()));
693
694 let properties = obj.list_properties();
695 assert_eq!(properties.len(), 6);
696 assert_eq!(properties[0].name(), "name");
697 assert_eq!(properties[1].name(), "construct-name");
698 assert_eq!(properties[2].name(), "constructed");
699 assert_eq!(properties[3].name(), "child");
700 assert_eq!(properties[4].name(), "answer");
701 assert_eq!(properties[5].name(), "array");
702 }
703
704 #[test]
705 fn test_create_child_object() {
706 let obj: ChildObject = Object::new();
707
708 assert_eq!(&obj, obj.imp().obj().as_ref());
709 }
710
711 #[test]
712 fn test_builder() {
713 let obj = Object::builder::<SimpleObject>()
714 .property("construct-name", "meh")
715 .property("name", "initial")
716 .build();
717
718 assert_eq!(
719 obj.property::<String>("construct-name"),
720 String::from("meh")
721 );
722
723 assert_eq!(obj.property::<String>("name"), String::from("initial"));
724 }
725
726 #[test]
727 fn test_set_property() {
728 let obj = Object::builder::<SimpleObject>()
729 .property("construct-name", "meh")
730 .property("name", "initial")
731 .build();
732
733 assert_eq!(
734 obj.property::<String>("construct-name"),
735 String::from("meh")
736 );
737
738 assert_eq!(
739 obj.property::<String>("construct-name"),
740 String::from("meh")
741 );
742
743 assert_eq!(obj.property::<String>("name"), String::from("initial"));
744 obj.set_property("name", "test");
745 assert_eq!(obj.property::<String>("name"), String::from("test"));
746
747 let child = Object::with_type(ChildObject::static_type());
748 obj.set_property("child", &child);
749 }
750
751 #[test]
752 fn builder_property_if() {
753 use crate::ValueArray;
754
755 let array = ["val0", "val1"];
756 let obj = Object::builder::<SimpleObject>()
757 .property_if("name", "some name", true)
758 .property_if("answer", 21i32, 6 != 9)
759 .property_if("array", ValueArray::new(["val0", "val1"]), array.len() == 2)
760 .build();
761
762 assert_eq!(obj.property::<String>("name").as_str(), "some name");
763 assert_eq!(
764 obj.property::<Option<String>>("name").as_deref(),
765 Some("some name")
766 );
767 assert_eq!(obj.property::<i32>("answer"), 21);
768 assert!(obj
769 .property::<ValueArray>("array")
770 .iter()
771 .map(|val| val.get::<&str>().unwrap())
772 .eq(array));
773
774 let obj = Object::builder::<SimpleObject>()
775 .property_if("name", "some name", false)
776 .property_if("answer", 21i32, 6 == 9)
777 .property_if("array", ValueArray::new(array), array.len() == 4)
778 .build();
779
780 assert!(obj.property::<Option<String>>("name").is_none());
781 assert_eq!(obj.property::<i32>("answer"), 42);
782 assert!(obj
783 .property::<ValueArray>("array")
784 .iter()
785 .map(|val| val.get::<&str>().unwrap())
786 .eq(["default0", "default1"]));
787 }
788
789 #[test]
790 fn builder_property_if_some() {
791 use crate::ValueArray;
792
793 let array = ["val0", "val1"];
794 let obj = Object::builder::<SimpleObject>()
795 .property_if_some("name", Some("some name"))
796 .property_if_some("answer", Some(21i32))
797 .property_if_some("array", Some(ValueArray::new(array)))
798 .build();
799
800 assert_eq!(obj.property::<String>("name").as_str(), "some name");
801 assert_eq!(
802 obj.property::<Option<String>>("name").as_deref(),
803 Some("some name")
804 );
805 assert_eq!(obj.property::<i32>("answer"), 21);
806 assert!(obj
807 .property::<ValueArray>("array")
808 .iter()
809 .map(|val| val.get::<&str>().unwrap())
810 .eq(array));
811
812 let obj = Object::builder::<SimpleObject>()
813 .property_if_some("name", Option::<&str>::None)
814 .property_if_some("answer", Option::<i32>::None)
815 .property_if_some("array", Option::<ValueArray>::None)
816 .build();
817
818 assert!(obj.property::<Option<String>>("name").is_none());
819 assert_eq!(obj.property::<i32>("answer"), 42);
820 assert!(obj
821 .property::<ValueArray>("array")
822 .iter()
823 .map(|val| val.get::<&str>().unwrap())
824 .eq(["default0", "default1"]));
825 }
826
827 #[test]
828 fn builder_property_from_iter() {
829 use crate::ValueArray;
830
831 let array = ["val0", "val1"];
832 let obj = Object::builder::<SimpleObject>()
833 .property_from_iter::<ValueArray>("array", &array)
834 .build();
835
836 assert!(obj
837 .property::<ValueArray>("array")
838 .iter()
839 .map(|val| val.get::<&str>().unwrap())
840 .eq(array));
841
842 let obj = Object::builder::<SimpleObject>()
843 .property_from_iter::<ValueArray>("array", Vec::<&str>::new())
844 .build();
845
846 assert!(obj.property::<ValueArray>("array").is_empty());
847 }
848
849 #[test]
850 fn builder_property_if_not_empty() {
851 use crate::ValueArray;
852
853 let array = ["val0", "val1"];
854 let obj = Object::builder::<SimpleObject>()
855 .property_if_not_empty::<ValueArray>("array", &array)
856 .build();
857
858 assert!(obj
859 .property::<ValueArray>("array")
860 .iter()
861 .map(|val| val.get::<&str>().unwrap())
862 .eq(array));
863
864 let empty_vec = Vec::<String>::new();
865 let obj = Object::builder::<SimpleObject>()
866 .property_if_not_empty::<ValueArray>("array", &empty_vec)
867 .build();
868
869 assert!(obj
870 .property::<ValueArray>("array")
871 .iter()
872 .map(|val| val.get::<&str>().unwrap())
873 .eq(["default0", "default1"]));
874 }
875
876 #[test]
877 #[should_panic = "property 'construct-name' of type 'SimpleObject' is not writable"]
878 fn test_set_property_non_writable() {
879 let obj = Object::builder::<SimpleObject>()
880 .property("construct-name", "meh")
881 .property("name", "initial")
882 .build();
883
884 obj.set_property("construct-name", "test");
885 }
886
887 #[test]
888 #[should_panic = "property 'test' of type 'SimpleObject' not found"]
889 fn test_set_property_not_found() {
890 let obj = Object::builder::<SimpleObject>()
891 .property("construct-name", "meh")
892 .property("name", "initial")
893 .build();
894
895 obj.set_property("test", true);
896 }
897
898 #[test]
899 #[should_panic = "property 'constructed' of type 'SimpleObject' is not writable"]
900 fn test_set_property_not_writable() {
901 let obj = Object::builder::<SimpleObject>()
902 .property("construct-name", "meh")
903 .property("name", "initial")
904 .build();
905
906 obj.set_property("constructed", false);
907 }
908
909 #[test]
910 #[should_panic = "property 'name' of type 'SimpleObject' can't be set from the given type (expected: 'gchararray', got: 'gboolean')"]
911 fn test_set_property_wrong_type() {
912 let obj = Object::builder::<SimpleObject>()
913 .property("construct-name", "meh")
914 .property("name", "initial")
915 .build();
916
917 obj.set_property("name", false);
918 }
919
920 #[test]
921 #[should_panic = "property 'child' of type 'SimpleObject' can't be set from the given type (expected: 'ChildObject', got: 'SimpleObject')"]
922 fn test_set_property_wrong_type_2() {
923 let obj = Object::builder::<SimpleObject>()
924 .property("construct-name", "meh")
925 .property("name", "initial")
926 .build();
927
928 let other_obj = Object::with_type(SimpleObject::static_type());
929
930 obj.set_property("child", &other_obj);
931 }
932
933 #[test]
934 #[should_panic = "Can't set construct property 'construct-name' for type 'SimpleObject' twice"]
935 fn test_construct_property_set_twice() {
936 let _obj = Object::builder::<SimpleObject>()
937 .property("construct-name", "meh")
938 .property("construct-name", "meh2")
939 .build();
940 }
941
942 #[test]
943 fn test_signals() {
944 use std::sync::{
945 atomic::{AtomicBool, Ordering},
946 Arc,
947 };
948
949 let obj = Object::builder::<SimpleObject>()
950 .property("name", "old-name")
951 .build();
952
953 let name_changed_triggered = Arc::new(AtomicBool::new(false));
954 let name_changed_clone = name_changed_triggered.clone();
955 obj.connect("name-changed", false, move |args| {
956 let _obj = args[0].get::<Object>().expect("Failed to get args[0]");
957 let name = args[1].get::<&str>().expect("Failed to get args[1]");
958
959 assert_eq!(name, "new-name");
960 name_changed_clone.store(true, Ordering::Relaxed);
961
962 None
963 });
964
965 assert_eq!(obj.property::<String>("name"), String::from("old-name"));
966 assert!(!name_changed_triggered.load(Ordering::Relaxed));
967
968 assert_eq!(
969 obj.emit_by_name::<String>("change-name", &[&"new-name"]),
970 "old-name"
971 );
972 assert!(name_changed_triggered.load(Ordering::Relaxed));
973
974 assert_eq!(obj.emit_by_name::<String>("return-string", &[]), "base");
975 }
976
977 #[test]
978 fn test_signal_return_expected_type() {
979 let obj = Object::with_type(SimpleObject::static_type());
980
981 obj.connect("create-string", false, move |_args| {
982 Some("return value 1".to_value())
983 });
984
985 obj.connect("create-string", false, move |_args| {
986 Some("return value 2".to_value())
987 });
988
989 let signal_id = imp::SimpleObject::signals()[2].signal_id();
990
991 let value = obj.emit::<String>(signal_id, &[]);
992 assert_eq!(value, "return value 1\nreturn value 2");
993 }
994
995 #[test]
996 fn test_signal_override() {
997 let obj = Object::builder::<SimpleSubObject>().build();
998
999 assert_eq!(obj.emit_by_name::<String>("return-string", &[]), "sub");
1000 }
1001
1002 #[test]
1003 fn test_callback_validity() {
1004 use std::sync::{
1005 atomic::{AtomicBool, Ordering},
1006 Arc,
1007 };
1008
1009 let obj = Object::builder::<SimpleObject>()
1010 .property("name", "old-name")
1011 .build();
1012
1013 let name_changed_triggered = Arc::new(AtomicBool::new(false));
1014 let name_changed_clone = name_changed_triggered.clone();
1015
1016 obj.connect_notify(Some("name"), move |_, _| {
1017 name_changed_clone.store(true, Ordering::Relaxed);
1018 });
1019 obj.notify("name");
1020 assert!(name_changed_triggered.load(Ordering::Relaxed));
1021 }
1022
1023 #[test]
1027 fn test_signal_return_expected_object_type() {
1028 let obj = Object::with_type(SimpleObject::static_type());
1029
1030 obj.connect("create-child-object", false, move |_args| {
1031 Some(Object::with_type(ChildObject::static_type()).to_value())
1032 });
1033 let value: glib::Object = obj.emit_by_name("create-child-object", &[]);
1034 assert!(value.type_().is_a(ChildObject::static_type()));
1035 }
1036}