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