/gobject

Test bed for gtk-rs-core experiments

Primary LanguageRustMIT LicenseMIT

Rust GObject Experiments

class macro

#[gobject::class(final)]
mod obj {
    #[derive(Default)]
    pub struct MyObj {
        #[property(get, set)]
        my_prop: std::cell::Cell<u64>,
    }
    impl MyObj {
        #[signal]
        fn abc(&self) {}
    }
}

fn main() {
    let obj: MyObj = glib::Object::new(&[]).unwrap();
    obj.set_my_prop(52);
    obj.emit_abc();
}

clone_block macro

#[gobject::clone_block]
fn main() {
    use glib::prelude::ObjectExt;

    let get_cell = {
        let cell = std::rc::Rc::new(std::cell::Cell::new(50u32));

        // equivalent to glib_clone!(@weak-allow-none cell => ...)
        let get_cell = move |#[weak] cell| cell.map(|c| c.get()).unwrap_or(0);
        cell.set(100);

        // arguments marked with #[weak] or #[strong] are passed implicitly
        assert_eq!(get_cell(), 100u32);
        get_cell
    };
    assert_eq!(get_cell(), 0u32);

    let concat = {
        let refcell = std::rc::Rc::new(std::cell::RefCell::new(String::from("Hello")));
        let obj: glib::Object = glib::Object::new(&[]).unwrap();
        let concat = move |#[strong] refcell, #[strong] obj, extra: &str| {
            format!("{} {} {}", refcell.borrow(), obj.type_().name(), extra)
        };
        assert_eq!(concat("World"), "Hello GObject World");
        refcell.replace(String::from("Goodbye"));
        concat
    };
    assert_eq!(concat("World"), "Goodbye GObject World");

    // other supported options

    // renaming:
    //     move |#[weak(self)] this| {}
    //     move |#[strong(self.mydata)] this| {}
    //
    // default panic:
    //     move |#[weak(or_panic)] value| {}
    //     move |#[weak(self or_panic)] this| {}
    //     #[default_panic] move |#[weak(self)] this| {}
    //
    // default return:
    //     move |#[weak(or_return)] value| {}
    //     move |#[weak(or_return 123)] value| {}
    //     move |#[weak(self or_return)] this| {}
    //     move |#[weak(self or_return 123)] this| {}
    //     #[default_return] move |#[weak(self)] this| {}
    //     #[default_return 123] move |#[weak(self)] this| {}
    //
    // default alternative:
    //     move |#[weak(or 123)] value| {}
    //     move |#[weak(self.myvalue or 123)] value| {}
    //
    // forcing an Option when another default is present:
    //     #[default_panic] move |#[weak(self)] this, #[weak(allow_none)] value| {}
    //     #[default_panic] move |#[weak(self)] this, #[weak(self.myvalue allow_none)] value| {}

    // equivalent to glib::closure!
    let add = #[closure] |a: i32, b: i32| a + b;
    assert_eq!(add.invoke::<i32>(&[&3i32, &7i32]), 10);

    let obj: glib::Object = glib::Object::new(&[]).unwrap();

    // equivalent to glib::closure_local!
    let closure = move |#[watch] obj| obj.type_().name().to_owned();
    assert_eq!(closure.invoke::<String>(&[]), "GObject");

    // strong and weak references work with closures too
    let get_cell = {
        let cell = std::rc::Rc::new(std::cell::Cell::new(50u32));
        let get_cell = #[closure(local)] move |#[weak] cell| cell.map(|c| c.get()).unwrap_or(0);
        cell.set(100);
        assert_eq!(get_cell.invoke::<u32>(&[]), 100);
        get_cell
    };
    assert_eq!(get_cell.invoke::<u32>(&[]), 0);

    // rest parameters are supported as the last argument of closures
    let sum = #[closure] |x: i32, #[rest] rest: &[glib::Value]| -> i32 {
        x + rest.iter().map(|v| v.get::<i32>().unwrap()).sum::<i32>()
    };
    assert_eq!(sum.invoke::<i32>(&[&10i32, &100i32, &1000i32]), 1110i32);
}

This repo also has a clone-blockify tool that attempts to convert Rust source files from glib::clone! and glib::closure! into the clone_block format.