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