Overiding internal GtkEventControllerKeys

Hi,
I am currently rewriting the xffm application from GTK3 to GTK4. This application has a preference for keyboard input over all else. In GTK3 the main window is connected to the “key-press-event” signal.

This is no longer an option in GTK4. Instead a GtkEventControllerKey is created and associated to the main window with the “key-pressed” signal.

This seems to work, until a GtkNotebook is added. When that happens, all keys are still processed by GtkEventControllerKey, except the space key (keyval=32, 0x20).

So my guess is that the notebook has an internal GtkEventControllerKey that returns TRUE on receiving a space key.

The workaround (or hack) I found was to create another GtkEventControllerKey and asociate it to the GtkNotebook widget.

Instead, is some way to place the user created GtkEventControllerKey before all other GtkEventControllerKey which may exist by default?

This is the test code to illustrate the situation. Remove the comment from the second addKeyController() and the space key will be processed correctly.

#include <stdio.h>
#include <gtk/gtk.h>
    static gboolean
    on_keypress (GtkEventControllerKey* self,
          guint keyval,
          guint keycode,
          GdkModifierType state,
          gpointer data)
{
        fprintf(stderr, "window_keyboard_event: keyval=%d (0x%x), keycode=%d (0x%x), modifying=%d, data= %p\n", 
            keyval, keyval, keycode, keycode, state, data);
}
    static void addKeyController(GtkWidget *widget){
        GtkEventController *keyController = gtk_event_controller_key_new();
        gtk_widget_add_controller(GTK_WIDGET(widget), keyController);
        g_signal_connect (G_OBJECT (keyController), "key-pressed", 
            G_CALLBACK (on_keypress), NULL);
    }

    int main(int argc, const char **argv){
        gtk_init ();
        GtkWindow *mainWindow = GTK_WINDOW(gtk_window_new ());
        addKeyController(GTK_WIDGET(mainWindow));
        GtkWidget *notebook = gtk_notebook_new();
        gtk_window_set_child(mainWindow, notebook);
        GtkWidget *label1 = gtk_label_new("foo");
        GtkWidget *label2 = gtk_label_new("bar");
        gtk_notebook_append_page (GTK_NOTEBOOK(notebook), label2, label1);
        //addKeyController(notebook);

        gtk_window_present (mainWindow);  
        while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
            g_main_context_iteration (NULL, TRUE);
    }

Hi,

You can set the propagation phase to “capture”:

gtk_event_controller_get_propagation_phase (keyController, GTK_PHASE_CAPTURE);

That will allow the GtkWindow to receive the event before the GtkNotebook.

More infos about events handling in the official doc: Gtk – 4.0: Overview of GTK input and event handling

By default, events use the “bubble” phase, i.e. evaluated first by the focused widget, then up to the toplevel.

Note: your callback on_keypress() shall return FALSE to propagate the event further, or TRUE to stop it.

1 Like

Thanks! Just what I was looking for.

1 Like

Just a typo correction. The function call is

gtk_event_controller_set_propagation_phase

not

gtk_event_controller_get_propagation_phase
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.