How to claim right-click gesture's "released" event in GTK4

When dealing with nested hierarchy of widgets, what is the correct/recommended way to prevent a parent widget’s signal handler from being called if a child widget has already handled the event that both parent and child have registered to receive.

I am trying to do this by setting the gesture’s state to GTK_EVENT_SEQUENCE_CLAIMED in the child widget’s handler but the parent’s handler is still getting called.

The input event in question is mouse right-click release, using a GtkGestureClick event controller.

Curiously, my method seems to be working in GTK v3.98. Does that mean this is a bug in GTK’s master branch (I last pulled on Sept. 7)? Or was it buggy behavior in v3.98 that has since been corrected?

Also, is the correct way to achieve this in GTK3 (when not using event controllers) returning TRUE from an event handler?

Thanks!

1 Like

Claiming a sequence is generally the way to prevent other controllers from acting on it.

If you can boil your code down to a testcase with just two widgets and two event controllers, it might be good to file an issue, so we can look at it in detail.

Thanks. I will try to create a simplified example and share.

/* Compile with:
 * $ gcc `pkg-config --cflags gtk4` `pkg-config --libs gtk4` -o click-released-test click-released-test.c
*/

#include <gtk/gtk.h>

static void
on_inner_widget_right_btn_pressed (GtkGestureClick *gesture,
                                   int                n_press,
                                   double             x,
                                   double             y,
                                   GtkWidget         *widget)
{
  g_print ("on_inner_widget_right_btn_pressed() called\n");
}

static void
on_inner_widget_right_btn_released (GtkGestureClick *gesture,
                                    int              n_press,
                                    double           x,
                                    double           y,
                                    GtkWidget       *widget)
{
  g_print ("on_inner_widget_right_btn_released() called\n");

  gtk_gesture_set_state (GTK_GESTURE (gesture),
                         GTK_EVENT_SEQUENCE_CLAIMED);
}

static void
on_outer_widget_right_btn_released (GtkGestureClick *gesture,
                                    int              n_press,
                                    double           x,
                                    double           y,
                                    GtkWidget       *widget)
{
  g_print ("on_outer_widget_right_btn_released() called\n");
}

static void
activate (GtkApplication *app,
          gpointer        user_data)
{
  GtkWidget *window;
  GtkWidget *outer;
  GtkWidget *inner;
  GtkGesture *gesture;

  /* Add colored border via css for easily identifying widget*/
  GtkCssProvider *provider = gtk_css_provider_new();
  gtk_css_provider_load_from_data (provider,
                                   ".thin_gray_border {border:2px solid gray;}\n"
                                   ".thin_red_border {border:2px solid red;}",
                                   -1);

  gtk_style_context_add_provider_for_display (gdk_display_get_default(),
                                              GTK_STYLE_PROVIDER(provider),
                                              GTK_STYLE_PROVIDER_PRIORITY_USER);

  window = gtk_application_window_new (app);
  gtk_window_set_title (GTK_WINDOW (window), "Claim Right Button Click “released”");
  gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);

  /* Use GtkGrid as outer widget */
  outer = gtk_grid_new ();
  gtk_grid_insert_row (GTK_GRID (outer), 0);
  gtk_grid_insert_row (GTK_GRID (outer), 1);
  gtk_grid_insert_column (GTK_GRID (outer), 0);
  gtk_grid_insert_column (GTK_GRID (outer), 1);
  gtk_style_context_add_class (gtk_widget_get_style_context(outer), "thin_gray_border");

  /* Register for mouse right button click "released" event on outer widget*/
  gesture = gtk_gesture_click_new ();
  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 3);
  g_signal_connect (gesture, "released",
                    G_CALLBACK (on_outer_widget_right_btn_released), outer);
  gtk_widget_add_controller (outer, GTK_EVENT_CONTROLLER (gesture));

  gtk_window_set_child (GTK_WINDOW (window), outer);

  /* Use GtkLabel as inner widget */
  inner = gtk_label_new ("Inner Widget");
  gtk_style_context_add_class (gtk_widget_get_style_context (inner), "thin_red_border");
  gtk_widget_set_size_request (inner, 100, 100);

  /* Register for mouse right button click "pressed" and "released" events on inner widget*/
  gesture = gtk_gesture_click_new ();
  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 3);
  g_signal_connect (gesture, "pressed",
                    G_CALLBACK (on_inner_widget_right_btn_pressed), inner);
  g_signal_connect (gesture, "released",
                    G_CALLBACK (on_inner_widget_right_btn_released), inner);
  gtk_widget_add_controller (inner, GTK_EVENT_CONTROLLER (gesture));

  gtk_grid_attach (GTK_GRID (outer), inner, 0, 0, 1, 1);

  gtk_widget_show (window);
}

int
main (int    argc,
      char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.gnome.discourse-4194", G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);

  return status;
}

Sorry for inlined source code in reply - couldn’t figure out how to upload source code here.

Thanks.

I’ve put a potential fix here: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/2576

Thanks! I will test the fix and report back.

The linked MR fixes the issue. Thanks!