woboq / qmetaobject-rs

Integrate Qml and Rust by building the QMetaObject at compile time.
MIT License
647 stars 89 forks source link

Proposal: rust signals should be done differently #100

Open ratijas opened 4 years ago

ratijas commented 4 years ago

This is a tracking issue for signal / slot mechanism wrappers in Rust.

From Rust side, RustSignal and other satellite types should be done differently, in a more type-safe and convenient way.

TODO:

Origin of this discussion: https://github.com/woboq/qmetaobject-rs/pull/97#discussion_r432318559

rubdos commented 4 years ago

Just chipping in on this discussion! While looking at other Qt bindings is useful, it's also useful to keep in mind that proc_macro's are getting quite a bit of stability lately, and maybe the other bindings don't use that to their full extent yet.

What would be cool is the ability to annotate otherwise normal Rust methods:

use qmetaobject::QObject;

#[derive(QObject)]
#[qmetaobject::signal(contentsChanged)]
struct Foo {
    inner: Vec<u32>,

    #[qmetaobject::property]
    #[notify(contentsChanged)]
    is_active: bool,
}

impl Foo {
    #[qmetaobject::method(Foo::addInteger)]
    fn add(&mut self, i: u32) { self.contents_changed() } // note that it is possible to auto-map the casing!
}

EDIT: I've also added some slot and property syntax idea's.

rubdos commented 4 years ago

Note that I'm only talking from an ergonomic point of view, I don't know whether this is physically possible to implement.

ogoffart commented 4 years ago

this particular suntax is indeed not possible because the #[derive(QObject)] do not have any knowledge of the other impls. But there is certainly room for improvement. When i first developed this crate, only the derive macro was stable. Also i was not familiar with the rust convention, and laybe qt_property! would have been better as a #[property] and there could be better ways to register methods. Especially signals could be done as associated const or something like that instead of fields.

rubdos commented 4 years ago

I suppose methods could get registered as an attribute under the derive too, then? Like

#[derive(QObject)]
#[qmetaobject::method(fooBar)]
struct Foo {}
rubdos commented 4 years ago

Another comment that can be taken into account in this rework: handling the renaming/capitalization of methods, we're putting [#[allow(non_snake_case)](https://gitlab.com/rubdos/whisperfish/-/issues/53) all over the place right now.

ratijas commented 4 years ago

I've been out for a while. Came back after two new minor Rust versions has already shipped.

Looking at the release notes of 1.45.0, they even took an example from the gnome-class crate. I don't know much about GObject and Gnome concepts, but it looks somewhat reminiscent to something familiar. Maybe worth investigating deeper.

Also, note how GNOME project took initiative to create and support Rust wrappers on their own. (I'm looking at you, The Qt Company :eyes:)

https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#stabilizing-function-like-procedural-macros-in-expressions-patterns-and-statements https://gitlab.gnome.org/federico/gnome-class

iovxw commented 3 years ago

How about this? Just add two keywords:

// Just a normal struct
struct Foo {
    inner: Vec<u32>,
    the_real_is_active: bool,
}

#[qmetaobject(rename_all_signal = "camelCase")]
impl QObject for Foo { // QObject is the qt_base_class!
    signal contents_changed;

    #[notify(contents_changed)]
    #[bind(the_real_is_active)] // Or #[read(func)] #[write(func)]
    property is_active: bool;

    fn add(&mut self, i: u32) { self.contents_changed() }
}