|
| 1 | +use glib::prelude::*; |
| 2 | +use glib::subclass::prelude::*; |
| 3 | + |
| 4 | +/// The ffi module includes the exported C API of the object. |
| 5 | +#[allow(dead_code)] |
| 6 | +pub mod ffi { |
| 7 | + /// The instance pointer is used for references to the object. |
| 8 | + #[repr(C)] |
| 9 | + pub struct Instance(std::ffi::c_void); |
| 10 | + |
| 11 | + /// Custom class struct for the [`Pet`](super::Pet) class. |
| 12 | + /// |
| 13 | + /// The class struct is used to implement the vtable for method dispatch. The first field |
| 14 | + /// *must* be a pointer to the parent type. In our case, this is |
| 15 | + /// [`GObjectClass`](glib::gobject_ffi::GObjectClass) |
| 16 | + #[derive(Copy, Clone, Debug)] |
| 17 | + #[repr(C)] |
| 18 | + pub struct Class { |
| 19 | + /// The first field in a class struct must always be the parent class struct |
| 20 | + parent_class: glib::gobject_ffi::GObjectClass, |
| 21 | + |
| 22 | + /// Virtual method for the [`PetImpl::pet`](super::PetImpl::pet) trait method |
| 23 | + pub(super) pet: fn(&super::Pet) -> bool, |
| 24 | + |
| 25 | + /// Virtual method for the [`PetImpl::feed`](super::PetImpl::feed) trait method |
| 26 | + pub(super) feed: fn(&super::Pet), |
| 27 | + } |
| 28 | + |
| 29 | + /// Every class struct is required to implement the `ClassStruct` trait |
| 30 | + /// |
| 31 | + /// Safety: This impl is unsafe because it requires the struct to be `repr(C)` and |
| 32 | + /// the first field must be the parent class. |
| 33 | + unsafe impl glib::subclass::types::ClassStruct for Class { |
| 34 | + type Type = super::imp::Pet; |
| 35 | + } |
| 36 | + |
| 37 | + /// Implement Deref to the parent class for convenience. |
| 38 | + impl std::ops::Deref for Class { |
| 39 | + type Target = glib::gobject_ffi::GObjectClass; |
| 40 | + |
| 41 | + fn deref(&self) -> &Self::Target { |
| 42 | + &self.parent_class |
| 43 | + } |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +/// Private (implementation) module of the class. This is not exported. |
| 48 | +mod imp { |
| 49 | + use glib::subclass::prelude::*; |
| 50 | + |
| 51 | + /// The Pet implementation struct |
| 52 | + #[derive(Default)] |
| 53 | + pub struct Pet {} |
| 54 | + |
| 55 | + #[glib::object_subclass] |
| 56 | + impl ObjectSubclass for Pet { |
| 57 | + /// This name is exported to the gobject type system and must be unique between all loaded |
| 58 | + /// shared libraries. |
| 59 | + /// |
| 60 | + /// Usually this is achieved by adding a short prefix to all names coming from a |
| 61 | + /// particular app / library. |
| 62 | + const NAME: &'static str = "Pet"; |
| 63 | + |
| 64 | + /// The [`Pet`](super::Pet) class is abstract and instances to it can't be created. |
| 65 | + /// |
| 66 | + /// If an instance of this class were instantiated it would panic. |
| 67 | + const ABSTRACT: bool = true; |
| 68 | + |
| 69 | + type Type = super::Pet; |
| 70 | + |
| 71 | + /// Override the class struct with the custom [class](super::ffi::Class) |
| 72 | + type Class = super::ffi::Class; |
| 73 | + |
| 74 | + /// Initialize the [class struct](super::ffi::Class) with the default implementations of the |
| 75 | + /// virtual methods. |
| 76 | + fn class_init(klass: &mut Self::Class) { |
| 77 | + klass.pet = |obj| obj.imp().pet_default(); |
| 78 | + klass.feed = |obj| obj.imp().feed_default(); |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + impl ObjectImpl for Pet {} |
| 83 | + |
| 84 | + impl Pet { |
| 85 | + /// Default implementation of [`PetImpl::pet`](super::PetImpl::pet) |
| 86 | + fn pet_default(&self) -> bool { |
| 87 | + // The default behaviour is unsuccessful pets |
| 88 | + println!("Pet::pet_default"); |
| 89 | + false |
| 90 | + } |
| 91 | + |
| 92 | + /// Default implementation of [`PetImpl::feed`](super::PetImpl::feed) |
| 93 | + fn feed_default(&self) { |
| 94 | + println!("Pet::feed_default"); |
| 95 | + } |
| 96 | + } |
| 97 | +} |
| 98 | + |
| 99 | +glib::wrapper! { |
| 100 | + /// The `Pet` class acts as a base class for pets and provides two virtual methods. |
| 101 | + pub struct Pet(ObjectSubclass<imp::Pet>); |
| 102 | +} |
| 103 | + |
| 104 | +/// The `PetExt` trait contains public methods of all [`Pet`] objects |
| 105 | +/// |
| 106 | +/// These methods need to call the appropriate vfunc from the vtable. |
| 107 | +pub trait PetExt: IsA<Pet> { |
| 108 | + /// Calls the [`PetImpl::pet`] vfunc |
| 109 | + fn pet(&self) -> bool { |
| 110 | + let this = self.upcast_ref(); |
| 111 | + let class = this.class(); |
| 112 | + |
| 113 | + (class.as_ref().pet)(this) |
| 114 | + } |
| 115 | + |
| 116 | + /// Calls the [`PetImpl::feed`] vfunc |
| 117 | + fn feed(&self) { |
| 118 | + let this = self.upcast_ref(); |
| 119 | + let class = this.class(); |
| 120 | + |
| 121 | + (class.as_ref().feed)(this) |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +/// Implement PetExt for all [`Pet`] subclasses (and `Pet` itself) |
| 126 | +impl<T: IsA<Pet>> PetExt for T {} |
| 127 | + |
| 128 | +/// The `PetImpl` trait contains overridable virtual function definitions for [`Pet`] objects. |
| 129 | +pub trait PetImpl: ObjectImpl { |
| 130 | + /// Default implementation of a virtual method. |
| 131 | + /// |
| 132 | + /// This always calls into the implementation of the parent class so that if |
| 133 | + /// the subclass does not explicitly implement it, the behaviour of its |
| 134 | + /// parent class will be preserved. |
| 135 | + fn pet(&self) -> bool { |
| 136 | + self.parent_pet() |
| 137 | + } |
| 138 | + |
| 139 | + /// Default implementation of a virtual method. |
| 140 | + /// |
| 141 | + /// This always calls into the implementation of the parent class so that if |
| 142 | + /// the subclass does not explicitly implement it, the behaviour of its |
| 143 | + /// parent class will be preserved. |
| 144 | + fn feed(&self) { |
| 145 | + self.parent_feed(); |
| 146 | + } |
| 147 | +} |
| 148 | + |
| 149 | +/// The `PetImplExt` trait contains non-overridable methods for subclasses to use. |
| 150 | +/// |
| 151 | +/// These are supposed to be called only from inside implementations of `Pet` subclasses. |
| 152 | +pub trait PetImplExt: PetImpl { |
| 153 | + /// Chains up to the parent implementation of [`PetImpl::pet`] |
| 154 | + fn parent_pet(&self) -> bool { |
| 155 | + let data = Self::type_data(); |
| 156 | + let parent_class = unsafe { &*(data.as_ref().parent_class() as *const ffi::Class) }; |
| 157 | + let pet = parent_class.pet; |
| 158 | + |
| 159 | + pet(unsafe { self.obj().unsafe_cast_ref() }) |
| 160 | + } |
| 161 | + |
| 162 | + /// Chains up to the parent implementation of [`PetImpl::feed`] |
| 163 | + fn parent_feed(&self) { |
| 164 | + let data = Self::type_data(); |
| 165 | + let parent_class = unsafe { &*(data.as_ref().parent_class() as *const ffi::Class) }; |
| 166 | + let feed = parent_class.feed; |
| 167 | + |
| 168 | + feed(unsafe { self.obj().unsafe_cast_ref() }); |
| 169 | + } |
| 170 | +} |
| 171 | + |
| 172 | +/// The `PetImplExt` trait is implemented for all subclasses that have [`Pet`] in the class hierarchy |
| 173 | +impl<T: PetImpl> PetImplExt for T {} |
| 174 | + |
| 175 | +/// To make this class subclassable we need to implement IsSubclassable |
| 176 | +unsafe impl<Obj: PetImpl> IsSubclassable<Obj> for Pet { |
| 177 | + /// Override the virtual method function pointers in subclasses to call directly into the |
| 178 | + /// `PetImpl` of the subclass. |
| 179 | + /// |
| 180 | + /// Note that this is only called for actual subclasses and not `Pet` itself: `Pet` does |
| 181 | + /// not implement `PetImpl` and handles this inside `ObjectSubclass::class_init()` for |
| 182 | + /// providing the default implementation of the virtual methods. |
| 183 | + fn class_init(class: &mut glib::Class<Self>) { |
| 184 | + Self::parent_class_init::<Obj>(class); |
| 185 | + |
| 186 | + let klass = class.as_mut(); |
| 187 | + |
| 188 | + klass.pet = |obj| { |
| 189 | + let this = unsafe { obj.unsafe_cast_ref::<<Obj as ObjectSubclass>::Type>().imp() }; |
| 190 | + PetImpl::pet(this) |
| 191 | + }; |
| 192 | + |
| 193 | + klass.feed = |obj| { |
| 194 | + let this = unsafe { obj.unsafe_cast_ref::<<Obj as ObjectSubclass>::Type>().imp() }; |
| 195 | + PetImpl::feed(this); |
| 196 | + }; |
| 197 | + } |
| 198 | +} |
0 commit comments