glib/subclass/
object.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Module that contains all types needed for creating a direct subclass of `GObject`
5//! or implementing virtual methods of it.
6
7use 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
17// rustdoc-stripper-ignore-next
18/// Trait for implementors of `glib::Object` subclasses.
19///
20/// This allows overriding the virtual methods of `glib::Object`. Except for
21/// `finalize` as implementing `Drop` would allow the same behavior.
22pub trait ObjectImpl: ObjectSubclass<Type: IsA<Object>> {
23    // rustdoc-stripper-ignore-next
24    /// Properties installed for this type.
25    fn properties() -> &'static [ParamSpec] {
26        &[]
27    }
28
29    // rustdoc-stripper-ignore-next
30    /// Signals installed for this type.
31    fn signals() -> &'static [Signal] {
32        &[]
33    }
34
35    // rustdoc-stripper-ignore-next
36    /// Property setter.
37    ///
38    /// This is called whenever the property of this specific subclass with the
39    /// given index is set. The new value is passed as `glib::Value`.
40    ///
41    /// `value` is guaranteed to be of the correct type for the given property.
42    // rustdoc-stripper-ignore-next-stop
43    /// the generic setter for all properties of this type. Should be
44    ///  overridden for every type with properties. If implementations of
45    ///  `set_property` don't emit property change notification explicitly, this will
46    ///  be done implicitly by the type system. However, if the notify signal is
47    ///  emitted explicitly, the type system will not emit it a second time.
48    // rustdoc-stripper-ignore-next-stop
49    /// the generic setter for all properties of this type. Should be
50    ///  overridden for every type with properties. If implementations of
51    ///  `set_property` don't emit property change notification explicitly, this will
52    ///  be done implicitly by the type system. However, if the notify signal is
53    ///  emitted explicitly, the type system will not emit it a second time.
54    fn set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
55        unimplemented!()
56    }
57
58    // rustdoc-stripper-ignore-next
59    /// Property getter.
60    ///
61    /// This is called whenever the property value of the specific subclass with the
62    /// given index should be returned.
63    ///
64    /// The returned `Value` must be of the correct type for the given property.
65    // rustdoc-stripper-ignore-next-stop
66    /// the generic getter for all properties of this type. Should be
67    ///  overridden for every type with properties.
68    // rustdoc-stripper-ignore-next-stop
69    /// the generic getter for all properties of this type. Should be
70    ///  overridden for every type with properties.
71    #[doc(alias = "get_property")]
72    fn property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
73        unimplemented!()
74    }
75
76    // rustdoc-stripper-ignore-next
77    /// Constructed.
78    ///
79    /// This is called once construction of the instance is finished.
80    ///
81    /// Should chain up to the parent class' implementation.
82    // rustdoc-stripper-ignore-next-stop
83    /// the `constructed` function is called by [`Object::new()`][crate::Object::new()] as the
84    ///  final step of the object creation process. At the point of the call, all
85    ///  construction properties have been set on the object. The purpose of this
86    ///  call is to allow for object initialisation steps that can only be performed
87    ///  after construction properties have been set. `constructed` implementors
88    ///  should chain up to the `constructed` call of their parent class to allow it
89    ///  to complete its initialisation.
90    // rustdoc-stripper-ignore-next-stop
91    /// the `constructed` function is called by [`Object::new()`][crate::Object::new()] as the
92    ///  final step of the object creation process. At the point of the call, all
93    ///  construction properties have been set on the object. The purpose of this
94    ///  call is to allow for object initialisation steps that can only be performed
95    ///  after construction properties have been set. `constructed` implementors
96    ///  should chain up to the `constructed` call of their parent class to allow it
97    ///  to complete its initialisation.
98    fn constructed(&self) {
99        self.parent_constructed();
100    }
101
102    // rustdoc-stripper-ignore-next
103    /// Disposes of the object.
104    ///
105    /// When `dispose()` ends, the object should not hold any reference to any other member object.
106    /// The object is also expected to be able to answer client method invocations (with possibly an
107    /// error code but no memory violation) until it is dropped. `dispose()` can be executed more
108    /// than once.
109    // rustdoc-stripper-ignore-next-stop
110    /// the `dispose` function is supposed to drop all references to other
111    ///  objects, but keep the instance otherwise intact, so that client method
112    ///  invocations still work. It may be run multiple times (due to reference
113    ///  loops). Before returning, `dispose` should chain up to the `dispose` method
114    ///  of the parent class.
115    // rustdoc-stripper-ignore-next-stop
116    /// the `dispose` function is supposed to drop all references to other
117    ///  objects, but keep the instance otherwise intact, so that client method
118    ///  invocations still work. It may be run multiple times (due to reference
119    ///  loops). Before returning, `dispose` should chain up to the `dispose` method
120    ///  of the parent class.
121    fn dispose(&self) {}
122
123    // rustdoc-stripper-ignore-next
124    /// Function to be called when property change is notified for with
125    /// `self.notify("property")`.
126    // rustdoc-stripper-ignore-next-stop
127    /// Emits a "notify" signal for the property `property_name` on `self`.
128    ///
129    /// When possible, eg. when signaling a property change from within the class
130    /// that registered the property, you should use [`ObjectExt::notify_by_pspec()`][crate::prelude::ObjectExt::notify_by_pspec()]
131    /// instead.
132    ///
133    /// Note that emission of the notify signal may be blocked with
134    /// [`ObjectExt::freeze_notify()`][crate::prelude::ObjectExt::freeze_notify()]. In this case, the signal emissions are queued
135    /// and will be emitted (in reverse order) when [`ObjectExt::thaw_notify()`][crate::prelude::ObjectExt::thaw_notify()] is
136    /// called.
137    // rustdoc-stripper-ignore-next-stop
138    /// Emits a "notify" signal for the property `property_name` on `self`.
139    ///
140    /// When possible, eg. when signaling a property change from within the class
141    /// that registered the property, you should use [`ObjectExt::notify_by_pspec()`][crate::prelude::ObjectExt::notify_by_pspec()]
142    /// instead.
143    ///
144    /// Note that emission of the notify signal may be blocked with
145    /// [`ObjectExt::freeze_notify()`][crate::prelude::ObjectExt::freeze_notify()]. In this case, the signal emissions are queued
146    /// and will be emitted (in reverse order) when [`ObjectExt::thaw_notify()`][crate::prelude::ObjectExt::thaw_notify()] is
147    /// called.
148    fn notify(&self, pspec: &ParamSpec) {
149        self.parent_notify(pspec)
150    }
151
152    /// emits property change notification for a bunch
153    ///  of properties. Overriding `dispatch_properties_changed` should be rarely
154    ///  needed.
155    // rustdoc-stripper-ignore-next-stop
156    /// emits property change notification for a bunch
157    ///  of properties. Overriding `dispatch_properties_changed` should be rarely
158    ///  needed.
159    fn dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
160        self.parent_dispatch_properties_changed(pspecs)
161    }
162}
163
164#[doc(alias = "get_property")]
165unsafe extern "C" fn property<T: ObjectImpl>(
166    obj: *mut gobject_ffi::GObject,
167    id: u32,
168    value: *mut gobject_ffi::GValue,
169    pspec: *mut gobject_ffi::GParamSpec,
170) {
171    let instance = &*(obj as *mut T::Instance);
172    let imp = instance.imp();
173
174    let v = imp.property(id as usize, &from_glib_borrow(pspec));
175
176    // We first unset the value we get passed in, in case it contained
177    // any previous data. Then we directly overwrite it with our new
178    // value, and pass ownership of the contained data to the C GValue
179    // by forgetting it on the Rust side.
180    //
181    // Without this, by using the GValue API, we would have to create
182    // a copy of the value when setting it on the destination just to
183    // immediately free the original value afterwards.
184    gobject_ffi::g_value_unset(value);
185    let v = mem::ManuallyDrop::new(v);
186    ptr::write(value, ptr::read(v.to_glib_none().0));
187}
188
189unsafe extern "C" fn set_property<T: ObjectImpl>(
190    obj: *mut gobject_ffi::GObject,
191    id: u32,
192    value: *mut gobject_ffi::GValue,
193    pspec: *mut gobject_ffi::GParamSpec,
194) {
195    let instance = &*(obj as *mut T::Instance);
196    let imp = instance.imp();
197    imp.set_property(
198        id as usize,
199        &*(value as *mut Value),
200        &from_glib_borrow(pspec),
201    );
202}
203
204unsafe extern "C" fn constructed<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
205    let instance = &*(obj as *mut T::Instance);
206    let imp = instance.imp();
207
208    imp.constructed();
209}
210
211unsafe extern "C" fn notify<T: ObjectImpl>(
212    obj: *mut gobject_ffi::GObject,
213    pspec: *mut gobject_ffi::GParamSpec,
214) {
215    let instance = &*(obj as *mut T::Instance);
216    let imp = instance.imp();
217    imp.notify(&from_glib_borrow(pspec));
218}
219
220unsafe extern "C" fn dispatch_properties_changed<T: ObjectImpl>(
221    obj: *mut gobject_ffi::GObject,
222    n_pspecs: u32,
223    pspecs: *mut *mut gobject_ffi::GParamSpec,
224) {
225    let instance = &*(obj as *mut T::Instance);
226    let imp = instance.imp();
227    imp.dispatch_properties_changed(Slice::from_glib_borrow_num(pspecs, n_pspecs as _));
228}
229
230unsafe extern "C" fn dispose<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
231    let instance = &*(obj as *mut T::Instance);
232    let imp = instance.imp();
233
234    imp.dispose();
235
236    // Chain up to the parent's dispose.
237    let data = T::type_data();
238    let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
239    if let Some(ref func) = (*parent_class).dispose {
240        func(obj);
241    }
242}
243
244// rustdoc-stripper-ignore-next
245/// Trait containing only the property related functions of [`ObjectImpl`].
246/// Implemented by the [`Properties`](crate::Properties) macro.
247/// When implementing `ObjectImpl` you may want to delegate the function calls to this trait.
248pub trait DerivedObjectProperties: ObjectSubclass {
249    // rustdoc-stripper-ignore-next
250    /// Properties installed for this type.
251    fn derived_properties() -> &'static [ParamSpec] {
252        &[]
253    }
254
255    // rustdoc-stripper-ignore-next
256    /// Similar to [`ObjectImpl`](trait.ObjectImpl.html) but auto-generated by the [`Properties`](crate::Properties) macro
257    /// to allow handling more complex use-cases.
258    fn derived_set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
259        unimplemented!()
260    }
261
262    // rustdoc-stripper-ignore-next
263    /// Similar to [`ObjectImpl`](trait.ObjectImpl.html) but auto-generated by the [`Properties`](crate::Properties) macro
264    /// to allow handling more complex use-cases.
265    fn derived_property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
266        unimplemented!()
267    }
268}
269
270// rustdoc-stripper-ignore-next
271/// Extension trait for `glib::Object`'s class struct.
272///
273/// This contains various class methods and allows subclasses to override signal class handlers.
274pub unsafe trait ObjectClassSubclassExt: Sized + 'static {
275    fn override_signal_class_handler<F>(&mut self, name: &str, class_handler: F)
276    where
277        F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
278    {
279        unsafe {
280            super::types::signal_override_class_handler(
281                name,
282                *(self as *mut _ as *mut ffi::GType),
283                class_handler,
284            );
285        }
286    }
287}
288
289unsafe impl ObjectClassSubclassExt for crate::Class<Object> {}
290
291unsafe impl<T: ObjectImpl> IsSubclassable<T> for Object {
292    fn class_init(class: &mut crate::Class<Self>) {
293        let klass = class.as_mut();
294        klass.set_property = Some(set_property::<T>);
295        klass.get_property = Some(property::<T>);
296        klass.constructed = Some(constructed::<T>);
297        klass.notify = Some(notify::<T>);
298        klass.dispatch_properties_changed = Some(dispatch_properties_changed::<T>);
299        klass.dispose = Some(dispose::<T>);
300
301        let pspecs = <T as ObjectImpl>::properties();
302        if !pspecs.is_empty() {
303            unsafe {
304                let mut pspecs_ptrs = Vec::with_capacity(pspecs.len() + 1);
305
306                pspecs_ptrs.push(ptr::null_mut());
307
308                for pspec in pspecs {
309                    pspecs_ptrs.push(pspec.to_glib_none().0);
310                }
311
312                gobject_ffi::g_object_class_install_properties(
313                    klass,
314                    pspecs_ptrs.len() as u32,
315                    pspecs_ptrs.as_mut_ptr(),
316                );
317            }
318        }
319
320        let type_ = T::type_();
321        let signals = <T as ObjectImpl>::signals();
322        for signal in signals {
323            signal.register(type_);
324        }
325    }
326
327    #[inline]
328    fn instance_init(_instance: &mut super::InitializingObject<T>) {}
329}
330
331pub trait ObjectImplExt: ObjectImpl {
332    // rustdoc-stripper-ignore-next
333    /// Chain up to the parent class' implementation of `glib::Object::constructed()`.
334    #[inline]
335    fn parent_constructed(&self) {
336        unsafe {
337            let data = Self::type_data();
338            let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
339
340            if let Some(ref func) = (*parent_class).constructed {
341                func(self.obj().unsafe_cast_ref::<Object>().to_glib_none().0);
342            }
343        }
344    }
345
346    // rustdoc-stripper-ignore-next
347    /// Chain up to the parent class' implementation of `glib::Object::notify()`.
348    #[inline]
349    fn parent_notify(&self, pspec: &ParamSpec) {
350        unsafe {
351            let data = Self::type_data();
352            let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
353
354            if let Some(ref func) = (*parent_class).notify {
355                func(
356                    self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
357                    pspec.to_glib_none().0,
358                );
359            }
360        }
361    }
362
363    // rustdoc-stripper-ignore-next
364    /// Chain up to the parent class' implementation of `glib::Object::dispatch_properties_changed()`.
365    #[inline]
366    fn parent_dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
367        unsafe {
368            let data = Self::type_data();
369            let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
370
371            if let Some(ref func) = (*parent_class).dispatch_properties_changed {
372                func(
373                    self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
374                    pspecs.len() as _,
375                    pspecs.as_ptr() as *mut _,
376                );
377            }
378        }
379    }
380
381    // rustdoc-stripper-ignore-next
382    /// Chain up to parent class signal handler.
383    fn signal_chain_from_overridden(
384        &self,
385        token: &super::SignalClassHandlerToken,
386        values: &[Value],
387    ) -> Option<Value> {
388        unsafe {
389            super::types::signal_chain_from_overridden(self.obj().as_ptr() as *mut _, token, values)
390        }
391    }
392}
393
394impl<T: ObjectImpl> ObjectImplExt for T {}
395
396#[cfg(test)]
397mod test {
398    use std::cell::RefCell;
399
400    use super::*;
401    // We rename the current crate as glib, since the macros in glib-macros
402    // generate the glib namespace through the crate_ident_new utility,
403    // and that returns `glib` (and not `crate`) when called inside the glib crate
404    use crate as glib;
405
406    mod imp {
407        use std::sync::OnceLock;
408
409        use super::*;
410
411        // A dummy `Object` to test setting an `Object` property and returning an `Object` in signals
412        #[derive(Default)]
413        pub struct ChildObject;
414
415        #[glib::object_subclass]
416        impl ObjectSubclass for ChildObject {
417            const NAME: &'static str = "ChildObject";
418            type Type = super::ChildObject;
419        }
420
421        impl ObjectImpl for ChildObject {}
422
423        pub struct SimpleObject {
424            name: RefCell<Option<String>>,
425            construct_name: RefCell<Option<String>>,
426            constructed: RefCell<bool>,
427            answer: RefCell<i32>,
428            array: RefCell<Vec<String>>,
429        }
430
431        impl Default for SimpleObject {
432            fn default() -> Self {
433                SimpleObject {
434                    name: Default::default(),
435                    construct_name: Default::default(),
436                    constructed: Default::default(),
437                    answer: RefCell::new(42i32),
438                    array: RefCell::new(vec!["default0".to_string(), "default1".to_string()]),
439                }
440            }
441        }
442
443        #[glib::object_subclass]
444        impl ObjectSubclass for SimpleObject {
445            const NAME: &'static str = "SimpleObject";
446            type Type = super::SimpleObject;
447            type Interfaces = (super::Dummy,);
448        }
449
450        impl ObjectImpl for SimpleObject {
451            fn properties() -> &'static [ParamSpec] {
452                static PROPERTIES: OnceLock<Vec<ParamSpec>> = OnceLock::new();
453                PROPERTIES.get_or_init(|| {
454                    vec![
455                        crate::ParamSpecString::builder("name").build(),
456                        crate::ParamSpecString::builder("construct-name")
457                            .construct_only()
458                            .build(),
459                        crate::ParamSpecBoolean::builder("constructed")
460                            .read_only()
461                            .build(),
462                        crate::ParamSpecObject::builder::<super::ChildObject>("child").build(),
463                        crate::ParamSpecInt::builder("answer")
464                            .default_value(42i32)
465                            .build(),
466                        crate::ParamSpecValueArray::builder("array").build(),
467                    ]
468                })
469            }
470
471            fn signals() -> &'static [super::Signal] {
472                static SIGNALS: OnceLock<Vec<super::Signal>> = OnceLock::new();
473                SIGNALS.get_or_init(|| {
474                    vec![
475                        super::Signal::builder("name-changed")
476                            .param_types([String::static_type()])
477                            .build(),
478                        super::Signal::builder("change-name")
479                            .param_types([String::static_type()])
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                                let new_name = args[1]
487                                    .get::<String>()
488                                    .expect("Failed to get Object from args[1]");
489                                let imp = obj.imp();
490
491                                let old_name = imp.name.replace(Some(new_name));
492
493                                obj.emit_by_name::<()>("name-changed", &[&*imp.name.borrow()]);
494
495                                Some(old_name.to_value())
496                            })
497                            .build(),
498                        super::Signal::builder("create-string")
499                            .return_type::<String>()
500                            .accumulator(|_hint, acc, val| {
501                                // join all strings from signal handlers by newline
502                                let mut acc = acc
503                                    .get_owned::<Option<String>>()
504                                    .unwrap()
505                                    .map(|mut acc| {
506                                        acc.push('\n');
507                                        acc
508                                    })
509                                    .unwrap_or_default();
510                                acc.push_str(val.get::<&str>().unwrap());
511                                std::ops::ControlFlow::Continue(acc.to_value())
512                            })
513                            .build(),
514                        super::Signal::builder("create-child-object")
515                            .return_type::<super::ChildObject>()
516                            .build(),
517                        super::Signal::builder("return-string")
518                            .return_type::<String>()
519                            .action()
520                            .class_handler(|args| {
521                                let _obj = args[0]
522                                    .get::<super::SimpleObject>()
523                                    .expect("Failed to get Object from args[0]");
524                                Some("base".to_value())
525                            })
526                            .build(),
527                    ]
528                })
529            }
530
531            fn set_property(&self, _id: usize, value: &Value, pspec: &crate::ParamSpec) {
532                match pspec.name() {
533                    "name" => {
534                        let name = value
535                            .get()
536                            .expect("type conformity checked by 'Object::set_property'");
537                        self.name.replace(name);
538                        self.obj()
539                            .emit_by_name::<()>("name-changed", &[&*self.name.borrow()]);
540                    }
541                    "construct-name" => {
542                        let name = value
543                            .get()
544                            .expect("type conformity checked by 'Object::set_property'");
545                        self.construct_name.replace(name);
546                    }
547                    "child" => {
548                        // not stored, only used to test `set_property` with `Objects`
549                    }
550                    "answer" => {
551                        let answer = value
552                            .get()
553                            .expect("type conformity checked by 'Object::set_property'");
554                        self.answer.replace(answer);
555                    }
556                    "array" => {
557                        let value = value
558                            .get::<crate::ValueArray>()
559                            .expect("type conformity checked by 'Object::set_property'");
560                        let mut array = self.array.borrow_mut();
561                        array.clear();
562                        array.extend(value.iter().map(|v| v.get().unwrap()));
563                    }
564                    _ => unimplemented!(),
565                }
566            }
567
568            fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> Value {
569                match pspec.name() {
570                    "name" => self.name.borrow().to_value(),
571                    "construct-name" => self.construct_name.borrow().to_value(),
572                    "constructed" => self.constructed.borrow().to_value(),
573                    "answer" => self.answer.borrow().to_value(),
574                    "array" => crate::ValueArray::new(self.array.borrow().iter()).to_value(),
575                    _ => unimplemented!(),
576                }
577            }
578
579            fn constructed(&self) {
580                self.parent_constructed();
581
582                debug_assert_eq!(self as *const _, self.obj().imp() as *const _);
583
584                *self.constructed.borrow_mut() = true;
585            }
586        }
587
588        #[derive(Default)]
589        pub struct SimpleSubObject;
590
591        #[glib::object_subclass]
592        impl ObjectSubclass for SimpleSubObject {
593            const NAME: &'static str = "SimpleSubObject";
594            type Type = super::SimpleSubObject;
595            type ParentType = super::SimpleObject;
596
597            fn class_init(class: &mut Self::Class) {
598                class.override_signal_class_handler("return-string", |token, args| {
599                    let obj = args[0]
600                        .get::<super::SimpleSubObject>()
601                        .expect("Failed to get Object from args[0]");
602
603                    let res = obj.imp().signal_chain_from_overridden(token, args);
604                    assert_eq!(res.unwrap().get::<&str>().unwrap(), "base");
605
606                    Some("sub".to_value())
607                });
608            }
609        }
610
611        impl ObjectImpl for SimpleSubObject {}
612
613        impl SimpleObjectImpl for SimpleSubObject {}
614
615        #[derive(Clone, Copy)]
616        #[repr(C)]
617        pub struct DummyInterface {
618            parent: gobject_ffi::GTypeInterface,
619        }
620
621        unsafe impl InterfaceStruct for DummyInterface {
622            type Type = Dummy;
623        }
624
625        pub enum Dummy {}
626
627        #[glib::object_interface]
628        impl ObjectInterface for Dummy {
629            const NAME: &'static str = "Dummy";
630            type Interface = DummyInterface;
631        }
632    }
633
634    wrapper! {
635        pub struct ChildObject(ObjectSubclass<imp::ChildObject>);
636    }
637
638    wrapper! {
639        pub struct SimpleObject(ObjectSubclass<imp::SimpleObject>);
640    }
641
642    pub trait SimpleObjectImpl: ObjectImpl {}
643
644    unsafe impl<Obj: SimpleObjectImpl> IsSubclassable<Obj> for SimpleObject {}
645
646    wrapper! {
647        pub struct SimpleSubObject(ObjectSubclass<imp::SimpleSubObject>) @extends SimpleObject;
648    }
649
650    wrapper! {
651        pub struct Dummy(ObjectInterface<imp::Dummy>);
652    }
653
654    unsafe impl<T: ObjectSubclass> IsImplementable<T> for Dummy {}
655
656    #[test]
657    fn test_create() {
658        let type_ = SimpleObject::static_type();
659        let obj = Object::with_type(type_);
660
661        assert!(obj.type_().is_a(Dummy::static_type()));
662
663        // Assert that the object representation is equivalent to the underlying C GObject pointer
664        assert_eq!(
665            mem::size_of::<SimpleObject>(),
666            mem::size_of::<ffi::gpointer>()
667        );
668        assert_eq!(obj.as_ptr() as ffi::gpointer, unsafe {
669            *(&obj as *const _ as *const ffi::gpointer)
670        });
671
672        assert!(obj.property::<bool>("constructed"));
673
674        let weak = obj.downgrade();
675        drop(obj);
676        assert!(weak.upgrade().is_none());
677    }
678
679    #[test]
680    fn test_sub_create() {
681        let obj = Object::builder::<SimpleSubObject>().build();
682        assert!(obj.type_().is_a(SimpleObject::static_type()));
683        assert_eq!(obj.type_(), SimpleSubObject::static_type());
684        assert!(obj.property::<bool>("constructed"));
685    }
686
687    #[test]
688    fn test_properties() {
689        let type_ = SimpleObject::static_type();
690        let obj = Object::with_type(type_);
691
692        assert!(obj.type_().is_a(Dummy::static_type()));
693
694        let properties = obj.list_properties();
695        assert_eq!(properties.len(), 6);
696        assert_eq!(properties[0].name(), "name");
697        assert_eq!(properties[1].name(), "construct-name");
698        assert_eq!(properties[2].name(), "constructed");
699        assert_eq!(properties[3].name(), "child");
700        assert_eq!(properties[4].name(), "answer");
701        assert_eq!(properties[5].name(), "array");
702    }
703
704    #[test]
705    fn test_create_child_object() {
706        let obj: ChildObject = Object::new();
707
708        assert_eq!(&obj, obj.imp().obj().as_ref());
709    }
710
711    #[test]
712    fn test_builder() {
713        let obj = Object::builder::<SimpleObject>()
714            .property("construct-name", "meh")
715            .property("name", "initial")
716            .build();
717
718        assert_eq!(
719            obj.property::<String>("construct-name"),
720            String::from("meh")
721        );
722
723        assert_eq!(obj.property::<String>("name"), String::from("initial"));
724    }
725
726    #[test]
727    fn test_set_property() {
728        let obj = Object::builder::<SimpleObject>()
729            .property("construct-name", "meh")
730            .property("name", "initial")
731            .build();
732
733        assert_eq!(
734            obj.property::<String>("construct-name"),
735            String::from("meh")
736        );
737
738        assert_eq!(
739            obj.property::<String>("construct-name"),
740            String::from("meh")
741        );
742
743        assert_eq!(obj.property::<String>("name"), String::from("initial"));
744        obj.set_property("name", "test");
745        assert_eq!(obj.property::<String>("name"), String::from("test"));
746
747        let child = Object::with_type(ChildObject::static_type());
748        obj.set_property("child", &child);
749    }
750
751    #[test]
752    fn builder_property_if() {
753        use crate::ValueArray;
754
755        let array = ["val0", "val1"];
756        let obj = Object::builder::<SimpleObject>()
757            .property_if("name", "some name", true)
758            .property_if("answer", 21i32, 6 != 9)
759            .property_if("array", ValueArray::new(["val0", "val1"]), array.len() == 2)
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("name", "some name", false)
776            .property_if("answer", 21i32, 6 == 9)
777            .property_if("array", ValueArray::new(array), array.len() == 4)
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_if_some() {
791        use crate::ValueArray;
792
793        let array = ["val0", "val1"];
794        let obj = Object::builder::<SimpleObject>()
795            .property_if_some("name", Some("some name"))
796            .property_if_some("answer", Some(21i32))
797            .property_if_some("array", Some(ValueArray::new(array)))
798            .build();
799
800        assert_eq!(obj.property::<String>("name").as_str(), "some name");
801        assert_eq!(
802            obj.property::<Option<String>>("name").as_deref(),
803            Some("some name")
804        );
805        assert_eq!(obj.property::<i32>("answer"), 21);
806        assert!(obj
807            .property::<ValueArray>("array")
808            .iter()
809            .map(|val| val.get::<&str>().unwrap())
810            .eq(array));
811
812        let obj = Object::builder::<SimpleObject>()
813            .property_if_some("name", Option::<&str>::None)
814            .property_if_some("answer", Option::<i32>::None)
815            .property_if_some("array", Option::<ValueArray>::None)
816            .build();
817
818        assert!(obj.property::<Option<String>>("name").is_none());
819        assert_eq!(obj.property::<i32>("answer"), 42);
820        assert!(obj
821            .property::<ValueArray>("array")
822            .iter()
823            .map(|val| val.get::<&str>().unwrap())
824            .eq(["default0", "default1"]));
825    }
826
827    #[test]
828    fn builder_property_from_iter() {
829        use crate::ValueArray;
830
831        let array = ["val0", "val1"];
832        let obj = Object::builder::<SimpleObject>()
833            .property_from_iter::<ValueArray>("array", &array)
834            .build();
835
836        assert!(obj
837            .property::<ValueArray>("array")
838            .iter()
839            .map(|val| val.get::<&str>().unwrap())
840            .eq(array));
841
842        let obj = Object::builder::<SimpleObject>()
843            .property_from_iter::<ValueArray>("array", Vec::<&str>::new())
844            .build();
845
846        assert!(obj.property::<ValueArray>("array").is_empty());
847    }
848
849    #[test]
850    fn builder_property_if_not_empty() {
851        use crate::ValueArray;
852
853        let array = ["val0", "val1"];
854        let obj = Object::builder::<SimpleObject>()
855            .property_if_not_empty::<ValueArray>("array", &array)
856            .build();
857
858        assert!(obj
859            .property::<ValueArray>("array")
860            .iter()
861            .map(|val| val.get::<&str>().unwrap())
862            .eq(array));
863
864        let empty_vec = Vec::<String>::new();
865        let obj = Object::builder::<SimpleObject>()
866            .property_if_not_empty::<ValueArray>("array", &empty_vec)
867            .build();
868
869        assert!(obj
870            .property::<ValueArray>("array")
871            .iter()
872            .map(|val| val.get::<&str>().unwrap())
873            .eq(["default0", "default1"]));
874    }
875
876    #[test]
877    #[should_panic = "property 'construct-name' of type 'SimpleObject' is not writable"]
878    fn test_set_property_non_writable() {
879        let obj = Object::builder::<SimpleObject>()
880            .property("construct-name", "meh")
881            .property("name", "initial")
882            .build();
883
884        obj.set_property("construct-name", "test");
885    }
886
887    #[test]
888    #[should_panic = "property 'test' of type 'SimpleObject' not found"]
889    fn test_set_property_not_found() {
890        let obj = Object::builder::<SimpleObject>()
891            .property("construct-name", "meh")
892            .property("name", "initial")
893            .build();
894
895        obj.set_property("test", true);
896    }
897
898    #[test]
899    #[should_panic = "property 'constructed' of type 'SimpleObject' is not writable"]
900    fn test_set_property_not_writable() {
901        let obj = Object::builder::<SimpleObject>()
902            .property("construct-name", "meh")
903            .property("name", "initial")
904            .build();
905
906        obj.set_property("constructed", false);
907    }
908
909    #[test]
910    #[should_panic = "property 'name' of type 'SimpleObject' can't be set from the given type (expected: 'gchararray', got: 'gboolean')"]
911    fn test_set_property_wrong_type() {
912        let obj = Object::builder::<SimpleObject>()
913            .property("construct-name", "meh")
914            .property("name", "initial")
915            .build();
916
917        obj.set_property("name", false);
918    }
919
920    #[test]
921    #[should_panic = "property 'child' of type 'SimpleObject' can't be set from the given type (expected: 'ChildObject', got: 'SimpleObject')"]
922    fn test_set_property_wrong_type_2() {
923        let obj = Object::builder::<SimpleObject>()
924            .property("construct-name", "meh")
925            .property("name", "initial")
926            .build();
927
928        let other_obj = Object::with_type(SimpleObject::static_type());
929
930        obj.set_property("child", &other_obj);
931    }
932
933    #[test]
934    #[should_panic = "Can't set construct property 'construct-name' for type 'SimpleObject' twice"]
935    fn test_construct_property_set_twice() {
936        let _obj = Object::builder::<SimpleObject>()
937            .property("construct-name", "meh")
938            .property("construct-name", "meh2")
939            .build();
940    }
941
942    #[test]
943    fn test_signals() {
944        use std::sync::{
945            atomic::{AtomicBool, Ordering},
946            Arc,
947        };
948
949        let obj = Object::builder::<SimpleObject>()
950            .property("name", "old-name")
951            .build();
952
953        let name_changed_triggered = Arc::new(AtomicBool::new(false));
954        let name_changed_clone = name_changed_triggered.clone();
955        obj.connect("name-changed", false, move |args| {
956            let _obj = args[0].get::<Object>().expect("Failed to get args[0]");
957            let name = args[1].get::<&str>().expect("Failed to get args[1]");
958
959            assert_eq!(name, "new-name");
960            name_changed_clone.store(true, Ordering::Relaxed);
961
962            None
963        });
964
965        assert_eq!(obj.property::<String>("name"), String::from("old-name"));
966        assert!(!name_changed_triggered.load(Ordering::Relaxed));
967
968        assert_eq!(
969            obj.emit_by_name::<String>("change-name", &[&"new-name"]),
970            "old-name"
971        );
972        assert!(name_changed_triggered.load(Ordering::Relaxed));
973
974        assert_eq!(obj.emit_by_name::<String>("return-string", &[]), "base");
975    }
976
977    #[test]
978    fn test_signal_return_expected_type() {
979        let obj = Object::with_type(SimpleObject::static_type());
980
981        obj.connect("create-string", false, move |_args| {
982            Some("return value 1".to_value())
983        });
984
985        obj.connect("create-string", false, move |_args| {
986            Some("return value 2".to_value())
987        });
988
989        let signal_id = imp::SimpleObject::signals()[2].signal_id();
990
991        let value = obj.emit::<String>(signal_id, &[]);
992        assert_eq!(value, "return value 1\nreturn value 2");
993    }
994
995    #[test]
996    fn test_signal_override() {
997        let obj = Object::builder::<SimpleSubObject>().build();
998
999        assert_eq!(obj.emit_by_name::<String>("return-string", &[]), "sub");
1000    }
1001
1002    #[test]
1003    fn test_callback_validity() {
1004        use std::sync::{
1005            atomic::{AtomicBool, Ordering},
1006            Arc,
1007        };
1008
1009        let obj = Object::builder::<SimpleObject>()
1010            .property("name", "old-name")
1011            .build();
1012
1013        let name_changed_triggered = Arc::new(AtomicBool::new(false));
1014        let name_changed_clone = name_changed_triggered.clone();
1015
1016        obj.connect_notify(Some("name"), move |_, _| {
1017            name_changed_clone.store(true, Ordering::Relaxed);
1018        });
1019        obj.notify("name");
1020        assert!(name_changed_triggered.load(Ordering::Relaxed));
1021    }
1022
1023    // Note: can't test type mismatch in signals since panics across FFI boundaries
1024    // are UB. See https://github.com/gtk-rs/glib/issues/518
1025
1026    #[test]
1027    fn test_signal_return_expected_object_type() {
1028        let obj = Object::with_type(SimpleObject::static_type());
1029
1030        obj.connect("create-child-object", false, move |_args| {
1031            Some(Object::with_type(ChildObject::static_type()).to_value())
1032        });
1033        let value: glib::Object = obj.emit_by_name("create-child-object", &[]);
1034        assert!(value.type_().is_a(ChildObject::static_type()));
1035    }
1036}