GNOME Bugzilla – Bug 351810
C++ bindings for GtkBinding*
Last modified: 2018-05-22 12:08:14 UTC
Gtkmm currently does not have language bindings for installing widget keybindings with GtkBinding*. GtkBinding* is documented here: http://developer.gnome.org/doc/API/2.0/gtk/gtk-Bindings.html
Is there any documentation about how this should be used. For instance, a C example?
Unfortunately I couldn't find any except GTK+'s code for e.g. GtkWidget, GtkWindow and GtkScale. Here's a link to GtkScale's code: http://cvs.gnome.org/viewcvs/gtk%2B/gtk/gtkscale.c?view=markup In the class initializer, the code first creates a GtkBindingSet for the class using gtk_binding_set_by_class(). Then, key-signal entries are added to map keys to signal handlers using gtk_binding_entry_add_signal(), complete with arguments used to invoke them. Then, in the key-press-event handler, the event struct is passed to gtk_bindings_activate_event() which looks up and emits the corresponding signal. Note that this is exactly what GtkWidget's handler does. For GtkWidget-derived objects, there is no need to perform the last step as it is already taken care of by GtkWidget's handler.
Here is an example from my program: static void gtk_goban_class_init (GtkGobanClass *class) { static GtkUtilsBindingInfo navigation_bindings[] = { {GDK_Left, 0, GOBAN_NAVIGATE_BACK}, {GDK_Page_Up, 0, GOBAN_NAVIGATE_BACK_FAST}, {GDK_Right, 0, GOBAN_NAVIGATE_FORWARD}, {GDK_Page_Down, 0, GOBAN_NAVIGATE_FORWARD_FAST}, {GDK_Up, 0, GOBAN_NAVIGATE_PREVIOUS_VARIATION}, {GDK_Down, 0, GOBAN_NAVIGATE_NEXT_VARIATION}, {GDK_Home, 0, GOBAN_NAVIGATE_ROOT}, {GDK_End, 0, GOBAN_NAVIGATE_VARIATION_END} }; GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); GtkBindingSet *binding_set; ... binding_set = gtk_binding_set_by_class (class); gtk_utils_add_similar_bindings (binding_set, "navigate", navigation_bindings, (sizeof navigation_bindings / sizeof (GtkUtilsBindingInfo))); } /* Elsewhere. */ void gtk_utils_add_similar_bindings (GtkBindingSet *binding_set, const gchar *signal_name, GtkUtilsBindingInfo *bindings, int num_bindings) { int k; g_return_if_fail (binding_set); g_return_if_fail (signal_name); g_return_if_fail (bindings); g_return_if_fail (num_bindings > 0); for (k = 0; k < num_bindings; k++) { gtk_binding_entry_add_signal (binding_set, bindings[k].keyval, bindings[k].modifiers, signal_name, 1, G_TYPE_INT, bindings[k].signal_parameter); } } Note that you are done with using gtk_binding_set_by_class() and gtk_binding_entry_add_signal(). Everything else is just utility for my own use. No special handling of the created binding set is required, it is automatic in the sense that it is handled by GTK+ itself.
However, there are two difficulties I can see for a C++ implementation. First is that standard usage of GtkBindingSet relies on GType and, as far as I understand, all Gtk::Widget descendants will have one GType in a Gtkmm program. This may be solved with `typeid'. Second is that GtkBindingSet uses C-level signals and I'm not sure how to use C++-level (sigc++) signals instead.
Another problem is that user-defined libsigc++ signals are not registered with the GObject system, and gtk_binding_activate_event() needs to look them up by name to emit the right signals.
I thing Gtkmm can use a proxy object to relay C-level signal emissions to C++ signals. In other words, I propose to use a somewhat different syntax for C++ that doesn't expect signal names, but rather uses sigc++ slots, for instance signal emission slots, but those could be anything.
OK, I have a working implementation, see below. It uses some internal utilities, but those are in no way critical. Please let me know if you are interested.
Created attachment 71612 [details] header file
Created attachment 71613 [details] source file
Let's take a look at this when we branch for gtkmm 2.12. Among other things, this should follow existing conventions, such as capitalized class names without _ underscores. We'll need an example to show how to use this. That could be an addition to the custom widget example, I guess.
Created attachment 82710 [details] somewhat cleaned up sources + example of usage I stripped the sources of all dependency on my program (which is now written in PyGTK anyway.) Also added a quick example. It features two widgets of same class and cursor keys work in the one which is focused at the moment (to demonstrate difference from window-global accelerators.) I would appreciate if someone in Gtkmm team could take over this code, since I don't plan to work on Gtkmm myself. I'm willing to provide help, though.
I also took the latest version of the files from my program. No idea if they differ from what I posted before. A nice idea that would make usage simpler is to implement something like C-level function gtk_binding_set_by_class (). So, user-derived classes wouldn't have to define specific on_key_press/on_key_release handlers, but just add bindings to result of Gtk::BindingSet <...>::by_class(). Then all Gtkmm classes would call by_class() itself in default handlers and if it returns an existing object, would call activate_event() on it. I'm not sure if that is difficult to implement, but since it requires changing existing Gtkmm code, I don't volunteer.
Note to self: Don't ignore this. http://library.gnome.org/devel/gtk/unstable/gtk3-Bindings.html
I'm still interesting in this if someone wants to create a git patch for gtkmm 3. Time is running out though.
Last chance for a patch.
Punted to gtkmm 3.2 then.
(In reply to Chong Kai Xiong from comment #2) > In the class initializer, the code first creates a GtkBindingSet for the > class using gtk_binding_set_by_class(). Then, key-signal entries are added > to map keys to signal handlers using gtk_binding_entry_add_signal(), > complete with arguments used to invoke them. As you've seen, gtkmm 3 doesn't support any custom code in class initialisers. So, you had to approximate it using a static member and having the first instance constructed populate said member. This presumably works for you but is not ideal from a design POV. gtkmm 4/master does support custom class init methods, so that might be better suited to wrapping GtkBindings. (In reply to Paul Pogonyshev from comment #4) > However, there are two difficulties I can see for a C++ implementation. > First is that standard usage of GtkBindingSet relies on GType and, as far as > I understand, all Gtk::Widget descendants will have one GType in a Gtkmm > program. This may be solved with `typeid'. No, each glibmm/gtkmm class will have the GType of the C class it wrapps. > Second is that GtkBindingSet uses C-level signals and I'm not sure how to > use C++-level (sigc++) signals instead. Is that why you convert between int/long/pointer, as if these are interchangeable, when they're really not? That whole pattern seems like a timebomb waiting to go off and probably a bad idea for including in any library. GLib itself warns about this: Remember, you may not store pointers in integers. This is not portable in any way, shape or form. These macros only allow storing integers in pointers, and only preserve 32 bits of the integer; values outside the range of a 32-bit integer will be mangled.
>> as far as I understand, all Gtk::Widget descendants will have one GType in a >> Gtkmm program. > > No, each glibmm/gtkmm class will have the GType of the C class it wraps. The connection between the GType class hierarchy and the C++ hierarchy is somewhat curious. Let's take part of a hierarchy as an example. In C++/gtkmm: class Gtk::Widget : public ... class Gtk::Container : public Gtk::Widget class Gtk::Box : public Gtk::Container In GType/gtk+: class GtkWidget : public ... class GtkContainer : public GtkWidget class GtkBox : public GtkContainer In GType/gtkmm add: class gtkmm__GtkWidget : public GtkWidget // Gtk::Widget class gtkmm__GtkContainer : public GtkContainer // Gtk::Container class gtkmm__GtkBox : public GtkBox // Gtk::Box If a user of gtkmm derives a class such as class MyBox : public Gtk::Box in the usual way, implicitly calling Glib::ObjectBase's default constructor, then MyBox will have the same GType as Gtk::Box.
Interesting, thanks. So, if a new name is needed, we call Glib::ObjectBase("name") manually passing it? Which is always possible because we need virtual inheritance, and that makes us responsible for calling all base constructors. Related to this is Bug 546220: "Maybe all derived classes should get their own GType automatically."
-- GitLab Migration Automatic Message -- This bug has been migrated to GNOME's GitLab instance and has been closed from further activity. You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/gtkmm/issues/3.