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