Skip to content
This repository has been archived by the owner on Jun 8, 2021. It is now read-only.

Migrate subclassing infrastructure to glib-rs #392

Merged
merged 26 commits into from
Nov 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9ffa8f2
Add an associated type for the Rust class type to Wrapper
sdroege Nov 18, 2018
667dc55
Merge gobject-subclass infrastructure rewrite into glib-rs
sdroege Nov 18, 2018
15cabbb
Remove unneeded IsAClass trait
sdroege Nov 18, 2018
da2ad12
Implement Object class methods on ObjectClass
sdroege Nov 18, 2018
3571551
Add methods for up/downcasting between different class struct types
sdroege Nov 18, 2018
a36380f
Rename subclassing macros to include a glib_ prefix
sdroege Nov 18, 2018
6eb97be
Hide subclassing infrastructure behind the "subclassing" feature
sdroege Nov 18, 2018
d035662
Move ObjectSubclass type parameter into IsSubclassable trait
sdroege Nov 18, 2018
9597e69
Don't use floating reference guard for finalize
sdroege Nov 18, 2018
52467d9
Improve subclassing unit test to ensure the object is not leaked
sdroege Nov 18, 2018
336af86
Rename TypeData::get_interfaces() to ::get_interface_data() and re-pu…
sdroege Nov 18, 2018
dc7befe
Re-export some types from the subclass modules
sdroege Nov 18, 2018
78b8247
Include subclass type in the InitializingType wrapper
sdroege Nov 18, 2018
52b6118
Don't provide the object to ObjectSubclass::new() anymore
sdroege Nov 19, 2018
5abddc4
Automatically override vfuncs and don't require subclasses to do so
sdroege Nov 19, 2018
059a28c
ParamSpec constructors can't return NULL
sdroege Nov 19, 2018
26c31ba
Implement a more extensible scheme for object properties
sdroege Nov 19, 2018
4080b53
Add a code example to the documentation of the subclass module
sdroege Nov 19, 2018
3eee4c7
Use usize for the property index
sdroege Nov 19, 2018
153ff73
Extend Object subclassing unit test to check if the constructed virtu…
sdroege Nov 19, 2018
059f77e
Also include subclassing module if the dox feature is enabled
sdroege Nov 19, 2018
fee41c7
Improve subclassing docs
sdroege Nov 19, 2018
b775960
Update license/copyright headers
sdroege Nov 19, 2018
9698750
Remove unneeded clones in example/docs
sdroege Nov 19, 2018
eb2a554
Add a link to the subclass module in the main library documentation
sdroege Nov 19, 2018
20aa0f0
Add a note about subclassing to the README.md
sdroege Nov 19, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ v2_52 = ["v2_50", "glib-sys/v2_52"]
v2_54 = ["v2_52", "glib-sys/v2_54", "gobject-sys/v2_54"]
v2_56 = ["v2_54", "glib-sys/v2_56"]
futures = ["futures-preview", "v2_36"]
subclassing = []
dox = ["glib-sys/dox", "gobject-sys/dox"]
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

GLib bindings for Rust.

Alongside GLib bindings, this crate also includes the infrastructure to
subclass `GObject` for extending libraries like GTK+. A code example can
be found in the documentation of this crate in the `subclass` module.

