Skip to main content

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