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