Key-press-event receives GDK_DELETE instead of GDK_KEY_PRESS

I’m creating a multi line text view with GTK3 where I want to catch certain key events. The idea is to let the default action happen when the user presses enter and insert a line break into the text when the user presses either SHIFT+Enter or CTRL+Enter. My setup looks like this:

// Base dialog
GtkWidget* dialog = gtk_dialog_new();
// no automatic layout since I position everything myself
GtkWidget* content_area_fixed = gtk_fixed_new();
gtk_widget_set_size_request(content_area_fixed, someWidth, someHeight);

gtk_container_add(GTK_CONTAINER(content_area_automatic), content_area_fixed);
GtkWidget* content_area = content_area_fixed;

I’m creating a multi line text input like this and subscribing to the key-press-eventand key-release-event of the text view like this:

// rough break down of multi line text input
auto textBuffer = gtk_text_buffer_new(NULL);
gtk_text_buffer_set_text(textBuffer, "", 0);
g_signal_connect_after(textBuffer, "insert-text", G_CALLBACK(onTextBuffer_insert_text), NULL);

auto textView = (GtkTextView*)gtk_text_view_new_with_buffer(textBuffer);
gtk_text_view_set_accepts_tab(textView, false);

// Connect to handling keyboard events
gtk_widget_add_events(GTK_WIDGET(content_area), GDK_KEY_PRESS_MASK);
g_signal_connect_after(GTK_WIDGET(textView), "key-press-event", G_CALLBACK(onTextBuffer_key_event), NULL);
g_signal_connect_after(GTK_WIDGET(textView), "key-release-event", G_CALLBACK(onTextBuffer_key_event), NULL);

// add scrollable window which allows the size to stay fixed with multi-line text
GtkWidget* scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(scrolledwindow), (GtkWidget*)textView);
gtk_container_add(GTK_CONTAINER(content_area), scrolledwindow);
// omitting setting position and size to keep example short

Finally, the key-event callback looks like this:

// keyboard input callback for text view
static gboolean onTextBuffer_key_event(GtkWidget* self, GdkEventKey event, gpointer user_data)
{
    switch (event.type) {
        case GDK_KEY_PRESS:
        {
            std::cout << "Pressing HW " << event.hardware_keycode << " (keyval " << event.keyval << ")" << std::endl;
            return TRUE;
        }
        case GDK_KEY_RELEASE:
        {
            std::cout << "Releasing HW " << event.hardware_keycode << " (keyval " << event.keyval << ")" << std::endl;
            return TRUE;
        }
        default: 
        {
            std::cout << "Other event type not interested in received " << event.type << std::endl;
            return FALSE;
        }
    }
}

When running that dialog via gtk_widget_show_all(dialog), all I’m seeing in the output for any key press or release like a, shift or enter is:

Other event type not interested in received 0

So basically, I’m only receiving GDK_DELETE and not a single GDK_KEY_PRESS or GDK_KEY_PRESS. Also, the keyval of every event is 32767/0x7fff.

I also tried different widgets as the first parameter for gtk_widget_add_events() like GTK_WIDGET(scrolledwindow), GTK_WIDGET(textView) or GTK_WIDGET(dialog) but none of them changed the situation.

Any idea what I can do to get the actual key events while the user is editing the text view?
Also, maybe there’s an easier approach to make the enter key emit the dialog’s default response while the text view has focus?

You probably need to connect before the default handlers in GtkTextView, else they might block your handlers.

Consider using GtkEventControllerKey instead of these old events, as the events are removed in GTK4. In that case you would pick a suitable PropagationPhase to let your events get called at the right point, probably bubble; see here.

1 Like

Thanks a lot for looking into this!

You probably need to connect before the default handlers in GtkTextView, else they might block your handlers.

I’ve tried to immediately connect after creating the text view like this, but the situation remains unchanged:

auto textView = (GtkTextView*)gtk_text_view_new_with_buffer(textBuffer);
// Connect to handling keyboard events
gtk_widget_add_events(GTK_WIDGET(content_area), GDK_KEY_PRESS_MASK);
g_signal_connect_after(GTK_WIDGET(textView), "key-press-event", G_CALLBACK(onTextBuffer_key_event), NULL);
g_signal_connect_after(GTK_WIDGET(textView), "key-release-event", G_CALLBACK(onTextBuffer_key_event), NULL);

I don’t see how I could connect the signals any sooner than that.

Consider using GtkEventControllerKey instead

Thanks, this didn’t pop up in my searches. I’ll try to rewrite this tomorrow, maybe that approach works better.

Haha, sorry - I meant you could try using g_signal_connect() instead of g_signal_connect_after(), since the former function connects the signal before the default handler (unlike the latter, which - well, y’know! :wink: )

1 Like

Haha, sorry - I meant you could try using g_signal_connect() instead of g_signal_connect_after()

Alright, makes sense! The situation remains the same, unfortunately.

However, the GtkEventControllerKey approach is sending the correct keyvals!

There’s just a weird problem with the user_data parameter, it never seems to be set to what I set it up during signal connect:

auto eventControllerkey = gtk_event_controller_key_new(GTK_WIDGET(textView));
g_signal_connect_object(eventControllerkey, "key-pressed", G_CALLBACK(onTextViewKeyPress), G_OBJECT(dialog), G_CONNECT_SWAPPED);
std::cout << dialog << std::endl;

This prints out something like 0x555555cb19a0. But when the callback is called, the user_data pointer is different:

static gboolean onTextViewKeyPress(GtkEventControllerKey* controller, guint keyval, guint keycode, GdkModifierType state, gpointer user_data)
{
    std::cout << user_data << std::endl;
    GtkDialog* dialog = GTK_DIALOG(user_data);
    return false;
}

It’ll print this:

0x5555559e3dd0
(dialog:49634): GLib-GObject-CRITICAL **: 15:09:14.434: invalid cast from 'GtkEventControllerKey' to 'GtkDialog'

I have a different button callback where the pointer to the GtkDialog object is transferred perfectly fine and I’ve built it up in a similar way:

g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(buttonClicked), G_OBJECT(dialog));

Any idea why the user_data is incorrect for key-pressed signals and why it appears to be of type GtkEventControllerKey'?

Well, yes, you connected with G_CONNECT_SWAPPED, so that causes the instance and user_data arguments to be swapped when your handler is called. I would just use g_signal_connect(), not g_signal_connect_object() with a flag you clearly don’t want. :wink:

1 Like

Ah, thank you so much! That was the “problem” :slight_smile:

1 Like

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