Hello,
After investigating what Gtk::Builder does with callbacks I figured
out there are issues.
The first issue is mismatch between GTK C callbacks and GTK Ruby
callbacks.
In C the GTK functions have a required first argument which is pointer
to self. This is because C has no notion of object and self has to be
passed explicitly.
Then in C to get rid of a window you call gtk_widget_destroy(window)
but in ruby you call window.destroy meaning that methods take one
fewer argument than gtk C functions.
This is not accounted for by signal_connect in Ruby.
In C when you connect a signal the first argument passed to the
handler is the object that triggered the signal and it is passed as
pointer to that kind of object. Obviously, this is meant to be the
self pointer. If the signal passes additional data or the user
supplied additional data when connecting the signal this is passed in
additional arguments. When the signal is connected as swapped the user
data and self is swapped when calling the callback.
Now in Ruby you don’t connect a function, you connect a method. This
method is already attached to an object - ‘self’. The first argument
passed in by GTK is totally redundant when the receiver is the object
which triggers the signal. Worse, when connecting existing GTK
function as a signal handler and it takes any arguments in Ruby
(unlike destroy which takes none because it takes only one in C) you
have to wrap it to discard this first argument. Now I don’t expect
this to be very common use but the possibility exists in C but not in
Ruby.
Another issue is argument count. While in C extra arguments are
ignored in Ruby they are ignored so long as you connect a block. When
you connect a method and GTK passes extra arguments your callback
fails and your application crashes. In C extra arguments are just
ignored which is often used. Again, you may need to wrap a method in
Ruby to make it a useful callback if the argument count does not
match.
Either of this could be resolved by inserting a wrapper automatically
at gobj_sig_connect_impl to discard any extra args and/or discard the
first arg when it is the same as the receiver.
I am not sure if this is useful and wanted feature, though. It would
change the API but if anybody used the cases that would change then
they would see these issues for sure. Still it’s easy enough to
rearrange arguments and redispatch the callbacks in user Ruby code,
too.
The other issue is with Gtk::Builder. Given the issues above it does a
lousy job of connecting signals, it can’t do any better. The results
are nowhere near what the C connections would be.
What works properly are signals with no user data (object in glade
files). Builder calls the user supplied block to get a handler and
connects that. The swapped option does not make any sense in this case
and is rightfully ignored.
What somewhat works are swapped signals with user data. These are
often used to connect a “close” button to call a function which
disposes of a window when the button is clicked. As these functions
take only the window in C and no arguments in Ruby this works OK - all
arguments are ignored. Connecting a GTK function that takes arguments
would not work this way because of the extra argument generated when
going between C and Ruby.
What is broken are signals with user data which are not swapped.
They are simply handled as swapped, the swapped flag is ignored
completely. I guess these should be connected just like signals
without user data but pass in the extra argument. It’s not quite
consistent with C gtk and never will be so long as the extra argument
is generated but at least it would be an useful option.
Thanks
Michal