- [Gtk-rs project site](http://gtk-rs.org/)

- [Online documentation](http://gtk-rs.org/docs/)
Expand Down
10 changes: 10 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
//! `ObjectExt` and `gtk::WidgetExt`), which are blanketly implemented for all
//! their subtypes.
//!
//! For creating new subclasses of `Object` or other object types this crate has to be compiled
//! with the `subclassing` feature to enable the [`subclass`](subclass/index.html) module. Check
//! the module's documentation for further details and a code example.
//!
//! # Under the hood
//!
//! GLib-based libraries largely operate on pointers to various boxed or
Expand Down Expand Up @@ -93,8 +97,10 @@ pub use file_error::FileError;
pub use object::{
Cast,
IsA,
IsClassFor,
Object,
ObjectExt,
ObjectClass,
WeakRef,
SendWeakRef,
};
Expand Down Expand Up @@ -220,3 +226,7 @@ pub(crate) fn get_thread_id() -> usize {
thread_local!(static THREAD_ID: usize = next_thread_id());
THREAD_ID.with(|&x| x)
}

#[macro_use]
#[cfg(any(feature = "dox", feature="subclassing"))]
pub mod subclass;
sdroege marked this conversation as resolved.
Show resolved Hide resolved
190 changes: 148 additions & 42 deletions src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,74 @@ unsafe impl<T> IsA<T> for T
where T: StaticType + Wrapper + Into<ObjectRef> + UnsafeFrom<ObjectRef> +
for<'a> ToGlibPtr<'a, *mut <T as Wrapper>::GlibType> { }

/// Trait for mapping a class struct type to its corresponding instance type.
pub unsafe trait IsClassFor: Sized + 'static {
/// Corresponding Rust instance type for this class.
type Instance;

/// Get the type id for this class.
fn get_type(&self) -> Type {
unsafe {
let klass = self as *const _ as *const gobject_ffi::GTypeClass;
from_glib((*klass).g_type)
}
}

/// Casts this class to a reference to a parent type's class.
fn upcast_ref<U: IsClassFor>(&self) -> &U
where Self::Instance: IsA<U::Instance>,
U::Instance: Wrapper + StaticType + UnsafeFrom<ObjectRef>
{
unsafe {
let klass = self as *const _ as *const U;
&*klass
}
}

/// Casts this class to a mutable reference to a parent type's class.
fn upcast_ref_mut<U: IsClassFor>(&mut self) -> &mut U
where Self::Instance: IsA<U::Instance>,
U::Instance: Wrapper + StaticType + UnsafeFrom<ObjectRef>
{
unsafe {
let klass = self as *mut _ as *mut U;
&mut *klass
}
}

/// Casts this class to a reference to a child type's class or
/// fails if this class is not implementing the child class.
fn downcast_ref<U: IsClassFor>(&self) -> Option<&U>
where U::Instance: IsA<Self::Instance>,
Self::Instance: Wrapper + StaticType + UnsafeFrom<ObjectRef>
{
if !self.get_type().is_a(&U::Instance::static_type()) {
return None;
}

unsafe {
let klass = self as *const _ as *const U;
Some(&*klass)
}
}

/// Casts this class to a mutable reference to a child type's class or
/// fails if this class is not implementing the child class.
fn downcast_ref_mut<U: IsClassFor>(&mut self) -> Option<&mut U>
where U::Instance: IsA<Self::Instance>,
Self::Instance: Wrapper + StaticType + UnsafeFrom<ObjectRef>
{
if !self.get_type().is_a(&U::Instance::static_type()) {
return None;
}

unsafe {
let klass = self as *mut _ as *mut U;
Some(&mut *klass)
}
}
}

/// Downcasts support.
pub trait Downcast<T> {
/// Checks if it's possible to downcast to `T`.
Expand Down Expand Up @@ -307,7 +375,7 @@ glib_wrapper! {
/// Wrapper implementations for Object types. See `glib_wrapper!`.
#[macro_export]
macro_rules! glib_object_wrapper {
([$($attr:meta)*] $name:ident, $ffi_name:path, $ffi_class_name:path, @get_type $get_type_expr:expr) => {
([$($attr:meta)*] $name:ident, $ffi_name:path, $ffi_class_name:path, $rust_class_name:path, @get_type $get_type_expr:expr) => {
$(#[$attr])*
// Always derive Hash/Ord (and below impl Debug, PartialEq, Eq, PartialOrd) for object
// types. Due to inheritance and up/downcasting we must implement these by pointer or
Expand Down Expand Up @@ -339,6 +407,7 @@ macro_rules! glib_object_wrapper {
impl $crate::wrapper::Wrapper for $name {
type GlibType = $ffi_name;
type GlibClassType = $ffi_class_name;
type RustClassType = $rust_class_name;
}

#[doc(hidden)]
Expand Down Expand Up @@ -690,9 +759,10 @@ macro_rules! glib_object_wrapper {
glib_object_wrapper!(@munch_impls $name, $($implements)*);
};

([$($attr:meta)*] $name:ident, $ffi_name:path, $ffi_class_name:path, @get_type $get_type_expr:expr,
@implements $($implements:tt)*) => {
glib_object_wrapper!([$($attr)*] $name, $ffi_name, $ffi_class_name, @get_type $get_type_expr);
([$($attr:meta)*] $name:ident, $ffi_name:path, $ffi_class_name:path, $rust_class_name:path,
@get_type $get_type_expr:expr, @implements $($implements:tt)*) => {
glib_object_wrapper!([$($attr)*] $name, $ffi_name, $ffi_class_name, $rust_class_name,
@get_type $get_type_expr);
glib_object_wrapper!(@munch_impls $name, $($implements)*);

#[doc(hidden)]
Expand All @@ -716,16 +786,16 @@ macro_rules! glib_object_wrapper {
unsafe impl $crate::object::IsA<$crate::object::Object> for $name { }
};

([$($attr:meta)*] $name:ident, $ffi_name:path, $ffi_class_name:path, @get_type $get_type_expr:expr,
([$($attr:meta)*] $name:ident, $ffi_name:path, $ffi_class_name:path, $rust_class_name:path, @get_type $get_type_expr:expr,
[$($implements:path),*]) => {
glib_object_wrapper!([$($attr)*] $name, $ffi_name, $ffi_class_name, @get_type $get_type_expr,
@implements $($implements),*);
}
glib_object_wrapper!([$($attr)*] $name, $ffi_name, $ffi_class_name, $rust_class_name,
@get_type $get_type_expr, @implements $($implements),*);
};
}

glib_object_wrapper! {
[doc = "The base class in the object hierarchy."]
Object, GObject, GObjectClass, @get_type gobject_ffi::g_object_get_type()
Object, GObject, GObjectClass, ObjectClass, @get_type gobject_ffi::g_object_get_type()
}

impl Object {
Expand Down Expand Up @@ -764,6 +834,7 @@ impl Object {

pub trait ObjectExt: IsA<Object> {
fn get_type(&self) -> Type;
fn get_object_class(&self) -> &ObjectClass;

fn set_property<'a, N: Into<&'a str>>(&self, property_name: N, value: &ToValue) -> Result<(), BoolError>;
fn get_property<'a, N: Into<&'a str>>(&self, property_name: N) -> Result<Value, BoolError>;
Expand Down Expand Up @@ -794,10 +865,14 @@ pub trait ObjectExt: IsA<Object> {

impl<T: IsA<Object> + SetValue> ObjectExt for T {
fn get_type(&self) -> Type {
self.get_object_class().get_type()
}

fn get_object_class(&self) -> &ObjectClass {
unsafe {
let obj = self.to_glib_none().0;
let klass = (*obj).g_type_instance.g_class as *mut gobject_ffi::GTypeClass;
from_glib((*klass).g_type)
let klass = (*obj).g_type_instance.g_class as *const ObjectClass;
&*klass
}
}

Expand Down Expand Up @@ -932,46 +1007,19 @@ impl<T: IsA<Object> + SetValue> ObjectExt for T {
}

fn has_property<'a, N: Into<&'a str>>(&self, property_name: N, type_: Option<Type>) -> Result<(), BoolError> {
let property_name = property_name.into();
let ptype = self.get_property_type(property_name);

match (ptype, type_) {
(None, _) => Err(BoolError("Invalid property name")),
(Some(_), None) => Ok(()),
(Some(ptype), Some(type_)) => {
if ptype == type_ {
Ok(())
} else {
Err(BoolError("Invalid property type"))
}
},
}
self.get_object_class().has_property(property_name, type_)
}

fn get_property_type<'a, N: Into<&'a str>>(&self, property_name: N) -> Option<Type> {
self.find_property(property_name).map(|pspec| pspec.get_value_type())
self.get_object_class().get_property_type(property_name)
}

fn find_property<'a, N: Into<&'a str>>(&self, property_name: N) -> Option<::ParamSpec> {
let property_name = property_name.into();
unsafe {
let obj = self.to_glib_none().0;
let klass = (*obj).g_type_instance.g_class as *mut gobject_ffi::GObjectClass;

from_glib_none(gobject_ffi::g_object_class_find_property(klass, property_name.to_glib_none().0))
}
self.get_object_class().find_property(property_name)
}

fn list_properties(&self) -> Vec<::ParamSpec> {
unsafe {
let obj = self.to_glib_none().0;
let klass = (*obj).g_type_instance.g_class as *mut gobject_ffi::GObjectClass;

let mut n_properties = 0;

let props = gobject_ffi::g_object_class_list_properties(klass, &mut n_properties);
FromGlibContainer::from_glib_none_num(props, n_properties as usize)
}
self.get_object_class().list_properties()
}

fn connect<'a, N, F>(&self, signal_name: N, after: bool, callback: F) -> Result<SignalHandlerId, BoolError>
Expand Down Expand Up @@ -1124,6 +1172,64 @@ impl<T: IsA<Object> + SetValue> ObjectExt for T {
}
}

/// Class struct for `glib::Object`.
///
/// All actual functionality is provided via the [`ObjectClassExt`] trait.
///
/// [`ObjectClassExt`]: trait.ObjectClassExt.html
#[repr(C)]
pub struct ObjectClass(gobject_ffi::GObjectClass);

impl ObjectClass {
pub fn has_property<'a, N: Into<&'a str>>(&self, property_name: N, type_: Option<Type>) -> Result<(), BoolError> {
let property_name = property_name.into();
let ptype = self.get_property_type(property_name);

match (ptype, type_) {
(None, _) => Err(BoolError("Invalid property name")),
(Some(_), None) => Ok(()),
(Some(ptype), Some(type_)) => {
if ptype == type_ {
Ok(())
} else {
Err(BoolError("Invalid property type"))
}
},
}
}

pub fn get_property_type<'a, N: Into<&'a str>>(&self, property_name: N) -> Option<Type> {
self.find_property(property_name).map(|pspec| pspec.get_value_type())
}

pub fn find_property<'a, N: Into<&'a str>>(&self, property_name: N) -> Option<::ParamSpec> {
let property_name = property_name.into();
unsafe {
let klass = self as *const _ as *const gobject_ffi::GObjectClass;

from_glib_none(gobject_ffi::g_object_class_find_property(klass as *mut _, property_name.to_glib_none().0))
}
}

pub fn list_properties(&self) -> Vec<::ParamSpec> {
unsafe {
let klass = self as *const _ as *const gobject_ffi::GObjectClass;

let mut n_properties = 0;

let props = gobject_ffi::g_object_class_list_properties(klass as *mut _, &mut n_properties);
FromGlibContainer::from_glib_none_num(props, n_properties as usize)
}
}
}

unsafe impl IsClassFor for ObjectClass {
type Instance = Object;
}

unsafe impl Send for ObjectClass {}
unsafe impl Sync for ObjectClass {}

pub struct WeakRef<T: IsA<Object> + ?Sized>(Box<gobject_ffi::GWeakRef>, PhantomData<*const T>);

impl<T: IsA<Object> + StaticType + UnsafeFrom<ObjectRef> + Wrapper + ?Sized> WeakRef<T> {
Expand Down
Loading