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