diff --git a/src/bauhaus/bauhaus.c b/src/bauhaus/bauhaus.c index edb14a90e748..18a54880ab3e 100644 --- a/src/bauhaus/bauhaus.c +++ b/src/bauhaus/bauhaus.c @@ -171,7 +171,15 @@ static double INNER_PADDING = 4.0; static void _popup_reject(void); static void _popup_hide(void); static gboolean _popup_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data); -static gboolean _popup_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data); +static void _window_motion_handle(GtkWidget *widget, + gdouble root_x, + gdouble root_y, + GdkModifierType state); +static gboolean _popup_key_press(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + gpointer user_data); static void _combobox_set(dt_bauhaus_widget_t *w, const int pos, const gboolean mute); @@ -517,8 +525,9 @@ static void _slider_zoom_toast(dt_bauhaus_widget_t *w) g_free(max_text); } -static gboolean _popup_scroll(GtkWidget *widget, - GdkEventScroll *event, +static void _popup_scroll_cb(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, gpointer user_data) { dt_bauhaus_widget_t *w = darktable.bauhaus->current; @@ -526,19 +535,17 @@ static gboolean _popup_scroll(GtkWidget *widget, { // match keyboard: right & down -> next int delta_x = 0, delta_y = 0; - if(dt_gui_get_scroll_unit_deltas(event, &delta_x, &delta_y)) - { - int delta = abs(delta_x) > abs(delta_y) ? delta_x : delta_y; + dt_gui_get_scroll_unit_deltas_fallback(dx, dy, &delta_x, &delta_y); + const int delta = abs(delta_x) > abs(delta_y) ? delta_x : delta_y; + if(delta != 0) _combobox_next_sensitive(w, delta, 0, w->combobox.mute_scrolling); - } } else { int delta = 0; - if(dt_gui_get_scroll_unit_delta(event, &delta)) - _slider_zoom_range(w, delta); + dt_gui_get_scroll_unit_delta_fallback(dy, &delta); + _slider_zoom_range(w, delta); } - return TRUE; } static void _window_moved_to_rect(GdkWindow *window, @@ -596,9 +603,10 @@ static void _window_position(const int offset) 0, - pop->offset + pop->offcut); } -static gboolean _window_motion_notify(GtkWidget *widget, - GdkEventMotion *event, - gpointer user_data) +static void _window_motion_handle(GtkWidget *widget, + gdouble root_x, + gdouble root_y, + GdkModifierType state) { dt_bauhaus_t *bh = darktable.bauhaus; dt_bauhaus_popup_t *pop = &bh->popup; @@ -612,16 +620,16 @@ static gboolean _window_motion_notify(GtkWidget *widget, // recalculate event coords so we get useful values outside window GdkWindow *window = gtk_widget_get_window(pop->area); gdk_window_get_origin(window, &allocation.x, &allocation.y); - const gint ex = dt_gdk_event_get_root_x(event) - allocation.x; - const gint ey = dt_gdk_event_get_root_y(event) - allocation.y; + const gint ex = root_x - allocation.x; + const gint ey = root_y - allocation.y; - const int tol = DT_PIXEL_APPLY_DPI(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK ? 400 : 50); + const int tol = DT_PIXEL_APPLY_DPI(state & GDK_BUTTON1_MASK ? 400 : 50); if(ex < - tol || ex > allocation.width + tol || ey + pop->offcut < - tol || ey + pop->offcut > pop->position.height + tol) { _popup_reject(); - return TRUE; + return; } if(bh->keys_cnt == 0) _stop_cursor(); @@ -658,7 +666,7 @@ static gboolean _window_motion_notify(GtkWidget *widget, : _slider_get_line_offset (pop->oldpos, 5.0 * powf(10.0f, -d->digits) / (d->max - d->min) / fabsf(d->factor), bh->mouse_x / (width - _widget_get_quad_width(w)), bh->mouse_y / width, ht / width); - if(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK + if(state & GDK_BUTTON1_MASK || (bh->mouse_line_distance && ((bh->mouse_line_distance * mouse_off <= 0) ^ (fabsf(bh->mouse_line_distance - mouse_off) > .5f)))) @@ -673,7 +681,7 @@ static gboolean _window_motion_notify(GtkWidget *widget, const int active = (bh->mouse_y - w->top_gap) / bh->line_height; if(active >= 0 && active < d->entries->len) { - if(_combobox_entry(d, active)->sensitive && dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK) + if(_combobox_entry(d, active)->sensitive && state & GDK_BUTTON1_MASK) { if(active != d->active) _combobox_set(w, active, w->combobox.mute_scrolling); @@ -682,72 +690,114 @@ static gboolean _window_motion_notify(GtkWidget *widget, } gtk_widget_queue_draw(pop->area); - return TRUE; } -static gboolean _popup_leave_notify(GtkWidget *widget, - GdkEventCrossing *event, - gpointer user_data) +// GTK4 TODO: This popup uses GTK_WINDOW_POPUP which has compatibility issues +// with GtkEventControllerKey and GtkEventControllerMotion on GTK3 — motion +// and key events don't reach the controllers on this window type. The signal +// handlers below are the fallback. For GTK4, the popup should be migrated to +// GtkPopover or a GtkWindow with proper event controllers. + +static gboolean _window_motion_handler(GtkWidget *widget, GdkEventMotion *event, + gpointer user_data) { - gtk_widget_set_state_flags(widget, GTK_STATE_FLAG_NORMAL, TRUE); + _window_motion_handle(widget, + dt_gdk_event_get_root_x(event), + dt_gdk_event_get_root_y(event), + dt_gdk_event_get_state(event)); return TRUE; } -static gboolean _popup_button_release(GtkWidget *widget, - GdkEventButton *event, +static gboolean _popup_leave_handler(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data) { - if(darktable.bauhaus->change_active && dt_gdk_event_get_button(event) != GDK_BUTTON_MIDDLE) - _popup_hide(); - + gtk_widget_set_state_flags(widget, GTK_STATE_FLAG_NORMAL, TRUE); return TRUE; } -static gboolean _popup_button_press(GtkWidget *widget, - GdkEventButton *event, +static gboolean _popup_key_handler(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { - if(dt_gdk_event_get_window(event) != gtk_widget_get_window(widget)) - { - _popup_reject(); - return TRUE; - } + return _popup_key_press(NULL, dt_gdk_event_get_keyval(event), 0, dt_gdk_event_get_state(event), NULL); +} +static void _popup_button_release_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + gpointer user_data) +{ + if(darktable.bauhaus->change_active && gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_MIDDLE) + _popup_hide(); +} + +static void _popup_button_press_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + gpointer user_data) +{ dt_bauhaus_t *bh = darktable.bauhaus; dt_bauhaus_widget_t *w = bh->current; + const guint button = gtk_gesture_single_get_current_button(gesture); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + // reject clicks that come from outside the popup area window + GdkEvent *cur_event = gtk_get_current_event(); + if(cur_event) + { + if(dt_gdk_event_get_window(cur_event) != gtk_widget_get_window(dt_gui_get_widget(gesture))) + { + gdk_event_free(cur_event); + _popup_reject(); + return; + } + gdk_event_free(cur_event); + } + + if(button == GDK_BUTTON_PRIMARY) { // only accept left mouse click gtk_widget_set_state_flags(GTK_WIDGET(w), GTK_STATE_FLAG_FOCUSED, FALSE); if(w->type == DT_BAUHAUS_COMBOBOX - && !dt_gui_long_click(dt_gdk_event_get_time(event), bh->opentime)) + && !dt_gui_long_click(gdk_event_get_time(gtk_gesture_get_last_event(GTK_GESTURE(gesture), NULL)), bh->opentime)) { // counts as double click, reset: - if(!(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK) && w->field + GdkModifierType state; + gtk_get_current_event_state(&state); + if(!(dt_modifier_is(state, GDK_CONTROL_MASK) && w->field && dt_gui_presets_autoapply_for_module((dt_iop_module_t *)w->module, GTK_WIDGET(w)))) dt_bauhaus_widget_reset(GTK_WIDGET(w)); } bh->change_active = TRUE; - event->state |= GDK_BUTTON1_MASK; - _window_motion_notify(widget, (GdkEventMotion*)event, user_data); + GtkWidget *w_current = dt_gui_get_widget(gesture); + GdkEvent *current_event = gtk_get_current_event(); + if(current_event) + { + GdkModifierType motion_state = dt_gdk_event_get_state(current_event); + motion_state |= GDK_BUTTON1_MASK; + _window_motion_handle(w_current, + dt_gdk_event_get_root_x(current_event), + dt_gdk_event_get_root_y(current_event), + motion_state); + gdk_event_free(current_event); + } } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_MIDDLE && w->type == DT_BAUHAUS_SLIDER) + else if(button == GDK_BUTTON_MIDDLE && w->type == DT_BAUHAUS_SLIDER) _slider_zoom_range(w, 0); else _popup_reject(); - - return TRUE; } static void _window_show(GtkWidget *w, gpointer user_data) { // make sure combo popup handles button release gtk_grab_add(GTK_WIDGET(user_data)); + // grab keyboard focus so the popup receives key events + gtk_widget_grab_focus(GTK_WIDGET(user_data)); } static void _widget_leave(GtkEventControllerMotion *controller, @@ -976,6 +1026,7 @@ void dt_bauhaus_init() gtk_window_set_modal(GTK_WINDOW(pop->window), TRUE); gtk_window_set_type_hint(GTK_WINDOW(pop->window), GDK_WINDOW_TYPE_HINT_POPUP_MENU); + gtk_widget_add_events(pop->window, GDK_POINTER_MOTION_MASK); pop->area = gtk_drawing_area_new(); g_object_set(pop->area, "expand", TRUE, NULL); @@ -994,13 +1045,14 @@ void dt_bauhaus_init() g_signal_connect(gtk_widget_get_window(pop->window), "moved-to-rect", G_CALLBACK(_window_moved_to_rect), NULL); g_signal_connect(window, "show", G_CALLBACK(_window_show), area); - g_signal_connect(window, "motion-notify-event", G_CALLBACK(_window_motion_notify), NULL); g_signal_connect(area, "draw", G_CALLBACK(_popup_draw), NULL); - g_signal_connect(area, "leave-notify-event", G_CALLBACK(_popup_leave_notify), NULL); - g_signal_connect(area, "button-press-event", G_CALLBACK(_popup_button_press), NULL); - g_signal_connect(area, "button-release-event", G_CALLBACK (_popup_button_release), NULL); - g_signal_connect(area, "key-press-event", G_CALLBACK(_popup_key_press), NULL); - g_signal_connect(area, "scroll-event", G_CALLBACK(_popup_scroll), NULL); + g_signal_connect(window, "motion-notify-event", G_CALLBACK(_window_motion_handler), NULL); + g_signal_connect(area, "leave-notify-event", G_CALLBACK(_popup_leave_handler), NULL); + g_signal_connect(area, "key-press-event", G_CALLBACK(_popup_key_handler), NULL); + dt_gui_connect_click_all(area, _popup_button_press_cb, _popup_button_release_cb, NULL); + dt_gui_connect_scroll(area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _popup_scroll_cb, NULL); dt_action_define(&darktable.control->actions_focus, NULL, N_("sliders"), NULL, &_action_def_focus_slider); @@ -3762,16 +3814,18 @@ static void _slider_set_normalized(dt_bauhaus_widget_t *w, float pos) } } -static gboolean _popup_key_press(GtkWidget *widget, - GdkEventKey *event, - gpointer user_data) +static gboolean _popup_key_press(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + gpointer user_data) { dt_bauhaus_t *bh = darktable.bauhaus; dt_bauhaus_widget_t *w = bh->current; const gboolean is_combo = w->type == DT_BAUHAUS_COMBOBOX; int delta = -1; - switch(dt_gdk_event_get_keyval(event)) + switch(keyval) { case GDK_KEY_BackSpace: case GDK_KEY_Delete: @@ -3837,22 +3891,37 @@ static gboolean _popup_key_press(GtkWidget *widget, if(is_combo) _combobox_next_sensitive(w, delta, 0, w->combobox.mute_scrolling); else - _slider_add_step(GTK_WIDGET(w), delta, dt_gdk_event_get_state(event), FALSE); + _slider_add_step(GTK_WIDGET(w), delta, state, FALSE); break; default: - if(!event->string || !g_utf8_validate(event->string, -1, NULL)) return FALSE; - const gunichar c = g_utf8_get_char(event->string); - if(!g_unichar_isprint(c)) return FALSE; - const long int char_width = g_utf8_next_char(event->string) - event->string; + { + GdkEvent *event = gtk_get_current_event(); + if(!event) return FALSE; + const gchar *str = ((GdkEventKey *)event)->string; + if(!str || !g_utf8_validate(str, -1, NULL)) + { + gdk_event_free(event); + return FALSE; + } + const gunichar c = g_utf8_get_char(str); + if(!g_unichar_isprint(c)) + { + gdk_event_free(event); + return FALSE; + } + const long int char_width = g_utf8_next_char(str) - str; if(bh->keys_cnt + 1 + char_width < DT_BAUHAUS_MAX_TEXT - && (is_combo || strchr("0123456789.,%%+-*Xx/:^~ ()", event->string[0]))) + && (is_combo || strchr("0123456789.,%%+-*Xx/:^~ ()", str[0]))) { // only accept valid keys for slider; combo is checked in _popup_draw - strncpy(bh->keys + bh->keys_cnt, event->string, char_width); + strncpy(bh->keys + bh->keys_cnt, str, char_width); bh->keys_cnt += char_width; if(!is_combo) _start_cursor(-1); } + gdk_event_free(event); + break; + } } gtk_widget_queue_draw(bh->popup.area); return TRUE; @@ -3867,7 +3936,7 @@ static void _widget_button_press(GtkGestureSingle *gesture, dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); dt_bauhaus_t *bh = darktable.bauhaus; gboolean passthrough_from_histogram = - gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture)) != widget; + dt_gui_get_widget(gesture) != widget; if(!passthrough_from_histogram) { diff --git a/src/chart/main.c b/src/chart/main.c index 60bce19eb21b..59de32d50c16 100644 --- a/src/chart/main.c +++ b/src/chart/main.c @@ -16,6 +16,7 @@ along with darktable. If not, see . */ #include "common/gdk_event_utils.h" +#include "gui/gtk.h" #include "chart/dtcairo.h" #include "chart/colorchart.h" @@ -77,7 +78,7 @@ typedef struct dt_lut_t } dt_lut_t; // boring helper functions -static void init_image(dt_lut_t *self, image_t *image, GCallback motion_cb); +static void init_image(dt_lut_t *self, image_t *image, void (*motion_cb)(GtkEventControllerMotion *, gdouble, gdouble, dt_lut_t *)); static void image_lab_to_xyz(float *image, const int width, const int height); static void map_boundingbox_to_view(image_t *image, point_t *bb); static point_t map_point_to_view(image_t *image, point_t p); @@ -98,9 +99,9 @@ static void get_pixel_region(const image_t *const image, const point_t *const co int *x_end, int *y_end); static void reset_bb(image_t *image); static void free_image(image_t *image); -static gboolean handle_motion(GtkWidget *widget, GdkEventMotion *event, dt_lut_t *self, image_t *image); +static gboolean handle_motion(GtkWidget *widget, gdouble mx, gdouble my, dt_lut_t *self, image_t *image); static int find_closest_corner(point_t *bb, float x, float y); -static void map_mouse_to_0_1(GtkWidget *widget, GdkEventMotion *event, image_t *image, float *x, float *y); +static void map_mouse_to_0_1(GtkWidget *widget, gdouble mx, gdouble my, image_t *image, float *x, float *y); static void update_corner(image_t *image, int which, float *x, float *y); static gboolean open_source_image(dt_lut_t *self, const char *filename); static gboolean open_reference_image(dt_lut_t *self, const char *filename); @@ -170,37 +171,43 @@ static point_t map_point_to_view(image_t *image, point_t p) return result; } -static gboolean motion_notify_callback_source(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +static void _motion_cb_source(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_lut_t *self) { - dt_lut_t *self = (dt_lut_t *)user_data; - const gboolean res = handle_motion(widget, event, self, &self->source); + GtkWidget *widget = dt_gui_get_widget(controller); + const gboolean res = handle_motion(widget, x, y, self, &self->source); if(res) { collect_source_patches(self); update_table(self); } - return res; } -static gboolean motion_notify_callback_reference(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +static void _motion_cb_reference(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_lut_t *self) { - dt_lut_t *self = (dt_lut_t *)user_data; - const gboolean res = handle_motion(widget, event, self, &self->reference); + GtkWidget *widget = dt_gui_get_widget(controller); + const gboolean res = handle_motion(widget, x, y, self, &self->reference); if(res) { collect_reference_patches(self); update_table(self); } - return res; } -static gboolean handle_motion(GtkWidget *widget, GdkEventMotion *event, dt_lut_t *self, image_t *image) +static gboolean handle_motion(GtkWidget *widget, gdouble mx, gdouble my, dt_lut_t *self, image_t *image) { - if(!(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK) || !image->image) return FALSE; + GdkModifierType state; + gtk_get_current_event_state(&state); + if(!(state & GDK_BUTTON1_MASK) || !image->image) return FALSE; // mouse -> 0..1 float x, y; - map_mouse_to_0_1(widget, event, image, &x, &y); + map_mouse_to_0_1(widget, mx, my, image, &x, &y); // dragging the crosses is hard when they are not in a cornerof the bb but sprinkled over the chart int closest_corner = find_closest_corner(image->bb, x, y); @@ -255,13 +262,13 @@ static int find_closest_corner(point_t *bb, float x, float y) return closest_corner; } -static void map_mouse_to_0_1(GtkWidget *widget, GdkEventMotion *event, image_t *image, float *x, float *y) +static void map_mouse_to_0_1(GtkWidget *widget, gdouble mx, gdouble my, image_t *image, float *x, float *y) { guint width = gtk_widget_get_allocated_width(widget); guint height = gtk_widget_get_allocated_height(widget); - *x = (dt_gdk_event_get_x(event) - image->offset_x) / (width - 2.0 * image->offset_x); - *y = (dt_gdk_event_get_y(event) - image->offset_y) / (height - 2.0 * image->offset_y); + *x = (mx - image->offset_x) / (width - 2.0 * image->offset_x); + *y = (my - image->offset_y) / (height - 2.0 * image->offset_y); } static void update_corner(image_t *image, int which, float *x, float *y) @@ -1189,7 +1196,7 @@ static GtkWidget *create_notebook_page_source(dt_lut_t *self) gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("size:"), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), source_shrink, TRUE, TRUE, 0); - init_image(self, &self->source, G_CALLBACK(motion_notify_callback_source)); + init_image(self, &self->source, _motion_cb_source); self->source.draw_colored = TRUE; gtk_box_pack_start(GTK_BOX(page), self->source.drawing_area, TRUE, TRUE, 0); @@ -1242,7 +1249,7 @@ static GtkWidget *create_notebook_page_reference(dt_lut_t *self) gtk_box_pack_start(GTK_BOX(reference_image_box), reference_shrink, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), reference_image_box, TRUE, TRUE, 0); - init_image(self, &self->reference, G_CALLBACK(motion_notify_callback_reference)); + init_image(self, &self->reference, _motion_cb_reference); self->reference.draw_colored = FALSE; gtk_box_pack_start(GTK_BOX(page), self->reference.drawing_area, TRUE, TRUE, 0); @@ -1621,7 +1628,7 @@ static void reset_bb(image_t *image) image->bb[BOTTOM_LEFT].y = 0.95; } -static void init_image(dt_lut_t *self, image_t *image, GCallback motion_cb) +static void init_image(dt_lut_t *self, image_t *image, void (*motion_cb)(GtkEventControllerMotion *, gdouble, gdouble, dt_lut_t *)) { memset(image, 0x00, sizeof(image_t)); image->chart = &self->chart; @@ -1631,7 +1638,7 @@ static void init_image(dt_lut_t *self, image_t *image, GCallback motion_cb) GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); g_signal_connect(image->drawing_area, "size-allocate", G_CALLBACK(size_allocate_callback), image); g_signal_connect(image->drawing_area, "draw", G_CALLBACK(draw_image_callback), image); - g_signal_connect(image->drawing_area, "motion-notify-event", G_CALLBACK(motion_cb), self); + dt_gui_connect_motion(image->drawing_area, motion_cb, NULL, NULL, self); } static void free_image(image_t *image) diff --git a/src/develop/blend_gui.c b/src/develop/blend_gui.c index b9289771678d..c6449d2788b1 100644 --- a/src/develop/blend_gui.c +++ b/src/develop/blend_gui.c @@ -741,11 +741,15 @@ static void _blendop_blend_mode_callback(GtkWidget *combo, } } -static gboolean _blendop_blend_order_clicked(GtkWidget *button, - GdkEventButton *event, +static void _blendop_blend_order_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *module) { - DT_GUARD_GUI_UPDATE(TRUE); + DT_GUARD_GUI_UPDATE(); + + GtkWidget *button = dt_gui_get_widget(gesture); dt_develop_blend_params_t *bp = module->blend_params; const gboolean active = !(bp->blend_mode & DEVELOP_BLEND_REVERSE); @@ -759,8 +763,6 @@ static gboolean _blendop_blend_order_clicked(GtkWidget *button, dt_dev_add_history_item(darktable.develop, module, TRUE); dt_control_queue_redraw_widget(GTK_WIDGET(button)); - - return TRUE; } static void _blendop_masks_combine_callback(GtkWidget *combo, @@ -1348,66 +1350,74 @@ static void _blendop_blendif_feathering_callback(GtkWidget *slider, } } -static gboolean _blendop_blendif_showmask_clicked(GtkToggleButton *button, - GdkEventButton *event, +static void _blendop_blendif_showmask_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *module) { - DT_GUARD_GUI_UPDATE(TRUE); - - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { - const gboolean has_mask_display = - module->request_mask_display - & (DT_DEV_PIXELPIPE_DISPLAY_MASK - | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL); + DT_GUARD_GUI_UPDATE(); - module->request_mask_display &= - ~(DT_DEV_PIXELPIPE_DISPLAY_MASK - | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL - | DT_DEV_PIXELPIPE_DISPLAY_ANY); + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) return; - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK | GDK_SHIFT_MASK)) - module->request_mask_display |= - (DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL); - else if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK)) - module->request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_CHANNEL; - else if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) - module->request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_MASK; - else - module->request_mask_display |= - (has_mask_display ? DT_DEV_PIXELPIPE_DISPLAY_NONE : DT_DEV_PIXELPIPE_DISPLAY_MASK); + GtkWidget *button = dt_gui_get_widget(gesture); - gtk_toggle_button_set_active - (button, - module->request_mask_display != DT_DEV_PIXELPIPE_DISPLAY_NONE); + const gboolean has_mask_display = + module->request_mask_display + & (DT_DEV_PIXELPIPE_DISPLAY_MASK + | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL); + + module->request_mask_display &= + ~(DT_DEV_PIXELPIPE_DISPLAY_MASK + | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL + | DT_DEV_PIXELPIPE_DISPLAY_ANY); + + GdkModifierType state; + gtk_get_current_event_state(&state); + + if(dt_modifier_is(state, GDK_CONTROL_MASK | GDK_SHIFT_MASK)) + module->request_mask_display |= + (DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL); + else if(dt_modifier_is(state, GDK_SHIFT_MASK)) + module->request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_CHANNEL; + else if(dt_modifier_is(state, GDK_CONTROL_MASK)) + module->request_mask_display |= DT_DEV_PIXELPIPE_DISPLAY_MASK; + else + module->request_mask_display |= + (has_mask_display ? DT_DEV_PIXELPIPE_DISPLAY_NONE : DT_DEV_PIXELPIPE_DISPLAY_MASK); - if(module->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(module->off), TRUE); + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(button), + module->request_mask_display != DT_DEV_PIXELPIPE_DISPLAY_NONE); - DT_ENTER_GUI_UPDATE(); + if(module->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(module->off), TRUE); - // (re)set the header mask indicator too - if(module->mask_indicator) - gtk_toggle_button_set_active - (GTK_TOGGLE_BUTTON(module->mask_indicator), - module->request_mask_display != DT_DEV_PIXELPIPE_DISPLAY_NONE); + DT_ENTER_GUI_UPDATE(); - DT_LEAVE_GUI_UPDATE(); + // (re)set the header mask indicator too + if(module->mask_indicator) + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(module->mask_indicator), + module->request_mask_display != DT_DEV_PIXELPIPE_DISPLAY_NONE); - dt_iop_request_focus(module); - dt_iop_refresh_center(module); - } + DT_LEAVE_GUI_UPDATE(); - return TRUE; + dt_iop_request_focus(module); + dt_iop_refresh_center(module); } -static gboolean _blendop_masks_modes_none_clicked(GtkWidget *button, - GdkEventButton *event, +static void _blendop_masks_modes_none_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *module) { - DT_GUARD_GUI_UPDATE(TRUE); + DT_GUARD_GUI_UPDATE(); + GtkWidget *button = dt_gui_get_widget(gesture); dt_iop_gui_blend_data_t *data = module->blend_data; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && data->selected_mask_mode != button) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY + && data->selected_mask_mode != button) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->selected_mask_mode), FALSE); // unsets currently toggled if any @@ -1421,8 +1431,6 @@ static gboolean _blendop_masks_modes_none_clicked(GtkWidget *button, /* and finally remove hinter messages */ dt_control_hinter_message(""); } - - return TRUE; } static gboolean _blendop_masks_modes_toggle(GtkToggleButton *button, @@ -1494,51 +1502,70 @@ static gboolean _blendop_masks_modes_toggle(GtkToggleButton *button, return TRUE; } -static gboolean _blendop_masks_modes_uni_toggled(GtkToggleButton *button, - GdkEventButton *event, +static void _blendop_masks_modes_uni_toggled(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *module) { - return _blendop_masks_modes_toggle(button, module, DEVELOP_MASK_ENABLED); + GtkWidget *button = dt_gui_get_widget(gesture); + _blendop_masks_modes_toggle(GTK_TOGGLE_BUTTON(button), module, DEVELOP_MASK_ENABLED); } -static gboolean _blendop_masks_modes_drawn_toggled(GtkToggleButton *button, - GdkEventButton *event, +static void _blendop_masks_modes_drawn_toggled(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *module) { - return _blendop_masks_modes_toggle(button, module, - DEVELOP_MASK_ENABLED | DEVELOP_MASK_MASK); + GtkWidget *button = dt_gui_get_widget(gesture); + _blendop_masks_modes_toggle(GTK_TOGGLE_BUTTON(button), module, + DEVELOP_MASK_ENABLED | DEVELOP_MASK_MASK); } -static gboolean _blendop_masks_modes_param_toggled(GtkToggleButton *button, - GdkEventButton *event, +static void _blendop_masks_modes_param_toggled(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *module) { - return _blendop_masks_modes_toggle(button, module, - DEVELOP_MASK_ENABLED | DEVELOP_MASK_CONDITIONAL); + GtkWidget *button = dt_gui_get_widget(gesture); + _blendop_masks_modes_toggle(GTK_TOGGLE_BUTTON(button), module, + DEVELOP_MASK_ENABLED | DEVELOP_MASK_CONDITIONAL); } -static gboolean _blendop_masks_modes_both_toggled(GtkToggleButton *button, - GdkEventButton *event, - dt_iop_module_t *module) +static void _blendop_masks_modes_both_toggled(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *module) { - return _blendop_masks_modes_toggle(button, module, - DEVELOP_MASK_ENABLED | DEVELOP_MASK_MASK_CONDITIONAL); + GtkWidget *button = dt_gui_get_widget(gesture); + _blendop_masks_modes_toggle(GTK_TOGGLE_BUTTON(button), module, + DEVELOP_MASK_ENABLED | DEVELOP_MASK_MASK_CONDITIONAL); } -static gboolean _blendop_masks_modes_raster_toggled(GtkToggleButton *button, - GdkEventButton *event, +static void _blendop_masks_modes_raster_toggled(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *module) { - return _blendop_masks_modes_toggle(button, module, - DEVELOP_MASK_ENABLED | DEVELOP_MASK_RASTER); + GtkWidget *button = dt_gui_get_widget(gesture); + _blendop_masks_modes_toggle(GTK_TOGGLE_BUTTON(button), module, + DEVELOP_MASK_ENABLED | DEVELOP_MASK_RASTER); } -static gboolean _blendop_blendif_suppress_toggled(GtkToggleButton *togglebutton, - GdkEventButton *event, +static void _blendop_blendif_suppress_toggled(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *module) { + GtkWidget *togglebutton_w = dt_gui_get_widget(gesture); + GtkToggleButton *togglebutton = GTK_TOGGLE_BUTTON(togglebutton_w); module->suppress_mask = !gtk_toggle_button_get_active(togglebutton); - DT_GUARD_GUI_UPDATE(FALSE); + DT_GUARD_GUI_UPDATE(); if(module->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(module->off), TRUE); dt_iop_request_focus(module); @@ -1547,12 +1574,12 @@ static gboolean _blendop_blendif_suppress_toggled(GtkToggleButton *togglebutton, dt_control_queue_redraw_widget(GTK_WIDGET(togglebutton)); dt_iop_refresh_center(module); - - return TRUE; } -static gboolean _blendop_blendif_reset(GtkButton *button, - GdkEventButton *event, +static void _blendop_blendif_reset(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *module) { module->blend_params->blendif = module->default_blendop_params->blendif; @@ -1564,15 +1591,15 @@ static gboolean _blendop_blendif_reset(GtkButton *button, dt_iop_color_picker_reset(module, FALSE); dt_iop_gui_update_blendif(module); dt_dev_add_history_item(darktable.develop, module, TRUE); - - return TRUE; } -static gboolean _blendop_blendif_invert(GtkButton *button, - GdkEventButton *event, +static void _blendop_blendif_invert(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *module) { - DT_GUARD_GUI_UPDATE(TRUE); + DT_GUARD_GUI_UPDATE(); const dt_iop_gui_blend_data_t *data = module->blend_data; @@ -1598,21 +1625,21 @@ static gboolean _blendop_blendif_invert(GtkButton *button, module->blend_params->mask_combine ^= DEVELOP_COMBINE_INCL; dt_iop_gui_update_blending(module); dt_dev_add_history_item(darktable.develop, module, TRUE); - - return TRUE; } -static gboolean _blendop_masks_add_shape(GtkWidget *widget, - GdkEventButton *event, +static void _blendop_masks_add_shape(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *self) { - if(DT_IN_GUI_UPDATE() - || dt_gdk_event_get_button(event) != GDK_BUTTON_PRIMARY) - return TRUE; + GtkWidget *widget = dt_gui_get_widget(gesture); dt_iop_gui_blend_data_t *bd = self->blend_data; - const gboolean continuous = dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); + GdkModifierType state; + gtk_get_current_event_state(&state); + const gboolean continuous = dt_modifier_is(state, GDK_CONTROL_MASK); // find out who we are int this = -1; @@ -1625,13 +1652,13 @@ static gboolean _blendop_masks_add_shape(GtkWidget *widget, } } - if(this < 0) return FALSE; + if(this < 0) return; #ifdef HAVE_AI if(bd->masks_type[this] == DT_MASKS_OBJECT && !dt_masks_object_available()) { dt_control_log(_("AI model is not available. Check preferences > AI")); - return TRUE; + return; } #endif @@ -1659,88 +1686,86 @@ static gboolean _blendop_masks_add_shape(GtkWidget *widget, } dt_control_queue_redraw_center(); - - return TRUE; } -static gboolean _blendop_masks_show_and_edit(GtkWidget *widget, - GdkEventButton *event, +static void _blendop_masks_show_and_edit(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(FALSE); - darktable.develop->form_gui->creation_continuous = FALSE; darktable.develop->form_gui->creation_continuous_module = NULL; dt_iop_gui_blend_data_t *bd = self->blend_data; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { - dt_iop_request_focus(self); - - DT_ENTER_GUI_UPDATE(); + dt_iop_request_focus(self); - dt_iop_color_picker_reset(self, FALSE); + DT_ENTER_GUI_UPDATE(); - dt_masks_form_t *grp = dt_masks_get_from_id(darktable.develop, - self->blend_params->mask_id); - if(grp && (grp->type & DT_MASKS_GROUP) && grp->points) - { - const gboolean control_button_pressed = - dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); + dt_iop_color_picker_reset(self, FALSE); - switch(bd->masks_shown) - { - case DT_MASKS_EDIT_FULL: - bd->masks_shown = control_button_pressed - ? DT_MASKS_EDIT_RESTRICTED - : DT_MASKS_EDIT_OFF; - break; + GdkModifierType state; + gtk_get_current_event_state(&state); - case DT_MASKS_EDIT_RESTRICTED: - bd->masks_shown = !control_button_pressed - ? DT_MASKS_EDIT_FULL - : DT_MASKS_EDIT_OFF; - break; + dt_masks_form_t *grp = dt_masks_get_from_id(darktable.develop, + self->blend_params->mask_id); + if(grp && (grp->type & DT_MASKS_GROUP) && grp->points) + { + const gboolean control_button_pressed = + dt_modifier_is(state, GDK_CONTROL_MASK); - default: - case DT_MASKS_EDIT_OFF: - bd->masks_shown = control_button_pressed - ? DT_MASKS_EDIT_RESTRICTED - : DT_MASKS_EDIT_FULL; - } - } - else + switch(bd->masks_shown) { - bd->masks_shown = DT_MASKS_EDIT_OFF; - /* remove hinter messages */ - dt_control_hinter_message(""); - } + case DT_MASKS_EDIT_FULL: + bd->masks_shown = control_button_pressed + ? DT_MASKS_EDIT_RESTRICTED + : DT_MASKS_EDIT_OFF; + break; - gtk_toggle_button_set_active - (GTK_TOGGLE_BUTTON(bd->masks_edit), bd->masks_shown != DT_MASKS_EDIT_OFF); - dt_masks_set_edit_mode(self, bd->masks_shown); + case DT_MASKS_EDIT_RESTRICTED: + bd->masks_shown = !control_button_pressed + ? DT_MASKS_EDIT_FULL + : DT_MASKS_EDIT_OFF; + break; - // set all add shape buttons to inactive - for(int n = 0; n < DEVELOP_MASKS_NB_SHAPES; n++) - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_shapes[n]), FALSE); + default: + case DT_MASKS_EDIT_OFF: + bd->masks_shown = control_button_pressed + ? DT_MASKS_EDIT_RESTRICTED + : DT_MASKS_EDIT_FULL; + } + } + else + { + bd->masks_shown = DT_MASKS_EDIT_OFF; + /* remove hinter messages */ + dt_control_hinter_message(""); + } - DT_LEAVE_GUI_UPDATE(); + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON(bd->masks_edit), bd->masks_shown != DT_MASKS_EDIT_OFF); + dt_masks_set_edit_mode(self, bd->masks_shown); - return TRUE; - } + // set all add shape buttons to inactive + for(int n = 0; n < DEVELOP_MASKS_NB_SHAPES; n++) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_shapes[n]), FALSE); - return FALSE; + DT_LEAVE_GUI_UPDATE(); } -static gboolean _blendop_masks_polarity_callback(GtkToggleButton *togglebutton, - GdkEventButton *event, +static void _blendop_masks_polarity_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(TRUE); + DT_GUARD_GUI_UPDATE(); + + GtkWidget *togglebutton = dt_gui_get_widget(gesture); - const int active = !gtk_toggle_button_get_active(togglebutton); - gtk_toggle_button_set_active(togglebutton, active); + const int active = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton), active); dt_develop_blend_params_t *bp = self->blend_params; @@ -1750,9 +1775,7 @@ static gboolean _blendop_masks_polarity_callback(GtkToggleButton *togglebutton, bp->mask_combine &= ~DEVELOP_COMBINE_MASKS_POS; dt_dev_add_history_item(darktable.develop, self, TRUE); - dt_control_queue_redraw_widget(GTK_WIDGET(togglebutton)); - - return TRUE; + dt_control_queue_redraw_widget(togglebutton); } gboolean blend_color_picker_apply(dt_iop_module_t *module, @@ -2192,28 +2215,33 @@ static void _blendop_blendif_channel_mask_view_toggle // magic mode: if mouse cursor enters a gradient slider with shift // and/or control pressed we enter channel display and/or mask display // mode -static gboolean _blendop_blendif_enter(GtkWidget *widget, - GdkEventCrossing *event, - dt_iop_module_t *module) +static void _blendop_blendif_enter_cb(GtkEventControllerMotion *controller, + double x, double y, + dt_iop_module_t *module) { - DT_GUARD_GUI_UPDATE(FALSE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_gui_blend_data_t *data = module->blend_data; dt_dev_pixelpipe_display_mask_t mode = DT_DEV_PIXELPIPE_DISPLAY_NONE; - // depending on shift modifiers we activate channel and/or mask display - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK | GDK_CONTROL_MASK)) + GdkModifierType state; + if(gtk_get_current_event_state(&state)) { - mode = (DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL); - } - else if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK)) - { - mode = DT_DEV_PIXELPIPE_DISPLAY_CHANNEL; - } - else if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) - { - mode = DT_DEV_PIXELPIPE_DISPLAY_MASK; + // depending on shift modifiers we activate channel and/or mask display + if(dt_modifier_is(state, GDK_SHIFT_MASK | GDK_CONTROL_MASK)) + { + mode = (DT_DEV_PIXELPIPE_DISPLAY_MASK | DT_DEV_PIXELPIPE_DISPLAY_CHANNEL); + } + else if(dt_modifier_is(state, GDK_SHIFT_MASK)) + { + mode = DT_DEV_PIXELPIPE_DISPLAY_CHANNEL; + } + else if(dt_modifier_is(state, GDK_CONTROL_MASK)) + { + mode = DT_DEV_PIXELPIPE_DISPLAY_MASK; + } } dt_pthread_mutex_lock(&data->lock); @@ -2235,7 +2263,6 @@ static gboolean _blendop_blendif_enter(GtkWidget *widget, _blendop_blendif_channel_mask_view(widget, module, mode); gtk_widget_grab_focus(widget); - return FALSE; } @@ -2265,11 +2292,10 @@ static gboolean _blendop_blendif_leave_delayed(gpointer data) } // de-activate magic mode when leaving the gradient slider -static gboolean _blendop_blendif_leave(GtkWidget *widget, - GdkEventCrossing *event, - dt_iop_module_t *module) +static void _blendop_blendif_leave_cb(GtkEventControllerMotion *controller, + dt_iop_module_t *module) { - DT_GUARD_GUI_UPDATE(FALSE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; dt_iop_gui_blend_data_t *data = module->blend_data; @@ -2283,24 +2309,25 @@ static gboolean _blendop_blendif_leave(GtkWidget *widget, != (data->save_for_leave & ~DT_DEV_PIXELPIPE_DISPLAY_STICKY))) data->timeout_handle = g_timeout_add(1000, _blendop_blendif_leave_delayed, module); dt_pthread_mutex_unlock(&data->lock); - - return FALSE; } -static gboolean _blendop_blendif_key_press(GtkWidget *widget, - GdkEventKey *event, - dt_iop_module_t *module) +static gboolean _blendop_blendif_key_press_cb(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_iop_module_t *module) { - DT_GUARD_GUI_UPDATE(FALSE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return FALSE; + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_gui_blend_data_t *data = module->blend_data; gboolean handled = FALSE; const int tab = data->tab; const int in_out = (widget == GTK_WIDGET(data->filter[1].slider)) ? 1 : 0; - switch(dt_gdk_event_get_keyval(event)) + switch(keyval) { case GDK_KEY_a: case GDK_KEY_A: @@ -2656,12 +2683,8 @@ void dt_iop_gui_init_blendif(GtkWidget *blendw, dt_iop_module_t *module) G_CALLBACK(_blendop_blendif_sliders_callback), bd); g_signal_connect(G_OBJECT(sl->slider), "value-reset", G_CALLBACK(_blendop_blendif_sliders_reset_callback), bd); - g_signal_connect(G_OBJECT(sl->slider), "leave-notify-event", - G_CALLBACK(_blendop_blendif_leave), module); - g_signal_connect(G_OBJECT(sl->slider), "enter-notify-event", - G_CALLBACK(_blendop_blendif_enter), module); - g_signal_connect(G_OBJECT(sl->slider), "key-press-event", - G_CALLBACK(_blendop_blendif_key_press), module); + dt_gui_connect_motion(sl->slider, NULL, _blendop_blendif_enter_cb, _blendop_blendif_leave_cb, module); + dt_gui_connect_key(sl->slider, _blendop_blendif_key_press_cb, module); g_signal_connect(G_OBJECT(sl->polarity), "toggled", G_CALLBACK(_blendop_blendif_polarity_callback), bd); @@ -2957,20 +2980,22 @@ void dt_iop_gui_update_raster(dt_iop_module_t *module) _raster_combo_populate(bd->raster_combo, &module); } -static gboolean _raster_polarity_callback(GtkToggleButton *togglebutton, - GdkEventButton *event, +static void _raster_polarity_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(TRUE); + DT_GUARD_GUI_UPDATE(); + + GtkWidget *togglebutton = dt_gui_get_widget(gesture); dt_develop_blend_params_t *bp = self->blend_params; bp->raster_mask_invert ^= TRUE; - gtk_toggle_button_set_active(togglebutton, bp->raster_mask_invert); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton), bp->raster_mask_invert); dt_dev_add_history_item(darktable.develop, self, TRUE); - - return TRUE; } void dt_iop_gui_init_raster(GtkWidget *blendw, dt_iop_module_t *module) diff --git a/src/develop/imageop.c b/src/develop/imageop.c index 1f7531228f20..bb9099601310 100644 --- a/src/develop/imageop.c +++ b/src/develop/imageop.c @@ -449,27 +449,27 @@ void dt_iop_init_pipe(dt_iop_module_t *module, piece->blendop_data = calloc(1, sizeof(dt_develop_blend_params_t)); } -static gboolean _header_enter_notify_callback(GtkWidget *eventbox, - GdkEventCrossing *event, - gpointer user_data) +static void _header_enter_notify_callback(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + gpointer user_data) { darktable.control->element = GPOINTER_TO_INT(user_data); - return FALSE; } -static gboolean _header_motion_notify_show_callback(GtkWidget *eventbox, - GdkEventCrossing *event, - dt_iop_module_t *module) +static void _header_motion_notify_show_callback(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *module) { darktable.control->element = DT_ACTION_ELEMENT_SHOW; - return dt_iop_show_hide_header_buttons(module, event, TRUE, FALSE); + dt_iop_show_hide_header_buttons(module, NULL, TRUE, FALSE); } -static gboolean _header_motion_notify_hide_callback(GtkWidget *eventbox, - GdkEventCrossing *event, - dt_iop_module_t *module) +static void _header_motion_notify_hide_callback(GtkEventControllerMotion *controller, + dt_iop_module_t *module) { - return dt_iop_show_hide_header_buttons(module, event, FALSE, FALSE); + dt_iop_show_hide_header_buttons(module, NULL, FALSE, FALSE); } static void _header_menu_deactivate_callback(GtkMenuShell *menushell, @@ -824,6 +824,12 @@ static void _gui_duplicate_callback(GtkButton *button, dt_iop_module_t *base) dt_iop_gui_rename_module(module); } +static gboolean _rename_module_key_pressed(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_iop_module_t *module); + static gboolean _rename_module_key_press(GtkWidget *entry, GdkEventKey *event, dt_iop_module_t *module) @@ -919,17 +925,15 @@ void dt_iop_gui_rename_module(dt_iop_module_t *module) gtk_widget_hide(module->instance_name); gtk_widget_add_events(entry, GDK_FOCUS_CHANGE_MASK); - g_signal_connect(entry, "key-press-event", - G_CALLBACK(_rename_module_key_press), module); + dt_gui_connect_key(entry, _rename_module_key_pressed, module); g_signal_connect(entry, "focus-out-event", G_CALLBACK(_rename_module_key_press), module); g_signal_connect(entry, "style-updated", G_CALLBACK(_rename_module_resize), module); g_signal_connect(entry, "changed", G_CALLBACK(_rename_module_resize), module); - g_signal_connect(entry, "enter-notify-event", - G_CALLBACK(_header_enter_notify_callback), - GINT_TO_POINTER(DT_ACTION_ELEMENT_SHOW)); + dt_gui_connect_motion(entry, NULL, _header_enter_notify_callback, NULL, + GINT_TO_POINTER(DT_ACTION_ELEMENT_SHOW)); dt_iop_show_hide_header_buttons(module, NULL, FALSE, TRUE); // before adding entry gtk_box_pack_start(GTK_BOX(module->header), entry, TRUE, TRUE, 0); @@ -1056,18 +1060,77 @@ static gboolean _gui_multiinstance_callback(GtkButton *button, return TRUE; } -static gboolean _gui_off_button_press(GtkButton *w, - GdkEventButton *e, - dt_iop_module_t *module) +static void _gui_off_button_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *module) { if(module->operation_tags() & IOP_TAG_DISTORT) { DT_CONTROL_SIGNAL_RAISE(DT_SIGNAL_DEVELOP_DISTORT); } - if(!DT_IN_GUI_UPDATE() && dt_modifier_is(e->state, GDK_CONTROL_MASK)) + if(!DT_IN_GUI_UPDATE() && (dt_key_modifier_state() & GDK_CONTROL_MASK)) { dt_iop_request_focus(dt_dev_gui_module() == module ? NULL : module); + } +} + +static void _gui_multiinstance_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *module) +{ + _gui_multiinstance_callback(NULL, NULL, module); +} + +static gboolean _rename_module_key_pressed(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_iop_module_t *module) +{ + GtkWidget *entry = dt_gui_get_widget(controller); + gboolean ended = FALSE; + + if(keyval == GDK_KEY_Return || keyval == GDK_KEY_KP_Enter) + { + if(gtk_entry_get_text_length(GTK_ENTRY(entry)) > 0) + { + const gchar *name = gtk_entry_get_text(GTK_ENTRY(entry)); + gchar *current_name = dt_util_localize_segmented_name(module->multi_name, FALSE); + if(g_strcmp0(current_name, name) != 0) + { + dt_iop_update_multi_name(module, name, TRUE, TRUE, TRUE); + } + g_free(current_name); + } + else + { + dt_iop_update_multi_name(module, "", FALSE, FALSE, TRUE); + } + + dt_dev_write_history(darktable.develop); + dt_image_synch_xmp(darktable.develop->image_storage.id); + ended = TRUE; + } + else if(keyval == GDK_KEY_Escape) + { + ended = TRUE; + } + + if(ended) + { + gtk_widget_show(module->instance_name); + g_signal_handlers_disconnect_by_func(entry, + G_CALLBACK(_rename_module_key_press), + module); + gtk_widget_destroy(entry); + dt_iop_show_hide_header_buttons(module, NULL, TRUE, FALSE); + dt_iop_gui_update_header(module); + dt_masks_group_update_name(module); return TRUE; } @@ -2305,6 +2368,7 @@ void dt_iop_gui_reset(dt_iop_module_t *module) DT_LEAVE_GUI_UPDATE(); } +// kept for direct callers from accelerators static gboolean _gui_reset_callback(GtkButton *button, GdkEventButton *event, dt_iop_module_t *module) @@ -2345,6 +2409,46 @@ static gboolean _gui_reset_callback(GtkButton *button, return TRUE; } +static void _gui_reset_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *module) +{ + // never use the callback if module is always disabled + const gboolean disabled = !module->default_enabled && module->hide_enable_button; + if(disabled) return; + + // Ctrl is used to apply any auto-presets to the current module + // If Ctrl was not pressed, or no auto-presets were applied, reset the module parameters + if(!((dt_key_modifier_state() & GDK_CONTROL_MASK) + && dt_gui_presets_autoapply_for_module(module, NULL))) + { + // if a drawn mask is set, remove it from the list + if(dt_is_valid_maskid(module->blend_params->mask_id)) + { + dt_masks_form_t *grp = + dt_masks_get_from_id(darktable.develop, module->blend_params->mask_id); + if(grp) dt_masks_form_remove(module, NULL, grp); + } + /* reset to default params */ + dt_iop_reload_defaults(module); + dt_iop_commit_blend_params(module, module->default_blendop_params); + + /* reset ui to its defaults */ + dt_iop_gui_reset(module); + + /* update ui to default params*/ + dt_iop_gui_update(module); + + dt_dev_add_history_item(module->dev, module, TRUE); + } + + // rebuild the accelerators + dt_iop_connect_accels_multi(module->so); +} + +// kept for direct callers from accelerators static gboolean _presets_popup_callback(GtkButton *button, GdkEventButton *event, dt_iop_module_t *module) @@ -2363,21 +2467,37 @@ static gboolean _presets_popup_callback(GtkButton *button, return TRUE; } -static gboolean _presets_scroll_callback(GtkWidget *widget, - GdkEventScroll *event, - dt_iop_module_t *module) +static void _presets_popup_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *module) { - if(dt_gui_ignore_scroll(event)) return FALSE; + const gboolean disabled = !module->default_enabled && module->hide_enable_button; + if(disabled) return; + + GtkWidget *button = dt_gui_get_widget(gesture); + GtkMenu *menu = dt_gui_presets_popup_menu_show_for_module(module); + + g_signal_connect(G_OBJECT(menu), "deactivate", + G_CALLBACK(_header_menu_deactivate_callback), module); + + dt_gui_menu_popup(menu, + button, GDK_GRAVITY_SOUTH_EAST, GDK_GRAVITY_NORTH_EAST); +} + +static void _presets_scrolled(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_iop_module_t *module) +{ + if(dy == 0.0 && dx == 0.0) return; // preset cycling: right==down==next int delta_x = 0, delta_y = 0; - if(dt_gui_get_scroll_unit_deltas(event, &delta_x, &delta_y)) - { - const int delta = abs(delta_x) > abs(delta_y) ? delta_x : delta_y; - dt_gui_presets_apply_adjacent_preset(module, delta); - } - - return TRUE; + dt_gui_get_scroll_unit_deltas_fallback(dx, dy, &delta_x, &delta_y); + const int delta = abs(delta_x) > abs(delta_y) ? delta_x : delta_y; + dt_gui_presets_apply_adjacent_preset(module, delta); } gboolean dt_iop_has_focus(const dt_iop_module_t *module) @@ -2586,48 +2706,59 @@ void dt_iop_gui_update_expanded(dt_iop_module_t *module) dtgtk_expander_set_expanded(DTGTK_EXPANDER(module->expander), expanded); } -static gboolean _iop_plugin_body_button_press(GtkWidget *w, - GdkEventButton *e, - gpointer user_data) +static void _iop_plugin_body_pressed(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *module) { - dt_iop_module_t *module = (dt_iop_module_t *)user_data; - if(e->button == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { dt_iop_request_focus(module); - return TRUE; } - else if(e->button == GDK_BUTTON_SECONDARY) + else if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY) { _presets_popup_callback(NULL, NULL, module); - - return TRUE; } - return FALSE; } -static gboolean _iop_plugin_header_button_release(GtkWidget *w, - GdkEventButton *e, - gpointer user_data) +static void _iop_plugin_header_released(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *module) { - if(e->type == GDK_2BUTTON_PRESS || e->type == GDK_3BUTTON_PRESS) return TRUE; - if(GTK_IS_BUTTON(gtk_get_event_widget((GdkEvent*)e))) return FALSE; + if(n_press >= 2) return; - dt_iop_module_t *module = (dt_iop_module_t *)user_data; + // ignore clicks on buttons inside the header (presets, reset, enable, multiinstance) + // GTK4: use gtk_gesture_get_last_event(gesture) instead of gtk_get_current_event() + GdkEvent *event = gtk_get_current_event(); + if(event) + { + if(GTK_IS_BUTTON(gtk_get_event_widget(event))) + { + gdk_event_free(event); + return; + } + gdk_event_free(event); + } + + const guint button = gtk_gesture_single_get_current_button(gesture); - if(e->button == GDK_BUTTON_PRIMARY) + if(button == GDK_BUTTON_PRIMARY) { - if(dt_modifier_is(e->state, GDK_SHIFT_MASK | GDK_CONTROL_MASK)) + if(dt_modifier_is(dt_key_modifier_state(), GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ; // do nothing (for easier dragging) - else if(dt_modifier_is(e->state, GDK_CONTROL_MASK)) + else if(dt_modifier_is(dt_key_modifier_state(), GDK_CONTROL_MASK)) { dt_iop_gui_rename_module(module); - return TRUE; + return; } else { const gboolean collapse_others = !dt_conf_get_bool("darkroom/ui/single_module") - != (!dt_modifier_is(e->state, GDK_SHIFT_MASK)); + != (!dt_modifier_is(dt_key_modifier_state(), GDK_SHIFT_MASK)); dt_iop_gui_set_expanded(module, !module->expanded, collapse_others); @@ -2637,16 +2768,14 @@ static gboolean _iop_plugin_header_button_release(GtkWidget *w, //used to take focus away from module search text input box when module selected gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui)); - return TRUE; + return; } } - else if(e->button == GDK_BUTTON_SECONDARY) + else if(button == GDK_BUTTON_SECONDARY) { _presets_popup_callback(NULL, NULL, module); - - return TRUE; + return; } - return FALSE; } static void _header_size_callback(GtkWidget *widget, @@ -3023,12 +3152,10 @@ GtkWidget *dt_iop_gui_header_button(dt_iop_module_t *module, GtkWidget *header) { GtkWidget *button; - gpointer callback = _gui_multiinstance_callback; if(element == DT_ACTION_ELEMENT_ENABLE) { button = dtgtk_togglebutton_new(paint, 0, module); - callback = _gui_off_button_press; char tooltip[512]; gchar *module_label = dt_history_item_get_name(module); @@ -3047,23 +3174,27 @@ GtkWidget *dt_iop_gui_header_button(dt_iop_module_t *module, button = dtgtk_button_new(paint, 0, NULL); if(element == DT_ACTION_ELEMENT_RESET) { - callback = _gui_reset_callback; gtk_widget_set_tooltip_text (button, _("reset parameters\nctrl+click to reapply any automatic presets")); } else if(element == DT_ACTION_ELEMENT_PRESETS) { - callback = _presets_popup_callback; - g_signal_connect(button, "scroll-event", - G_CALLBACK(_presets_scroll_callback), module); - gtk_widget_add_events(button, darktable.gui->scroll_mask); + dt_gui_connect_scroll(button, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _presets_scrolled, module); } gtk_box_pack_end(GTK_BOX(header), button, FALSE, FALSE, 0); } - g_signal_connect(button, "enter-notify-event", - G_CALLBACK(_header_enter_notify_callback), - GINT_TO_POINTER(element)); - g_signal_connect_data(button, "button-press-event", G_CALLBACK(callback), module, NULL, 0); + dt_gui_connect_motion(button, NULL, _header_enter_notify_callback, NULL, + GINT_TO_POINTER(element)); + dt_gui_connect_click(button, (element == DT_ACTION_ELEMENT_ENABLE) + ? _gui_off_button_clicked + : (element == DT_ACTION_ELEMENT_RESET) + ? _gui_reset_clicked + : (element == DT_ACTION_ELEMENT_PRESETS) + ? _presets_popup_clicked + : _gui_multiinstance_clicked, + NULL, module); dt_action_define(&module->so->actions, NULL, NULL, button, NULL); gtk_widget_show(button); @@ -3180,22 +3311,16 @@ void dt_iop_gui_set_expander(dt_iop_module_t *module) module->header = header; /* setup the header box */ - g_signal_connect(G_OBJECT(header_evb), "button-release-event", - G_CALLBACK(_iop_plugin_header_button_release), module); - gtk_widget_add_events(header_evb, GDK_POINTER_MOTION_MASK); - g_signal_connect(G_OBJECT(header_evb), "enter-notify-event", - G_CALLBACK(_header_motion_notify_show_callback), module); - g_signal_connect(G_OBJECT(header_evb), "leave-notify-event", - G_CALLBACK(_header_motion_notify_hide_callback), module); + dt_gui_connect_click(header_evb, NULL, _iop_plugin_header_released, module); + dt_gui_connect_motion(header_evb, NULL, + _header_motion_notify_show_callback, + _header_motion_notify_hide_callback, module); /* connect mouse button callbacks for focus and presets */ - g_signal_connect(G_OBJECT(body_evb), "button-press-event", - G_CALLBACK(_iop_plugin_body_button_press), module); - gtk_widget_add_events(body_evb, GDK_POINTER_MOTION_MASK); - g_signal_connect(G_OBJECT(body_evb), "enter-notify-event", - G_CALLBACK(_header_motion_notify_show_callback), module); - g_signal_connect(G_OBJECT(body_evb), "leave-notify-event", - G_CALLBACK(_header_motion_notify_hide_callback), module); + dt_gui_connect_click(body_evb, _iop_plugin_body_pressed, NULL, module); + dt_gui_connect_motion(body_evb, NULL, + _header_motion_notify_show_callback, + _header_motion_notify_hide_callback, module); /* * initialize the header widgets @@ -3236,9 +3361,8 @@ void dt_iop_gui_set_expander(dt_iop_module_t *module) } dt_action_define(&module->so->actions, NULL, NULL, lab, NULL); - g_signal_connect(lab, "enter-notify-event", - G_CALLBACK(_header_enter_notify_callback), - GINT_TO_POINTER(DT_ACTION_ELEMENT_SHOW)); + dt_gui_connect_motion(lab, NULL, _header_enter_notify_callback, NULL, + GINT_TO_POINTER(DT_ACTION_ELEMENT_SHOW)); /* add right side header buttons */ module->presets_button = dt_iop_gui_header_button(module, diff --git a/src/develop/imageop_gui.c b/src/develop/imageop_gui.c index 5112a3d8b75b..1e74365ae622 100644 --- a/src/develop/imageop_gui.c +++ b/src/develop/imageop_gui.c @@ -241,12 +241,36 @@ GtkWidget *dt_bauhaus_toggle_from_params(dt_iop_module_t *self, const char *para return toggle; } +/* Claim the event sequence in CAPTURE phase so the togglebutton's + * internal GtkGestureMultiPress (GTK_PHASE_BUBBLE) does NOT process + * the event. This prevents GtkButton from emitting "clicked" and + * toggling the button state behind our callback, which would conflict + * with callbacks that implement radio-button behaviour by explicitly + * managing all toggle states. + * + * GTK4 migration: the pattern is the same — just rename + * GtkGestureMultiPress to GtkGestureClick. */ +static void _gesture_begin_claim(GtkGesture *gesture, + GdkEventSequence *sequence, + gpointer user_data) +{ + gtk_gesture_set_sequence_state(gesture, sequence, GTK_EVENT_SEQUENCE_CLAIMED); +} + GtkWidget *dt_iop_togglebutton_new(dt_iop_module_t *self, const char *section, const gchar *label, const gchar *ctrl_label, GCallback callback, gboolean local, guint accel_key, GdkModifierType mods, DTGTKCairoPaintIconFunc paint, GtkWidget *box) { GtkWidget *w = dtgtk_togglebutton_new(paint, 0, NULL); - g_signal_connect_data(G_OBJECT(w), "button-press-event", callback, self, NULL, 0); + { + GtkGesture *gesture = gtk_gesture_multi_press_new(w); + gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(gesture), + GTK_PHASE_CAPTURE); + g_object_weak_ref(G_OBJECT(w), (GWeakNotify)g_object_unref, gesture); + g_signal_connect_data(gesture, "pressed", callback, self, NULL, 0); + g_signal_connect(gesture, "begin", G_CALLBACK(_gesture_begin_claim), NULL); + gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 0); + } if(!ctrl_label) gtk_widget_set_tooltip_text(w, _(label)); diff --git a/src/dtgtk/culling.c b/src/dtgtk/culling.c index 22f1c5c58200..df2b61bec7d1 100644 --- a/src/dtgtk/culling.c +++ b/src/dtgtk/culling.c @@ -651,26 +651,33 @@ static gboolean _event_gesture(GtkWidget *widget, return TRUE; } -static gboolean _event_scroll(GtkWidget *widget, - GdkEvent *event, - gpointer user_data) +static void _event_scroll(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_culling_t *table) { - GdkEventScroll *e = (GdkEventScroll *)event; - dt_culling_t *table = (dt_culling_t *)user_data; + GdkEvent *event = gtk_get_current_event(); + if(!event) return; + + GdkDevice *device = dt_gdk_event_get_source_device(event); + const GdkScrollDirection direction = dt_gdk_event_get_scroll_direction(event); + const gboolean is_stop = gdk_event_is_scroll_stop_event(event); + const GdkModifierType state = dt_gdk_event_get_state(event); + const gdouble x_root = dt_gdk_event_get_root_x(event); + const gdouble y_root = dt_gdk_event_get_root_y(event); - GdkDevice *device = gdk_event_get_source_device(event); dt_print(DT_DEBUG_INPUT, "[culling scroll] direction=%d smooth=%s stop=%s ctrl=%s" " device='%s' source-type=%d x_root=%.1f y_root=%.1f" " delta_x=%.3f delta_y=%.3f state=0x%x", - e->direction, - e->direction == GDK_SCROLL_SMOOTH ? "yes" : "no", - e->is_stop ? "yes" : "no", - dt_modifier_is(e->state, GDK_CONTROL_MASK) ? "yes" : "no", + direction, + direction == GDK_SCROLL_SMOOTH ? "yes" : "no", + is_stop ? "yes" : "no", + dt_modifier_is(state, GDK_CONTROL_MASK) ? "yes" : "no", device ? gdk_device_get_name(device) : "", device ? (int)gdk_device_get_source(device) : -1, - e->x_root, e->y_root, - e->delta_x, e->delta_y, e->state); + x_root, y_root, + dx, dy, state); // ctrl + smooth scroll: apply fractional zoom on every event so the zoom // feels seamless rather than stepped. This must come before the integer- @@ -683,19 +690,19 @@ static gboolean _event_scroll(GtkWidget *widget, // must be finalised regardless. (If no zoom was pending this is a no-op, and // the safety-net timer in _thumbs_zoom_add covers devices that never emit a // stop event at all.) - if(e->direction == GDK_SCROLL_SMOOTH && e->is_stop) + if(direction == GDK_SCROLL_SMOOTH && is_stop) { + gdk_event_free(event); dt_culling_zoom_end(table); - return TRUE; + return; } - if(e->direction == GDK_SCROLL_SMOOTH && !e->is_stop - && dt_modifiers_include(e->state, GDK_CONTROL_MASK)) + if(direction == GDK_SCROLL_SMOOTH && !is_stop + && dt_modifiers_include(state, GDK_CONTROL_MASK)) { - gdouble dx = 0.0, dy = 0.0; - if(dt_gui_get_scroll_deltas(e, &dx, &dy) && (dx != 0.0 || dy != 0.0)) + if(dx != 0.0 || dy != 0.0) { - // dt_gui_get_scroll_deltas gives the raw fractional platform delta. + // controller dx/dy gives the raw fractional platform delta. // Scale so that one full unit of scroll (delta_y == 1.0) matches the // 0.5 zoom_delta of a discrete mouse-wheel click. right==up==zoom-in const gdouble delta = fabs(dx) > fabs(dy) ? -dx : dy; @@ -705,22 +712,23 @@ static gboolean _event_scroll(GtkWidget *widget, GdkWindow *win = gtk_widget_get_window(table->widget); if(win) gdk_window_get_origin(win, &ox, &oy); - const float x_culling = e->x_root - ox; - const float y_culling = e->y_root - oy; + const float x_culling = x_root - ox; + const float y_culling = y_root - oy; dt_print(DT_DEBUG_INPUT, "[culling scroll] ctrl+smooth zoom_delta=%.4f x_culling=%.1f y_culling=%.1f", zoom_delta, x_culling, y_culling); if(fabsf(zoom_delta) > 0.001f) - _thumbs_zoom_add(table, zoom_delta, x_culling, y_culling, e->state, TRUE); + _thumbs_zoom_add(table, zoom_delta, x_culling, y_culling, state, TRUE); } - return TRUE; + gdk_event_free(event); + return; } // Smooth scroll (touchpad two-finger swipe): pan zoomed images or navigate images. // We check before the unit-delta path so fractional smooth scroll is used for panning // with full fidelity rather than being accumulated into integer steps. - if(e->direction == GDK_SCROLL_SMOOTH && !e->is_stop - && !dt_modifiers_include(e->state, GDK_CONTROL_MASK)) + if(direction == GDK_SCROLL_SMOOTH && !is_stop + && !dt_modifiers_include(state, GDK_CONTROL_MASK)) { // Check if any thumbnail is zoomed in; if so, pan instead of navigate. float fz = 1.0f; @@ -734,29 +742,31 @@ static gboolean _event_scroll(GtkWidget *widget, fz, fz > 1.0f ? "pan path" : "navigate path"); if(fz > 1.0f) { - gdouble dx = 0.0, dy = 0.0; - if(dt_gui_get_scroll_deltas(e, &dx, &dy) && (dx != 0.0 || dy != 0.0)) + if(dx != 0.0 || dy != 0.0) { - // dt_gui_get_scroll_deltas returns platform-normalised fractional units; + // controller dx/dy is platform-normalised fractional units; // scale to pixel-scale (matches the factor used by the center-widget pan path). dt_print(DT_DEBUG_INPUT, "[culling scroll] panning dx=%.3f dy=%.3f (scaled: dx=%.1f dy=%.1f)", dx, dy, dx * 50.0, dy * 50.0); - dt_culling_pan_move(table, (float)(-dx * 50.0), (float)(-dy * 50.0), e->state); + dt_culling_pan_move(table, (float)(-dx * 50.0), (float)(-dy * 50.0), state); } else { - dt_print(DT_DEBUG_INPUT, "[culling scroll] smooth pan: no delta from dt_gui_get_scroll_deltas"); + dt_print(DT_DEBUG_INPUT, "[culling scroll] smooth pan: no delta from controller"); } - return TRUE; + gdk_event_free(event); + return; } } + // discrete scroll: use unit deltas for zoom/navigation + const GdkEventScroll *scroll_event = (const GdkEventScroll *)event; int delta_x = 0, delta_y = 0; - if(dt_gui_get_scroll_unit_deltas(e, &delta_x, &delta_y)) + if(dt_gui_get_scroll_unit_deltas(scroll_event, &delta_x, &delta_y)) { const gboolean is_horizontal = abs(delta_x) > abs(delta_y); - if(dt_modifiers_include(e->state, GDK_CONTROL_MASK)) + if(dt_modifiers_include(state, GDK_CONTROL_MASK)) { // zooming: right==up==zoom-in const int delta = is_horizontal ? -delta_x : delta_y; @@ -766,8 +776,8 @@ static gboolean _event_scroll(GtkWidget *widget, GdkWindow *win = gtk_widget_get_window(table->widget); if(win) gdk_window_get_origin(win, &ox, &oy); - const float x_culling = e->x_root - ox; - const float y_culling = e->y_root - oy; + const float x_culling = x_root - ox; + const float y_culling = y_root - oy; dt_print(DT_DEBUG_INPUT, "[culling scroll] ctrl+scroll zoom_delta=%.2f x_culling=%.1f y_culling=%.1f", zoom_delta, x_culling, y_culling); @@ -775,7 +785,7 @@ static gboolean _event_scroll(GtkWidget *widget, // smooth is_stop event to finalise it, so reload the surface immediately // (still cheap — the native mipmap surface cache is reused). Leaving it // deferred would freeze the image at a stale, scaled-up resolution. - _thumbs_zoom_add(table, zoom_delta, x_culling, y_culling, e->state, FALSE); + _thumbs_zoom_add(table, zoom_delta, x_culling, y_culling, state, FALSE); } else { @@ -786,7 +796,7 @@ static gboolean _event_scroll(GtkWidget *widget, _thumbs_move(table, move); } } - return TRUE; + gdk_event_free(event); } static gboolean _event_draw(GtkWidget *widget, @@ -809,118 +819,137 @@ static gboolean _event_draw(GtkWidget *widget, return FALSE; // let's propagate this event } -static gboolean _event_leave_notify(GtkWidget *widget, - GdkEventCrossing *event, - gpointer user_data) +static void _event_leave_cb(GtkEventControllerMotion *controller, + dt_culling_t *table) { - dt_culling_t *table = (dt_culling_t *)user_data; + GtkWidget *widget = dt_gui_get_widget(controller); // if the leaving cause is the hide of the widget, no mouseover change if(!gtk_widget_is_visible(widget)) { table->mouse_inside = FALSE; - return FALSE; + return; } - // if we leave thumbtable in favour of an inferior (a thumbnail) - // it's not a real leave ! same if this is not a mouse move action - // (shortcut that activate a button for example) - if(event->detail == GDK_NOTIFY_INFERIOR - || event->mode == GDK_CROSSING_GTK_GRAB - || event->mode == GDK_CROSSING_GRAB) - return FALSE; - table->mouse_inside = FALSE; - dt_control_set_mouse_over_id(NO_IMGID); - return TRUE; + + // check crossing detail: don't clear mouse_over_id when leaving to + // a child widget (thumbnail) or due to a grab + GdkEvent *event = gtk_get_current_event(); + if(event) + { + if(event->crossing.detail != GDK_NOTIFY_INFERIOR + && event->crossing.mode != GDK_CROSSING_GTK_GRAB + && event->crossing.mode != GDK_CROSSING_GRAB) + dt_control_set_mouse_over_id(NO_IMGID); + gdk_event_free(event); + } } -static gboolean _event_enter_notify(GtkWidget *widget, - GdkEventCrossing *event, - gpointer user_data) +static void _event_enter_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_culling_t *table) { - // we only handle the case where we enter thumbtable from an - // inferior (a thumbnail) this is when the mouse enter an "empty" - // area of thumbtable - if(event->detail != GDK_NOTIFY_INFERIOR) return FALSE; - - dt_control_set_mouse_over_id(NO_IMGID); - return TRUE; + // when entering the culling area from a child thumbnail (INFERIOR), + // clear the mouse-over id since we're now in the empty area + GdkEvent *event = gtk_get_current_event(); + if(event) + { + if(event->crossing.detail == GDK_NOTIFY_INFERIOR) + dt_control_set_mouse_over_id(NO_IMGID); + gdk_event_free(event); + } } -static gboolean _event_button_press(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static void _event_button_press_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_culling_t *table) { - dt_culling_t *table = (dt_culling_t *)user_data; + /* + * GTK3 bridge for GDK_2BUTTON_PRESS (see dt_gui_connect_double_click): + * GtkGestureMultiPress doesn't process multi-press events, so when a + * double/triple-click is detected by the classic "button-press-event" + * signal handler, it calls this callback with gesture=NULL. In that + * case the button is always GDK_BUTTON_PRIMARY (GDK_2BUTTON_PRESS only + * occurs for primary button). + * + * GTK4 migration: remove the ternary and just call + * gtk_gesture_single_get_current_button(gesture) directly — GtkGestureClick + * will provide a valid gesture pointer for all n_press values. */ + const guint button = gesture + ? gtk_gesture_single_get_current_button(gesture) + : GDK_BUTTON_PRIMARY; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY - && dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS) + if(button == GDK_BUTTON_PRIMARY && n_press == 1) { // make sure any edition field loses the focus gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui)); } - if(dt_gdk_event_get_button(event) == GDK_BUTTON_MIDDLE) + if(button == GDK_BUTTON_MIDDLE) { - // convert screen coordinates to culling coordinates - int ox = 0, oy = 0; - GdkWindow *win = gtk_widget_get_window(table->widget); - if(win) - gdk_window_get_origin(win, &ox, &oy); - const float x_culling = dt_gdk_event_get_root_x(event) - ox; - const float y_culling = dt_gdk_event_get_root_y(event) - oy; - // if shift is pressed, we work only with image hovered - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK)) - _toggle_zoom_current(table, x_culling, y_culling); + GdkModifierType state; + gtk_get_current_event_state(&state); + if(dt_modifier_is(state, GDK_SHIFT_MASK)) + _toggle_zoom_current(table, x, y); else - _toggle_zoom_all(table, x_culling, y_culling); - return TRUE; + _toggle_zoom_all(table, x, y); + return; } - const dt_imgid_t id = dt_control_get_mouse_over_id(); - if(dt_is_valid_imgid(id) - && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY - && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) - { - // we have to set again the selected image, because it was deselected - // during the previous GDK_BUTTON_PRESS event - const dt_imgid_t old_selection = table->selection; - table->selection = id; - dt_view_manager_switch(darktable.view_manager, "darkroom"); - if (id != old_selection) + if(button == GDK_BUTTON_PRIMARY && n_press == 2) + { + const dt_imgid_t id = dt_control_get_mouse_over_id(); + if(dt_is_valid_imgid(id)) { - _update_selected_thumbnail(table, old_selection); - dt_act_on_reset_cache(TRUE); - dt_act_on_reset_cache(FALSE); + // we have to set again the selected image, because it was deselected + // during the previous GDK_BUTTON_PRESS event + const dt_imgid_t old_selection = table->selection; + table->selection = id; + dt_view_manager_switch(darktable.view_manager, "darkroom"); + if(id != old_selection) + { + _update_selected_thumbnail(table, old_selection); + dt_act_on_reset_cache(TRUE); + dt_act_on_reset_cache(FALSE); + } } - return TRUE; + return; } - table->pan_x = dt_gdk_event_get_root_x(event); - table->pan_y = dt_gdk_event_get_root_y(event); + // start panning — need root coordinates for pan tracking + const GdkEvent *current = gtk_get_current_event(); + if(current) + gdk_event_get_root_coords(current, &table->pan_x, &table->pan_y); table->panning = TRUE; - return TRUE; } -static gboolean _event_motion_notify(GtkWidget *widget, - GdkEventMotion *event, - gpointer user_data) +static void _event_motion_notify_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_culling_t *table) { - dt_culling_t *table = (dt_culling_t *)user_data; + // get root coordinates for pan tracking + const GdkEvent *current = gtk_get_current_event(); + gdouble root_x = 0, root_y = 0; + if(current) gdk_event_get_root_coords(current, &root_x, &root_y); + table->mouse_inside = TRUE; if(!table->panning) { - table->pan_x = dt_gdk_event_get_root_x(event); - table->pan_y = dt_gdk_event_get_root_y(event); - return FALSE; + table->pan_x = root_x; + table->pan_y = root_y; + return; } // get the max zoom of all images const int max_in_memory_images = _get_max_in_memory_images(); if(table->mode == DT_CULLING_MODE_CULLING && table->thumbs_count > max_in_memory_images) - return FALSE; + return; float fz = 1.0f; for(GList *l = table->list; l; l = g_list_next(l)) @@ -931,14 +960,14 @@ static gboolean _event_motion_notify(GtkWidget *widget, if(table->panning && fz > 1.0f) { - const double x = dt_gdk_event_get_root_x(event); - const double y = dt_gdk_event_get_root_y(event); // we want the images to stay in the screen const float scale = darktable.gui->ppd_thb / darktable.gui->ppd; - const float valx = (x - table->pan_x) * scale; - const float valy = (y - table->pan_y) * scale; + const float valx = (root_x - table->pan_x) * scale; + const float valy = (root_y - table->pan_y) * scale; - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK)) + GdkModifierType state; + gtk_get_current_event_state(&state); + if(dt_modifier_is(state, GDK_SHIFT_MASK)) { const dt_imgid_t mouseid = dt_control_get_mouse_over_id(); for(GList *l = table->list; l; l = g_list_next(l)) @@ -983,8 +1012,8 @@ static gboolean _event_motion_notify(GtkWidget *widget, th->zoomy = mindy; } - table->pan_x = x; - table->pan_y = y; + table->pan_x = root_x; + table->pan_y = root_y; } for(GList *l = table->list; l; l = g_list_next(l)) @@ -992,22 +1021,21 @@ static gboolean _event_motion_notify(GtkWidget *widget, dt_thumbnail_t *th = l->data; dt_thumbnail_image_refresh_position(th); } - return TRUE; } -static gboolean _event_button_release(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static void _event_button_release_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_culling_t *table) { - dt_culling_t *table = (dt_culling_t *)user_data; table->panning = FALSE; const dt_imgid_t overid = dt_control_get_mouse_over_id(); // if the act_on algorithm need a specific culling "selection", // we use a very simple culling-specific selection if(dt_act_on_use_culling_selection() - && dt_is_valid_imgid(overid) - && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + && dt_is_valid_imgid(overid)) { const dt_imgid_t old_sel = table->selection; if(table->selection == overid) @@ -1026,8 +1054,6 @@ static gboolean _event_button_release(GtkWidget *widget, dt_act_on_reset_cache(TRUE); dt_act_on_reset_cache(FALSE); } - - return TRUE; } // called each time the preference change, to update specific parts @@ -1212,12 +1238,7 @@ dt_culling_t *dt_culling_new(const dt_culling_mode_t mode) // set widget signals gtk_widget_set_events(table->widget, GDK_EXPOSURE_MASK - | GDK_POINTER_MOTION_MASK - | GDK_BUTTON_PRESS_MASK - | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK - | GDK_ENTER_NOTIFY_MASK - | GDK_LEAVE_NOTIFY_MASK | GDK_TOUCHPAD_GESTURE_MASK); dt_gui_add_class(table->widget, "dt_transparent_background"); @@ -1225,20 +1246,22 @@ dt_culling_t *dt_culling_new(const dt_culling_mode_t mode) g_signal_connect(G_OBJECT(table->widget), "event", G_CALLBACK(_event_gesture), table); - g_signal_connect(G_OBJECT(table->widget), "scroll-event", - G_CALLBACK(_event_scroll), table); + dt_gui_connect_scroll(table->widget, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _event_scroll, table); g_signal_connect(G_OBJECT(table->widget), "draw", G_CALLBACK(_event_draw), table); - g_signal_connect(G_OBJECT(table->widget), "leave-notify-event", - G_CALLBACK(_event_leave_notify), table); - g_signal_connect(G_OBJECT(table->widget), "enter-notify-event", - G_CALLBACK(_event_enter_notify), table); - g_signal_connect(G_OBJECT(table->widget), "button-press-event", - G_CALLBACK(_event_button_press), table); - g_signal_connect(G_OBJECT(table->widget), "motion-notify-event", - G_CALLBACK(_event_motion_notify), table); - g_signal_connect(G_OBJECT(table->widget), "button-release-event", - G_CALLBACK(_event_button_release), table); + dt_gui_connect_motion(table->widget, _event_motion_notify_cb, _event_enter_cb, _event_leave_cb, table); + dt_gui_connect_click_all(table->widget, _event_button_press_cb, _event_button_release_cb, table); + /* GTK3 bridge: GtkGestureMultiPress does not process GDK_2BUTTON_PRESS. + * dt_gui_connect_double_click forwards double/triple clicks via a + * "button-press-event" signal handler. The callback checks for NULL + * gesture (meaning it came from this bridge) and uses GDK_BUTTON_PRIMARY. + * + * GTK4 migration: remove this call. GtkGestureClick handles n_press + * natively and the callback can use gtk_gesture_single_get_button() + * safely on the real gesture pointer. */ + dt_gui_connect_double_click(table->widget, _event_button_press_cb, table); // we register globals signals DT_CONTROL_SIGNAL_CONNECT(DT_SIGNAL_MOUSE_OVER_IMAGE_CHANGE, diff --git a/src/dtgtk/range.c b/src/dtgtk/range.c index a376ef8b1900..0d8b6c5bc531 100644 --- a/src/dtgtk/range.c +++ b/src/dtgtk/range.c @@ -1181,15 +1181,13 @@ static void _popup_show(GtkDarktableRangeSelect *range, GtkWidget *w) } } -static gboolean _event_entry_press(GtkWidget *w, GdkEventButton *e, gpointer user_data) +static void _event_entry_press_cb(GtkGestureSingle *gesture, int n_press, double x, double y, GtkDarktableRangeSelect *range) { - GtkDarktableRangeSelect *range = (GtkDarktableRangeSelect *)user_data; - if(e->button == GDK_BUTTON_SECONDARY) + GtkWidget *w = dt_gui_get_widget(gesture); + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY) { _popup_show(range, w); - return TRUE; } - return FALSE; } static void _event_entry_activated(GtkWidget *entry, gpointer user_data) @@ -1509,10 +1507,9 @@ void dtgtk_range_select_redraw(GtkDarktableRangeSelect *range) gtk_widget_queue_draw(range->band); } -static gboolean _event_band_motion(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +static void _event_band_motion_cb(GtkEventControllerMotion *controller, double x, double y, GtkDarktableRangeSelect *range) { - GtkDarktableRangeSelect *range = (GtkDarktableRangeSelect *)user_data; - range->current_x_px = dt_gdk_event_get_x(event) - range->alloc_padding.x; + range->current_x_px = x - range->alloc_padding.x; // if we are outside the graph, don't go further const gboolean inside = (range->current_x_px >= 0 && range->current_x_px <= range->alloc_padding.width); @@ -1521,13 +1518,13 @@ static gboolean _event_band_motion(GtkWidget *widget, GdkEventMotion *event, gpo range->mouse_inside = HOVER_OUTSIDE; dt_control_change_cursor("default"); _current_hide_popup(range); - return TRUE; + return; } _current_show_popup(range); // point the popup to the current position gint wx, wy; gtk_widget_translate_coordinates(range->band, gtk_widget_get_toplevel(range->band), 0, 0, &wx, &wy); - GdkRectangle rect = { dt_gdk_event_get_x(event), 0, 1, gtk_widget_get_allocated_height(range->band) }; + GdkRectangle rect = { x, 0, 1, gtk_widget_get_allocated_height(range->band) }; gtk_popover_set_pointing_to(GTK_POPOVER(range->cur_window), &rect); const double smin_r = (range->bounds & DT_RANGE_BOUND_MIN) ? range->min_r : range->select_min_r; @@ -1556,32 +1553,32 @@ static gboolean _event_band_motion(GtkWidget *widget, GdkEventMotion *event, gpo dt_control_change_cursor("default"); } gtk_widget_queue_draw(range->band); - return TRUE; } -static gboolean _event_band_leave(GtkWidget *w, GdkEventCrossing *e, gpointer user_data) +static void _event_band_leave_cb(GtkEventControllerMotion *controller, GtkDarktableRangeSelect *range) { - GtkDarktableRangeSelect *range = (GtkDarktableRangeSelect *)user_data; range->mouse_inside = HOVER_OUTSIDE; dt_control_change_cursor("default"); _current_hide_popup(range); gtk_widget_queue_draw(range->band); - return TRUE; } -static gboolean _event_band_press(GtkWidget *w, GdkEventButton *e, gpointer user_data) +static void _event_band_press_cb(GtkGestureSingle *gesture, int n_press, double x, double y, GtkDarktableRangeSelect *range) { - GtkDarktableRangeSelect *range = (GtkDarktableRangeSelect *)user_data; - if(e->button == GDK_BUTTON_PRIMARY && e->type == GDK_2BUTTON_PRESS) + const guint button = gtk_gesture_single_get_current_button(gesture); + GdkModifierType state; + gtk_get_current_event_state(&state); + + if(button == GDK_BUTTON_PRIMARY && n_press == 2) { dtgtk_range_select_set_selection(range, DT_RANGE_BOUND_MIN | DT_RANGE_BOUND_MAX, range->min_r, range->max_r, TRUE, TRUE); } - else if(e->button == GDK_BUTTON_PRIMARY) + else if(button == GDK_BUTTON_PRIMARY) { - if(!range->mouse_inside) return TRUE; - const double pos_r = _graph_value_from_pos(range, e->x - range->alloc_padding.x, TRUE); + if(!range->mouse_inside) return; + const double pos_r = _graph_value_from_pos(range, x - range->alloc_padding.x, TRUE); if(range->mouse_inside == HOVER_MAX) { range->bounds &= ~DT_RANGE_BOUND_MAX; @@ -1593,7 +1590,7 @@ static gboolean _event_band_press(GtkWidget *w, GdkEventButton *e, gpointer user range->select_min_r = range->select_max_r; range->select_max_r = pos_r; } - else if(dt_modifier_is(e->state, GDK_SHIFT_MASK)) + else if(dt_modifier_is(state, GDK_SHIFT_MASK)) { // if we have shift pressed, we only want to set the second bound which is done in release range->bounds &= ~DT_RANGE_BOUND_FIXED; @@ -1610,17 +1607,15 @@ static gboolean _event_band_press(GtkWidget *w, GdkEventButton *e, gpointer user gtk_widget_queue_draw(range->band); } - else if(e->button == GDK_BUTTON_SECONDARY) + else if(button == GDK_BUTTON_SECONDARY) { _popup_show(range, range->band); } - return TRUE; } -static gboolean _event_band_release(GtkWidget *w, GdkEventButton *e, gpointer user_data) +static void _event_band_release_cb(GtkGestureSingle *gesture, int n_press, double x, double y, GtkDarktableRangeSelect *range) { - GtkDarktableRangeSelect *range = (GtkDarktableRangeSelect *)user_data; - if(!range->set_selection) return TRUE; - range->select_max_r = _graph_value_from_pos(range, e->x - range->alloc_padding.x, TRUE); + if(!range->set_selection) return; + range->select_max_r = _graph_value_from_pos(range, x - range->alloc_padding.x, TRUE); const double min_pos_px = _graph_value_to_pos(range, range->select_min_r); // we verify that the values are in the right order @@ -1632,7 +1627,7 @@ static gboolean _event_band_release(GtkWidget *w, GdkEventButton *e, gpointer us } // we also set the bounds - if(fabs(e->x - range->alloc_padding.x - min_pos_px) < 2) + if(fabs(x - range->alloc_padding.x - min_pos_px) < 2) range->bounds = DT_RANGE_BOUND_FIXED; else { @@ -1652,8 +1647,6 @@ static gboolean _event_band_release(GtkWidget *w, GdkEventButton *e, gpointer us range->set_selection = FALSE; dtgtk_range_select_set_selection(range, range->bounds, range->select_min_r, range->select_max_r, TRUE, FALSE); - - return TRUE; } // Public functions @@ -1686,14 +1679,13 @@ GtkWidget *dtgtk_range_select_new(const gchar *property, const gboolean show_ent // the graph band range->band = gtk_drawing_area_new(); - gtk_widget_set_events(range->band, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK + gtk_widget_set_events(range->band, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + | GDK_STRUCTURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK); g_signal_connect(G_OBJECT(range->band), "draw", G_CALLBACK(_event_band_draw), range); - g_signal_connect(G_OBJECT(range->band), "button-press-event", G_CALLBACK(_event_band_press), range); - g_signal_connect(G_OBJECT(range->band), "button-release-event", G_CALLBACK(_event_band_release), range); - g_signal_connect(G_OBJECT(range->band), "motion-notify-event", G_CALLBACK(_event_band_motion), range); - g_signal_connect(G_OBJECT(range->band), "leave-notify-event", G_CALLBACK(_event_band_leave), range); g_signal_connect(G_OBJECT(range->band), "style-updated", G_CALLBACK(_dt_pref_changed), range); + dt_gui_connect_click_all(range->band, _event_band_press_cb, _event_band_release_cb, range); + dt_gui_connect_motion(range->band, _event_band_motion_cb, NULL, _event_band_leave_cb, range); gtk_widget_set_name(GTK_WIDGET(range->band), "dt-range-band"); gtk_widget_set_can_default(range->band, TRUE); @@ -1726,7 +1718,7 @@ GtkWidget *dtgtk_range_select_new(const gchar *property, const gboolean show_ent _entry_set_tooltip(range->entry_min, BOUND_MIN, range->type); g_signal_connect(G_OBJECT(range->entry_min), "activate", G_CALLBACK(_event_entry_activated), range); g_signal_connect(G_OBJECT(range->entry_min), "focus-out-event", G_CALLBACK(_event_entry_focus_out), range); - g_signal_connect(G_OBJECT(range->entry_min), "button-press-event", G_CALLBACK(_event_entry_press), range); + dt_gui_connect_click_all(range->entry_min, _event_entry_press_cb, NULL, range); range->entry_max = dt_ui_entry_new(0); gtk_widget_set_can_default(range->entry_max, TRUE); @@ -1734,7 +1726,7 @@ GtkWidget *dtgtk_range_select_new(const gchar *property, const gboolean show_ent _entry_set_tooltip(range->entry_max, BOUND_MAX, range->type); g_signal_connect(G_OBJECT(range->entry_max), "activate", G_CALLBACK(_event_entry_activated), range); g_signal_connect(G_OBJECT(range->entry_max), "focus-out-event", G_CALLBACK(_event_entry_focus_out), range); - g_signal_connect(G_OBJECT(range->entry_max), "button-press-event", G_CALLBACK(_event_entry_press), range); + dt_gui_connect_click_all(range->entry_max, _event_entry_press_cb, NULL, range); dt_gui_box_add(vbox, dt_gui_hbox(dt_gui_expand(range->entry_min), dt_gui_expand(range->entry_max))); } diff --git a/src/dtgtk/resetlabel.c b/src/dtgtk/resetlabel.c index 78198de16e01..0fac2de58ea8 100644 --- a/src/dtgtk/resetlabel.c +++ b/src/dtgtk/resetlabel.c @@ -17,6 +17,7 @@ */ #include "common/gdk_event_utils.h" +#include "gui/gtk.h" #include "dtgtk/resetlabel.h" G_DEFINE_TYPE(GtkDarktableResetLabel, dtgtk_reset_label, GTK_TYPE_EVENT_BOX); @@ -29,17 +30,20 @@ static void dtgtk_reset_label_init(GtkDarktableResetLabel *label) { } -static gboolean _reset_label_callback(GtkDarktableResetLabel *label, GdkEventButton *event, gpointer user_data) +static void _reset_label_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + gpointer user_data) { - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(n_press >= 2) { + GtkDarktableResetLabel *label = DTGTK_RESET_LABEL(dt_gui_get_widget(gesture)); memcpy(((char *)label->module->params) + label->offset, ((char *)label->module->default_params) + label->offset, label->size); dt_iop_gui_update(label->module); dt_dev_add_history_item(darktable.develop, label->module, FALSE); - return TRUE; } - return FALSE; } // public functions @@ -64,8 +68,7 @@ GtkWidget *dtgtk_reset_label_new(const gchar *text, dt_iop_module_t *module, voi gtk_event_box_set_visible_window(GTK_EVENT_BOX(label), FALSE); gtk_widget_set_tooltip_text(GTK_WIDGET(label), _("double-click to reset")); gtk_container_add(GTK_CONTAINER(label), GTK_WIDGET(label->lb)); - gtk_widget_add_events(GTK_WIDGET(label), GDK_BUTTON_PRESS_MASK); - g_signal_connect(G_OBJECT(label), "button-press-event", G_CALLBACK(_reset_label_callback), (gpointer)NULL); + dt_gui_connect_click(label, _reset_label_callback, NULL, NULL); return (GtkWidget *)label; } diff --git a/src/dtgtk/thumbnail.c b/src/dtgtk/thumbnail.c index 16d300b1021d..8fe8677c1a0b 100644 --- a/src/dtgtk/thumbnail.c +++ b/src/dtgtk/thumbnail.c @@ -15,7 +15,6 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#include "common/gdk_event_utils.h" /** this is the thumbnail class for the lighttable module. */ #include "common/extra_optimizations.h" @@ -1025,38 +1024,41 @@ static void _thumbs_show_overlays(dt_thumbnail_t *thumb) } } -static gboolean _event_main_motion(GtkWidget *widget, - GdkEventMotion *event, - gpointer user_data) +static void _event_main_motion_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_thumbnail_t *thumb) { - if(!user_data) return TRUE; - dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; + if(!thumb) return; // first, we hide the block overlays after a delay if the mouse hasn't move _thumbs_show_overlays(thumb); if(!thumb->mouse_over && !thumb->disable_mouseover) dt_control_set_mouse_over_id(thumb->imgid); - return FALSE; } -static gboolean _event_rating_press(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static void _event_rating_press_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_thumbnail_t *thumb) { - return TRUE; + // claims the click via the gesture } -static gboolean _event_rating_release(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static void _event_rating_release_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_thumbnail_t *thumb) { - dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; + GtkWidget *widget = dt_gui_get_widget(gesture); if(thumb->disable_actions) - return FALSE; + return; if(dtgtk_thumbnail_btn_is_hidden(widget)) - return FALSE; + return; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && !thumb->moved) + if(!thumb->moved) { dt_view_image_over_t rating = DT_VIEW_DESERT; if(widget == thumb->w_reject) @@ -1081,25 +1083,28 @@ static gboolean _event_rating_release(GtkWidget *widget, g_list_prepend(NULL, GINT_TO_POINTER(thumb->imgid))); } } - return TRUE; } -static gboolean _event_grouping_release(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static void _event_grouping_release_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_thumbnail_t *thumb) { - dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; + GtkWidget *widget = dt_gui_get_widget(gesture); if(thumb->disable_actions) - return FALSE; + return; if(dtgtk_thumbnail_btn_is_hidden(widget)) - return FALSE; + return; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && !thumb->moved) + if(!thumb->moved) { + GdkModifierType state; + gtk_get_current_event_state(&state); //TODO: will succeed if either or *both* of Shift and Control are //pressed. Do we want this? - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK) - | dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) + if(dt_modifier_is(state, GDK_SHIFT_MASK) + | dt_modifier_is(state, GDK_CONTROL_MASK)) { // just add the whole group to the selection. TODO: make this // also work for collapsed groups. @@ -1131,20 +1136,21 @@ static gboolean _event_grouping_release(GtkWidget *widget, DT_COLLECTION_PROP_UNDEF, NULL); } - return FALSE; } -static gboolean _event_audio_release(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static void _event_audio_release_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_thumbnail_t *thumb) { - dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; + GtkWidget *widget = dt_gui_get_widget(gesture); if(thumb->disable_actions) - return FALSE; + return; if(dtgtk_thumbnail_btn_is_hidden(widget)) - return FALSE; + return; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && !thumb->moved) + if(!thumb->moved) { gboolean start_audio = TRUE; if(darktable.view_manager->audio.audio_player_id != -1) @@ -1160,7 +1166,6 @@ static gboolean _event_audio_release(GtkWidget *widget, dt_view_audio_start(darktable.view_manager, thumb->imgid); } } - return FALSE; } // this is called each time the images info change @@ -1308,69 +1313,81 @@ static void _dt_mipmaps_updated_callback(gpointer instance, gtk_widget_queue_draw(thumb->w_main); } -static gboolean _event_box_enter_leave(GtkWidget *widget, - GdkEventCrossing *event, - gpointer user_data) +static void _event_box_enter_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_thumbnail_t *thumb) { - dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; + GtkWidget *widget = dt_gui_get_widget(controller); - if(dt_gdk_event_get_type(event) == GDK_ENTER_NOTIFY && !thumb->disable_mouseover) + if(!thumb->disable_mouseover) dt_control_set_mouse_over_id(thumb->imgid); - _set_flag(widget, GTK_STATE_FLAG_PRELIGHT, (dt_gdk_event_get_type(event) == GDK_ENTER_NOTIFY)); - _set_flag(thumb->w_image_box, GTK_STATE_FLAG_PRELIGHT, - (dt_gdk_event_get_type(event) == GDK_ENTER_NOTIFY)); - return FALSE; + _set_flag(widget, GTK_STATE_FLAG_PRELIGHT, TRUE); + _set_flag(thumb->w_image_box, GTK_STATE_FLAG_PRELIGHT, TRUE); } -static gboolean _event_image_enter_leave(GtkWidget *widget, - GdkEventCrossing *event, - gpointer user_data) +static void _event_box_leave_cb(GtkEventControllerMotion *controller, + dt_thumbnail_t *thumb) { - dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; + GtkWidget *widget = dt_gui_get_widget(controller); - if(dt_gdk_event_get_type(event) == GDK_ENTER_NOTIFY && !thumb->disable_mouseover) + _set_flag(widget, GTK_STATE_FLAG_PRELIGHT, FALSE); + _set_flag(thumb->w_image_box, GTK_STATE_FLAG_PRELIGHT, FALSE); +} + +static void _event_image_enter_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_thumbnail_t *thumb) +{ + if(!thumb->disable_mouseover) dt_control_set_mouse_over_id(thumb->imgid); - _set_flag(thumb->w_image_box, GTK_STATE_FLAG_PRELIGHT, - (dt_gdk_event_get_type(event) == GDK_ENTER_NOTIFY)); - return FALSE; + _set_flag(thumb->w_image_box, GTK_STATE_FLAG_PRELIGHT, TRUE); } -static gboolean _event_btn_enter_leave(GtkWidget *widget, - GdkEventCrossing *event, - gpointer user_data) +static void _event_image_leave_cb(GtkEventControllerMotion *controller, + dt_thumbnail_t *thumb) { - dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; + _set_flag(thumb->w_image_box, GTK_STATE_FLAG_PRELIGHT, FALSE); +} - if(dt_gdk_event_get_type(event) == GDK_ENTER_NOTIFY) - { - if(widget == thumb->w_reject) - darktable.control->element = DT_VIEW_REJECT; +static void _event_btn_enter_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_thumbnail_t *thumb) +{ + GtkWidget *widget = dt_gui_get_widget(controller); - if(thumb->disable_actions) - return TRUE; + if(widget == thumb->w_reject) + darktable.control->element = DT_VIEW_REJECT; - if(!thumb->disable_mouseover) - dt_control_set_mouse_over_id(thumb->imgid); - _set_flag(thumb->w_image_box, GTK_STATE_FLAG_PRELIGHT, TRUE); - _thumb_update_tags_tooltip(thumb); - } - else if(dt_gdk_event_get_type(event) == GDK_LEAVE_NOTIFY) - { - if(widget == thumb->w_reject) - darktable.control->element = -1; - } + if(thumb->disable_actions) + return; - return FALSE; + if(!thumb->disable_mouseover) + dt_control_set_mouse_over_id(thumb->imgid); + _set_flag(thumb->w_image_box, GTK_STATE_FLAG_PRELIGHT, TRUE); + _thumb_update_tags_tooltip(thumb); } -static gboolean _event_star_enter(GtkWidget *widget, - GdkEventCrossing *event, - gpointer user_data) +static void _event_btn_leave_cb(GtkEventControllerMotion *controller, + dt_thumbnail_t *thumb) { - dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; - if(thumb->disable_actions) return TRUE; + GtkWidget *widget = dt_gui_get_widget(controller); + + if(widget == thumb->w_reject) + darktable.control->element = -1; +} + +static void _event_star_enter_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_thumbnail_t *thumb) +{ + GtkWidget *widget = dt_gui_get_widget(controller); + if(thumb->disable_actions) return; if(!thumb->disable_mouseover) dt_control_set_mouse_over_id(thumb->imgid); @@ -1390,21 +1407,18 @@ static gboolean _event_star_enter(GtkWidget *widget, pre = FALSE; } } - return TRUE; } -static gboolean _event_star_leave(GtkWidget *widget, - GdkEventCrossing *event, - gpointer user_data) + +static void _event_star_leave_cb(GtkEventControllerMotion *controller, + dt_thumbnail_t *thumb) { - dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; - if(thumb->disable_actions) return TRUE; + if(thumb->disable_actions) return; for(int i = 0; i < MAX_STARS; i++) { _set_flag(thumb->w_stars[i], GTK_STATE_FLAG_PRELIGHT, FALSE); gtk_widget_queue_draw(thumb->w_stars[i]); } - return TRUE; } // we only want to specify that the mouse is hovereing the thumbnail @@ -1415,7 +1429,14 @@ static gboolean _event_main_drag_motion(GtkWidget *widget, const guint time, gpointer user_data) { - _event_main_motion(widget, NULL, user_data); + if(user_data) + { + dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; + _thumbs_show_overlays(thumb); + + if(!thumb->mouse_over && !thumb->disable_mouseover) + dt_control_set_mouse_over_id(thumb->imgid); + } return TRUE; } @@ -1476,14 +1497,8 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, // the background thumb->w_back = gtk_event_box_new(); - gtk_widget_set_events(thumb->w_back, - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_STRUCTURE_MASK - | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK - | GDK_POINTER_MOTION_MASK); gtk_widget_set_name(thumb->w_back, "thumb-back"); - g_signal_connect(G_OBJECT(thumb->w_back), "motion-notify-event", - G_CALLBACK(_event_main_motion), thumb); + dt_gui_connect_motion(thumb->w_back, _event_main_motion_cb, NULL, NULL, thumb); gtk_widget_show(thumb->w_back); gtk_container_add(GTK_CONTAINER(thumb->w_main), thumb->w_back); @@ -1509,16 +1524,7 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, GtkWidget *evt_image = gtk_event_box_new(); gtk_widget_set_valign(evt_image, GTK_ALIGN_FILL); gtk_widget_set_halign(evt_image, GTK_ALIGN_FILL); - gtk_widget_set_events(evt_image, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_STRUCTURE_MASK - | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK - | GDK_POINTER_MOTION_MASK); - g_signal_connect(G_OBJECT(evt_image), "motion-notify-event", - G_CALLBACK(_event_main_motion), thumb); - g_signal_connect(G_OBJECT(evt_image), "enter-notify-event", - G_CALLBACK(_event_image_enter_leave), thumb); - g_signal_connect(G_OBJECT(evt_image), "leave-notify-event", - G_CALLBACK(_event_image_enter_leave), thumb); + dt_gui_connect_motion(evt_image, _event_main_motion_cb, _event_image_enter_cb, _event_image_leave_cb, thumb); gtk_widget_show(evt_image); gtk_overlay_add_overlay(GTK_OVERLAY(thumb->w_image_box), evt_image); thumb->w_image = gtk_drawing_area_new(); @@ -1526,18 +1532,9 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, gtk_widget_set_valign(thumb->w_image, GTK_ALIGN_CENTER); gtk_widget_set_halign(thumb->w_image, GTK_ALIGN_CENTER); // the size will be defined at the end, inside dt_thumbnail_resize - gtk_widget_set_events(thumb->w_image, GDK_BUTTON_PRESS_MASK - | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK - | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK - | GDK_POINTER_MOTION_MASK); g_signal_connect(G_OBJECT(thumb->w_image), "draw", G_CALLBACK(_event_image_draw), thumb); - g_signal_connect(G_OBJECT(thumb->w_image), "motion-notify-event", - G_CALLBACK(_event_main_motion), thumb); - g_signal_connect(G_OBJECT(thumb->w_image), "enter-notify-event", - G_CALLBACK(_event_image_enter_leave), thumb); - g_signal_connect(G_OBJECT(thumb->w_image), "leave-notify-event", - G_CALLBACK(_event_image_enter_leave), thumb); + dt_gui_connect_motion(thumb->w_image, _event_main_motion_cb, _event_image_enter_cb, _event_image_leave_cb, thumb); g_signal_connect(G_OBJECT(thumb->w_image), "style-updated", G_CALLBACK(_event_image_style_updated), thumb); gtk_widget_show(thumb->w_image); @@ -1561,12 +1558,7 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, // the infos background thumb->w_bottom_eb = gtk_event_box_new(); gtk_widget_set_name(thumb->w_bottom_eb, "thumb-bottom"); - g_signal_connect(G_OBJECT(thumb->w_bottom_eb), "enter-notify-event", - G_CALLBACK(_event_box_enter_leave), - thumb); - g_signal_connect(G_OBJECT(thumb->w_bottom_eb), "leave-notify-event", - G_CALLBACK(_event_box_enter_leave), - thumb); + dt_gui_connect_motion(thumb->w_bottom_eb, NULL, _event_box_enter_cb, _event_box_leave_cb, thumb); gtk_widget_set_valign(thumb->w_bottom_eb, GTK_ALIGN_END); gtk_widget_set_halign(thumb->w_bottom_eb, GTK_ALIGN_CENTER); gtk_widget_show(thumb->w_bottom_eb); @@ -1600,29 +1592,16 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, gtk_widget_set_valign(thumb->w_reject, GTK_ALIGN_END); gtk_widget_set_halign(thumb->w_reject, GTK_ALIGN_START); gtk_widget_show(thumb->w_reject); - g_signal_connect(G_OBJECT(thumb->w_reject), "button-press-event", - G_CALLBACK(_event_rating_press), thumb); - g_signal_connect(G_OBJECT(thumb->w_reject), "button-release-event", - G_CALLBACK(_event_rating_release), thumb); - g_signal_connect(G_OBJECT(thumb->w_reject), "enter-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); - g_signal_connect(G_OBJECT(thumb->w_reject), "leave-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); + dt_gui_connect_click(thumb->w_reject, _event_rating_press_cb, _event_rating_release_cb, thumb); + dt_gui_connect_motion(thumb->w_reject, NULL, _event_btn_enter_cb, _event_btn_leave_cb, thumb); gtk_overlay_add_overlay(GTK_OVERLAY(overlays_parent), thumb->w_reject); // the stars for(int i = 0; i < MAX_STARS; i++) { thumb->w_stars[i] = dtgtk_thumbnail_btn_new(dtgtk_cairo_paint_star, 0, NULL); - g_signal_connect(G_OBJECT(thumb->w_stars[i]), "enter-notify-event", - G_CALLBACK(_event_star_enter), thumb); - g_signal_connect(G_OBJECT(thumb->w_stars[i]), "leave-notify-event", - G_CALLBACK(_event_star_leave), thumb); - g_signal_connect(G_OBJECT(thumb->w_stars[i]), "button-press-event", - G_CALLBACK(_event_rating_press), thumb); - g_signal_connect(G_OBJECT(thumb->w_stars[i]), "button-release-event", - G_CALLBACK(_event_rating_release), - thumb); + dt_gui_connect_click(thumb->w_stars[i], _event_rating_press_cb, _event_rating_release_cb, thumb); + dt_gui_connect_motion(thumb->w_stars[i], NULL, _event_star_enter_cb, _event_star_leave_cb, thumb); gtk_widget_set_name(thumb->w_stars[i], "thumb-star"); dt_action_define(&darktable.control->actions_thumb, NULL, "rating", thumb->w_stars[i], &dt_action_def_rating); @@ -1641,10 +1620,7 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, gtk_widget_set_valign(thumb->w_color, GTK_ALIGN_END); gtk_widget_set_halign(thumb->w_color, GTK_ALIGN_END); gtk_widget_set_no_show_all(thumb->w_color, TRUE); - g_signal_connect(G_OBJECT(thumb->w_color), "enter-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); - g_signal_connect(G_OBJECT(thumb->w_color), "leave-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); + dt_gui_connect_motion(thumb->w_color, NULL, _event_btn_enter_cb, _event_btn_leave_cb, thumb); gtk_overlay_add_overlay(GTK_OVERLAY(overlays_parent), thumb->w_color); // the local copy indicator @@ -1654,12 +1630,7 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, gtk_widget_set_valign(thumb->w_local_copy, GTK_ALIGN_START); gtk_widget_set_halign(thumb->w_local_copy, GTK_ALIGN_END); gtk_widget_set_no_show_all(thumb->w_local_copy, TRUE); - g_signal_connect(G_OBJECT(thumb->w_local_copy), "enter-notify-event", - G_CALLBACK(_event_btn_enter_leave), - thumb); - g_signal_connect(G_OBJECT(thumb->w_local_copy), "leave-notify-event", - G_CALLBACK(_event_btn_enter_leave), - thumb); + dt_gui_connect_motion(thumb->w_local_copy, NULL, _event_btn_enter_cb, _event_btn_leave_cb, thumb); gtk_overlay_add_overlay(GTK_OVERLAY(overlays_parent), thumb->w_local_copy); // the altered icon @@ -1668,10 +1639,7 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, gtk_widget_set_valign(thumb->w_altered, GTK_ALIGN_START); gtk_widget_set_halign(thumb->w_altered, GTK_ALIGN_END); gtk_widget_set_no_show_all(thumb->w_altered, TRUE); - g_signal_connect(G_OBJECT(thumb->w_altered), "enter-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); - g_signal_connect(G_OBJECT(thumb->w_altered), "leave-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); + dt_gui_connect_motion(thumb->w_altered, NULL, _event_btn_enter_cb, _event_btn_leave_cb, thumb); gtk_overlay_add_overlay(GTK_OVERLAY(overlays_parent), thumb->w_altered); // the tags icon @@ -1680,21 +1648,14 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, gtk_widget_set_valign(thumb->w_tags, GTK_ALIGN_START); gtk_widget_set_halign(thumb->w_tags, GTK_ALIGN_END); gtk_widget_set_no_show_all(thumb->w_tags, TRUE); - g_signal_connect(G_OBJECT(thumb->w_tags), "enter-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); - g_signal_connect(G_OBJECT(thumb->w_tags), "leave-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); + dt_gui_connect_motion(thumb->w_tags, NULL, _event_btn_enter_cb, _event_btn_leave_cb, thumb); gtk_overlay_add_overlay(GTK_OVERLAY(overlays_parent), thumb->w_tags); // the group bouton thumb->w_group = dtgtk_thumbnail_btn_new(dtgtk_cairo_paint_grouping, 0, NULL); gtk_widget_set_name(thumb->w_group, "thumb-group-audio"); - g_signal_connect(G_OBJECT(thumb->w_group), "button-release-event", - G_CALLBACK(_event_grouping_release), thumb); - g_signal_connect(G_OBJECT(thumb->w_group), "enter-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); - g_signal_connect(G_OBJECT(thumb->w_group), "leave-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); + dt_gui_connect_click(thumb->w_group, _event_rating_press_cb, _event_grouping_release_cb, thumb); + dt_gui_connect_motion(thumb->w_group, NULL, _event_btn_enter_cb, _event_btn_leave_cb, thumb); gtk_widget_set_valign(thumb->w_group, GTK_ALIGN_START); gtk_widget_set_halign(thumb->w_group, GTK_ALIGN_END); gtk_widget_set_no_show_all(thumb->w_group, TRUE); @@ -1703,12 +1664,8 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, // the sound icon thumb->w_audio = dtgtk_thumbnail_btn_new(dtgtk_cairo_paint_audio, 0, NULL); gtk_widget_set_name(thumb->w_audio, "thumb-group-audio"); - g_signal_connect(G_OBJECT(thumb->w_audio), "button-release-event", - G_CALLBACK(_event_audio_release), thumb); - g_signal_connect(G_OBJECT(thumb->w_audio), "enter-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); - g_signal_connect(G_OBJECT(thumb->w_audio), "leave-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); + dt_gui_connect_click(thumb->w_audio, _event_rating_press_cb, _event_audio_release_cb, thumb); + dt_gui_connect_motion(thumb->w_audio, NULL, _event_btn_enter_cb, _event_btn_leave_cb, thumb); gtk_widget_set_valign(thumb->w_audio, GTK_ALIGN_START); gtk_widget_set_halign(thumb->w_audio, GTK_ALIGN_END); gtk_widget_set_no_show_all(thumb->w_audio, TRUE); @@ -1716,8 +1673,7 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, // the zoom indicator thumb->w_zoom_eb = gtk_event_box_new(); - g_signal_connect(G_OBJECT(thumb->w_zoom_eb), "enter-notify-event", - G_CALLBACK(_event_btn_enter_leave), thumb); + dt_gui_connect_motion(thumb->w_zoom_eb, NULL, _event_btn_enter_cb, NULL, thumb); gtk_widget_set_name(thumb->w_zoom_eb, "thumb-zoom"); gtk_widget_set_valign(thumb->w_zoom_eb, GTK_ALIGN_START); gtk_widget_set_halign(thumb->w_zoom_eb, GTK_ALIGN_START); diff --git a/src/dtgtk/thumbtable.c b/src/dtgtk/thumbtable.c index a9b4d60987c5..a3068559d991 100644 --- a/src/dtgtk/thumbtable.c +++ b/src/dtgtk/thumbtable.c @@ -1117,34 +1117,39 @@ static gboolean _event_scroll_compressed(gpointer user_data) return FALSE; } -static gboolean _event_scroll(GtkWidget *widget, - GdkEvent *event, - dt_thumbtable_t *table) +static void _event_scroll(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_thumbtable_t *table) { - GdkEventScroll *e = (GdkEventScroll *)event; + GdkEvent *event = gtk_get_current_event(); + if(!event) return; + const GdkEventScroll *e = (const GdkEventScroll *)event; + const GdkModifierType state = dt_gdk_event_get_state(event); // file manager can either scroll fractionally and smoothly for precision // touch pads, or in one-thumbnail increments for clicky scroll wheels, // except while control is held, as that indicates zooming if(table->mode == DT_THUMBTABLE_MODE_FILEMANAGER - && !dt_modifier_is(e->state, GDK_CONTROL_MASK)) + && !dt_modifier_is(state, GDK_CONTROL_MASK)) { gdouble deltaf = 0.f; gboolean did_scroll; if(dt_conf_get_bool("thumbtable_fractional_scrolling")) { - gdouble deltaf_x, deltaf_y; - did_scroll = dt_gui_get_scroll_deltas(e, &deltaf_x, &deltaf_y); - if (did_scroll) { - // file manager scroll: tilt right (delta_x >) 0 or scroll down (delta_y > 0) -> down (towards the last image) - deltaf = fabs(deltaf_x) > fabs(deltaf_y) ? deltaf_x : deltaf_y; + // use controller dx/dy directly for fractional scrolling + did_scroll = (dx != 0.0 || dy != 0.0); + if(did_scroll) + { + // file manager scroll: tilt right (dx > 0) or scroll down (dy > 0) -> down + deltaf = fabs(dx) > fabs(dy) ? dx : dy; } } else { int delta_x, delta_y; did_scroll = dt_gui_get_scroll_unit_deltas(e, &delta_x, &delta_y); - if (did_scroll) + if(did_scroll) { deltaf = abs(delta_x) > abs(delta_y) ? delta_x : delta_y; } @@ -1159,8 +1164,8 @@ static gboolean _event_scroll(GtkWidget *widget, } table->scroll_value += deltaf; } - // we stop here to avoid scrolledwindow to move - return TRUE; + gdk_event_free(event); + return; } // filmstrip and zoom mode always use clicky scroll: @@ -1170,7 +1175,7 @@ static gboolean _event_scroll(GtkWidget *widget, { // for zoomable, scroll = zoom if(table->mode == DT_THUMBTABLE_MODE_ZOOM - || dt_modifier_is(e->state, GDK_CONTROL_MASK)) + || dt_modifier_is(state, GDK_CONTROL_MASK)) { // up==right==zoom in const int delta = abs(delta_x) > abs(delta_y) ? -delta_x : delta_y; @@ -1192,7 +1197,7 @@ static gboolean _event_scroll(GtkWidget *widget, { // filmstrip scroll: tilt right (delta_x >) 0 or scroll down (delta_y > 0) -> down (towards the last image) const int delta = abs(delta_x) > abs(delta_y) ? delta_x : delta_y; - _move(table, -delta * (dt_modifier_is(e->state, GDK_SHIFT_MASK) + _move(table, -delta * (dt_modifier_is(state, GDK_SHIFT_MASK) ? table->view_width - table->thumb_size : table->thumb_size), 0, TRUE); @@ -1202,8 +1207,7 @@ static gboolean _event_scroll(GtkWidget *widget, dt_control_set_mouse_over_id(th->imgid); } } - // we stop here to avoid scrolled window to move - return TRUE; + gdk_event_free(event); } static void _line_to(cairo_t *cr, @@ -1375,43 +1379,48 @@ static gboolean _event_draw(GtkWidget *widget, return FALSE; // let's propagate this event } -static gboolean _event_leave_notify(GtkWidget *widget, - GdkEventCrossing *event, - dt_thumbtable_t *table) +static void _event_leave_cb(GtkEventControllerMotion *controller, + dt_thumbtable_t *table) { + GtkWidget *widget = dt_gui_get_widget(controller); // if the leaving cause is the hide of the widget, no mouseover change if(!gtk_widget_is_visible(widget)) { table->mouse_inside = FALSE; - return FALSE; + return; } - // if we leave thumbtable in favour of an inferior (a thumbnail) - // it's not a real leave ! same if this is not a mouse move action - // (shortcut that activate a button for example) - if(event->detail == GDK_NOTIFY_INFERIOR - || event->mode == GDK_CROSSING_GTK_GRAB - || event->mode == GDK_CROSSING_GRAB) - return FALSE; - table->mouse_inside = FALSE; - dt_control_set_mouse_over_id(NO_IMGID); - return TRUE; + + // check crossing detail: don't clear mouse_over_id when leaving to + // a child widget (thumbnail) or due to a grab + GdkEvent *event = gtk_get_current_event(); + if(event) + { + if(event->crossing.detail != GDK_NOTIFY_INFERIOR + && event->crossing.mode != GDK_CROSSING_GTK_GRAB + && event->crossing.mode != GDK_CROSSING_GRAB) + dt_control_set_mouse_over_id(NO_IMGID); + gdk_event_free(event); + } } -static gboolean _event_enter_notify(GtkWidget *widget, - GdkEventCrossing *event, - dt_thumbtable_t *table) +static void _event_enter_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_thumbtable_t *table) { dt_set_backthumb_time(0.0); - // we only handle the case where we enter thumbtable from an inferior (a thumbnail) - // this is when the mouse enter an "empty" area of thumbtable - if(event->detail != GDK_NOTIFY_INFERIOR) - return FALSE; - - dt_control_set_mouse_over_id(NO_IMGID); - return TRUE; + // when entering the thumbtable area from a child thumbnail (INFERIOR), + // clear the mouse-over id since we're now in the empty area + GdkEvent *event = gtk_get_current_event(); + if(event) + { + if(event->crossing.detail == GDK_NOTIFY_INFERIOR) + dt_control_set_mouse_over_id(NO_IMGID); + gdk_event_free(event); + } } static gboolean _do_select_single(gpointer user_data) @@ -1427,25 +1436,41 @@ static gboolean _do_select_single(gpointer user_data) return FALSE; } -static gboolean _event_button_press(GtkWidget *widget, - GdkEventButton *event, - dt_thumbtable_t *table) +static void _event_button_press_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_thumbtable_t *table) { dt_set_backthumb_time(0.0); + /* + * GTK3 bridge for GDK_2BUTTON_PRESS (see dt_gui_connect_double_click): + * GtkGestureMultiPress doesn't process multi-press events, so when a + * double/triple-click is detected by the classic "button-press-event" + * signal handler, it calls this callback with gesture=NULL. In that + * case the button is always GDK_BUTTON_PRIMARY (GDK_2BUTTON_PRESS only + * occurs for primary button). + * + * GTK4 migration: remove the ternary and just call + * gtk_gesture_single_get_current_button(gesture) directly — GtkGestureClick + * will provide a valid gesture pointer for all n_press values. */ + const guint button = gesture + ? gtk_gesture_single_get_current_button(gesture) + : GDK_BUTTON_PRIMARY; const dt_imgid_t id = dt_control_get_mouse_over_id(); - if(dt_is_valid_imgid(id) && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(button == GDK_BUTTON_PRIMARY && n_press == 2) { - // double-click - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + // double-click + if(dt_is_valid_imgid(id)) { switch(table->mode) { case DT_THUMBTABLE_MODE_FILEMANAGER: case DT_THUMBTABLE_MODE_ZOOM: dt_view_manager_switch(darktable.view_manager, "darkroom"); - break; + return; case DT_THUMBTABLE_MODE_FILMSTRIP: if(dt_view_get_current() == DT_VIEW_DARKROOM) @@ -1455,35 +1480,34 @@ static gboolean _event_button_press(GtkWidget *widget, g_source_remove(table->sel_single_cb); table->sel_single_cb = 0; } - // disable next BUTTON_RELEASE event (see _event_motion_release) + // disable next BUTTON_RELEASE event table->to_selid = -1; // unselect currently edited picture, select new one dt_selection_deselect(darktable.selection, darktable.develop->image_storage.id); dt_selection_select(darktable.selection, id); DT_CONTROL_SIGNAL_RAISE(DT_SIGNAL_VIEWMANAGER_THUMBTABLE_ACTIVATE, id); - return FALSE; + return; } default: break; } } - - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS - && table->mode == DT_THUMBTABLE_MODE_FILMSTRIP) - return FALSE; } - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS) + if(button == GDK_BUTTON_PRIMARY && n_press == 1 + && table->mode == DT_THUMBTABLE_MODE_FILMSTRIP) + return; + + if(button == GDK_BUTTON_PRIMARY && n_press == 1) { // make sure any edition field loses the focus gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui)); } - if(table->mode != DT_THUMBTABLE_MODE_ZOOM - && !dt_is_valid_imgid(id) - && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY - && dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS) + if(button == GDK_BUTTON_PRIMARY && n_press == 1 + && table->mode != DT_THUMBTABLE_MODE_ZOOM + && !dt_is_valid_imgid(id)) { const dt_view_type_flags_t cv = dt_view_get_current(); @@ -1499,20 +1523,20 @@ static gboolean _event_button_press(GtkWidget *widget, darktable.develop->image_storage.id); } - PangoRectangle *button = &table->manual_button; - if(dt_gdk_event_get_x(event) < button->x && dt_gdk_event_get_x(event) > button->x - button->width - && dt_gdk_event_get_y(event) < button->y && dt_gdk_event_get_y(event) > button->y - button->height) + PangoRectangle *button_rect = &table->manual_button; + if(x < button_rect->x && x > button_rect->x - button_rect->width + && y < button_rect->y && y > button_rect->y - button_rect->height) { dt_gui_show_help(NULL); } - return TRUE; + return; } if(table->mode != DT_THUMBTABLE_MODE_ZOOM) - return TRUE; + return; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS) + if(button == GDK_BUTTON_PRIMARY && n_press == 1) { table->dragging = TRUE; table->drag_dx = table->drag_dy = 0; @@ -1521,22 +1545,26 @@ static gboolean _event_button_press(GtkWidget *widget, if(table->drag_thumb) table->drag_thumb->moved = FALSE; } - return TRUE; } -static gboolean _event_motion_notify(GtkWidget *widget, - GdkEventMotion *event, - dt_thumbtable_t *table) +static void _event_motion_notify_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_thumbtable_t *table) { dt_set_backthumb_time(0.0); table->mouse_inside = TRUE; - gboolean ret = FALSE; + // get root coordinates for drag tracking + const GdkEvent *current = gtk_get_current_event(); + gdouble root_x = 0, root_y = 0; + if(current) gdk_event_get_root_coords(current, &root_x, &root_y); + if(table->dragging && table->mode == DT_THUMBTABLE_MODE_ZOOM) { - const int dx = ceil(dt_gdk_event_get_root_x(event)) - table->last_x; - const int dy = ceil(dt_gdk_event_get_root_y(event)) - table->last_y; + const int dx = ceil(root_x) - table->last_x; + const int dy = ceil(root_y) - table->last_y; _move(table, dx, dy, TRUE); table->drag_dx += dx; table->drag_dy += dy; @@ -1547,17 +1575,17 @@ static gboolean _event_motion_notify(GtkWidget *widget, table->drag_thumb->moved = ((abs(table->drag_dx) + abs(table->drag_dy)) > DT_PIXEL_APPLY_DPI(8)); } - ret = TRUE; } - table->last_x = ceil(dt_gdk_event_get_root_x(event)); - table->last_y = ceil(dt_gdk_event_get_root_y(event)); - return ret; + table->last_x = ceil(root_x); + table->last_y = ceil(root_y); } -static gboolean _event_button_release(GtkWidget *widget, - GdkEventButton *event, - dt_thumbtable_t *table) +static void _event_button_release_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_thumbtable_t *table) { // we select only in LIGHTTABLE, DARKROOM & MAP mode const dt_view_type_flags_t cv = dt_view_get_current(); @@ -1566,21 +1594,22 @@ static gboolean _event_button_release(GtkWidget *widget, && cv != DT_VIEW_LIGHTTABLE && cv != DT_VIEW_MAP && cv != DT_VIEW_PRINT) - return FALSE; + return; dt_set_backthumb_time(0.0); const dt_imgid_t id = dt_control_get_mouse_over_id(); - if(dt_is_valid_imgid(id) - && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY - && dt_gdk_event_get_type(event) == GDK_BUTTON_RELEASE) + GdkModifierType state; + gtk_get_current_event_state(&state); + + if(dt_is_valid_imgid(id)) { - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK) - || dt_modifier_is(dt_gdk_event_get_state(event), GDK_MOD2_MASK)) // CMD key on macOS + if(dt_modifier_is(state, GDK_CONTROL_MASK) + || dt_modifier_is(state, GDK_MOD2_MASK)) // CMD key on macOS { dt_selection_toggle(darktable.selection, id); } - else if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK)) + else if(dt_modifier_is(state, GDK_SHIFT_MASK)) { dt_selection_select_range(darktable.selection, id); } @@ -1602,7 +1631,8 @@ static gboolean _event_button_release(GtkWidget *widget, } else { - GtkSettings *settings = gtk_widget_get_settings(GTK_WIDGET (widget)); + GtkWidget *w = dt_gui_get_widget(gesture); + GtkSettings *settings = gtk_widget_get_settings(GTK_WIDGET(w)); guint double_click_time = 400; if(settings) @@ -1631,7 +1661,7 @@ static gboolean _event_button_release(GtkWidget *widget, // Left now if not in zoom mode if(table->mode != DT_THUMBTABLE_MODE_ZOOM) - return TRUE; + return; // in some case, image_over_id can get out of sync at the end of dragging // this happen esp. if the pointer as been out of the center area during drag @@ -1662,7 +1692,6 @@ static gboolean _event_button_release(GtkWidget *widget, // we register the position dt_conf_set_int("lighttable/zoomable/last_pos_x", table->thumbs_area.x); dt_conf_set_int("lighttable/zoomable/last_pos_y", table->thumbs_area.y); - return TRUE; } // set scrollbars visibility @@ -2572,10 +2601,8 @@ dt_thumbtable_t *dt_thumbtable_new() // set widget signals gtk_widget_set_events(table->widget, - GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_STRUCTURE_MASK - | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); + GDK_EXPOSURE_MASK + | GDK_STRUCTURE_MASK); dt_gui_add_class(table->widget, "dt_transparent_background"); gtk_widget_set_can_focus(table->widget, TRUE); @@ -2594,20 +2621,22 @@ dt_thumbtable_t *dt_thumbtable_new() g_signal_connect(table->widget, "drag-data-received", G_CALLBACK(dt_thumbtable_event_dnd_received), table); - g_signal_connect(G_OBJECT(table->widget), "scroll-event", - G_CALLBACK(_event_scroll), table); + dt_gui_connect_scroll(table->widget, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _event_scroll, table); g_signal_connect(G_OBJECT(table->widget), "draw", G_CALLBACK(_event_draw), table); - g_signal_connect(G_OBJECT(table->widget), "leave-notify-event", - G_CALLBACK(_event_leave_notify), table); - g_signal_connect(G_OBJECT(table->widget), "enter-notify-event", - G_CALLBACK(_event_enter_notify), table); - g_signal_connect(G_OBJECT(table->widget), "button-press-event", - G_CALLBACK(_event_button_press), table); - g_signal_connect(G_OBJECT(table->widget), "motion-notify-event", - G_CALLBACK(_event_motion_notify), table); - g_signal_connect(G_OBJECT(table->widget), "button-release-event", - G_CALLBACK(_event_button_release), table); + dt_gui_connect_motion(table->widget, _event_motion_notify_cb, _event_enter_cb, _event_leave_cb, table); + dt_gui_connect_click_all(table->widget, _event_button_press_cb, _event_button_release_cb, table); + /* GTK3 bridge: GtkGestureMultiPress does not process GDK_2BUTTON_PRESS. + * dt_gui_connect_double_click forwards double/triple clicks via a + * "button-press-event" signal handler. The callback checks for NULL + * gesture (meaning it came from this bridge) and uses GDK_BUTTON_PRIMARY. + * + * GTK4 migration: remove this call. GtkGestureClick handles n_press + * natively and the callback can use gtk_gesture_single_get_button() + * safely on the real gesture pointer. */ + dt_gui_connect_double_click(table->widget, _event_button_press_cb, table); // we register globals signals DT_CONTROL_SIGNAL_CONNECT(DT_SIGNAL_COLLECTION_CHANGED, diff --git a/src/gui/accelerators.c b/src/gui/accelerators.c index 5267e9fdaee8..d1070e9f3ab9 100644 --- a/src/gui/accelerators.c +++ b/src/gui/accelerators.c @@ -1988,10 +1988,43 @@ static void _shortcut_row_activated(GtkTreeView *tree_view, _grab_in_tree_view(tree_view); } -static gboolean _view_key_pressed(GtkWidget *widget, - GdkEventKey *event, - gpointer user_data) +static gboolean _shortcut_delete_selected(GtkTreeView *view) { + GtkTreeSelection *selection = gtk_tree_view_get_selection(view); + GtkTreeIter iter; + GtkTreeModel *model = NULL; + if(!gtk_tree_selection_get_selected(selection, &model, &iter)) + return FALSE; + + GSequenceIter *shortcut_iter = NULL; + gtk_tree_model_get(model, &iter, 0, &shortcut_iter, -1); + + if(_is_shortcut_category(shortcut_iter)) + return FALSE; + + dt_shortcut_t *s = g_sequence_get(shortcut_iter); + + if(dt_gui_show_yes_no_dialog(_("removing shortcut"), "", + s->is_default ? s->views ? + _("disable the selected default shortcut?") : + _("restore the selected default shortcut?") : + _("remove the selected shortcut?"))) + { + _remove_shortcut(shortcut_iter); + + dt_shortcuts_save(NULL, FALSE); + } + + return TRUE; +} + +static gboolean _view_key_pressed_cb(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + GtkWidget *search_entry) +{ + GtkWidget *widget = dt_gui_get_widget(controller); GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreeSelection *selection = gtk_tree_view_get_selection(view); @@ -2002,7 +2035,7 @@ static gboolean _view_key_pressed(GtkWidget *widget, if(!strcmp(gtk_widget_get_name(widget), "actions_view")) { // if control key pressed, copy lua command to clipboard (CTRL+C will work) - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) + if(dt_modifier_is(state, GDK_CONTROL_MASK)) { dt_shortcut_t shortcut = { .speed = 1.0 }; gtk_tree_model_get(model, &iter, 0, &shortcut.action, -1); @@ -2020,32 +2053,20 @@ static gboolean _view_key_pressed(GtkWidget *widget, dt_shortcut_t *s = g_sequence_get(shortcut_iter); // if control key pressed, copy lua command to clipboard (CTRL+C will work) - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK) && s->views) + if(dt_modifier_is(state, GDK_CONTROL_MASK) && s->views) { _shortcut_copy_lua(NULL, s, NULL); } - // GDK_KEY_BackSpace moves to parent in tree - if(dt_gdk_event_get_keyval(event) == GDK_KEY_Delete || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Delete) - { - if(dt_gui_show_yes_no_dialog(_("removing shortcut"), "", - s->is_default ? s->views ? - _("disable the selected default shortcut?") : - _("restore the selected default shortcut?") : - _("remove the selected shortcut?"))) - { - _remove_shortcut(shortcut_iter); - - dt_shortcuts_save(NULL, FALSE); - } - - return TRUE; - } + // GDK_KEY_BackSpace is the delete key on macOS; it is also + // accepted when deleting presets, so keep it consistent here + if(keyval == GDK_KEY_Delete || keyval == GDK_KEY_KP_Delete || keyval == GDK_KEY_BackSpace) + return _shortcut_delete_selected(view); } } } - return dt_gui_search_start(widget, event, user_data); + return dt_gui_search_start(widget, (GdkEventKey *)gtk_get_current_event(), GTK_SEARCH_ENTRY(search_entry)); } static void _add_shortcuts_to_tree() @@ -2260,23 +2281,46 @@ static gboolean _action_find_and_expand(GtkTreeModel *model, return FALSE; } -static gboolean _action_view_click(GtkWidget *widget, - GdkEventButton *event, - gpointer data) +/* Claim the event sequence in CAPTURE phase so the treeview's internal + * GtkGestureMultiPress (GTK_PHASE_BUBBLE) does NOT process clicks. + * The pre-migration button-press-event handler consumed the events + * (return TRUE), which suppressed the internal gesture; the gesture + * controller replacement must do the same, otherwise the two gestures + * fight over row selection and expansion. + * + * GTK4 migration: the pattern is the same — just rename + * GtkGestureMultiPress to GtkGestureClick. */ +static void _gesture_begin_claim(GtkGesture *gesture, + GdkEventSequence *sequence, + gpointer user_data) +{ + gtk_gesture_set_sequence_state(gesture, sequence, GTK_EVENT_SEQUENCE_CLAIMED); +} + +static void _action_view_click_cb(GtkGestureSingle *gesture, int n_press, double x, double y, GtkTreeStore *model_data) { + GtkWidget *widget = dt_gui_get_widget(gesture); GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreeModel *model = gtk_tree_view_get_model(view); + const guint button = gtk_gesture_single_get_current_button(gesture); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(button == GDK_BUTTON_PRIMARY) { GtkTreeSelection *selection = gtk_tree_view_get_selection(view); + /* gesture coordinates are relative to the widget allocation, while + * gtk_tree_view_get_path_at_pos() expects bin-window coordinates */ + gint bin_x, bin_y; + gtk_tree_view_convert_widget_to_bin_window_coords(view, (gint)x, (gint)y, &bin_x, &bin_y); + GtkTreePath *path = NULL; - if(gtk_tree_view_get_path_at_pos(view, (gint)dt_gdk_event_get_x(event), (gint)dt_gdk_event_get_y(event), + if(gtk_tree_view_get_path_at_pos(view, bin_x, bin_y, &path, NULL, NULL, NULL)) { - if(dt_gdk_event_get_type(event) == GDK_DOUBLE_BUTTON_PRESS) + if(n_press >= 2) { + /* the internal gesture that would emit "row-activated" on + * double-click is suppressed, so activate the row ourselves */ gtk_tree_selection_select_path(selection, path); _action_row_activated(view, path, NULL, model); } @@ -2292,19 +2336,18 @@ static gboolean _action_view_click(GtkWidget *widget, } gtk_widget_grab_focus(widget); + gtk_tree_path_free(path); } else gtk_tree_selection_unselect_all(selection); } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) + else if(button == GDK_BUTTON_SECONDARY) { GtkTreeIter iter; gtk_tree_model_get_iter_first(model, &iter); _action_find_and_expand(model, &iter, view); } - - return TRUE; } static gboolean _action_view_show(GtkTreeView *view, @@ -2859,6 +2902,43 @@ static void _notice_clicked(GtkWidget *button, dt_conf_set_bool("accel/hide_notice", TRUE); } +/* The shortcuts treeview consumes GDK_KEY_BackSpace ("select-cursor-parent") + * in its own key handling before any handler connected on the treeview runs, + * so the delete keys are intercepted at the toplevel instead, before the key + * event is propagated to the focus widget. GDK_KEY_BackSpace is the delete + * key on macOS. */ +static gboolean _shortcuts_dialog_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) +{ + GtkWidget *view = user_data; + + if(gtk_window_get_focus(GTK_WINDOW(widget)) != view) + return FALSE; + + guint keyval; + gdk_event_get_keyval((GdkEvent *)event, &keyval); + + if(keyval == GDK_KEY_Delete || keyval == GDK_KEY_KP_Delete || keyval == GDK_KEY_BackSpace) + return _shortcut_delete_selected(GTK_TREE_VIEW(view)); + + return FALSE; +} + +static void _shortcuts_view_realized(GtkWidget *widget, gpointer user_data) +{ + if(g_object_get_data(G_OBJECT(widget), "accel-dialog-key-connected")) + return; + g_object_set_data(G_OBJECT(widget), "accel-dialog-key-connected", GINT_TO_POINTER(TRUE)); + + GtkWidget *toplevel = gtk_widget_get_toplevel(widget); + if(!toplevel || !GTK_IS_WINDOW(toplevel)) + return; + + g_signal_connect(G_OBJECT(toplevel), "key-press-event", + G_CALLBACK(_shortcuts_dialog_key_pressed), widget); +} + GtkWidget *dt_shortcuts_prefs(GtkWidget *widget) { // Save the shortcuts before editing @@ -2903,6 +2983,9 @@ GtkWidget *dt_shortcuts_prefs(GtkWidget *widget) g_signal_connect(G_OBJECT(search_shortcuts), "stop-search", G_CALLBACK(dt_gui_search_stop), shortcuts_view); gtk_tree_view_set_search_entry(shortcuts_view, GTK_ENTRY(search_shortcuts)); + // intercept the delete keys at the toplevel, see _shortcuts_dialog_key_pressed + g_signal_connect(G_OBJECT(shortcuts_view), "realize", + G_CALLBACK(_shortcuts_view_realized), NULL); gtk_tree_selection_set_select_function(gtk_tree_view_get_selection(shortcuts_view), _shortcut_selection_function, NULL, NULL); @@ -2910,8 +2993,7 @@ GtkWidget *dt_shortcuts_prefs(GtkWidget *widget) gtk_widget_set_name(GTK_WIDGET(shortcuts_view), "shortcuts_view"); g_signal_connect(G_OBJECT(shortcuts_view), "row-activated", G_CALLBACK(_shortcut_row_activated), filtered_shortcuts); - g_signal_connect(G_OBJECT(shortcuts_view), "key-press-event", - G_CALLBACK(_view_key_pressed), search_shortcuts); + dt_gui_connect_key(shortcuts_view, _view_key_pressed_cb, search_shortcuts); g_signal_connect(G_OBJECT(_shortcuts_store), "row-inserted", G_CALLBACK(_shortcut_row_inserted), shortcuts_view); @@ -3009,10 +3091,21 @@ GtkWidget *dt_shortcuts_prefs(GtkWidget *widget) gtk_widget_set_name(GTK_WIDGET(actions_view), "actions_view"); g_signal_connect(G_OBJECT(actions_view), "row-activated", G_CALLBACK(_action_row_activated), _actions_store); - g_signal_connect(G_OBJECT(actions_view), "button-press-event", - G_CALLBACK(_action_view_click), _actions_store); - g_signal_connect(G_OBJECT(actions_view), "key-press-event", - G_CALLBACK(_view_key_pressed), search_actions); + + { + // the internal treeview gesture would fight our click handling; + // claim the sequence in CAPTURE phase to suppress it, replicating + // the event consumption of the pre-migration handler + GtkGesture *gesture = gtk_gesture_multi_press_new(GTK_WIDGET(actions_view)); + gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(gesture), + GTK_PHASE_CAPTURE); + g_object_weak_ref(G_OBJECT(actions_view), (GWeakNotify)g_object_unref, gesture); + gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 0); + g_signal_connect(gesture, "pressed", G_CALLBACK(_action_view_click_cb), _actions_store); + g_signal_connect(gesture, "begin", G_CALLBACK(_gesture_begin_claim), NULL); + } + + dt_gui_connect_key(actions_view, _view_key_pressed_cb, search_actions); g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(actions_view)), "changed", G_CALLBACK(_action_selection_changed), shortcuts_view); @@ -4853,12 +4946,9 @@ dt_action_t *dt_action_locate(dt_action_t *owner, return owner; } -static gboolean _reset_element_on_leave(GtkWidget *widget, - GdkEvent *event, - gpointer user_data) +static void _reset_element_on_leave_cb(GtkEventControllerMotion *controller, gpointer user_data) { darktable.control->element = -1; - return FALSE; } dt_action_t *dt_action_define(dt_action_t *owner, @@ -4917,8 +5007,7 @@ dt_action_t *dt_action_define(dt_action_t *owner, } gtk_widget_set_has_tooltip(widget, TRUE); - g_signal_connect(G_OBJECT(widget), "leave-notify-event", - G_CALLBACK(_reset_element_on_leave), NULL); + dt_gui_connect_motion(widget, NULL, NULL, _reset_element_on_leave_cb, NULL); } } diff --git a/src/gui/color_picker_proxy.c b/src/gui/color_picker_proxy.c index 5d41713689eb..831f5d8c52e3 100644 --- a/src/gui/color_picker_proxy.c +++ b/src/gui/color_picker_proxy.c @@ -262,6 +262,16 @@ static void _color_picker_callback(GtkWidget *button, _color_picker_callback_button_press(button, NULL, self); } +static void _color_picker_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_color_picker_t *self) +{ + GtkWidget *button = dt_gui_get_widget(gesture); + _color_picker_callback_button_press(button, NULL, self); +} + void dt_iop_color_picker_set_cst(dt_iop_module_t *module, const dt_iop_colorspace_type_t picker_cst) { @@ -401,9 +411,7 @@ static GtkWidget *_color_picker_new(dt_iop_module_t *module, color_picker->picker_cst = cst; color_picker->fixed_cst = TRUE; } - g_signal_connect_data(G_OBJECT(button), "button-press-event", - G_CALLBACK(_color_picker_callback_button_press), - color_picker, (GClosureNotify)_color_picker_destroy, 0); + dt_gui_connect_click(button, _color_picker_clicked, NULL, color_picker); if(w) gtk_box_pack_start(GTK_BOX(w), button, FALSE, FALSE, 0); return button; diff --git a/src/gui/gtk.c b/src/gui/gtk.c index db66a1753834..39bdc7323a75 100644 --- a/src/gui/gtk.c +++ b/src/gui/gtk.c @@ -624,6 +624,36 @@ gboolean dt_gui_get_scroll_unit_delta(const GdkEventScroll *event, return FALSE; } +gboolean dt_gui_get_scroll_unit_deltas_fallback(const gdouble dx, + const gdouble dy, + int *delta_x, + int *delta_y) +{ + GdkEvent *event = gtk_get_current_event(); + if(event) + { + const gboolean ok = dt_gui_get_scroll_unit_deltas((const GdkEventScroll *)event, delta_x, delta_y); + gdk_event_free(event); + return ok; + } + // fallback: use raw values if no current event available + *delta_x = (int)dx; + *delta_y = (int)dy; + return TRUE; +} + +gboolean dt_gui_get_scroll_unit_delta_fallback(const gdouble dy, + int *delta) +{ + int delta_x, delta_y; + if(dt_gui_get_scroll_unit_deltas_fallback(0, dy, &delta_x, &delta_y)) + { + *delta = abs(delta_x) > abs(delta_y) ? -delta_x : delta_y; + return TRUE; + } + return FALSE; +} + static gboolean _draw_borders(GtkWidget *widget, cairo_t *crf, const gpointer user_data) @@ -4769,6 +4799,70 @@ GtkGestureSingle *(dt_gui_connect_click)(GtkWidget *widget, return (GtkGestureSingle *)gesture; } +/* + * GTK3 bridge for double/triple-click detection. + * + * GtkGestureMultiPress in GTK3 does not process GDK_2BUTTON_PRESS or + * GDK_3BUTTON_PRESS events at all: + * - gtk_gesture_handle_event() only dispatches GDK_BUTTON_PRESS (not 2/3) + * - gtk_gesture_multi_press_begin() checks for GDK_BUTTON_PRESS only + * - GDK always delivers GDK_2BUTTON_PRESS for the second press of a + * double-click (never a second GDK_BUTTON_PRESS) + * + * As a result, GtkGestureMultiPress's "pressed" signal with n_press=2 or 3 + * is NEVER emitted in GTK3. This helper works around it by connecting a + * classic "button-press-event" signal handler that catches GDK_2BUTTON_PRESS + * and GDK_3BUTTON_PRESS and forwards them to the same pressed callback with + * the correct n_press value. + * + * GTK4 migration: DELETE this entire helper (struct, free, handler, and both + * public functions). GtkGestureClick (the GTK4 replacement for + * GtkGestureMultiPress) uses an internal timer to detect multi-press patterns + * and correctly emits "pressed" with n_press=2/3 without needing GDK_2BUTTON_PRESS. + * The callers using dt_gui_connect_double_click() should simply remove those + * calls — the gesture alone will suffice. + */ +typedef struct _DblClkData +{ + GCallback cb; + gpointer data; +} DblClkData; + +static void _dbl_clk_free(gpointer p, GClosure *cl) +{ + g_free(p); +} + +static gboolean _dbl_clk_handler(GtkWidget *w, GdkEventButton *ev, gpointer user) +{ + DblClkData *d = user; + int n; + if(ev->type == GDK_2BUTTON_PRESS) n = 2; + else if(ev->type == GDK_3BUTTON_PRESS) n = 3; + else return GDK_EVENT_PROPAGATE; + ((void (*)(GtkGestureSingle *, int, double, double, gpointer))d->cb) + (NULL, n, ev->x, ev->y, d->data); + return GDK_EVENT_STOP; +} + +unsigned long +(dt_gui_connect_double_click)(GtkWidget *widget, + GCallback pressed, + gpointer data) +{ + DblClkData *d = g_malloc(sizeof(DblClkData)); + d->cb = pressed; + d->data = data; + return g_signal_connect_data(widget, "button-press-event", + G_CALLBACK(_dbl_clk_handler), + d, _dbl_clk_free, 0); +} + +void (dt_gui_disconnect_double_click)(GtkWidget *widget, unsigned long id) +{ + g_signal_handler_disconnect(widget, id); +} + GtkGesture *(dt_gui_connect_drag)(GtkWidget *widget, GCallback drag_begin, GCallback drag_end, @@ -4779,7 +4873,6 @@ GtkGesture *(dt_gui_connect_drag)(GtkWidget *widget, g_object_weak_ref(G_OBJECT (widget), (GWeakNotify) g_object_unref, gesture); // GTK4 GtkGesture *gesture = gtk_gesture_drag_new(); // gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(gesture)); - if(drag_begin) g_signal_connect(gesture, "drag-begin", G_CALLBACK(drag_begin), data); if(drag_end) g_signal_connect(gesture, "drag-end", G_CALLBACK(drag_end), data); if(drag_update) g_signal_connect(gesture, "drag-update", G_CALLBACK(drag_update), data); @@ -4820,7 +4913,7 @@ static gboolean _scroll_sidebar(GtkEventControllerScroll* controller, // decide whether to propogate them or scroll itself depending on // modifiers state, and this function will no longer be needed GtkWidget *const widget = - gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(controller)); + dt_gui_get_widget(controller); const GtkWidget *panel = NULL; if(dt_ui_panel_ancestor(darktable.gui->ui, DT_UI_PANEL_LEFT, widget)) panel = darktable.gui->ui->panels[DT_UI_PANEL_LEFT]; @@ -4946,6 +5039,79 @@ GtkEventController *(dt_gui_connect_scroll)(GtkWidget *widget, return controller; } +/* + * GTK3 bridge for GtkEventControllerKey. + * + * On GTK3, key events are delivered to the toplevel window, not to the + * focus widget, so a key controller with GTK_PHASE_TARGET attached to a + * widget never receives them. This bridge connects the classic + * "key-press-event" signal (which GTK routes to the focus widget) and + * forwards to the controller-style callback so the GTK4 API can be used + * unchanged. + * + * GTK4 migration: DELETE this entire bridge; dt_gui_connect_key() then + * connects the controller's "key-pressed" signal directly. + */ +#if !GTK_CHECK_VERSION(4, 0, 0) +typedef struct _KeyBridgeData +{ + GCallback cb; + gpointer data; + GtkEventControllerKey *controller; +} KeyBridgeData; + +static void _key_bridge_free(gpointer p, GClosure *cl) +{ + g_free(p); +} + +static gboolean _key_bridge_handler(GtkWidget *widget, GdkEventKey *event, gpointer user) +{ + KeyBridgeData *d = user; + guint keyval; + guint16 keycode; + GdkModifierType state; + gdk_event_get_keyval((GdkEvent *)event, &keyval); + gdk_event_get_keycode((GdkEvent *)event, &keycode); + gdk_event_get_state((GdkEvent *)event, &state); + + return ((gboolean(*)(GtkEventControllerKey *, guint, guint, GdkModifierType, gpointer))d->cb) + (d->controller, keyval, keycode, state, d->data); +} +#endif + +GtkEventController *(dt_gui_connect_key)(GtkWidget *widget, + GCallback pressed, + gpointer data) +{ + GtkEventController *controller = gtk_event_controller_key_new(widget); + gtk_event_controller_set_propagation_phase(controller, GTK_PHASE_TARGET); + g_object_weak_ref(G_OBJECT(widget), (GWeakNotify) g_object_unref, controller); + // GTK4 gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(controller)); + + if(pressed) + { +#if GTK_CHECK_VERSION(4, 0, 0) + g_signal_connect(controller, "key-pressed", G_CALLBACK(pressed), data); +#else + /* GtkEventControllerKey with GTK_PHASE_TARGET never fires on GTK3: + * key events are delivered to the toplevel window, not to the focus + * widget, so the TARGET-phase controllers are never reached. Connect + * the classic key-press-event signal instead, which GTK routes to the + * focus widget (this is what the pre-migration code did). The + * controller is still returned so callers can use dt_gui_get_widget() + * and the GTK4 migration keeps the controller path above. */ + KeyBridgeData *d = g_new0(KeyBridgeData, 1); + d->cb = pressed; + d->data = data; + d->controller = GTK_EVENT_CONTROLLER_KEY(controller); + g_signal_connect_data(widget, "key-press-event", + G_CALLBACK(_key_bridge_handler), d, _key_bridge_free, 0); +#endif + } + + return controller; +} static int busy_nest_count = 0; diff --git a/src/gui/gtk.h b/src/gui/gtk.h index 30fa078f9c12..7453badf47fd 100644 --- a/src/gui/gtk.h +++ b/src/gui/gtk.h @@ -247,6 +247,11 @@ gboolean dt_gui_get_scroll_delta(const GdkEventScroll *event, gdouble *delta); * Effectively makes smooth scroll events act like old-style unit * scroll events. */ gboolean dt_gui_get_scroll_unit_delta(const GdkEventScroll *event, int *delta); +/* Same as above but for use inside scroll-event-controller callbacks where the + * raw dx/dy are available but no GdkEventScroll *. Tries gtk_get_current_event() + * first for smooth-scroll accumulation, falls back to casting the raw values. */ +gboolean dt_gui_get_scroll_unit_deltas_fallback(gdouble dx, gdouble dy, int *delta_x, int *delta_y); +gboolean dt_gui_get_scroll_unit_delta_fallback(gdouble dy, int *delta); /* * new ui api @@ -545,7 +550,7 @@ void dt_gui_hide_collapsible_section(const dt_gui_collapsible_section_t *cs); gboolean dt_gui_long_click(const guint second, const guint first); -#define ASSERT_FUNC_TYPE(func, expected_type) (void)(1 ? (func) : (expected_type)0) +#define ASSERT_FUNC_TYPE(func, expected_type) (void)((expected_type)(func)) GtkGestureSingle *(dt_gui_connect_click)(GtkWidget *widget, GCallback pressed, @@ -558,6 +563,27 @@ GtkGestureSingle *(dt_gui_connect_click)(GtkWidget *widget, #define dt_gui_connect_click_all(widget, pressed, released, data) \ gtk_gesture_single_set_button(dt_gui_connect_click(widget, pressed, released, data), 0) +/* + * GTK3 bridge: GtkGestureMultiPress in GTK3 does not process + * GDK_2BUTTON_PRESS / GDK_3BUTTON_PRESS. See implementation in gtk.c + * for the full explanation and GTK4 migration instructions. + * + * Connection: call alongside dt_gui_connect_click() with the same + * pressed callback and data. The callback receives (NULL, n_press, x, y, data) + * — the NULL gesture indicates the call came from this bridge, so the callback + * must handle that gracefully (see culling.c/thumbtable.c for the pattern). + * + * GTK4 migration: remove all calls to dt_gui_connect_double_click() and + * delete this declaration. GtkGestureClick handles n_press natively. + */ +unsigned long dt_gui_connect_double_click(GtkWidget *widget, + GCallback pressed, + gpointer data); +#define dt_gui_connect_double_click(widget, pressed, data) ( \ + ASSERT_FUNC_TYPE(pressed, void(*)(GtkGestureSingle *, int, double, double, __typeof__(data))), \ + dt_gui_connect_double_click(GTK_WIDGET(widget), G_CALLBACK(pressed), (data))) +void dt_gui_disconnect_double_click(GtkWidget *widget, unsigned long id); + GtkGesture *(dt_gui_connect_drag)(GtkWidget *widget, GCallback drag_begin, GCallback drag_end, @@ -588,6 +614,16 @@ GtkEventController *(dt_gui_connect_scroll)(GtkWidget *widget, ASSERT_FUNC_TYPE(scroll, void(*)(GtkEventControllerScroll *, double, double, __typeof__(data))), \ dt_gui_connect_scroll(GTK_WIDGET(widget), (flags), G_CALLBACK(scroll), (data))) +GtkEventController *(dt_gui_connect_key)(GtkWidget *widget, + GCallback pressed, + gpointer data); +#define dt_gui_connect_key(widget, pressed, data) ( \ + ASSERT_FUNC_TYPE(pressed, gboolean(*)(GtkEventControllerKey *, guint, guint, GdkModifierType, __typeof__(data))), \ + dt_gui_connect_key(GTK_WIDGET(widget), G_CALLBACK(pressed), (data))) + +#define dt_gui_get_widget(controller) \ + gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(controller)) + #define dt_gui_claim(gesture) \ gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_CLAIMED) #define dt_gui_deny(gesture) \ diff --git a/src/gui/import_metadata.c b/src/gui/import_metadata.c index 482018f26f12..7ead2465b15a 100644 --- a/src/gui/import_metadata.c +++ b/src/gui/import_metadata.c @@ -15,8 +15,6 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#include "common/gdk_event_utils.h" - #include "common/debug.h" #include "common/metadata.h" #include "control/conf.h" @@ -67,15 +65,14 @@ static void _import_metadata_changed(GtkWidget *widget, gtk_combo_box_set_active(GTK_COMBO_BOX(w), -1); } -static gboolean _import_metadata_reset(GtkWidget *label, - GdkEventButton *event, - GtkWidget *widget) +static void _import_metadata_reset_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + GtkWidget *widget) { - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(n_press >= 2) { gtk_entry_set_text(GTK_ENTRY(widget), ""); } - return FALSE; } static void _metadata_reset_all(dt_import_metadata_t *metadata, @@ -105,15 +102,14 @@ static void _metadata_reset_all(dt_import_metadata_t *metadata, } } -static gboolean _import_metadata_reset_all(GtkWidget *label, - GdkEventButton *event, - dt_import_metadata_t *metadata) +static void _import_metadata_reset_all_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_import_metadata_t *metadata) { - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(n_press >= 2) { _metadata_reset_all(metadata, FALSE); } - return FALSE; } static void _import_metadata_toggled(GtkWidget *widget, @@ -452,8 +448,7 @@ static void _fill_metadata_grid(dt_import_metadata_t *metadata) g_free(setting); g_signal_connect(GTK_ENTRY(metadata_entry), "changed", G_CALLBACK(_import_metadata_changed), metadata); - g_signal_connect(GTK_EVENT_BOX(labelev), "button-press-event", - G_CALLBACK(_import_metadata_reset), metadata_entry); + dt_gui_connect_click_all(labelev, _import_metadata_reset_cb, NULL, metadata_entry); GtkWidget *metadata_imported = gtk_check_button_new(); g_object_set_data(G_OBJECT(metadata_imported), "tagname", md->tagname); @@ -489,8 +484,7 @@ void dt_import_metadata_init(dt_import_metadata_t *metadata) gtk_widget_set_tooltip_text(GTK_WIDGET(label), _("metadata to be applied per default" "\ndouble-click on a label to clear the corresponding entry" "\ndouble-click on 'preset' to clear all entries")); - g_signal_connect(GTK_EVENT_BOX(labelev), "button-press-event", - G_CALLBACK(_import_metadata_reset_all), metadata); + dt_gui_connect_click_all(labelev, _import_metadata_reset_all_cb, NULL, metadata); GtkWidget *presets = _set_up_combobox(metadata->m_model, DT_META_META_HEADER, metadata); @@ -527,8 +521,7 @@ void dt_import_metadata_init(dt_import_metadata_t *metadata) gtk_widget_set_tooltip_text(entry, _("comma separated list of tags")); g_signal_connect(GTK_ENTRY(entry), "changed", G_CALLBACK(_import_tags_changed), metadata); - g_signal_connect(GTK_EVENT_BOX(labelev), "button-press-event", - G_CALLBACK(_import_metadata_reset), entry); + dt_gui_connect_click_all(labelev, _import_metadata_reset_cb, NULL, entry); GtkWidget *tags_imported = gtk_check_button_new(); _set_up_toggle_button(tags_imported, dt_conf_get_bool("ui_last/import_last_tags_imported"), diff --git a/src/gui/preferences.c b/src/gui/preferences.c index a33784ecc2da..23963bca906f 100644 --- a/src/gui/preferences.c +++ b/src/gui/preferences.c @@ -98,12 +98,16 @@ static void tree_row_activated_presets(GtkTreeView *tree, gpointer data); static void tree_selection_changed(GtkTreeSelection *selection, gpointer data); -static gboolean tree_key_press(GtkWidget *widget, - GdkEventKey *event, - gpointer data); -static gboolean tree_key_press_presets(GtkWidget *widget, - GdkEventKey *event, - gpointer data); +static gboolean _search_key_pressed(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + GtkWidget *search_entry); +static gboolean _delete_preset_key_pressed(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + GtkTreeStore *model); static GtkWidget *_preferences_dialog; @@ -281,16 +285,16 @@ static void language_callback(GtkWidget *widget, restart_required = TRUE; } -static gboolean reset_language_widget(GtkWidget *label, - GdkEventButton *event, +static void reset_language_widget_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, GtkWidget *widget) { - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(n_press == 2) { dt_bauhaus_combobox_set(widget, darktable.l10n->sys_default); - return TRUE; } - return FALSE; } static gboolean _remove_panel_config(gpointer key, @@ -323,10 +327,12 @@ static void _reset_panels_clicked(GtkButton *button, gpointer user_data) } // forward declaration for use in init_tab_general -static gboolean -_gui_preferences_bool_click(GtkWidget *label, - GdkEventButton *event, - GtkWidget *widget); +static void +gui_preferences_bool_click_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + GtkWidget *widget); static void init_tab_general(GtkWidget *dialog, GtkWidget *stack, @@ -374,8 +380,7 @@ static void init_tab_general(GtkWidget *dialog, "(restart required)")); gtk_grid_attach(GTK_GRID(grid), labelev, 0, line++, 1, 1); gtk_grid_attach_next_to(GTK_GRID(grid), widget, labelev, GTK_POS_RIGHT, 1, 1); - g_signal_connect(G_OBJECT(labelev), "button-press-event", - G_CALLBACK(reset_language_widget), (gpointer)widget); + dt_gui_connect_click(labelev, reset_language_widget_cb, NULL, widget); // theme @@ -439,8 +444,7 @@ static void init_tab_general(GtkWidget *dialog, dt_conf_get_bool("use_system_font")); g_signal_connect(G_OBJECT(usesysfont), "toggled", G_CALLBACK(use_sys_font_callback), (gpointer)fontsize); - g_signal_connect(G_OBJECT(labelev), "button-press-event", - G_CALLBACK(_gui_preferences_bool_click), (gpointer)usesysfont); + dt_gui_connect_click(labelev, gui_preferences_bool_click_cb, NULL, usesysfont); //font size selector @@ -507,8 +511,7 @@ static void init_tab_general(GtkWidget *dialog, dt_conf_get_bool("themes/usercss")); g_signal_connect(G_OBJECT(tw->apply_toggle), "toggled", G_CALLBACK(usercss_callback), 0); - g_signal_connect(G_OBJECT(labelev), "button-press-event", - G_CALLBACK(_gui_preferences_bool_click), (gpointer)tw->apply_toggle); + dt_gui_connect_click(labelev, gui_preferences_bool_click_cb, NULL, tw->apply_toggle); //scrollable textarea with save button to allow user to directly modify user.css file GtkWidget *usercssbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); @@ -905,6 +908,24 @@ static gboolean _search_func(GtkTreeModel *model, return TRUE; } +static gboolean _search_key_pressed(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + GtkWidget *search_entry) +{ + GtkWidget *widget = dt_gui_get_widget(controller); + GtkSearchEntry *entry = GTK_SEARCH_ENTRY(search_entry); + // we build a temporary GdkEventKey to reuse dt_gui_search_start + GdkEventKey event; + memset(&event, 0, sizeof(event)); + event.type = GDK_KEY_PRESS; + event.keyval = keyval; + event.state = state; + event.is_modifier = (keyval >= GDK_KEY_Shift_L && keyval <= GDK_KEY_Hyper_R); + return dt_gui_search_start(widget, &event, entry); +} + static void init_tab_presets(GtkWidget *stack) { GtkTreeView *tree = GTK_TREE_VIEW(gtk_tree_view_new()); @@ -1014,8 +1035,7 @@ static void init_tab_presets(GtkWidget *stack) G_CALLBACK(dt_gui_search_stop), tree); g_signal_connect(G_OBJECT(search_presets), "stop-search", G_CALLBACK(dt_gui_search_stop), tree); - g_signal_connect(G_OBJECT(tree), "key-press-event", - G_CALLBACK(dt_gui_search_start), search_presets); + dt_gui_connect_key(tree, _search_key_pressed, search_presets); gtk_tree_view_set_search_entry(tree, GTK_ENTRY(search_presets)); GtkWidget *button = gtk_button_new_with_label(C_("preferences", "import...")); @@ -1040,8 +1060,7 @@ static void init_tab_presets(GtkWidget *stack) G_CALLBACK(tree_row_activated_presets), NULL); // A keypress may delete preset - g_signal_connect(G_OBJECT(tree), "key-press-event", - G_CALLBACK(tree_key_press_presets), (gpointer)model); + dt_gui_connect_key(tree, _delete_preset_key_pressed, model); // Setting up the search functionality gtk_tree_view_set_search_equal_func(tree, _search_func, tree, NULL); @@ -1153,18 +1172,22 @@ static void tree_row_activated_presets(GtkTreeView *tree, } } -static gboolean tree_key_press_presets(GtkWidget *widget, - GdkEventKey *event, - gpointer data) +static gboolean _delete_preset_key_pressed(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + GtkTreeStore *model_store) { - GtkTreeModel *model = (GtkTreeModel *)data; + GtkWidget *widget = dt_gui_get_widget(controller); + GtkTreeModel *model = GTK_TREE_MODEL(model_store); GtkTreeIter iter; GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); - // We can just ignore mod key presses outright - if(event->is_modifier) return FALSE; + // We can just ignore modifier keys outright + if((keyval >= GDK_KEY_Shift_L && keyval <= GDK_KEY_Hyper_R)) + return FALSE; - if(dt_gdk_event_get_keyval(event) == GDK_KEY_Delete || dt_gdk_event_get_keyval(event) == GDK_KEY_BackSpace) + if(keyval == GDK_KEY_Delete || keyval == GDK_KEY_BackSpace) { // If a leaf node is selected, delete that preset @@ -1345,23 +1368,22 @@ void dt_gui_preferences_bool_reset(GtkWidget *widget) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), def); } -static gboolean -_gui_preferences_bool_click(GtkWidget *label, - GdkEventButton *event, - GtkWidget *widget) +static void +gui_preferences_bool_click_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + GtkWidget *widget) { - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS) + if(n_press == 1) { const gboolean cur = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), !cur); - return TRUE; } - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + else if(n_press == 2) { dt_gui_preferences_bool_reset(widget); - return TRUE; } - return FALSE; } void dt_gui_preferences_bool_update(GtkWidget *widget) @@ -1390,8 +1412,7 @@ GtkWidget *dt_gui_preferences_bool(GtkGrid *grid, gtk_grid_attach(GTK_GRID(grid), w, swap ? col : (col + 1), line, 1, 1); g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(_gui_preferences_bool_callback), (gpointer)key); - g_signal_connect(G_OBJECT(labelev), "button-press-event", - G_CALLBACK(_gui_preferences_bool_click), (gpointer)w); + dt_gui_connect_click(labelev, gui_preferences_bool_click_cb, NULL, w); return w; } @@ -1410,17 +1431,17 @@ void dt_gui_preferences_int_reset(GtkWidget *widget) gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), def); } -static gboolean -_gui_preferences_int_reset(GtkWidget *label, - GdkEventButton *event, - GtkWidget *widget) +static void +gui_preferences_int_reset_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + GtkWidget *widget) { - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(n_press == 2) { dt_gui_preferences_int_reset(widget); - return TRUE; } - return FALSE; } void dt_gui_preferences_int_update(GtkWidget *widget) @@ -1451,8 +1472,7 @@ GtkWidget *dt_gui_preferences_int(GtkGrid *grid, gtk_grid_attach(GTK_GRID(grid), w, col + 1, line, 1, 1); g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(_gui_preferences_int_callback), (gpointer)key); - g_signal_connect(G_OBJECT(labelev), "button-press-event", - G_CALLBACK(_gui_preferences_int_reset), (gpointer)w); + dt_gui_connect_click(labelev, gui_preferences_int_reset_cb, NULL, w); return w; } @@ -1521,17 +1541,17 @@ void dt_gui_preferences_string_reset(GtkWidget *widget) gtk_entry_set_text(GTK_ENTRY(widget), str); } -static gboolean -_gui_preferences_string_reset(GtkWidget *label, - GdkEventButton *event, - GtkWidget *widget) +static void +gui_preferences_string_reset_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + GtkWidget *widget) { - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(n_press == 2) { dt_gui_preferences_string_reset(widget); - return TRUE; } - return FALSE; } void dt_gui_preferences_string_update(GtkWidget *widget) @@ -1562,8 +1582,7 @@ GtkWidget *dt_gui_preferences_string(GtkGrid *grid, gtk_grid_attach(GTK_GRID(grid), w, col + 1, line, 1, 1); g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(_gui_preferences_string_callback), (gpointer)key); - g_signal_connect(G_OBJECT(labelev), "button-press-event", - G_CALLBACK(_gui_preferences_string_reset), (gpointer)w); + dt_gui_connect_click(labelev, gui_preferences_string_reset_cb, NULL, w); return w; } diff --git a/src/gui/preferences_ai.c b/src/gui/preferences_ai.c index ec27e2eb7ac1..00e1fa5b632a 100644 --- a/src/gui/preferences_ai.c +++ b/src/gui/preferences_ai.c @@ -15,8 +15,6 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#include "common/gdk_event_utils.h" - #include "gui/preferences_ai.h" #include "bauhaus/bauhaus.h" #include "dtgtk/button.h" @@ -610,33 +608,28 @@ static void _on_provider_changed(GtkWidget *widget, gpointer user_data) } // double-click on label resets the enable toggle to default -static gboolean -_reset_enable_click(GtkWidget *label, GdkEventButton *event, GtkWidget *widget) +static void _reset_enable_click_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + GtkWidget *widget) { - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) - { - const gboolean def = dt_confgen_get_bool("plugins/ai/enabled", DT_DEFAULT); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), def); - return TRUE; - } - return FALSE; + if(n_press < 2) return; + const gboolean def = dt_confgen_get_bool("plugins/ai/enabled", DT_DEFAULT); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), def); } // double-click on label resets the provider combo to default -static gboolean -_reset_provider_click(GtkWidget *label, GdkEventButton *event, gpointer user_data) +static void +_reset_provider_click_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + gpointer user_data) { - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) - { - dt_prefs_ai_data_t *data = (dt_prefs_ai_data_t *)user_data; - const char *def = dt_confgen_get(DT_AI_CONF_PROVIDER, DT_DEFAULT); - dt_ai_provider_t provider = dt_ai_provider_from_string(def); - dt_bauhaus_combobox_set(data->provider_combo, - _provider_to_combo_idx(provider, - data->supported_providers)); - return TRUE; - } - return FALSE; + if(n_press < 2) return; + dt_prefs_ai_data_t *data = (dt_prefs_ai_data_t *)user_data; + const char *def = dt_confgen_get(DT_AI_CONF_PROVIDER, DT_DEFAULT); + dt_ai_provider_t provider = dt_ai_provider_from_string(def); + dt_bauhaus_combobox_set(data->provider_combo, + _provider_to_combo_idx(provider, + data->supported_providers)); } static void _on_model_selection_toggled(GtkCellRendererToggle *cell, @@ -1260,24 +1253,16 @@ static gboolean _on_query_tooltip(GtkWidget *widget, } // hand cursor on info column for downloaded rows -static gboolean _on_tree_motion(GtkWidget *widget, - GdkEventMotion *event, - gpointer user_data) +static void _on_tree_motion_cb(GtkEventControllerMotion *controller, + double x, double y, + gpointer user_data) { + GtkWidget *widget = dt_gui_get_widget(controller); GtkTreeView *tv = GTK_TREE_VIEW(widget); GdkWindow *bin = gtk_tree_view_get_bin_window(tv); - if(!bin) return FALSE; + if(!bin) return; gint bx, by; - if(dt_gdk_event_get_window(event) == bin) - { - bx = (gint)dt_gdk_event_get_x(event); - by = (gint)dt_gdk_event_get_y(event); - } - else - { - gtk_tree_view_convert_widget_to_bin_window_coords( - tv, (gint)dt_gdk_event_get_x(event), (gint)dt_gdk_event_get_y(event), &bx, &by); - } + gtk_tree_view_convert_widget_to_bin_window_coords(tv, (gint)x, (gint)y, &bx, &by); if(_info_active_at_bin(user_data, tv, bx, by)) { GdkCursor *cursor = gdk_cursor_new_from_name(gdk_window_get_display(bin), "pointer"); @@ -1288,32 +1273,35 @@ static gboolean _on_tree_motion(GtkWidget *widget, { gdk_window_set_cursor(bin, NULL); } - return FALSE; } // click on the ⓘ info column opens the model card -static gboolean _on_info_button_press(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static void _on_info_button_press_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + gpointer user_data) { - if(dt_gdk_event_get_type(event) != GDK_BUTTON_PRESS - || dt_gdk_event_get_button(event) != 1) - return FALSE; + GtkWidget *widget = dt_gui_get_widget(gesture); + if(gtk_gesture_single_get_button(gesture) != 1) return; dt_prefs_ai_data_t *data = (dt_prefs_ai_data_t *)user_data; GtkTreeView *tv = GTK_TREE_VIEW(widget); GtkTreePath *path = NULL; GtkTreeViewColumn *column = NULL; - if(!gtk_tree_view_get_path_at_pos(tv, (gint)dt_gdk_event_get_x(event), (gint)dt_gdk_event_get_y(event), + /* gesture coordinates are relative to the widget allocation, while + * gtk_tree_view_get_path_at_pos() expects bin-window coordinates */ + gint bin_x, bin_y; + gtk_tree_view_convert_widget_to_bin_window_coords(tv, (gint)x, (gint)y, &bin_x, &bin_y); + + if(!gtk_tree_view_get_path_at_pos(tv, bin_x, bin_y, &path, &column, NULL, NULL)) - return FALSE; + return; // only react to clicks on the info column if(column != data->info_col) { gtk_tree_path_free(path); - return FALSE; + return; } GtkTreeIter iter; @@ -1328,7 +1316,6 @@ static gboolean _on_info_button_press(GtkWidget *widget, g_free(model_id); } gtk_tree_path_free(path); - return TRUE; } #if !defined(__APPLE__) @@ -1439,13 +1426,14 @@ static void _on_detect_system_ort(GtkButton *button, gpointer user_data) g_list_free_full(found, (GDestroyNotify)dt_ai_ort_found_free); } -static gboolean _reset_ort_path_click(GtkWidget *w, GdkEventButton *e, gpointer user_data) +static void _reset_ort_path_click_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + gpointer user_data) { - if(e->type != GDK_2BUTTON_PRESS) return FALSE; + if(n_press < 2) return; dt_prefs_ai_data_t *data = (dt_prefs_ai_data_t *)user_data; gtk_entry_set_text(GTK_ENTRY(data->ort_path_entry), ""); _set_ort_path(data, ""); - return TRUE; } static void _on_ort_path_changed(GtkEntry *entry, gpointer user_data) { @@ -1546,7 +1534,7 @@ void init_tab_ai(GtkWidget *dialog, GtkWidget *stack) GtkWidget *enable_label = gtk_label_new(_("enable AI features")); gtk_widget_set_halign(enable_label, GTK_ALIGN_START); GtkWidget *enable_labelev = gtk_event_box_new(); - gtk_widget_add_events(enable_labelev, GDK_BUTTON_PRESS_MASK); + gtk_container_add(GTK_CONTAINER(enable_labelev), enable_label); gtk_event_box_set_visible_window(GTK_EVENT_BOX(enable_labelev), FALSE); @@ -1560,11 +1548,7 @@ void init_tab_ai(GtkWidget *dialog, GtkWidget *stack) "toggled", G_CALLBACK(_on_enable_toggled), data); - g_signal_connect( - enable_labelev, - "button-press-event", - G_CALLBACK(_reset_enable_click), - data->enable_toggle); + dt_gui_connect_click_all(enable_labelev, _reset_enable_click_cb, NULL, data->enable_toggle); // single grid for enable, provider, and ORT path (column alignment) GtkWidget *settings_grid = gtk_grid_new(); gtk_grid_set_row_spacing(GTK_GRID(settings_grid), DT_PIXEL_APPLY_DPI(3)); @@ -1591,7 +1575,7 @@ void init_tab_ai(GtkWidget *dialog, GtkWidget *stack) GtkWidget *provider_label = gtk_label_new(_("AI acceleration")); gtk_widget_set_halign(provider_label, GTK_ALIGN_START); GtkWidget *provider_labelev = gtk_event_box_new(); - gtk_widget_add_events(provider_labelev, GDK_BUTTON_PRESS_MASK); + gtk_container_add(GTK_CONTAINER(provider_labelev), provider_label); gtk_event_box_set_visible_window(GTK_EVENT_BOX(provider_labelev), FALSE); @@ -1608,10 +1592,7 @@ void init_tab_ai(GtkWidget *dialog, GtkWidget *stack) "value-changed", G_CALLBACK(_on_provider_changed), data); - g_signal_connect(provider_labelev, - "button-press-event", - G_CALLBACK(_reset_provider_click), - data); + dt_gui_connect_click_all(provider_labelev, _reset_provider_click_cb, NULL, data); g_signal_connect(data->gpu_combo, "value-changed", G_CALLBACK(_on_gpu_changed), data); @@ -1691,7 +1672,7 @@ void init_tab_ai(GtkWidget *dialog, GtkWidget *stack) g_signal_connect(browse_btn, "clicked", G_CALLBACK(_on_ort_browse_clicked), data); g_signal_connect(detect_btn, "clicked", G_CALLBACK(_on_detect_system_ort), data); g_signal_connect(data->ort_path_entry, "activate", G_CALLBACK(_on_ort_path_changed), data); - g_signal_connect(path_labelev, "button-press-event", G_CALLBACK(_reset_ort_path_click), data); + dt_gui_connect_click_all(path_labelev, _reset_ort_path_click_cb, NULL, data); } #endif // !__APPLE__ @@ -1860,11 +1841,8 @@ void init_tab_ai(GtkWidget *dialog, GtkWidget *stack) gtk_widget_set_has_tooltip(data->model_list, TRUE); g_signal_connect(data->model_list, "query-tooltip", G_CALLBACK(_on_query_tooltip), data); - gtk_widget_add_events(data->model_list, GDK_POINTER_MOTION_MASK); - g_signal_connect(data->model_list, "motion-notify-event", - G_CALLBACK(_on_tree_motion), data); - g_signal_connect(data->model_list, "button-press-event", - G_CALLBACK(_on_info_button_press), data); + dt_gui_connect_motion(data->model_list, _on_tree_motion_cb, NULL, NULL, data); + dt_gui_connect_click(data->model_list, _on_info_button_press_cb, NULL, data); // scrolled window for the list GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL); diff --git a/src/iop/ashift.c b/src/iop/ashift.c index 55b45f5c4b20..862f0838a6fa 100644 --- a/src/iop/ashift.c +++ b/src/iop/ashift.c @@ -5318,19 +5318,21 @@ static void cropmode_callback(GtkWidget *widget, dt_iop_module_t *self) _swap_shadow_crop_box(p,g); } -static int _event_fit_v_button_clicked(GtkWidget *widget, - const GdkEventButton *event, - dt_iop_module_t *self) +static void _event_fit_v_button_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(FALSE); + DT_GUARD_GUI_UPDATE(); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { dt_iop_ashift_params_t *p = self->params; dt_iop_ashift_gui_data_t *g = self->gui_data; - const int control = dt_modifiers_include(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); - const int shift = dt_modifiers_include(dt_gdk_event_get_state(event), GDK_SHIFT_MASK); + const int control = dt_modifiers_include(dt_key_modifier_state(), GDK_CONTROL_MASK); + const int shift = dt_modifiers_include(dt_key_modifier_state(), GDK_SHIFT_MASK); dt_iop_ashift_fitaxis_t fitaxis = ASHIFT_FIT_NONE; @@ -5361,24 +5363,25 @@ static int _event_fit_v_button_clicked(GtkWidget *widget, //also calls dt_control_queue_redraw_center dt_dev_add_history_item(darktable.develop, self, TRUE); _swap_shadow_crop_box(p, g); - return TRUE; + } - return FALSE; } -static int _event_fit_h_button_clicked(GtkWidget *widget, - const GdkEventButton *event, - dt_iop_module_t *self) +static void _event_fit_h_button_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(FALSE); + DT_GUARD_GUI_UPDATE(); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { dt_iop_ashift_params_t *p = self->params; dt_iop_ashift_gui_data_t *g = self->gui_data; - const int control = dt_modifiers_include(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); - const int shift = dt_modifiers_include(dt_gdk_event_get_state(event), GDK_SHIFT_MASK); + const int control = dt_modifiers_include(dt_key_modifier_state(), GDK_CONTROL_MASK); + const int shift = dt_modifiers_include(dt_key_modifier_state(), GDK_SHIFT_MASK); dt_iop_ashift_fitaxis_t fitaxis = ASHIFT_FIT_NONE; @@ -5409,24 +5412,25 @@ static int _event_fit_h_button_clicked(GtkWidget *widget, //also calls dt_control_queue_redraw_center dt_dev_add_history_item(darktable.develop, self, TRUE); _swap_shadow_crop_box(p, g); - return TRUE; + } - return FALSE; } -static int _event_fit_both_button_clicked(GtkWidget *widget, - const GdkEventButton *event, - dt_iop_module_t *self) +static void _event_fit_both_button_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(FALSE); + DT_GUARD_GUI_UPDATE(); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { dt_iop_ashift_params_t *p = self->params; dt_iop_ashift_gui_data_t *g = self->gui_data; - const int control = dt_modifiers_include(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); - const int shift = dt_modifiers_include(dt_gdk_event_get_state(event), GDK_SHIFT_MASK); + const int control = dt_modifiers_include(dt_key_modifier_state(), GDK_CONTROL_MASK); + const int shift = dt_modifiers_include(dt_key_modifier_state(), GDK_SHIFT_MASK); dt_iop_ashift_fitaxis_t fitaxis = ASHIFT_FIT_NONE; @@ -5459,26 +5463,28 @@ static int _event_fit_both_button_clicked(GtkWidget *widget, //also calls dt_control_queue_redraw_center dt_dev_add_history_item(darktable.develop, self, TRUE); _swap_shadow_crop_box(p, g); - return TRUE; + } - return FALSE; } -static int _event_structure_auto_clicked(GtkWidget *widget, - const GdkEventButton *event, - dt_iop_module_t *self) +static void _event_structure_auto_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(FALSE); + GtkWidget *widget = dt_gui_get_widget(gesture); + DT_GUARD_GUI_UPDATE(); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { dt_iop_ashift_params_t *p = self->params; dt_iop_ashift_gui_data_t *g = self->gui_data; _do_clean_structure(self, p, TRUE); - const int control = dt_modifiers_include(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); - const int shift = dt_modifiers_include(dt_gdk_event_get_state(event), GDK_SHIFT_MASK); + const int control = dt_modifiers_include(dt_key_modifier_state(), GDK_CONTROL_MASK); + const int shift = dt_modifiers_include(dt_key_modifier_state(), GDK_SHIFT_MASK); dt_iop_ashift_enhance_t enhance; @@ -5497,7 +5503,7 @@ static int _event_structure_auto_clicked(GtkWidget *widget, { _gui_update_structure_states(self, widget); dt_control_queue_redraw_center(); - return TRUE; + } else { @@ -5526,9 +5532,8 @@ static int _event_structure_auto_clicked(GtkWidget *widget, // also calls dt_control_queue_redraw_center dt_dev_add_history_item(darktable.develop, self, TRUE); - return TRUE; + } - return FALSE; } // routine that is called after preview image has been processed. we @@ -5853,12 +5858,14 @@ static float log2_curve(const float inval, const dt_bauhaus_curve_t dir) return outval; } -static int _event_structure_quad_clicked(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void _event_structure_quad_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_ashift_gui_data_t *g = self->gui_data; - DT_GUARD_GUI_UPDATE(FALSE); + DT_GUARD_GUI_UPDATE(); dt_iop_request_focus(self); @@ -5878,15 +5885,17 @@ static int _event_structure_quad_clicked(GtkWidget *widget, // also calls dt_control_queue_redraw_center dt_dev_add_history_item(darktable.develop, self, TRUE); - return TRUE; + } -static int _event_structure_lines_clicked(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void _event_structure_lines_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_ashift_gui_data_t *g = self->gui_data; - DT_GUARD_GUI_UPDATE(FALSE); + DT_GUARD_GUI_UPDATE(); dt_iop_request_focus(self); @@ -5906,7 +5915,7 @@ static int _event_structure_lines_clicked(GtkWidget *widget, // also calls dt_control_queue_redraw_center dt_dev_add_history_item(darktable.develop, self, TRUE); - return TRUE; + } void gui_init(dt_iop_module_t *self) @@ -6108,24 +6117,12 @@ void gui_init(dt_iop_module_t *self) (g->structure_quad, _("manually define perspective rectangle")); gtk_widget_set_tooltip_text(g->structure_lines, _("manually draw structure lines")); - g_signal_connect(G_OBJECT(g->fit_v), "button-press-event", - G_CALLBACK(_event_fit_v_button_clicked), - (gpointer)self); - g_signal_connect(G_OBJECT(g->fit_h), "button-press-event", - G_CALLBACK(_event_fit_h_button_clicked), - (gpointer)self); - g_signal_connect(G_OBJECT(g->fit_both), "button-press-event", - G_CALLBACK(_event_fit_both_button_clicked), - (gpointer)self); - g_signal_connect(G_OBJECT(g->structure_quad), "button-press-event", - G_CALLBACK(_event_structure_quad_clicked), - (gpointer)self); - g_signal_connect(G_OBJECT(g->structure_lines), "button-press-event", - G_CALLBACK(_event_structure_lines_clicked), - (gpointer)self); - g_signal_connect(G_OBJECT(g->structure_auto), "button-press-event", - G_CALLBACK(_event_structure_auto_clicked), - (gpointer)self); + dt_gui_connect_click(g->fit_v, _event_fit_v_button_clicked, NULL, self); + dt_gui_connect_click(g->fit_h, _event_fit_h_button_clicked, NULL, self); + dt_gui_connect_click(g->fit_both, _event_fit_both_button_clicked, NULL, self); + dt_gui_connect_click(g->structure_quad, _event_structure_quad_clicked, NULL, self); + dt_gui_connect_click(g->structure_lines, _event_structure_lines_clicked, NULL, self); + dt_gui_connect_click(g->structure_auto, _event_structure_auto_clicked, NULL, self); g_signal_connect(G_OBJECT(self->widget), "draw", G_CALLBACK(_event_draw), self); dt_action_define_iop(self, N_("fit"), diff --git a/src/iop/atrous.c b/src/iop/atrous.c index be17168bd28d..147a6f02a7f3 100644 --- a/src/iop/atrous.c +++ b/src/iop/atrous.c @@ -1025,17 +1025,26 @@ void gui_update(dt_iop_module_t *self) // gui stuff: -static gboolean area_enter_leave_notify(GtkWidget *widget, - GdkEventCrossing *event, - dt_iop_module_t *self) +static void area_enter_notify(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_atrous_gui_data_t *g = self->gui_data; - g->in_curve = dt_gdk_event_get_type(event) == GDK_ENTER_NOTIFY; + g->in_curve = TRUE; if(!g->dragging) g->x_move = -1; + gtk_widget_queue_draw(dt_gui_get_widget(controller)); +} - gtk_widget_queue_draw(widget); - return FALSE; +static void area_leave_notify(GtkEventControllerMotion *controller, + dt_iop_module_t *self) +{ + dt_iop_atrous_gui_data_t *g = self->gui_data; + g->in_curve = FALSE; + if(!g->dragging) + g->x_move = -1; + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } // fills in new parameters based on mouse position (in 0,1) @@ -1372,19 +1381,21 @@ static gboolean area_draw(GtkWidget *widget, return FALSE; } -static gboolean area_motion_notify(GtkWidget *widget, - GdkEventMotion *event, - dt_iop_module_t *self) +static void area_motion_notify(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_atrous_gui_data_t *g = self->gui_data; dt_iop_atrous_params_t *p = self->params; + GtkWidget *widget = dt_gui_get_widget(controller); const int inset = INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); const int height = allocation.height - 2 * inset - DT_RESIZE_HANDLE_SIZE; const int width = allocation.width - 2 * inset; - if(!g->dragging) g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; - g->mouse_y = 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + if(!g->dragging) g->mouse_x = CLAMP(x - inset, 0, width) / (float)width; + g->mouse_y = 1.0 - CLAMP(y - inset, 0, height) / (float)height; darktable.control->element = 0; @@ -1398,7 +1409,7 @@ static gboolean area_motion_notify(GtkWidget *widget, *p = g->drag_params; if(g->x_move >= 0) { - const float mx = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; + const float mx = CLAMP(x - inset, 0, width) / (float)width; if(g->x_move > 0 && g->x_move < BANDS - 1) { const float minx = p->x[g->channel][g->x_move - 1] + 0.001f; @@ -1413,7 +1424,7 @@ static gboolean area_motion_notify(GtkWidget *widget, gtk_widget_queue_draw(widget); dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + g->channel); } - else if(dt_gdk_event_get_y(event) > height) + else if(y > height) { // move x-positions g->x_move = 0; @@ -1452,14 +1463,20 @@ static gboolean area_motion_notify(GtkWidget *widget, g->x_move = -1; gtk_widget_queue_draw(widget); } - return TRUE; } -static gboolean area_button_press(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void area_button_press(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + + GtkWidget *widget = dt_gui_get_widget(gesture); + + if(n_press >= 2) { // reset current curve dt_iop_atrous_params_t *p = self->params; @@ -1473,7 +1490,7 @@ static gboolean area_button_press(GtkWidget *widget, } dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + g->channel2); } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + else { // set active point dt_iop_atrous_gui_data_t *g = self->gui_data; @@ -1485,46 +1502,45 @@ static gboolean area_button_press(GtkWidget *widget, const int width = allocation.width - 2 * inset; g->mouse_pick = dt_draw_curve_calc_value(g->minmax_curve, - CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width); - g->mouse_pick -= 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + CLAMP(x - inset, 0, width) / (float)width); + g->mouse_pick -= 1.0 - CLAMP(y - inset, 0, height) / (float)height; g->dragging = 1; - return TRUE; } - return FALSE; } -static gboolean area_button_release(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void area_button_release(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { - dt_iop_atrous_gui_data_t *g = self->gui_data; - g->dragging = 0; - reset_mix(self); - return TRUE; - } - return FALSE; + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + + dt_iop_atrous_gui_data_t *g = self->gui_data; + g->dragging = 0; + reset_mix(self); } -static gboolean area_scrolled(GtkWidget *widget, - GdkEventScroll *event, - dt_iop_module_t *self) +static void area_scrolled(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_iop_module_t *self) { dt_iop_atrous_gui_data_t *g = self->gui_data; + GtkWidget *widget = dt_gui_get_widget(controller); - if(dt_gui_ignore_scroll(event)) return FALSE; - - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_MOD1_MASK)) - return gtk_widget_event(GTK_WIDGET(g->channel_tabs), (GdkEvent*)event); + if(dt_modifier_eq(controller, GDK_MOD1_MASK)) + { + // FIXME: GTK4 - need to forward scroll event to notebook + return; + } - int delta_y; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) + if(dy != 0.0) { - g->mouse_radius = CLAMP(g->mouse_radius * (1.0 - 0.1 * delta_y), 0.25 / BANDS, 1.0); + g->mouse_radius = CLAMP(g->mouse_radius * (1.0 - 0.1 * dy), 0.25 / BANDS, 1.0); gtk_widget_queue_draw(widget); } - return TRUE; } static void tab_switch(GtkNotebook *notebook, @@ -1770,18 +1786,11 @@ void gui_init(dt_iop_module_t *self) dt_action_define_iop(self, NULL, N_("graph"), GTK_WIDGET(g->area), &_action_def_equalizer); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(area_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", - G_CALLBACK(area_button_press), self); - g_signal_connect(G_OBJECT(g->area), "button-release-event", - G_CALLBACK(area_button_release), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", - G_CALLBACK(area_motion_notify), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", - G_CALLBACK(area_enter_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "enter-notify-event", - G_CALLBACK(area_enter_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", - G_CALLBACK(area_scrolled), self); + dt_gui_connect_click(g->area, area_button_press, area_button_release, self); + dt_gui_connect_motion(g->area, area_motion_notify, area_enter_notify, area_leave_notify, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + area_scrolled, self); self->widget = dt_gui_vbox(g->channel_tabs, g->area); diff --git a/src/iop/basecurve.c b/src/iop/basecurve.c index c1ed5ffaad0f..ef972eac2dc5 100644 --- a/src/iop/basecurve.c +++ b/src/iop/basecurve.c @@ -1530,15 +1530,13 @@ void cleanup_global(dt_iop_module_so_t *self) self->data = NULL; } -static gboolean dt_iop_basecurve_leave_notify(GtkWidget *widget, - GdkEventCrossing *event, - dt_iop_module_t *self) +static void dt_iop_basecurve_leave_notify(GtkEventControllerMotion *controller, + dt_iop_module_t *self) { dt_iop_basecurve_gui_data_t *g = self->gui_data; - if(!(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK)) + if(!(dt_key_modifier_state() & GDK_BUTTON1_MASK)) g->selected = -1; - gtk_widget_queue_draw(widget); - return FALSE; + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } static float to_log(const float x, const float base) @@ -1775,10 +1773,12 @@ static gboolean _move_point_internal(dt_iop_module_t *self, float dy, const guint state); -static gboolean dt_iop_basecurve_motion_notify(GtkWidget *widget, - GdkEventMotion *event, - dt_iop_module_t *self) +static void dt_iop_basecurve_motion_notify(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_basecurve_gui_data_t *g = self->gui_data; dt_iop_basecurve_params_t *p = self->params; int ch = 0; @@ -1791,14 +1791,14 @@ static gboolean dt_iop_basecurve_motion_notify(GtkWidget *widget, int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset; const double old_m_x = g->mouse_x; const double old_m_y = g->mouse_y; - g->mouse_x = dt_gdk_event_get_x(event) - inset; - g->mouse_y = dt_gdk_event_get_y(event) - inset; + g->mouse_x = x - inset; + g->mouse_y = y - inset; const float mx = CLAMP(g->mouse_x, 0, width) / (float)width; const float my = 1.0f - CLAMP(g->mouse_y, 0, height) / (float)height; const float linx = to_lin(mx, g->loglogscale), liny = to_lin(my, g->loglogscale); - if(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK) + if(dt_key_modifier_state() & GDK_BUTTON1_MASK) { // got a vertex selected: if(g->selected >= 0) @@ -1812,7 +1812,7 @@ static gboolean dt_iop_basecurve_motion_notify(GtkWidget *widget, const float dy = to_lin(1 - g->mouse_y / height - translate_mouse_y, g->loglogscale) - to_lin(1 - old_m_y / height - translate_mouse_y, g->loglogscale); - return _move_point_internal(self, widget, dx, dy, dt_gdk_event_get_state(event)); + _move_point_internal(self, widget, dx, dy, dt_key_modifier_state()); } else if(nodes < MAXNODES && g->selected >= -1) { @@ -1842,13 +1842,15 @@ static gboolean dt_iop_basecurve_motion_notify(GtkWidget *widget, } if(g->selected >= 0) gtk_widget_grab_focus(widget); gtk_widget_queue_draw(widget); - return TRUE; } -static gboolean dt_iop_basecurve_button_press(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void dt_iop_basecurve_button_press(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(gesture); dt_iop_basecurve_params_t *p = self->params; const dt_iop_basecurve_params_t *const d = self->default_params; dt_iop_basecurve_gui_data_t *g = self->gui_data; @@ -1857,9 +1859,9 @@ static gboolean dt_iop_basecurve_button_press(GtkWidget *widget, int nodes = p->basecurve_nodes[ch]; dt_iop_basecurve_node_t *basecurve = p->basecurve[ch]; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK) + if(dt_modifier_is(dt_key_modifier_state(), GDK_CONTROL_MASK) && nodes < MAXNODES && g->selected == -1) { // if we are not on a node -> add a new node at the current x of the pointer and y of the curve at that x @@ -1867,8 +1869,8 @@ static gboolean dt_iop_basecurve_button_press(GtkWidget *widget, GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int width = allocation.width - 2 * inset; - g->mouse_x = dt_gdk_event_get_x(event) - inset; - g->mouse_y = dt_gdk_event_get_y(event) - inset; + g->mouse_x = x - inset; + g->mouse_y = y - inset; const float mx = CLAMP(g->mouse_x, 0, width) / (float)width; const float linx = to_lin(mx, g->loglogscale); @@ -1895,12 +1897,12 @@ static gboolean dt_iop_basecurve_button_press(GtkWidget *widget, (selected < nodes && basecurve[selected].x - linx <= 0.025))) { // evaluate the curve at the current x position - const float y = dt_draw_curve_calc_value(g->minmax_curve, linx); + const float curve_y = dt_draw_curve_calc_value(g->minmax_curve, linx); - if(y >= 0.0 && y <= 1.0) // never add something outside the viewport, you couldn't change it afterwards + if(curve_y >= 0.0 && curve_y <= 1.0) // never add something outside the viewport, you couldn't change it afterwards { // create a new node - selected = _add_node(basecurve, &p->basecurve_nodes[ch], linx, y); + selected = _add_node(basecurve, &p->basecurve_nodes[ch], linx, curve_y); // maybe set the new one as being selected float min = .04f; @@ -1908,7 +1910,7 @@ static gboolean dt_iop_basecurve_button_press(GtkWidget *widget, for(int k = 0; k < nodes; k++) { float other_y = to_log(basecurve[k].y, g->loglogscale); - float dist = (y - other_y) * (y - other_y); + float dist = (curve_y - other_y) * (curve_y - other_y); if(dist < min) g->selected = selected; } @@ -1916,9 +1918,8 @@ static gboolean dt_iop_basecurve_button_press(GtkWidget *widget, gtk_widget_queue_draw(GTK_WIDGET(g->area)); } } - return TRUE; } - else if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + else if(n_press >= 2) { // reset current curve p->basecurve_nodes[ch] = d->basecurve_nodes[ch]; @@ -1931,10 +1932,9 @@ static gboolean dt_iop_basecurve_button_press(GtkWidget *widget, g->selected = -2; // avoid motion notify re-inserting immediately. dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget); gtk_widget_queue_draw(GTK_WIDGET(g->area)); - return TRUE; } } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY && g->selected >= 0) + else if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY && g->selected >= 0) { if(g->selected == 0 || g->selected == nodes - 1) { @@ -1942,7 +1942,6 @@ static gboolean dt_iop_basecurve_button_press(GtkWidget *widget, basecurve[g->selected].y = basecurve[g->selected].x = reset_value; gtk_widget_queue_draw(GTK_WIDGET(g->area)); dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget); - return TRUE; } for(int k = g->selected; k < nodes - 1; k++) @@ -1955,9 +1954,7 @@ static gboolean dt_iop_basecurve_button_press(GtkWidget *widget, p->basecurve_nodes[ch]--; gtk_widget_queue_draw(GTK_WIDGET(g->area)); dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget); - return TRUE; } - return FALSE; } static gboolean _move_point_internal(dt_iop_module_t *self, @@ -1988,50 +1985,49 @@ static gboolean _move_point_internal(dt_iop_module_t *self, #define BASECURVE_DEFAULT_STEP (0.001f) -static gboolean _scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self) +static void _scrolled(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, dt_iop_module_t *self) { dt_iop_basecurve_gui_data_t *g = self->gui_data; + GtkWidget *widget = dt_gui_get_widget(controller); - if(dt_gui_ignore_scroll(event)) return FALSE; - - if(g->selected < 0) return TRUE; + if(g->selected < 0) return; - gdouble delta_y; - if(dt_gui_get_scroll_delta(event, &delta_y)) + if(dy != 0.0) { - delta_y *= -BASECURVE_DEFAULT_STEP; - return _move_point_internal(self, widget, 0.0, delta_y, dt_gdk_event_get_state(event)); + dy *= -BASECURVE_DEFAULT_STEP; + _move_point_internal(self, widget, 0.0, dy, dt_key_modifier_state()); } - - return TRUE; } -static gboolean dt_iop_basecurve_key_press(GtkWidget *widget, - GdkEventKey *event, - dt_iop_module_t *self) +static gboolean dt_iop_basecurve_key_press(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_basecurve_gui_data_t *g = self->gui_data; if(g->selected < 0) return TRUE; int handled = 0; float dx = 0.0f, dy = 0.0f; - if(dt_gdk_event_get_keyval(event) == GDK_KEY_Up || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Up) + if(keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up) { handled = 1; dy = BASECURVE_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Down || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Down) + else if(keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down) { handled = 1; dy = -BASECURVE_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Right || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Right) + else if(keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right) { handled = 1; dx = BASECURVE_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Left || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Left) + else if(keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left) { handled = 1; dx = -BASECURVE_DEFAULT_STEP; @@ -2039,7 +2035,7 @@ static gboolean dt_iop_basecurve_key_press(GtkWidget *widget, if(!handled) return FALSE; - return _move_point_internal(self, widget, dx, dy, dt_gdk_event_get_state(event)); + return _move_point_internal(self, widget, dx, dy, state); } #undef BASECURVE_DEFAULT_STEP @@ -2124,16 +2120,19 @@ void gui_init(dt_iop_module_t *self) g_signal_connect(G_OBJECT(g->logbase), "value-changed", G_CALLBACK(logbase_callback), self); dt_gui_box_add(self->widget, g->logbase); - gtk_widget_add_events(GTK_WIDGET(g->area), GDK_POINTER_MOTION_MASK | darktable.gui->scroll_mask - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); gtk_widget_set_can_focus(GTK_WIDGET(g->area), TRUE); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(dt_iop_basecurve_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", G_CALLBACK(dt_iop_basecurve_button_press), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", G_CALLBACK(dt_iop_basecurve_motion_notify), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", G_CALLBACK(dt_iop_basecurve_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", G_CALLBACK(_scrolled), self); - g_signal_connect(G_OBJECT(g->area), "key-press-event", G_CALLBACK(dt_iop_basecurve_key_press), self); + gtk_widget_add_events(GTK_WIDGET(g->area), + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | darktable.gui->scroll_mask); + dt_gui_connect_click(g->area, dt_iop_basecurve_button_press, NULL, self); + dt_gui_connect_motion(g->area, dt_iop_basecurve_motion_notify, NULL, dt_iop_basecurve_leave_notify, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _scrolled, self); + dt_gui_connect_key(g->area, dt_iop_basecurve_key_press, self); } void gui_cleanup(dt_iop_module_t *self) diff --git a/src/iop/colorbalance.c b/src/iop/colorbalance.c index 975af9eeb293..98876e14ace8 100644 --- a/src/iop/colorbalance.c +++ b/src/iop/colorbalance.c @@ -1797,7 +1797,11 @@ static void _configure_slider_blocks(gpointer instance, dt_iop_module_t *self) if(old_container) gtk_widget_show(new_container); } -static gboolean _cycle_layout_callback(GtkWidget *label, GdkEventButton *event, dt_iop_module_t *self) +static void _cycle_layout_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { gchar *layout = dt_conf_get_string("plugins/darkroom/colorbalance/layout"); @@ -1808,7 +1812,6 @@ static gboolean _cycle_layout_callback(GtkWidget *label, GdkEventButton *event, g_free(layout); _configure_slider_blocks(NULL, self); - return TRUE; } #define HSL_CALLBACK(which) \ @@ -1898,7 +1901,7 @@ void gui_init(dt_iop_module_t *self) gtk_widget_set_tooltip_text(g->main_label, _("click to cycle layout")); GtkWidget *main_label_box = gtk_event_box_new(); gtk_container_add(GTK_CONTAINER(main_label_box), g->main_label); - g_signal_connect(G_OBJECT(main_label_box), "button-release-event", G_CALLBACK(_cycle_layout_callback), self); + dt_gui_connect_click(main_label_box, NULL, _cycle_layout_callback, self); g->main_box = gtk_event_box_new(); // is filled in _configure_slider_blocks diff --git a/src/iop/colorchecker.c b/src/iop/colorchecker.c index 548790259d67..2e1037ed7044 100644 --- a/src/iop/colorchecker.c +++ b/src/iop/colorchecker.c @@ -1380,22 +1380,24 @@ static gboolean checker_draw(GtkWidget *widget, return TRUE; } -static gboolean checker_motion_notify( - GtkWidget *widget, - GdkEventMotion *event, +static void checker_motion_notify( + GtkEventControllerMotion *controller, + gdouble x, + gdouble y, dt_iop_module_t *self) { // highlight? dt_iop_colorchecker_params_t *p = self->params; dt_iop_colorchecker_gui_data_t *g = self->gui_data; + GtkWidget *widget = dt_gui_get_widget(controller); GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); const int width = allocation.width; const int height = allocation.height; - const float mouse_x = CLAMP(dt_gdk_event_get_x(event), 0, width); - const float mouse_y = CLAMP(dt_gdk_event_get_y(event), 0, height); + const float mouse_x = CLAMP(x, 0, width); + const float mouse_y = CLAMP(y, 0, height); int cells_x = 6, cells_y = 4; if(p->num_patches > 24) { @@ -1405,7 +1407,7 @@ static gboolean checker_motion_notify( const float mx = mouse_x * cells_x / (float)width; const float my = mouse_y * cells_y / (float)height; const int patch = (int)mx + cells_x * (int)my; - if(patch < 0 || patch >= p->num_patches) return FALSE; + if(patch < 0 || patch >= p->num_patches) return; char tooltip[1024]; snprintf(tooltip, sizeof(tooltip), _("(%2.2f %2.2f %2.2f)\n" @@ -1416,21 +1418,24 @@ static gboolean checker_motion_notify( "shift+click while color picking to replace patch"), p->source_L[patch], p->source_a[patch], p->source_b[patch]); gtk_widget_set_tooltip_text(g->area, tooltip); - return TRUE; } -static gboolean checker_button_press( - GtkWidget *widget, GdkEventButton *event, +static void checker_button_press( + GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *self) { dt_iop_colorchecker_params_t *p = self->params; dt_iop_colorchecker_gui_data_t *g = self->gui_data; + GtkWidget *widget = dt_gui_get_widget(gesture); GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int width = allocation.width, height = allocation.height; - const float mouse_x = CLAMP(dt_gdk_event_get_x(event), 0, width); - const float mouse_y = CLAMP(dt_gdk_event_get_y(event), 0, height); + const float mouse_x = CLAMP(x, 0, width); + const float mouse_y = CLAMP(y, 0, height); int cells_x = 6, cells_y = 4; if(p->num_patches > 24) { @@ -1440,9 +1445,12 @@ static gboolean checker_button_press( const float mx = mouse_x * cells_x / (float)width; const float my = mouse_y * cells_y / (float)height; int patch = (int)mx + cells_x*(int)my; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + + const guint button = gtk_gesture_single_get_current_button(gesture); + + if(button == GDK_BUTTON_PRIMARY && n_press >= 2) { // reset on double click - if(patch < 0 || patch >= p->num_patches) return FALSE; + if(patch < 0 || patch >= p->num_patches) return; p->target_L[patch] = p->source_L[patch]; p->target_a[patch] = p->source_a[patch]; p->target_b[patch] = p->source_b[patch]; @@ -1451,12 +1459,12 @@ static gboolean checker_button_press( _colorchecker_update_sliders(self); DT_LEAVE_GUI_UPDATE(); gtk_widget_queue_draw(g->area); - return TRUE; + return; } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY && (patch < p->num_patches)) + else if(button == GDK_BUTTON_SECONDARY && (patch < p->num_patches)) { // right click: delete patch, move others up - if(patch < 0 || patch >= p->num_patches) return FALSE; + if(patch < 0 || patch >= p->num_patches) return; memmove(p->target_L+patch, p->target_L+patch+1, sizeof(float)*(p->num_patches-1-patch)); memmove(p->target_a+patch, p->target_a+patch+1, @@ -1476,10 +1484,10 @@ static gboolean checker_button_press( _colorchecker_update_sliders(self); DT_LEAVE_GUI_UPDATE(); gtk_widget_queue_draw(g->area); - return TRUE; + return; } - else if((dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) && - dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK) && + else if(button == GDK_BUTTON_PRIMARY && + dt_modifier_is(dt_key_modifier_state(), GDK_SHIFT_MASK) && (self->request_color_pick == DT_REQUEST_COLORPICK_MODULE)) { // shift-left while colour picking: replace source colour @@ -1520,11 +1528,10 @@ static gboolean checker_button_press( g->patch = g->drawn_patch = patch; gtk_widget_queue_draw(g->area); } - return TRUE; + return; } if(patch >= p->num_patches) patch = p->num_patches-1; dt_bauhaus_combobox_set(g->combobox_patch, patch); - return FALSE; } void gui_init(dt_iop_module_t *self) @@ -1534,16 +1541,10 @@ void gui_init(dt_iop_module_t *self) // custom 24-patch widget in addition to combo box g->area = dtgtk_drawing_area_new_with_aspect_ratio(4.0/6.0); - gtk_widget_add_events(GTK_WIDGET(g->area), - GDK_POINTER_MOTION_MASK - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_LEAVE_NOTIFY_MASK); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(checker_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", - G_CALLBACK(checker_button_press), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", - G_CALLBACK(checker_motion_notify), self); + dt_gui_connect_click_all(g->area, checker_button_press, NULL, self); + dt_gui_connect_motion(g->area, checker_motion_notify, NULL, NULL, self); g->patch = 0; g->drawn_patch = -1; diff --git a/src/iop/colorcorrection.c b/src/iop/colorcorrection.c index 93c19dd2337d..f659010b6aaf 100644 --- a/src/iop/colorcorrection.c +++ b/src/iop/colorcorrection.c @@ -220,16 +220,26 @@ void gui_update(dt_iop_module_t *self) static gboolean dt_iop_colorcorrection_draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self); -static gboolean dt_iop_colorcorrection_motion_notify(GtkWidget *widget, GdkEventMotion *event, - dt_iop_module_t *self); -static gboolean dt_iop_colorcorrection_button_press(GtkWidget *widget, GdkEventButton *event, - dt_iop_module_t *self); -static gboolean dt_iop_colorcorrection_leave_notify(GtkWidget *widget, GdkEventCrossing *event, - dt_iop_module_t *self); -static gboolean dt_iop_colorcorrection_scrolled(GtkWidget *widget, GdkEventScroll *event, - dt_iop_module_t *self); -static gboolean dt_iop_colorcorrection_key_press(GtkWidget *widget, GdkEventKey *event, +static void dt_iop_colorcorrection_motion_notify(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self); +static void dt_iop_colorcorrection_button_press(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *self); +static void dt_iop_colorcorrection_leave_notify(GtkEventControllerMotion *controller, + dt_iop_module_t *self); +static void dt_iop_colorcorrection_scrolled(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_iop_module_t *self); +static gboolean dt_iop_colorcorrection_key_press(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_iop_module_t *self); void gui_init(dt_iop_module_t *self) { @@ -243,19 +253,19 @@ void gui_init(dt_iop_module_t *self) gtk_widget_set_tooltip_text(GTK_WIDGET(g->area), _("drag the line for split-toning. " "bright means highlights, dark means shadows. " "use mouse wheel to change saturation.")); - gtk_widget_add_events(GTK_WIDGET(g->area), GDK_POINTER_MOTION_MASK | darktable.gui->scroll_mask - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); gtk_widget_set_can_focus(GTK_WIDGET(g->area), TRUE); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(dt_iop_colorcorrection_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", G_CALLBACK(dt_iop_colorcorrection_button_press), - self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", G_CALLBACK(dt_iop_colorcorrection_motion_notify), - self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", G_CALLBACK(dt_iop_colorcorrection_leave_notify), - self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", G_CALLBACK(dt_iop_colorcorrection_scrolled), self); - g_signal_connect(G_OBJECT(g->area), "key-press-event", G_CALLBACK(dt_iop_colorcorrection_key_press), self); + gtk_widget_add_events(GTK_WIDGET(g->area), + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | darktable.gui->scroll_mask); + dt_gui_connect_click(g->area, dt_iop_colorcorrection_button_press, NULL, self); + dt_gui_connect_motion(g->area, dt_iop_colorcorrection_motion_notify, NULL, dt_iop_colorcorrection_leave_notify, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + dt_iop_colorcorrection_scrolled, self); + dt_gui_connect_key(g->area, dt_iop_colorcorrection_key_press, self); self->widget = dt_gui_vbox(g->area); g->slider = dt_bauhaus_slider_from_params(self, N_("saturation")); @@ -347,20 +357,23 @@ static gboolean dt_iop_colorcorrection_draw(GtkWidget *widget, cairo_t *crf, dt_ return TRUE; } -static gboolean dt_iop_colorcorrection_motion_notify(GtkWidget *widget, GdkEventMotion *event, - dt_iop_module_t *self) +static void dt_iop_colorcorrection_motion_notify(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_colorcorrection_gui_data_t *g = self->gui_data; dt_iop_colorcorrection_params_t *p = self->params; const int inset = DT_COLORCORRECTION_INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int width = allocation.width - 2 * inset, height = allocation.height - 2 * inset; - const float mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width); - const float mouse_y = CLAMP(height - 1 - dt_gdk_event_get_y(event) + inset, 0, height); + const float mouse_x = CLAMP(x - inset, 0, width); + const float mouse_y = CLAMP(height - 1 - y + inset, 0, height); const float ma = (2.0 * mouse_x - width) * DT_COLORCORRECTION_MAX / (float)width; const float mb = (2.0 * mouse_y - height) * DT_COLORCORRECTION_MAX / (float)height; - if(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK) + if(dt_key_modifier_state() & GDK_BUTTON1_MASK) { if(g->selected == 1) { @@ -388,13 +401,15 @@ static gboolean dt_iop_colorcorrection_motion_notify(GtkWidget *widget, GdkEvent } if(g->selected > 0) gtk_widget_grab_focus(widget); gtk_widget_queue_draw(GTK_WIDGET(g->area)); - return TRUE; } -static gboolean dt_iop_colorcorrection_button_press(GtkWidget *widget, GdkEventButton *event, - dt_iop_module_t *self) +static void dt_iop_colorcorrection_button_press(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY && n_press >= 2) { // double click resets: dt_iop_colorcorrection_gui_data_t *g = self->gui_data; @@ -416,63 +431,57 @@ static gboolean dt_iop_colorcorrection_button_press(GtkWidget *widget, GdkEventB dt_dev_add_history_item(darktable.develop, self, TRUE); } } - return TRUE; } - return FALSE; } -static gboolean dt_iop_colorcorrection_leave_notify(GtkWidget *widget, GdkEventCrossing *event, - dt_iop_module_t *self) +static void dt_iop_colorcorrection_leave_notify(GtkEventControllerMotion *controller, + dt_iop_module_t *self) { dt_iop_colorcorrection_gui_data_t *g = self->gui_data; gtk_widget_queue_draw(GTK_WIDGET(g->area)); - return TRUE; } -static gboolean dt_iop_colorcorrection_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self) +static void dt_iop_colorcorrection_scrolled(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_colorcorrection_gui_data_t *g = self->gui_data; dt_iop_colorcorrection_params_t *p = self->params; - if(dt_gui_ignore_scroll(event)) return FALSE; - - int delta_y; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) + if(dy != 0.0) { - p->saturation = CLAMP(p->saturation - 0.1 * delta_y, -3.0, 3.0); + p->saturation = CLAMP(p->saturation - 0.1 * dy, -3.0, 3.0); dt_bauhaus_slider_set(g->slider, p->saturation); gtk_widget_queue_draw(widget); } - - return TRUE; } #define COLORCORRECTION_DEFAULT_STEP (0.5f) -static gboolean dt_iop_colorcorrection_key_press(GtkWidget *widget, GdkEventKey *event, dt_iop_module_t *self) +static gboolean dt_iop_colorcorrection_key_press(GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_colorcorrection_gui_data_t *g = self->gui_data; dt_iop_colorcorrection_params_t *p = self->params; if(g->selected < 1) return FALSE; int handled = 0; float dx = 0.0f, dy = 0.0f; - if(dt_gdk_event_get_keyval(event) == GDK_KEY_Up || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Up) + if(keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up) { handled = 1; dy = COLORCORRECTION_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Down || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Down) + else if(keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down) { handled = 1; dy = -COLORCORRECTION_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Right || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Right) + else if(keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right) { handled = 1; dx = COLORCORRECTION_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Left || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Left) + else if(keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left) { handled = 1; dx = -COLORCORRECTION_DEFAULT_STEP; @@ -480,7 +489,7 @@ static gboolean dt_iop_colorcorrection_key_press(GtkWidget *widget, GdkEventKey if(!handled) return FALSE; - float multiplier = dt_accel_get_speed_multiplier(widget, dt_gdk_event_get_state(event)); + float multiplier = dt_accel_get_speed_multiplier(widget, state); dx *= multiplier; dy *= multiplier; diff --git a/src/iop/colorequal.c b/src/iop/colorequal.c index 10e8be9394db..e58d19d3b3da 100644 --- a/src/iop/colorequal.c +++ b/src/iop/colorequal.c @@ -2697,88 +2697,84 @@ static void _area_reset_nodes(dt_iop_colorequal_gui_data_t *g) } } -static gboolean _area_scrolled_callback(GtkWidget *widget, - GdkEventScroll *event, - const dt_iop_module_t *self) +static void _area_scrolled_callback(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_iop_module_t *self) { const dt_iop_colorequal_gui_data_t *g = self->gui_data; + GdkEvent *event = gtk_get_current_event(); + if(!event) return; + GtkWidget *w = dt_modifier_is(dt_gdk_event_get_state(event), GDK_MOD1_MASK) ? GTK_WIDGET(g->notebook) : _get_slider(g, g->selected); - return gtk_widget_event(w, (GdkEvent*)event); + gtk_widget_event(w, event); + gdk_event_free(event); } -static gboolean _area_motion_notify_callback(GtkWidget *widget, - const GdkEventMotion *event, - const dt_iop_module_t *self) +static void _area_motion_notify_callback(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_colorequal_gui_data_t *g = self->gui_data; if(g->dragging && g->on_node) - _area_set_pos(g, dt_gdk_event_get_y(event)); + _area_set_pos(g, y); else { // look if close to a node const float epsilon = DT_PIXEL_APPLY_DPI(10.0); const int oldsel = g->selected; const int oldon = g->on_node; - g->selected = (int)(((float)dt_gdk_event_get_x(event) - g->points[0][0]) + g->selected = (int)(((float)x - g->points[0][0]) / (g->points[1][0] - g->points[0][0]) + 0.5f) % NODES; - g->on_node = fabsf(g->points[g->selected][1] - (float)dt_gdk_event_get_y(event)) < epsilon; + g->on_node = fabsf(g->points[g->selected][1] - (float)y) < epsilon; darktable.control->element = g->selected; if(oldsel != g->selected || oldon != g->on_node) gtk_widget_queue_draw(GTK_WIDGET(g->area)); } - - return TRUE; } -static gboolean _area_button_press_callback(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void _area_button_press_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_colorequal_gui_data_t *g = self->gui_data; + const guint button = gtk_gesture_single_get_current_button(gesture); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_MIDDLE - || (dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY // Ctrl+Click alias for macOS - && dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK))) + if(button == GDK_BUTTON_MIDDLE + || (button == GDK_BUTTON_PRIMARY // Ctrl+Click alias for macOS + && dt_modifier_is(dt_key_modifier_state(), GDK_CONTROL_MASK))) { dt_conf_set_bool("plugins/darkroom/colorequal/show_sliders", gtk_notebook_get_n_pages(g->notebook) != 4); gui_update(self); } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + else if(button == GDK_BUTTON_PRIMARY) { - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) - { + if(n_press >= 2) _area_reset_nodes(g); - return TRUE; - } else - { g->dragging = TRUE; - } } - else - return gtk_widget_event(_get_slider(g, g->selected), (GdkEvent*)event); - - return FALSE; + // other buttons: forward not supported with event controllers, ignored } -static gboolean _area_button_release_callback(GtkWidget *widget, - const GdkEventButton *event, - const dt_iop_module_t *self) +static void _area_button_release_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_colorequal_gui_data_t *g = self->gui_data; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) g->dragging = FALSE; - return TRUE; - } - - return FALSE; } static void _area_size_callback(GtkWidget *widget, @@ -2982,21 +2978,16 @@ void gui_init(dt_iop_module_t *self) dt_action_define_iop(self, NULL, N_("graph"), GTK_WIDGET(g->area), &_action_def_coloreq); gtk_widget_set_tooltip_text(GTK_WIDGET(g->area), _("double-click to reset the curve\nmiddle-click to toggle sliders visibility\nalt+scroll to change page")); gtk_widget_set_can_focus(GTK_WIDGET(g->area), TRUE); + g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(_iop_colorequalizer_draw), self); gtk_widget_add_events(GTK_WIDGET(g->area), - GDK_BUTTON_PRESS_MASK - | GDK_POINTER_MOTION_MASK + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_SCROLL_MASK - | GDK_SMOOTH_SCROLL_MASK); - g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(_iop_colorequalizer_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", - G_CALLBACK(_area_button_press_callback), self); - g_signal_connect(G_OBJECT(g->area), "button-release-event", - G_CALLBACK(_area_button_release_callback), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", - G_CALLBACK(_area_motion_notify_callback), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", - G_CALLBACK(_area_scrolled_callback), self); + | darktable.gui->scroll_mask); + dt_gui_connect_click_all(g->area, _area_button_press_callback, _area_button_release_callback, self); + dt_gui_connect_motion(g->area, _area_motion_notify_callback, NULL, NULL, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _area_scrolled_callback, self); g_signal_connect(G_OBJECT(g->area), "size-allocate", G_CALLBACK(_area_size_callback), self); diff --git a/src/iop/colorzones.c b/src/iop/colorzones.c index e3ab99c47a8f..87dd0fa77df5 100644 --- a/src/iop/colorzones.c +++ b/src/iop/colorzones.c @@ -1657,24 +1657,22 @@ static gboolean _bottom_area_draw_callback(GtkWidget *widget, #undef DT_COLORZONES_CELLSI #undef DT_COLORZONES_CELLSJ -static gboolean _bottom_area_button_press_callback(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void _bottom_area_button_press_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_colorzones_gui_data_t *g = self->gui_data; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY && n_press >= 2) { // reset zoom level g->zoom_factor = 1.f; g->offset_x = g->offset_y = 0.f; gtk_widget_queue_draw(GTK_WIDGET(g->area)); - - return TRUE; } - - return FALSE; } static gboolean _sanity_check(const float x, @@ -1857,73 +1855,70 @@ static inline int _add_node(dt_iop_colorzones_node_t *curve, return selected; } -static gboolean _area_scrolled_callback(GtkWidget *widget, - GdkEventScroll *event, +static void _area_scrolled_callback(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_colorzones_gui_data_t *g = self->gui_data; dt_iop_colorzones_params_t *p = self->params; - if(dt_gui_ignore_scroll(event)) return FALSE; + if(dy == 0.0) return; - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_MOD1_MASK)) - return gtk_widget_event(GTK_WIDGET(g->channel_tabs), (GdkEvent*)event); - - int delta_y; + if(dt_modifier_is(dt_key_modifier_state(), GDK_MOD1_MASK)) + { + // FIXME: event forwarding to g->channel_tabs removed + return; + } if(darktable.develop->darkroom_skip_mouse_events) { - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) - { - GtkAllocation allocation; - gtk_widget_get_allocation(widget, &allocation); - - const float mx = g->mouse_x; - const float my = g->mouse_y; - const float linx = _mouse_to_curve(mx, g->zoom_factor, g->offset_x), - liny = _mouse_to_curve(my, g->zoom_factor, g->offset_y); + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); - g->zoom_factor *= 1.0 - 0.1 * delta_y; - if(g->zoom_factor < 1.f) g->zoom_factor = 1.f; + const float mx = g->mouse_x; + const float my = g->mouse_y; + const float linx = _mouse_to_curve(mx, g->zoom_factor, g->offset_x), + liny = _mouse_to_curve(my, g->zoom_factor, g->offset_y); - g->offset_x = linx - (mx / g->zoom_factor); - g->offset_y = liny - (my / g->zoom_factor); + g->zoom_factor *= 1.0 - 0.1 * dy; + if(g->zoom_factor < 1.f) g->zoom_factor = 1.f; - g->offset_x = CLAMP(g->offset_x, 0.f, (g->zoom_factor - 1.f) / g->zoom_factor); - g->offset_y = CLAMP(g->offset_y, 0.f, (g->zoom_factor - 1.f) / g->zoom_factor); + g->offset_x = linx - (mx / g->zoom_factor); + g->offset_y = liny - (my / g->zoom_factor); - gtk_widget_queue_draw(GTK_WIDGET(g->area)); - } + g->offset_x = CLAMP(g->offset_x, 0.f, (g->zoom_factor - 1.f) / g->zoom_factor); + g->offset_y = CLAMP(g->offset_y, 0.f, (g->zoom_factor - 1.f) / g->zoom_factor); - return TRUE; + gtk_widget_queue_draw(GTK_WIDGET(g->area)); + return; } - if(g->selected < 0 && !g->edit_by_area) return TRUE; + if(g->selected < 0 && !g->edit_by_area) return; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) - { - dt_iop_color_picker_reset(self, TRUE); + dt_iop_color_picker_reset(self, TRUE); - if(g->edit_by_area) - { - const int bands = p->curve_num_nodes[g->channel]; - g->mouse_radius = CLAMP(g->mouse_radius * (1.0 - 0.1 * delta_y), 0.2 / bands, 1.0); - gtk_widget_queue_draw(widget); - } - else - { - const float dy = delta_y * -DT_IOP_COLORZONES_DEFAULT_STEP; - return _move_point_internal(self, widget, g->selected, 0.f, dy, dt_gdk_event_get_state(event)); - } + if(g->edit_by_area) + { + const int bands = p->curve_num_nodes[g->channel]; + g->mouse_radius = CLAMP(g->mouse_radius * (1.0 - 0.1 * dy), 0.2 / bands, 1.0); + gtk_widget_queue_draw(widget); + } + else + { + dy *= -DT_IOP_COLORZONES_DEFAULT_STEP; + _move_point_internal(self, widget, g->selected, 0.f, dy, dt_key_modifier_state()); + gtk_widget_queue_draw(widget); } - - return TRUE; } -static gboolean _area_motion_notify_callback(GtkWidget *widget, - GdkEventMotion *event, +static void _area_motion_notify_callback(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_colorzones_gui_data_t *g = self->gui_data; dt_iop_colorzones_params_t *p = self->params; @@ -1941,10 +1936,10 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, const float mx = g->mouse_x; const float my = g->mouse_y; - g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; - g->mouse_y = 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + g->mouse_x = CLAMP(x - inset, 0, width) / (float)width; + g->mouse_y = 1.0 - CLAMP(y - inset, 0, height) / (float)height; - if(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK) + if(dt_key_modifier_state() & GDK_BUTTON1_MASK) { g->offset_x += (mx - g->mouse_x) / g->zoom_factor; g->offset_y += (my - g->mouse_y) / g->zoom_factor; @@ -1954,7 +1949,6 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, gtk_widget_queue_draw(GTK_WIDGET(g->area)); } - return TRUE; } const int ch = g->channel; @@ -1964,8 +1958,8 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, const double old_m_x = g->mouse_x; const double old_m_y = fabs(g->mouse_y); - g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; - g->mouse_y = 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + g->mouse_x = CLAMP(x - inset, 0, width) / (float)width; + g->mouse_y = 1.0 - CLAMP(y - inset, 0, height) / (float)height; darktable.control->element = (int)(8.0 * @@ -1973,7 +1967,7 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, g->zoom_factor, g->offset_x) + 0.5f) % 8; // move a node - if(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK) + if(dt_key_modifier_state() & GDK_BUTTON1_MASK) { if(g->edit_by_area) { @@ -2004,7 +1998,7 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, g->zoom_factor, g->offset_y); dt_iop_color_picker_reset(self, TRUE); - return _move_point_internal(self, widget, g->selected, dx, dy, dt_gdk_event_get_state(event)); + _move_point_internal(self, widget, g->selected, dx, dy, dt_key_modifier_state()); } } @@ -2020,7 +2014,7 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + ch); } } - else if(dt_gdk_event_get_y(event) > height) + else if(y > height) { g->x_move = 0; const int bands = p->curve_num_nodes[g->channel]; @@ -2043,7 +2037,7 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, } else { - if(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK) + if(dt_key_modifier_state() & GDK_BUTTON1_MASK) { if(nodes < DT_IOP_COLORZONES_MAXNODES && g->selected == -1) { @@ -2086,33 +2080,34 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, } gtk_widget_queue_draw(widget); - return TRUE; } -static gboolean _area_button_press_callback(GtkWidget *widget, - GdkEventButton *event, +static void _area_button_press_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(gesture); dt_iop_colorzones_gui_data_t *g = self->gui_data; dt_iop_colorzones_params_t *p = self->params; const dt_iop_colorzones_params_t *const d = self->default_params; - if(darktable.develop->darkroom_skip_mouse_events) return TRUE; + if(darktable.develop->darkroom_skip_mouse_events) return; int ch = g->channel; int nodes = p->curve_num_nodes[ch]; dt_iop_colorzones_node_t *curve = p->curve[ch]; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { - if(g->edit_by_area && dt_gdk_event_get_type(event) != GDK_2BUTTON_PRESS - && !dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) + if(g->edit_by_area && n_press < 2 + && !dt_modifier_is(dt_key_modifier_state(), GDK_CONTROL_MASK)) { g->dragging = 1; - return TRUE; } - else if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS - && dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK) + else if(n_press >= 1 + && dt_modifier_is(dt_key_modifier_state(), GDK_CONTROL_MASK) && nodes < DT_IOP_COLORZONES_MAXNODES && (g->selected == -1 || g->edit_by_area)) { @@ -2124,8 +2119,8 @@ static gboolean _area_button_press_callback(GtkWidget *widget, const int height = allocation.height - 2 * inset; const int width = allocation.width - 2 * inset; - g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; - g->mouse_y = 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + g->mouse_x = CLAMP(x - inset, 0, width) / (float)width; + g->mouse_y = 1.0 - CLAMP(y - inset, 0, height) / (float)height; const float mx = _mouse_to_curve(g->mouse_x, g->zoom_factor, g->offset_x); @@ -2147,14 +2142,14 @@ static gboolean _area_button_press_callback(GtkWidget *widget, if(selected == -1) selected = nodes; // evaluate the curve at the current x position - const float y = dt_draw_curve_calc_value(g->minmax_curve[ch], mx); + const float curve_y = dt_draw_curve_calc_value(g->minmax_curve[ch], mx); if(y >= 0.0f && y <= 1.0f) // never add something outside the // viewport, you couldn't change it // afterwards { // create a new node - selected = _add_node(curve, &p->curve_num_nodes[ch], mx, y); + selected = _add_node(curve, &p->curve_num_nodes[ch], mx, curve_y); // maybe set the new one as being selected const float min = .04f * .04f; // comparing against square @@ -2170,10 +2165,8 @@ static gboolean _area_button_press_callback(GtkWidget *widget, dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + ch); gtk_widget_queue_draw(GTK_WIDGET(g->area)); } - - return TRUE; } - else if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + else if(n_press >= 2) { // reset current curve p->curve_num_nodes[ch] = d->curve_num_nodes[ch]; @@ -2188,11 +2181,9 @@ static gboolean _area_button_press_callback(GtkWidget *widget, dt_iop_color_picker_reset(self, TRUE); dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + ch); gtk_widget_queue_draw(GTK_WIDGET(g->area)); - - return TRUE; } } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY && g->selected >= 0) + else if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY && g->selected >= 0) { if((g->selected == 0 || g->selected == nodes - 1) && p->splines_version == DT_IOP_COLORZONES_SPLINES_V1) @@ -2214,54 +2205,50 @@ static gboolean _area_button_press_callback(GtkWidget *widget, dt_iop_color_picker_reset(self, TRUE); gtk_widget_queue_draw(GTK_WIDGET(g->area)); dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + ch); - return TRUE; } // right click deletes the node, ctrl+right click reset the node to y-zero _delete_node(self, curve, &p->curve_num_nodes[ch], - g->selected, dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)); + g->selected, dt_modifier_is(dt_key_modifier_state(), GDK_CONTROL_MASK)); g->selected = -2; // avoid re-insertion of that point immediately after this - - return TRUE; } - - return FALSE; } -static gboolean _area_button_release_callback(GtkWidget *widget, - GdkEventButton *event, +static void _area_button_release_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *self) { - if(darktable.develop->darkroom_skip_mouse_events) return TRUE; + if(darktable.develop->darkroom_skip_mouse_events) return; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { dt_iop_colorzones_gui_data_t *g = self->gui_data; g->dragging = 0; - return TRUE; } - return FALSE; } -static gboolean _area_leave_notify_callback(GtkWidget *widget, - GdkEventCrossing *event, +static void _area_leave_notify_callback(GtkEventControllerMotion *controller, dt_iop_module_t *self) { - if(darktable.develop->darkroom_skip_mouse_events) return TRUE; + if(darktable.develop->darkroom_skip_mouse_events) return; dt_iop_colorzones_gui_data_t *g = self->gui_data; // for fluxbox g->mouse_y = -fabs(g->mouse_y); - if(!(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK)) + if(!(dt_key_modifier_state() & GDK_BUTTON1_MASK)) g->selected = -1; - gtk_widget_queue_draw(widget); - return TRUE; + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } -static gboolean _area_key_press_callback(GtkWidget *widget, - GdkEventKey *event, +static gboolean _area_key_press_callback(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_colorzones_gui_data_t *g = self->gui_data; if(darktable.develop->darkroom_skip_mouse_events) return FALSE; @@ -2270,22 +2257,22 @@ static gboolean _area_key_press_callback(GtkWidget *widget, int handled = 0; float dx = 0.0f, dy = 0.0f; - if(dt_gdk_event_get_keyval(event) == GDK_KEY_Up || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Up) + if(keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up) { handled = 1; dy = DT_IOP_COLORZONES_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Down || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Down) + else if(keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down) { handled = 1; dy = -DT_IOP_COLORZONES_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Right || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Right) + else if(keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right) { handled = 1; dx = DT_IOP_COLORZONES_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Left || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Left) + else if(keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left) { handled = 1; dx = -DT_IOP_COLORZONES_DEFAULT_STEP; @@ -2294,7 +2281,7 @@ static gboolean _area_key_press_callback(GtkWidget *widget, if(!handled) return FALSE; dt_iop_color_picker_reset(self, TRUE); - return _move_point_internal(self, widget, g->selected, dx, dy, dt_gdk_event_get_state(event)); + return _move_point_internal(self, widget, g->selected, dx, dy, dt_key_modifier_state()); } static void _channel_tabs_switch_callback(GtkNotebook *notebook, @@ -2711,25 +2698,22 @@ void gui_init(dt_iop_module_t *self) gtk_widget_set_can_focus(GTK_WIDGET(g->area), TRUE); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(_area_draw_callback), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", - G_CALLBACK(_area_button_press_callback), self); - g_signal_connect(G_OBJECT(g->area), "button-release-event", - G_CALLBACK(_area_button_release_callback), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", - G_CALLBACK(_area_motion_notify_callback), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", - G_CALLBACK(_area_leave_notify_callback), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", - G_CALLBACK(_area_scrolled_callback), self); - g_signal_connect(G_OBJECT(g->area), "key-press-event", - G_CALLBACK(_area_key_press_callback), self); + gtk_widget_add_events(GTK_WIDGET(g->area), + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK + | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | darktable.gui->scroll_mask); + dt_gui_connect_click(g->area, _area_button_press_callback, _area_button_release_callback, self); + dt_gui_connect_motion(g->area, _area_motion_notify_callback, NULL, _area_leave_notify_callback, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _area_scrolled_callback, self); + dt_gui_connect_key(g->area, _area_key_press_callback, self); - gtk_widget_add_events(GTK_WIDGET(g->bottom_area), GDK_BUTTON_PRESS_MASK); g_signal_connect(G_OBJECT(g->bottom_area), "draw", G_CALLBACK(_bottom_area_draw_callback), self); - g_signal_connect(G_OBJECT(g->bottom_area), "button-press-event", - G_CALLBACK(_bottom_area_button_press_callback), - self); + gtk_widget_add_events(GTK_WIDGET(g->bottom_area), GDK_BUTTON_PRESS_MASK); + dt_gui_connect_click(g->bottom_area, _bottom_area_button_press_callback, NULL, self); g->interpolator = dt_bauhaus_combobox_new_interpolation(self); dt_gui_box_add(self->widget, g->interpolator); diff --git a/src/iop/denoiseprofile.c b/src/iop/denoiseprofile.c index 4c5a6cbf4c8e..9fc04a510f86 100644 --- a/src/iop/denoiseprofile.c +++ b/src/iop/denoiseprofile.c @@ -3442,18 +3442,20 @@ static gboolean denoiseprofile_draw(GtkWidget *widget, return FALSE; } -static gboolean denoiseprofile_motion_notify(GtkWidget *widget, - GdkEventMotion *event, - dt_iop_module_t *self) +static void denoiseprofile_motion_notify(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_denoiseprofile_gui_data_t *g = self->gui_data; dt_iop_denoiseprofile_params_t *p = self->params; + GtkWidget *widget = dt_gui_get_widget(controller); const int inset = DT_IOP_DENOISE_PROFILE_INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset; - if(!g->dragging) g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; - g->mouse_y = 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + if(!g->dragging) g->mouse_x = CLAMP(x - inset, 0, width) / (float)width; + g->mouse_y = 1.0 - CLAMP(y - inset, 0, height) / (float)height; if(g->dragging) { *p = g->drag_params; @@ -3470,16 +3472,22 @@ static gboolean denoiseprofile_motion_notify(GtkWidget *widget, g->x_move = -1; } gtk_widget_queue_draw(widget); - return TRUE; } -static gboolean denoiseprofile_button_press(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void denoiseprofile_button_press(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_denoiseprofile_gui_data_t *g = self->gui_data; const int ch = g->channel; - if(dt_gdk_event_get_button(event) == 1 && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + + GtkWidget *widget = dt_gui_get_widget(gesture); + + if(n_press >= 2) { // reset current curve dt_iop_denoiseprofile_params_t *p = self->params; @@ -3493,7 +3501,7 @@ static gboolean denoiseprofile_button_press(GtkWidget *widget, dt_dev_add_history_item(darktable.develop, self, TRUE); gtk_widget_queue_draw(GTK_WIDGET(g->area)); } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + else { g->drag_params = *(dt_iop_denoiseprofile_params_t *)self->params; const int inset = DT_IOP_DENOISE_PROFILE_INSET; @@ -3502,57 +3510,46 @@ static gboolean denoiseprofile_button_press(GtkWidget *widget, int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset; g->mouse_pick = dt_draw_curve_calc_value(g->transition_curve, - CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width); - g->mouse_pick -= 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + CLAMP(x - inset, 0, width) / (float)width); + g->mouse_pick -= 1.0 - CLAMP(y - inset, 0, height) / (float)height; g->dragging = 1; - return TRUE; } - return FALSE; } -static gboolean denoiseprofile_button_release(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void denoiseprofile_button_release(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { - dt_iop_denoiseprofile_gui_data_t *g = self->gui_data; - g->dragging = 0; - return TRUE; - } - return FALSE; + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + + dt_iop_denoiseprofile_gui_data_t *g = self->gui_data; + g->dragging = 0; } -static gboolean denoiseprofile_leave_notify(GtkWidget *widget, - GdkEventCrossing *event, - dt_iop_module_t *self) +static void denoiseprofile_leave_notify(GtkEventControllerMotion *controller, + dt_iop_module_t *self) { dt_iop_denoiseprofile_gui_data_t *g = self->gui_data; if(!g->dragging) g->mouse_y = -1.0; - gtk_widget_queue_draw(widget); - return TRUE; + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } -static gboolean denoiseprofile_scrolled(GtkWidget *widget, - GdkEventScroll *event, - dt_iop_module_t *self) +static void denoiseprofile_scrolled(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_iop_module_t *self) { dt_iop_denoiseprofile_gui_data_t *g = self->gui_data; - if(dt_gui_ignore_scroll(event)) return FALSE; - - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_MOD1_MASK)) - return gtk_widget_event(GTK_WIDGET(g->channel > DT_DENOISE_PROFILE_B ? g->channel_tabs_Y0U0V0 : g->channel_tabs), (GdkEvent*)event); - - int delta_y; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) + if(dy != 0.0) { - g->mouse_radius = CLAMP(g->mouse_radius * (1.f - 0.1f * delta_y), + g->mouse_radius = CLAMP(g->mouse_radius * (1.f - 0.1f * dy), 0.2f / DT_IOP_DENOISE_PROFILE_BANDS, 1.f); - gtk_widget_queue_draw(widget); + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } - - return TRUE; } static void denoiseprofile_tab_switch(GtkNotebook *notebook, @@ -3632,16 +3629,11 @@ void gui_init(dt_iop_module_t *self) dt_action_define_iop(self, NULL, N_("graph"), GTK_WIDGET(g->area), NULL); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(denoiseprofile_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", - G_CALLBACK(denoiseprofile_button_press), self); - g_signal_connect(G_OBJECT(g->area), "button-release-event", - G_CALLBACK(denoiseprofile_button_release), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", - G_CALLBACK(denoiseprofile_motion_notify), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", - G_CALLBACK(denoiseprofile_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", - G_CALLBACK(denoiseprofile_scrolled), self); + dt_gui_connect_click(g->area, denoiseprofile_button_press, denoiseprofile_button_release, self); + dt_gui_connect_motion(g->area, denoiseprofile_motion_notify, NULL, denoiseprofile_leave_notify, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + denoiseprofile_scrolled, self); dt_gui_box_add(g->box_wavelets, g->channel_tabs, g->channel_tabs_Y0U0V0, g->area); diff --git a/src/iop/filmicrgb.c b/src/iop/filmicrgb.c index c452b571dc70..621c61d722bb 100644 --- a/src/iop/filmicrgb.c +++ b/src/iop/filmicrgb.c @@ -2714,7 +2714,11 @@ void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, apply_autotune(self); } -static void show_mask_callback(GtkToggleButton *button, GdkEventButton *event, const dt_iop_module_t *self) +static void show_mask_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + const dt_iop_module_t *self) { DT_TRY_GUI_UPDATE(); dt_iop_filmicrgb_gui_data_t *g = self->gui_data; @@ -4165,110 +4169,101 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, dt_iop_mo return FALSE; } -static gboolean area_button_press(GtkWidget *widget, const GdkEventButton *event, dt_iop_module_t *self) +static void area_button_press(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(TRUE); + DT_GUARD_GUI_UPDATE(); dt_iop_filmicrgb_gui_data_t *g = self->gui_data; dt_iop_request_focus(self); - if(g->active_button != DT_FILMIC_GUI_BUTTON_LAST) + if(g->active_button == DT_FILMIC_GUI_BUTTON_LAST) + return; + + const guint button = gtk_gesture_single_get_current_button(gesture); + + if(button == GDK_BUTTON_PRIMARY && n_press >= 2) { + // double click resets view + if(g->active_button == DT_FILMIC_GUI_BUTTON_TYPE) + { + g->gui_mode = DT_FILMIC_GUI_LOOK; + gtk_widget_queue_draw(GTK_WIDGET(g->area)); + dt_conf_set_int("plugins/darkroom/filmicrgb/graph_view", g->gui_mode); + return; + } + return; + } - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(button == GDK_BUTTON_PRIMARY) + { + // simple left click cycles through modes in positive direction + if(g->active_button == DT_FILMIC_GUI_BUTTON_TYPE) { - // double click resets view - if(g->active_button == DT_FILMIC_GUI_BUTTON_TYPE) - { + // cycle type of graph + if(g->gui_mode == DT_FILMIC_GUI_RANGES) g->gui_mode = DT_FILMIC_GUI_LOOK; - gtk_widget_queue_draw(GTK_WIDGET(g->area)); - dt_conf_set_int("plugins/darkroom/filmicrgb/graph_view", g->gui_mode); - return TRUE; - } else - { - return FALSE; - } + g->gui_mode++; + + gtk_widget_queue_draw(GTK_WIDGET(g->area)); + dt_conf_set_int("plugins/darkroom/filmicrgb/graph_view", g->gui_mode); + return; } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + else if(g->active_button == DT_FILMIC_GUI_BUTTON_LABELS) { - // simple left click cycles through modes in positive direction - if(g->active_button == DT_FILMIC_GUI_BUTTON_TYPE) - { - // cycle type of graph - if(g->gui_mode == DT_FILMIC_GUI_RANGES) - g->gui_mode = DT_FILMIC_GUI_LOOK; - else - g->gui_mode++; + g->gui_show_labels = !g->gui_show_labels; + gtk_widget_queue_draw(GTK_WIDGET(g->area)); + dt_conf_set_int("plugins/darkroom/filmicrgb/graph_show_labels", g->gui_show_labels); + return; + } + return; + } - gtk_widget_queue_draw(GTK_WIDGET(g->area)); - dt_conf_set_int("plugins/darkroom/filmicrgb/graph_view", g->gui_mode); - return TRUE; - } - else if(g->active_button == DT_FILMIC_GUI_BUTTON_LABELS) - { - g->gui_show_labels = !g->gui_show_labels; - gtk_widget_queue_draw(GTK_WIDGET(g->area)); - dt_conf_set_int("plugins/darkroom/filmicrgb/graph_show_labels", g->gui_show_labels); - return TRUE; - } + if(button == GDK_BUTTON_SECONDARY) + { + // simple right click cycles through modes in negative direction + if(g->active_button == DT_FILMIC_GUI_BUTTON_TYPE) + { + if(g->gui_mode == DT_FILMIC_GUI_LOOK) + g->gui_mode = DT_FILMIC_GUI_RANGES; else - { - // we should never get there since (g->active_button != DT_FILMIC_GUI_BUTTON_LAST) - // and any other case has been processed above. - return FALSE; - } + g->gui_mode--; + + gtk_widget_queue_draw(GTK_WIDGET(g->area)); + dt_conf_set_int("plugins/darkroom/filmicrgb/graph_view", g->gui_mode); + return; } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) + else if(g->active_button == DT_FILMIC_GUI_BUTTON_LABELS) { - // simple right click cycles through modes in negative direction - if(g->active_button == DT_FILMIC_GUI_BUTTON_TYPE) - { - if(g->gui_mode == DT_FILMIC_GUI_LOOK) - g->gui_mode = DT_FILMIC_GUI_RANGES; - else - g->gui_mode--; - - gtk_widget_queue_draw(GTK_WIDGET(g->area)); - dt_conf_set_int("plugins/darkroom/filmicrgb/graph_view", g->gui_mode); - return TRUE; - } - else if(g->active_button == DT_FILMIC_GUI_BUTTON_LABELS) - { - g->gui_show_labels = !g->gui_show_labels; - gtk_widget_queue_draw(GTK_WIDGET(g->area)); - dt_conf_set_int("plugins/darkroom/filmicrgb/graph_show_labels", g->gui_show_labels); - return TRUE; - } - else - { - return FALSE; - } + g->gui_show_labels = !g->gui_show_labels; + gtk_widget_queue_draw(GTK_WIDGET(g->area)); + dt_conf_set_int("plugins/darkroom/filmicrgb/graph_show_labels", g->gui_show_labels); + return; } } +} - return FALSE; +static void area_enter_notify(GtkEventControllerMotion *controller, gdouble x, gdouble y, dt_iop_module_t *self) +{ + dt_iop_filmicrgb_gui_data_t *g = self->gui_data; + g->gui_hover = TRUE; + gtk_widget_queue_draw(GTK_WIDGET(g->area)); } -static gboolean area_enter_leave_notify(GtkWidget *widget, const GdkEventCrossing *event, const dt_iop_module_t *self) +static void area_leave_notify(GtkEventControllerMotion *controller, dt_iop_module_t *self) { dt_iop_filmicrgb_gui_data_t *g = self->gui_data; - g->gui_hover = dt_gdk_event_get_type(event) == GDK_ENTER_NOTIFY; + g->gui_hover = FALSE; gtk_widget_queue_draw(GTK_WIDGET(g->area)); - return FALSE; } -static gboolean area_motion_notify(GtkWidget *widget, const GdkEventMotion *event, const dt_iop_module_t *self) +static void area_motion_notify(GtkEventControllerMotion *controller, gdouble x, gdouble y, dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(1); + DT_GUARD_GUI_UPDATE(); dt_iop_filmicrgb_gui_data_t *g = self->gui_data; - if(!g->gui_sizes_inited) return FALSE; - - // get in-widget coordinates - const float y = dt_gdk_event_get_y(event); - const float x = dt_gdk_event_get_x(event); + if(!g->gui_sizes_inited) return; if(x > 0. && x < g->allocation.width && y > 0. && y < g->allocation.height) g->gui_hover = TRUE; @@ -4326,13 +4321,12 @@ static gboolean area_motion_notify(GtkWidget *widget, const GdkEventMotion *even } if(save_active_button != g->active_button) gtk_widget_queue_draw(GTK_WIDGET(g->area)); - return TRUE; + return; } else { g->active_button = DT_FILMIC_GUI_BUTTON_LAST; - if(save_active_button != g->active_button) (GTK_WIDGET(g->area)); - return FALSE; + if(save_active_button != g->active_button) gtk_widget_queue_draw(GTK_WIDGET(g->area)); } } @@ -4354,10 +4348,8 @@ void gui_init(dt_iop_module_t *self) gtk_widget_set_can_focus(GTK_WIDGET(g->area), TRUE); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(dt_iop_tonecurve_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", G_CALLBACK(area_button_press), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", G_CALLBACK(area_enter_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "enter-notify-event", G_CALLBACK(area_enter_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", G_CALLBACK(area_motion_notify), self); + dt_gui_connect_click_all(g->area, area_button_press, NULL, self); + dt_gui_connect_motion(g->area, area_motion_notify, area_enter_notify, area_leave_notify, self); // Init GTK notebook static struct dt_action_def_t notebook_def = { }; diff --git a/src/iop/levels.c b/src/iop/levels.c index 01d88bbafa00..f03a30670ccf 100644 --- a/src/iop/levels.c +++ b/src/iop/levels.c @@ -47,11 +47,11 @@ DT_MODULE_INTROSPECTION(2, dt_iop_levels_params_t) static gboolean dt_iop_levels_area_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self); -static gboolean dt_iop_levels_motion_notify(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self); -static gboolean dt_iop_levels_button_press(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self); -static gboolean dt_iop_levels_button_release(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self); -static gboolean dt_iop_levels_leave_notify(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self); -static gboolean dt_iop_levels_scroll(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self); +static void dt_iop_levels_motion_notify(GtkEventControllerMotion *controller, gdouble x, gdouble y, dt_iop_module_t *self); +static void dt_iop_levels_button_press(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self); +static void dt_iop_levels_button_release(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self); +static void dt_iop_levels_leave_notify(GtkEventControllerMotion *controller, dt_iop_module_t *self); +static void dt_iop_levels_scroll(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, dt_iop_module_t *self); static void dt_iop_levels_autoadjust_callback(GtkRange *range, dt_iop_module_t *self); //static void dt_iop_levels_mode_callback(GtkWidget *combo, gpointer user_data); //static void dt_iop_levels_percentiles_callback(GtkWidget *slider, gpointer user_data); @@ -637,11 +637,11 @@ void gui_init(dt_iop_module_t *self) dt_action_define_iop(self, NULL, N_("levels"), GTK_WIDGET(g->area), NULL); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(dt_iop_levels_area_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", G_CALLBACK(dt_iop_levels_button_press), self); - g_signal_connect(G_OBJECT(g->area), "button-release-event", G_CALLBACK(dt_iop_levels_button_release), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", G_CALLBACK(dt_iop_levels_motion_notify), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", G_CALLBACK(dt_iop_levels_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", G_CALLBACK(dt_iop_levels_scroll), self); + dt_gui_connect_click(g->area, dt_iop_levels_button_press, dt_iop_levels_button_release, self); + dt_gui_connect_motion(g->area, dt_iop_levels_motion_notify, NULL, dt_iop_levels_leave_notify, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + dt_iop_levels_scroll, self); GtkWidget *autobutton = gtk_button_new_with_label(_("auto")); gtk_widget_set_tooltip_text(autobutton, _("apply auto levels")); @@ -697,12 +697,11 @@ void gui_cleanup(dt_iop_module_t *self) g_list_free(g->modes); } -static gboolean dt_iop_levels_leave_notify(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self) +static void dt_iop_levels_leave_notify(GtkEventControllerMotion *controller, dt_iop_module_t *self) { dt_iop_levels_gui_data_t *g = self->gui_data; g->mouse_x = g->mouse_y = -1.0; - gtk_widget_queue_draw(widget); - return TRUE; + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } static gboolean dt_iop_levels_area_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self) @@ -863,26 +862,27 @@ static void dt_iop_levels_move_handle(dt_iop_module_t *self, int handle_move, fl g->last_picked_color = -1; } -static gboolean dt_iop_levels_motion_notify(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self) +static void dt_iop_levels_motion_notify(GtkEventControllerMotion *controller, gdouble x, gdouble y, dt_iop_module_t *self) { dt_iop_levels_gui_data_t *g = self->gui_data; dt_iop_levels_params_t *p = self->params; + GtkWidget *widget = dt_gui_get_widget(controller); const int inset = DT_GUI_CURVE_EDITOR_INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int height = allocation.height - 2 * inset - DT_RESIZE_HANDLE_SIZE, width = allocation.width - 2 * inset; if(!g->dragging) { - g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width); + g->mouse_x = CLAMP(x - inset, 0, width); g->drag_start_percentage = (p->levels[1] - p->levels[0]) / (p->levels[2] - p->levels[0]); } - g->mouse_y = CLAMP(dt_gdk_event_get_y(event) - inset, 0, height); + g->mouse_y = CLAMP(y - inset, 0, height); if(g->dragging) { if(g->handle_move >= 0 && g->handle_move < 3) { - const float mx = (CLAMP(dt_gdk_event_get_x(event) - inset, 0, width)) / (float)width; + const float mx = (CLAMP(x - inset, 0, width)) / (float)width; dt_iop_levels_move_handle(self, g->handle_move, mx, p->levels, g->drag_start_percentage); } @@ -891,7 +891,7 @@ static gboolean dt_iop_levels_motion_notify(GtkWidget *widget, GdkEventMotion *e else { g->handle_move = 0; - const float mx = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; + const float mx = CLAMP(x - inset, 0, width) / (float)width; float dist = fabsf(p->levels[0] - mx); for(int k = 1; k < 3; k++) { @@ -904,77 +904,63 @@ static gboolean dt_iop_levels_motion_notify(GtkWidget *widget, GdkEventMotion *e } } gtk_widget_queue_draw(widget); - - return TRUE; } -static gboolean dt_iop_levels_button_press(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self) +static void dt_iop_levels_button_press(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self) { - // set active point - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { - if(darktable.develop->gui_module != self) dt_iop_request_focus(self); + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) - { - // Reset - dt_iop_levels_gui_data_t *g = self->gui_data; - memcpy(self->params, self->default_params, self->params_size); + if(darktable.develop->gui_module != self) dt_iop_request_focus(self); - // Needed in case the user scrolls or drags immediately after a reset, - // as drag_start_percentage is only updated when the mouse is moved. - g->drag_start_percentage = 0.5; - dt_dev_add_history_item(darktable.develop, self, TRUE); - gtk_widget_queue_draw(GTK_WIDGET(g->area)); - } - else - { - dt_iop_levels_gui_data_t *g = self->gui_data; - g->dragging = 1; - } - return TRUE; - } - return FALSE; -} + if(n_press >= 2) + { + // Reset + dt_iop_levels_gui_data_t *g = self->gui_data; + memcpy(self->params, self->default_params, self->params_size); -static gboolean dt_iop_levels_button_release(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self) -{ - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + // Needed in case the user scrolls or drags immediately after a reset, + // as drag_start_percentage is only updated when the mouse is moved. + g->drag_start_percentage = 0.5; + dt_dev_add_history_item(darktable.develop, self, TRUE); + gtk_widget_queue_draw(GTK_WIDGET(g->area)); + } + else { dt_iop_levels_gui_data_t *g = self->gui_data; - g->dragging = 0; - return TRUE; + g->dragging = 1; } - return FALSE; } -static gboolean dt_iop_levels_scroll(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self) +static void dt_iop_levels_button_release(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self) { + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + dt_iop_levels_gui_data_t *g = self->gui_data; - dt_iop_levels_params_t *p = self->params; + g->dragging = 0; +} - if(dt_gui_ignore_scroll(event)) return FALSE; +static void dt_iop_levels_scroll(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, dt_iop_module_t *self) +{ + dt_iop_levels_gui_data_t *g = self->gui_data; + dt_iop_levels_params_t *p = self->params; + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_color_picker_reset(self, TRUE); if(g->dragging) - { - return FALSE; - } + return; if(darktable.develop->gui_module != self) dt_iop_request_focus(self); - const float interval = 0.002 * dt_accel_get_speed_multiplier(widget, dt_gdk_event_get_state(event)); // Distance moved for each scroll event - int delta_y; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) + if(dy != 0.0) { - float new_position = p->levels[g->handle_move] - interval * delta_y; + const float interval = 0.002 * dt_accel_get_speed_multiplier(widget, dt_key_modifier_state()); + float new_position = p->levels[g->handle_move] - interval * dy; dt_iop_levels_move_handle(self, g->handle_move, new_position, p->levels, g->drag_start_percentage); dt_dev_add_history_item(darktable.develop, self, TRUE); - return TRUE; } - - return TRUE; // Ensure that scrolling the widget cannot move side panel } static void dt_iop_levels_autoadjust_callback(GtkRange *range, dt_iop_module_t *self) diff --git a/src/iop/liquify.c b/src/iop/liquify.c index 15728296bd35..4ae571472be5 100644 --- a/src/iop/liquify.c +++ b/src/iop/liquify.c @@ -2725,9 +2725,14 @@ void gui_post_expose(dt_iop_module_t *self, } } -static gboolean btn_make_radio_callback(GtkToggleButton *btn, - const GdkEventButton *event, - dt_iop_module_t *self); +static void _btn_make_radio_cb(GtkToggleButton *btn, + gboolean ctrl_pressed, + dt_iop_module_t *self); +static void btn_make_radio_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self); void gui_focus(dt_iop_module_t *self, const gboolean in) @@ -2735,7 +2740,7 @@ void gui_focus(dt_iop_module_t *self, if(!in) { dt_collection_hint_message(darktable.collection); - btn_make_radio_callback(NULL, NULL, self); + _btn_make_radio_cb(NULL, FALSE, self); } } @@ -3092,6 +3097,10 @@ int button_pressed(dt_iop_module_t *self, const uint32_t state, const float zoom_scale) { + // avoid unexpected back to lighttable mode on double-click + if(type == GDK_2BUTTON_PRESS && which == GDK_BUTTON_PRIMARY) + return 1; + dt_iop_liquify_gui_data_t *g = self->gui_data; dt_iop_liquify_params_t *p = self->params; @@ -3230,7 +3239,7 @@ int button_released(dt_iop_module_t *self, if(g->creation_continuous) _start_new_shape(self); else - btn_make_radio_callback(g->btn_node_tool, NULL, self); + _btn_make_radio_cb(GTK_TOGGLE_BUTTON(g->btn_node_tool), FALSE, self); handled = 2; } else if(gtk_toggle_button_get_active(g->btn_line_tool)) @@ -3291,7 +3300,7 @@ int button_released(dt_iop_module_t *self, else { g->status &= ~DT_LIQUIFY_STATUS_PREVIEW; - btn_make_radio_callback(g->btn_node_tool, NULL, self); + _btn_make_radio_cb(GTK_TOGGLE_BUTTON(g->btn_node_tool), FALSE, self); } handled = 2; goto done; @@ -3300,7 +3309,7 @@ int button_released(dt_iop_module_t *self, // right click on background toggles node tool if(g->last_hit.layer == DT_LIQUIFY_LAYER_BACKGROUND) { - btn_make_radio_callback(g->btn_node_tool, NULL, self); + _btn_make_radio_cb(GTK_TOGGLE_BUTTON(g->btn_node_tool), FALSE, self); handled = 1; goto done; } @@ -3522,9 +3531,9 @@ static void _liquify_cairo_paint_node_tool(cairo_t *cr, // we need this only because darktable has no radiobutton support -static gboolean btn_make_radio_callback(GtkToggleButton *btn, - const GdkEventButton *event, - dt_iop_module_t *self) +static void _btn_make_radio_cb(GtkToggleButton *btn, + gboolean ctrl_pressed, + dt_iop_module_t *self) { dt_iop_liquify_gui_data_t *g = self->gui_data; dt_iop_liquify_params_t *p = self->params; @@ -3533,10 +3542,10 @@ static gboolean btn_make_radio_callback(GtkToggleButton *btn, // does nothing (expect resetting the toggle button status). if(is_dragging(g) && g->temp && node_prev(p, g->temp)) { - return TRUE; + return; } - g->creation_continuous = event != NULL && dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); + g->creation_continuous = ctrl_pressed; dt_control_hinter_message(""); @@ -3550,8 +3559,22 @@ static gboolean btn_make_radio_callback(GtkToggleButton *btn, g->status &= ~DT_LIQUIFY_STATUS_PREVIEW; } + /* Called from GtkGestureMultiPress::pressed (via our CAPTURE-phase + * gesture that claims the sequence). The signal fires BEFORE the + * togglebutton toggles, so we manage all toggle states explicitly. */ + if(!btn) + { + /* NULL btn from gui_focus() / gui_reset() — disable all tools. */ + gtk_toggle_button_set_active(g->btn_point_tool, FALSE); + gtk_toggle_button_set_active(g->btn_line_tool, FALSE); + gtk_toggle_button_set_active(g->btn_curve_tool, FALSE); + gtk_toggle_button_set_active(g->btn_node_tool, FALSE); + dt_control_hinter_message(""); + return; + } + // now, let's enable and start a new form safely - if(!btn || !gtk_toggle_button_get_active(btn)) + if(!gtk_toggle_button_get_active(btn)) { gtk_toggle_button_set_active(g->btn_point_tool, btn == g->btn_point_tool); gtk_toggle_button_set_active(g->btn_line_tool, btn == g->btn_line_tool); @@ -3585,12 +3608,29 @@ static gboolean btn_make_radio_callback(GtkToggleButton *btn, } else { - gtk_toggle_button_set_active(btn, FALSE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(btn), FALSE); } sync_pipe(self, FALSE); +} - return TRUE; +static void btn_make_radio_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) +{ + GdkEvent *event = gtk_get_current_event(); + gboolean ctrl_pressed = FALSE; + if(event) + { + ctrl_pressed = dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); + gdk_event_free(event); + } + + GtkWidget *btn = dt_gui_get_widget(gesture); + + _btn_make_radio_cb(GTK_TOGGLE_BUTTON(btn), ctrl_pressed, self); } void gui_update(dt_iop_module_t *self) @@ -3676,7 +3716,7 @@ void gui_reset(dt_iop_module_t *self) g->dragging = NOWHERE; g->temp = NULL; g->status = 0; - btn_make_radio_callback(NULL, NULL, self); + _btn_make_radio_cb(NULL, FALSE, self); } // defgroup Button paint functions diff --git a/src/iop/lowlight.c b/src/iop/lowlight.c index 2cd9602b218e..14990657cfbd 100644 --- a/src/iop/lowlight.c +++ b/src/iop/lowlight.c @@ -664,22 +664,23 @@ static gboolean lowlight_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t * return FALSE; } -static gboolean lowlight_motion_notify(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self) +static void lowlight_motion_notify(GtkEventControllerMotion *controller, gdouble x, gdouble y, dt_iop_module_t *self) { dt_iop_lowlight_gui_data_t *g = self->gui_data; dt_iop_lowlight_params_t *p = self->params; + GtkWidget *widget = dt_gui_get_widget(controller); const int inset = DT_IOP_LOWLIGHT_INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int height = allocation.height - 2 * inset - DT_RESIZE_HANDLE_SIZE, width = allocation.width - 2 * inset; - if(!g->dragging) g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; - g->mouse_y = 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + if(!g->dragging) g->mouse_x = CLAMP(x - inset, 0, width) / (float)width; + g->mouse_y = 1.0 - CLAMP(y - inset, 0, height) / (float)height; if(g->dragging) { *p = g->drag_params; if(g->x_move >= 0) { - const float mx = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; + const float mx = CLAMP(x - inset, 0, width) / (float)width; if(g->x_move > 0 && g->x_move < DT_IOP_LOWLIGHT_BANDS - 1) { const float minx = p->transition_x[g->x_move - 1] + 0.001f; @@ -694,7 +695,7 @@ static gboolean lowlight_motion_notify(GtkWidget *widget, GdkEventMotion *event, gtk_widget_queue_draw(widget); dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget); } - else if(dt_gdk_event_get_y(event) > height) + else if(y > height) { g->x_move = 0; float dist = fabs(p->transition_x[0] - g->mouse_x); @@ -714,13 +715,17 @@ static gboolean lowlight_motion_notify(GtkWidget *widget, GdkEventMotion *event, g->x_move = -1; gtk_widget_queue_draw(widget); } - return TRUE; } -static gboolean lowlight_button_press(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self) +static void lowlight_button_press(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self) { dt_iop_lowlight_gui_data_t *g = self->gui_data; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + + GtkWidget *widget = dt_gui_get_widget(gesture); + + if(n_press >= 2) { // reset current curve dt_iop_lowlight_params_t *p = self->params; @@ -733,7 +738,7 @@ static gboolean lowlight_button_press(GtkWidget *widget, GdkEventButton *event, dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget); gtk_widget_queue_draw(GTK_WIDGET(g->area)); } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + else { g->drag_params = *(dt_iop_lowlight_params_t *)self->params; const int inset = DT_IOP_LOWLIGHT_INSET; @@ -741,47 +746,37 @@ static gboolean lowlight_button_press(GtkWidget *widget, GdkEventButton *event, gtk_widget_get_allocation(widget, &allocation); int height = allocation.height - 2 * inset - DT_RESIZE_HANDLE_SIZE, width = allocation.width - 2 * inset; g->mouse_pick - = dt_draw_curve_calc_value(g->transition_curve, CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width); - g->mouse_pick -= 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + = dt_draw_curve_calc_value(g->transition_curve, CLAMP(x - inset, 0, width) / (float)width); + g->mouse_pick -= 1.0 - CLAMP(y - inset, 0, height) / (float)height; g->dragging = 1; - return TRUE; } - return FALSE; } -static gboolean lowlight_button_release(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self) +static void lowlight_button_release(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { - dt_iop_lowlight_gui_data_t *g = self->gui_data; - g->dragging = 0; - return TRUE; - } - return FALSE; + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + + dt_iop_lowlight_gui_data_t *g = self->gui_data; + g->dragging = 0; } -static gboolean lowlight_leave_notify(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self) +static void lowlight_leave_notify(GtkEventControllerMotion *controller, dt_iop_module_t *self) { dt_iop_lowlight_gui_data_t *g = self->gui_data; if(!g->dragging) g->mouse_y = -1.0; - gtk_widget_queue_draw(widget); - return TRUE; + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } -static gboolean lowlight_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self) +static void lowlight_scrolled(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, dt_iop_module_t *self) { dt_iop_lowlight_gui_data_t *g = self->gui_data; - if(dt_gui_ignore_scroll(event)) return FALSE; - - int delta_y; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) + if(dy != 0.0) { - g->mouse_radius = CLAMP(g->mouse_radius * (1.0 - 0.1 * delta_y), 0.2 / DT_IOP_LOWLIGHT_BANDS, 1.0); - gtk_widget_queue_draw(widget); + g->mouse_radius = CLAMP(g->mouse_radius * (1.0 - 0.1 * dy), 0.2 / DT_IOP_LOWLIGHT_BANDS, 1.0); + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } - - return TRUE; } void gui_init(dt_iop_module_t *self) @@ -809,11 +804,11 @@ void gui_init(dt_iop_module_t *self) self->widget = dt_gui_vbox(g->area); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(lowlight_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", G_CALLBACK(lowlight_button_press), self); - g_signal_connect(G_OBJECT(g->area), "button-release-event", G_CALLBACK(lowlight_button_release), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", G_CALLBACK(lowlight_motion_notify), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", G_CALLBACK(lowlight_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", G_CALLBACK(lowlight_scrolled), self); + dt_gui_connect_click(g->area, lowlight_button_press, lowlight_button_release, self); + dt_gui_connect_motion(g->area, lowlight_motion_notify, NULL, lowlight_leave_notify, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + lowlight_scrolled, self); g->scale_blueness = dt_bauhaus_slider_from_params(self, "blueness"); dt_bauhaus_slider_set_format(g->scale_blueness, "%"); diff --git a/src/iop/lut3d.c b/src/iop/lut3d.c index e79efc566432..5df24ec94275 100644 --- a/src/iop/lut3d.c +++ b/src/iop/lut3d.c @@ -1473,15 +1473,18 @@ static void _lutname_callback(GtkTreeSelection *selection, dt_iop_module_t *self } } -static gboolean _mouse_scroll(GtkWidget *view, GdkEventScroll *event, dt_lib_module_t *self) +static void _mouse_scroll(GtkEventControllerScroll *controller, + gdouble dx, gdouble dy, + dt_iop_module_t *self) { + GtkWidget *view = dt_gui_get_widget(controller); GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); GtkTreeIter iter; GtkTreeModel *model = gtk_tree_view_get_model((GtkTreeView *)view); if(gtk_tree_selection_get_selected(selection, &model, &iter)) { gboolean next = FALSE; - if(dt_gdk_event_get_scroll_delta_y(event) > 0) + if(dy > 0) next = gtk_tree_model_iter_next(model, &iter); else next = gtk_tree_model_iter_previous(model, &iter); @@ -1491,10 +1494,9 @@ static gboolean _mouse_scroll(GtkWidget *view, GdkEventScroll *event, dt_lib_mod GtkTreePath *path = gtk_tree_model_get_path(model, &iter); gtk_tree_view_set_cursor((GtkTreeView *)view, path, NULL, FALSE); gtk_tree_path_free(path); - return TRUE; + return; } } - return FALSE; } #endif // HAVE_GMIC @@ -1748,7 +1750,9 @@ void gui_init(dt_iop_module_t *self) dt_gui_box_add(self->widget, g->lutwindow); g_signal_connect(G_OBJECT(g->lutentry), "changed", G_CALLBACK(_entry_callback), self); - g_signal_connect(G_OBJECT((GtkTreeView *)g->lutname), "scroll-event", G_CALLBACK(_mouse_scroll), (gpointer)self); + dt_gui_connect_scroll(g->lutname, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _mouse_scroll, self); #endif // HAVE_GMIC g->colorspace = dt_bauhaus_combobox_from_params(self, "colorspace"); diff --git a/src/iop/monochrome.c b/src/iop/monochrome.c index 60ab86691855..c91c302daa38 100644 --- a/src/iop/monochrome.c +++ b/src/iop/monochrome.c @@ -449,7 +449,7 @@ void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpi dt_control_queue_redraw_widget(self->widget); } -static gboolean _monochrome_motion_notify(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self) +static void _monochrome_motion_notify(GtkEventControllerMotion *controller, gdouble x, gdouble y, dt_iop_module_t *self) { dt_iop_monochrome_gui_data_t *g = self->gui_data; dt_iop_monochrome_params_t *p = self->params; @@ -457,94 +457,87 @@ static gboolean _monochrome_motion_notify(GtkWidget *widget, GdkEventMotion *eve { const float old_a = p->a, old_b = p->b; const int inset = DT_COLORCORRECTION_INSET; + GtkWidget *widget = dt_gui_get_widget(controller); GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int width = allocation.width - 2 * inset, height = allocation.height - 2 * inset; - const float mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width); - const float mouse_y = CLAMP(height - 1 - dt_gdk_event_get_y(event) + inset, 0, height); + const float mouse_x = CLAMP(x - inset, 0, width); + const float mouse_y = CLAMP(height - 1 - y + inset, 0, height); p->a = PANEL_WIDTH * (mouse_x - width * 0.5f) / (float)width; p->b = PANEL_WIDTH * (mouse_y - height * 0.5f) / (float)height; if(old_a != p->a || old_b != p->b) dt_dev_add_history_item(darktable.develop, self, TRUE); gtk_widget_queue_draw(GTK_WIDGET(g->area)); } - return TRUE; } -static gboolean _monochrome_button_press(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self) +static void _monochrome_button_press(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + + dt_iop_monochrome_gui_data_t *g = self->gui_data; + dt_iop_monochrome_params_t *p = self->params; + GtkWidget *widget = dt_gui_get_widget(gesture); + + dt_iop_color_picker_reset(self, TRUE); + if(n_press >= 2) { - dt_iop_monochrome_gui_data_t *g = self->gui_data; - dt_iop_monochrome_params_t *p = self->params; - dt_iop_color_picker_reset(self, TRUE); - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) - { - // reset - const dt_iop_monochrome_params_t *const p0 = self->default_params; - p->a = p0->a; - p->b = p0->b; - p->size = p0->size; - } - else - { - const int inset = DT_COLORCORRECTION_INSET; - GtkAllocation allocation; - gtk_widget_get_allocation(widget, &allocation); - int width = allocation.width - 2 * inset, height = allocation.height - 2 * inset; - const float mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width); - const float mouse_y = CLAMP(height - 1 - dt_gdk_event_get_y(event) + inset, 0, height); - p->a = PANEL_WIDTH * (mouse_x - width * 0.5f) / (float)width; - p->b = PANEL_WIDTH * (mouse_y - height * 0.5f) / (float)height; - g->dragging = 1; - g_object_set(G_OBJECT(widget), "has-tooltip", FALSE, (gchar *)0); - } - gtk_widget_queue_draw(GTK_WIDGET(g->area)); - return TRUE; + // reset + const dt_iop_monochrome_params_t *const p0 = self->default_params; + p->a = p0->a; + p->b = p0->b; + p->size = p0->size; + } + else + { + const int inset = DT_COLORCORRECTION_INSET; + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + int width = allocation.width - 2 * inset, height = allocation.height - 2 * inset; + const float mouse_x = CLAMP(x - inset, 0, width); + const float mouse_y = CLAMP(height - 1 - y + inset, 0, height); + p->a = PANEL_WIDTH * (mouse_x - width * 0.5f) / (float)width; + p->b = PANEL_WIDTH * (mouse_y - height * 0.5f) / (float)height; + g->dragging = 1; + g_object_set(G_OBJECT(widget), "has-tooltip", FALSE, (gchar *)0); } - return FALSE; + gtk_widget_queue_draw(GTK_WIDGET(g->area)); } -static gboolean _monochrome_button_release(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self) +static void _monochrome_button_release(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { - dt_iop_monochrome_gui_data_t *g = self->gui_data; - dt_iop_color_picker_reset(self, TRUE); - g->dragging = 0; - dt_dev_add_history_item(darktable.develop, self, TRUE); - g_object_set(G_OBJECT(widget), "has-tooltip", TRUE, (gchar *)0); - return TRUE; - } - return FALSE; + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + + dt_iop_monochrome_gui_data_t *g = self->gui_data; + GtkWidget *widget = dt_gui_get_widget(gesture); + dt_iop_color_picker_reset(self, TRUE); + g->dragging = 0; + dt_dev_add_history_item(darktable.develop, self, TRUE); + g_object_set(G_OBJECT(widget), "has-tooltip", TRUE, (gchar *)0); } -static gboolean _monochrome_leave_notify(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self) +static void _monochrome_leave_notify(GtkEventControllerMotion *controller, dt_iop_module_t *self) { dt_iop_monochrome_gui_data_t *g = self->gui_data; g->dragging = 0; gtk_widget_queue_draw(GTK_WIDGET(g->area)); - return TRUE; } -static gboolean _monochrome_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self) +static void _monochrome_scrolled(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, dt_iop_module_t *self) { dt_iop_monochrome_params_t *p = self->params; - if(dt_gui_ignore_scroll(event)) return FALSE; - dt_iop_color_picker_reset(self, TRUE); - int delta_y; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) + if(dy != 0.0) { const float old_size = p->size; - p->size = CLAMP(p->size - delta_y * 0.1, 0.5f, 3.0f); + p->size = CLAMP(p->size - dy * 0.1, 0.5f, 3.0f); if(old_size != p->size) dt_dev_add_history_item(darktable.develop, self, TRUE); - gtk_widget_queue_draw(widget); + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } - - return TRUE; } void gui_init(dt_iop_module_t *self) @@ -558,17 +551,16 @@ void gui_init(dt_iop_module_t *self) gtk_widget_set_tooltip_text(GTK_WIDGET(g->area), _("drag and scroll mouse wheel to adjust the virtual color filter")); dt_action_define_iop(self, NULL, N_("grid"), GTK_WIDGET(g->area), NULL); - gtk_widget_add_events(GTK_WIDGET(g->area), GDK_POINTER_MOTION_MASK - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_LEAVE_NOTIFY_MASK | darktable.gui->scroll_mask); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(_monochrome_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", G_CALLBACK(_monochrome_button_press), self); - g_signal_connect(G_OBJECT(g->area), "button-release-event", G_CALLBACK(_monochrome_button_release), - self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", G_CALLBACK(_monochrome_motion_notify), - self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", G_CALLBACK(_monochrome_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", G_CALLBACK(_monochrome_scrolled), self); + gtk_widget_add_events(GTK_WIDGET(g->area), + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK + | darktable.gui->scroll_mask); + dt_gui_connect_click(g->area, _monochrome_button_press, _monochrome_button_release, self); + dt_gui_connect_motion(g->area, _monochrome_motion_notify, NULL, _monochrome_leave_notify, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _monochrome_scrolled, self); g->highlights = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, N_("highlights"))); diff --git a/src/iop/rawdenoise.c b/src/iop/rawdenoise.c index 3c6e445e1342..f0043cbde319 100644 --- a/src/iop/rawdenoise.c +++ b/src/iop/rawdenoise.c @@ -766,16 +766,17 @@ static gboolean rawdenoise_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t return FALSE; } -static gboolean rawdenoise_motion_notify(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self) +static void rawdenoise_motion_notify(GtkEventControllerMotion *controller, gdouble x, gdouble y, dt_iop_module_t *self) { dt_iop_rawdenoise_gui_data_t *g = self->gui_data; dt_iop_rawdenoise_params_t *p = self->params; + GtkWidget *widget = dt_gui_get_widget(controller); const int inset = DT_IOP_RAWDENOISE_INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset; - if(!g->dragging) g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; - g->mouse_y = 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + if(!g->dragging) g->mouse_x = CLAMP(x - inset, 0, width) / (float)width; + g->mouse_y = 1.0 - CLAMP(y - inset, 0, height) / (float)height; if(g->dragging) { *p = g->drag_params; @@ -791,14 +792,18 @@ static gboolean rawdenoise_motion_notify(GtkWidget *widget, GdkEventMotion *even g->x_move = -1; gtk_widget_queue_draw(widget); } - return TRUE; } -static gboolean rawdenoise_button_press(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self) +static void rawdenoise_button_press(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self) { dt_iop_rawdenoise_gui_data_t *g = self->gui_data; const int ch = g->channel; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + + GtkWidget *widget = dt_gui_get_widget(gesture); + + if(n_press >= 2) { // reset current curve dt_iop_rawdenoise_params_t *p = self->params; @@ -811,7 +816,7 @@ static gboolean rawdenoise_button_press(GtkWidget *widget, GdkEventButton *event dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + ch); gtk_widget_queue_draw(GTK_WIDGET(g->area)); } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + else { g->drag_params = *(dt_iop_rawdenoise_params_t *)self->params; const int inset = DT_IOP_RAWDENOISE_INSET; @@ -819,50 +824,37 @@ static gboolean rawdenoise_button_press(GtkWidget *widget, GdkEventButton *event gtk_widget_get_allocation(widget, &allocation); int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset; g->mouse_pick - = dt_draw_curve_calc_value(g->transition_curve, CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width); - g->mouse_pick -= 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + = dt_draw_curve_calc_value(g->transition_curve, CLAMP(x - inset, 0, width) / (float)width); + g->mouse_pick -= 1.0 - CLAMP(y - inset, 0, height) / (float)height; g->dragging = 1; - return TRUE; } - return FALSE; } -static gboolean rawdenoise_button_release(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self) +static void rawdenoise_button_release(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { - dt_iop_rawdenoise_gui_data_t *g = self->gui_data; - g->dragging = 0; - return TRUE; - } - return FALSE; + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + + dt_iop_rawdenoise_gui_data_t *g = self->gui_data; + g->dragging = 0; } -static gboolean rawdenoise_leave_notify(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self) +static void rawdenoise_leave_notify(GtkEventControllerMotion *controller, dt_iop_module_t *self) { dt_iop_rawdenoise_gui_data_t *g = self->gui_data; if(!g->dragging) g->mouse_y = -1.0; - gtk_widget_queue_draw(widget); - return TRUE; + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } -static gboolean rawdenoise_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self) +static void rawdenoise_scrolled(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, dt_iop_module_t *self) { dt_iop_rawdenoise_gui_data_t *g = self->gui_data; - if(dt_gui_ignore_scroll(event)) return FALSE; - - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_MOD1_MASK)) - return gtk_widget_event(GTK_WIDGET(g->channel_tabs), (GdkEvent*)event); - - int delta_y; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) + if(dy != 0.0) { - g->mouse_radius = CLAMP(g->mouse_radius * (1.0 - 0.1 * delta_y), 0.2 / DT_IOP_RAWDENOISE_BANDS, 1.0); - gtk_widget_queue_draw(widget); + g->mouse_radius = CLAMP(g->mouse_radius * (1.0 - 0.1 * dy), 0.2 / DT_IOP_RAWDENOISE_BANDS, 1.0); + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } - - return TRUE; } static void rawdenoise_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, dt_iop_module_t *self) @@ -913,11 +905,11 @@ void gui_init(dt_iop_module_t *self) GtkWidget *box_raw = self->widget = dt_gui_vbox(g->channel_tabs, g->area); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(rawdenoise_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", G_CALLBACK(rawdenoise_button_press), self); - g_signal_connect(G_OBJECT(g->area), "button-release-event", G_CALLBACK(rawdenoise_button_release), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", G_CALLBACK(rawdenoise_motion_notify), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", G_CALLBACK(rawdenoise_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", G_CALLBACK(rawdenoise_scrolled), self); + dt_gui_connect_click(g->area, rawdenoise_button_press, rawdenoise_button_release, self); + dt_gui_connect_motion(g->area, rawdenoise_motion_notify, NULL, rawdenoise_leave_notify, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + rawdenoise_scrolled, self); g->threshold = dt_bauhaus_slider_from_params(self, "threshold"); dt_bauhaus_slider_set_soft_max(g->threshold, 0.1); diff --git a/src/iop/retouch.c b/src/iop/retouch.c index 7a825417a939..902cf02ba93b 100644 --- a/src/iop/retouch.c +++ b/src/iop/retouch.c @@ -1229,9 +1229,7 @@ static void rt_merge_from_scale_update(const int _merge_from_scale, dt_dev_add_history_item(darktable.develop, self, TRUE); } -static gboolean rt_wdbar_leave_notify(GtkWidget *widget, - GdkEventCrossing *event, - dt_iop_module_t *self) +static void rt_wdbar_leave_notify(GtkEventControllerMotion *controller, dt_iop_module_t *self) { dt_iop_retouch_gui_data_t *g = self->gui_data; @@ -1241,25 +1239,23 @@ static gboolean rt_wdbar_leave_notify(GtkWidget *widget, g->lower_margin = g->upper_margin = FALSE; gtk_widget_queue_draw(g->wd_bar); - return TRUE; } -static gboolean rt_wdbar_button_press(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void rt_wdbar_button_press(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self) { if(DT_IN_GUI_UPDATE()) - return TRUE; + return; dt_iop_request_focus(self); dt_iop_retouch_gui_data_t *g = self->gui_data; + GtkWidget *widget = dt_gui_get_widget(gesture); GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); const int inset = round(RT_WDBAR_INSET * allocation.height); const float box_w = (allocation.width - 2.0f * inset) / (float)RETOUCH_NO_SCALES; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { if(g->lower_margin) // bottom slider { @@ -1280,58 +1276,49 @@ static gboolean rt_wdbar_button_press(GtkWidget *widget, } gtk_widget_queue_draw(g->wd_bar); - return TRUE; } -static gboolean rt_wdbar_button_release(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void rt_wdbar_button_release(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, dt_iop_module_t *self) { dt_iop_retouch_gui_data_t *g = self->gui_data; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) g->is_dragging = 0; gtk_widget_queue_draw(g->wd_bar); - return TRUE; } -static gboolean rt_wdbar_scrolled(GtkWidget *widget, - GdkEventScroll *event, - dt_iop_module_t *self) +static void rt_wdbar_scrolled(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, dt_iop_module_t *self) { - if(dt_gui_ignore_scroll(event)) - return FALSE; - if(DT_IN_GUI_UPDATE()) - return TRUE; + return; dt_iop_retouch_params_t *p = self->params; dt_iop_retouch_gui_data_t *g = self->gui_data; dt_iop_request_focus(self); - int delta_y; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) + if(dy != 0.0) { if(g->lower_margin) // bottom slider - rt_num_scales_update(p->num_scales - delta_y, self); + rt_num_scales_update(p->num_scales - dy, self); else if(g->upper_margin) // top slider - rt_merge_from_scale_update(p->merge_from_scale - delta_y, self); + rt_merge_from_scale_update(p->merge_from_scale - dy, self); else if(g->curr_scale >= 0) - rt_curr_scale_update(p->curr_scale - delta_y, self); + rt_curr_scale_update(p->curr_scale - dy, self); } gtk_widget_queue_draw(g->wd_bar); - return TRUE; } -static gboolean rt_wdbar_motion_notify(GtkWidget *widget, - GdkEventMotion *event, - dt_iop_module_t *self) +static void rt_wdbar_motion_notify(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_retouch_gui_data_t *g = self->gui_data; dt_iop_retouch_params_t *p = self->params; + GtkWidget *widget = dt_gui_get_widget(controller); GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); @@ -1341,8 +1328,8 @@ static gboolean rt_wdbar_motion_notify(GtkWidget *widget, /* record mouse position within control */ - g->wdbar_mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, allocation.width - 2.0f * inset - 1.0f); - g->wdbar_mouse_y = dt_gdk_event_get_y(event); + g->wdbar_mouse_x = CLAMP(x - inset, 0, allocation.width - 2.0f * inset - 1.0f); + g->wdbar_mouse_y = y; g->curr_scale = g->wdbar_mouse_x / box_w; g->lower_cursor = g->upper_cursor = FALSE; @@ -1374,7 +1361,6 @@ static gboolean rt_wdbar_motion_notify(GtkWidget *widget, rt_merge_from_scale_update(g->curr_scale, self); gtk_widget_queue_draw(g->wd_bar); - return TRUE; } static int rt_scale_has_shapes(dt_iop_retouch_params_t *p, @@ -1626,22 +1612,26 @@ void color_picker_apply(dt_iop_module_t *self, dt_dev_add_history_item(darktable.develop, self, TRUE); } -static gboolean rt_copypaste_scale_callback(GtkToggleButton *togglebutton, - GdkEventButton *event, - dt_iop_module_t *self) +static void rt_copypaste_scale_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { - DT_TRY_GUI_UPDATE(TRUE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; + + GtkWidget *widget = dt_gui_get_widget(gesture); int scale_copied = 0; - const int active = !gtk_toggle_button_get_active(togglebutton); + const int active = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); dt_iop_retouch_params_t *p = self->params; dt_iop_retouch_gui_data_t *g = self->gui_data; - if(togglebutton == (GtkToggleButton *)g->bt_copy_scale) + if(widget == g->bt_copy_scale) { g->copied_scale = (active) ? p->curr_scale : -1; } - else if(togglebutton == (GtkToggleButton *)g->bt_paste_scale) + else if(widget == g->bt_paste_scale) { rt_paste_forms_from_scale(p, g->copied_scale, p->curr_scale); rt_show_forms_for_current_scale(self); @@ -1657,18 +1647,18 @@ static gboolean rt_copypaste_scale_callback(GtkToggleButton *togglebutton, gtk_widget_set_sensitive(g->bt_paste_scale, g->copied_scale >= 0); - DT_LEAVE_GUI_UPDATE(); - if(scale_copied) dt_dev_add_history_item(darktable.develop, self, TRUE); - - return TRUE; } -static gboolean rt_display_wavelet_scale_callback(GtkToggleButton *togglebutton, - GdkEventButton *event, - dt_iop_module_t *self) +static void rt_display_wavelet_scale_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(TRUE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; + + GtkWidget *widget = dt_gui_get_widget(gesture); dt_iop_retouch_params_t *p = self->params; dt_iop_retouch_gui_data_t *g = self->gui_data; @@ -1680,16 +1670,16 @@ static gboolean rt_display_wavelet_scale_callback(GtkToggleButton *togglebutton, dt_control_log(_("cannot display scales when the blending mask is displayed")); DT_ENTER_GUI_UPDATE(); - gtk_toggle_button_set_active(togglebutton, FALSE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), FALSE); DT_LEAVE_GUI_UPDATE(); - return TRUE; + return; } if(self->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), 1); dt_iop_request_focus(self); - g->display_wavelet_scale = !gtk_toggle_button_get_active(togglebutton); + g->display_wavelet_scale = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); rt_show_hide_controls(self); @@ -1711,8 +1701,7 @@ static gboolean rt_display_wavelet_scale_callback(GtkToggleButton *togglebutton, dt_dev_reprocess_center(self->dev, self->iop_order); - gtk_toggle_button_set_active(togglebutton, g->display_wavelet_scale); - return TRUE; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), g->display_wavelet_scale); } static void rt_develop_ui_pipe_finished_callback(gpointer instance, @@ -1754,11 +1743,13 @@ static void rt_develop_ui_pipe_finished_callback(gpointer instance, gtk_widget_queue_draw(GTK_WIDGET(g->wd_bar)); } -static gboolean rt_auto_levels_callback(GtkToggleButton *togglebutton, - GdkEventButton *event, - dt_iop_module_t *self) +static void rt_auto_levels_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(FALSE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; dt_iop_retouch_gui_data_t *g = self->gui_data; @@ -1773,8 +1764,6 @@ static gboolean rt_auto_levels_callback(GtkToggleButton *togglebutton, dt_iop_gui_leave_critical_section(self); dt_iop_refresh_center(self); - - return TRUE; } static void rt_mask_opacity_callback(GtkWidget *slider, @@ -1813,19 +1802,25 @@ void gui_post_expose(dt_iop_module_t *self, } } -static gboolean rt_edit_masks_callback(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void rt_edit_masks_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(FALSE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; // if we don't have the focus, request for it and quit, gui_focus() do the rest if(darktable.develop->gui_module != self) { dt_iop_request_focus(self); - return FALSE; + return; } + GdkEvent *event = gtk_get_current_event(); + const guint button = event ? dt_gdk_event_get_button(event) : 0; + const GdkModifierType state = event ? dt_gdk_event_get_state(event) : 0; + dt_iop_gui_blend_data_t *bd = self->blend_data; dt_iop_retouch_gui_data_t *g = self->gui_data; @@ -1845,7 +1840,7 @@ static gboolean rt_edit_masks_callback(GtkWidget *widget, gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_ellipse), FALSE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_brush), FALSE); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(button == GDK_BUTTON_PRIMARY) { DT_ENTER_GUI_UPDATE(); @@ -1856,7 +1851,7 @@ static gboolean rt_edit_masks_callback(GtkWidget *widget, if(grp && (grp->type & DT_MASKS_GROUP) && grp->points) { const gboolean control_button_pressed = - dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); + dt_modifier_is(state, GDK_CONTROL_MASK); switch(bd->masks_shown) { @@ -1890,24 +1885,32 @@ static gboolean rt_edit_masks_callback(GtkWidget *widget, && (darktable.develop->gui_module == self)); DT_LEAVE_GUI_UPDATE(); - - return TRUE; } - return TRUE; + if(event) gdk_event_free(event); } -static gboolean rt_add_shape_callback(GtkWidget *widget, - GdkEventButton *e, - dt_iop_module_t *self) +static void rt_add_shape_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { dt_iop_retouch_gui_data_t *g = self->gui_data; - DT_GUARD_GUI_UPDATE(FALSE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; + + GtkWidget *widget = dt_gui_get_widget(gesture); dt_iop_color_picker_reset(self, TRUE); - const gboolean creation_continuous = dt_modifier_is(e->state, GDK_CONTROL_MASK); + gboolean creation_continuous = FALSE; + GdkEvent *event = gtk_get_current_event(); + if(event) + { + creation_continuous = dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); + gdk_event_free(event); + } rt_add_shape(widget, creation_continuous, self); @@ -1919,35 +1922,40 @@ static gboolean rt_add_shape_callback(GtkWidget *widget, rt_shape_is_being_added(self, DT_MASKS_ELLIPSE)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_brush), rt_shape_is_being_added(self, DT_MASKS_BRUSH)); - - return TRUE; } -static gboolean rt_select_algorithm_callback(GtkToggleButton *togglebutton, - GdkEventButton *e, - dt_iop_module_t *self) +static void rt_select_algorithm_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { - DT_TRY_GUI_UPDATE(FALSE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; + + GtkWidget *widget = dt_gui_get_widget(gesture); dt_iop_retouch_params_t *p = self->params; dt_iop_retouch_gui_data_t *g = self->gui_data; dt_iop_retouch_algo_type_t new_algo = DT_IOP_RETOUCH_HEAL; - if(togglebutton == (GtkToggleButton *)g->bt_blur) + if(widget == g->bt_blur) new_algo = DT_IOP_RETOUCH_BLUR; - else if(togglebutton == (GtkToggleButton *)g->bt_clone) + else if(widget == g->bt_clone) new_algo = DT_IOP_RETOUCH_CLONE; - else if(togglebutton == (GtkToggleButton *)g->bt_heal) + else if(widget == g->bt_heal) new_algo = DT_IOP_RETOUCH_HEAL; - else if(togglebutton == (GtkToggleButton *)g->bt_fill) + else if(widget == g->bt_fill) new_algo = DT_IOP_RETOUCH_FILL; // check if we have to do something gboolean accept = TRUE; + GdkEvent *event = gtk_get_current_event(); + const GdkModifierType state = event ? dt_gdk_event_get_state(event) : 0; + const int index = rt_get_selected_shape_index(p); - if(index >= 0 && dt_modifier_is(e->state, GDK_CONTROL_MASK)) + if(index >= 0 && dt_modifier_is(state, GDK_CONTROL_MASK)) { if(new_algo != p->rt_forms[index].algorithm) { @@ -1982,11 +1990,11 @@ static gboolean rt_select_algorithm_callback(GtkToggleButton *togglebutton, if(!accept) { - DT_LEAVE_GUI_UPDATE(); - return FALSE; + if(event) gdk_event_free(event); + return; } - if(index >= 0 && dt_modifier_is(e->state, GDK_CONTROL_MASK)) + if(index >= 0 && dt_modifier_is(state, GDK_CONTROL_MASK)) { if(p->algorithm != p->rt_forms[index].algorithm) { @@ -2019,12 +2027,12 @@ static gboolean rt_select_algorithm_callback(GtkToggleButton *togglebutton, dt_control_queue_redraw_center(); } - DT_LEAVE_GUI_UPDATE(); + if(event) gdk_event_free(event); dt_dev_add_history_item(darktable.develop, self, TRUE); // if we have the shift key pressed, we set it as default - if(dt_modifier_is(e->state, GDK_SHIFT_MASK)) + if(dt_modifier_is(state, GDK_SHIFT_MASK)) { dt_conf_set_int("plugins/darkroom/retouch/default_algo", p->algorithm); // and we show a toat msg to confirm @@ -2037,15 +2045,17 @@ static gboolean rt_select_algorithm_callback(GtkToggleButton *togglebutton, else if(p->algorithm == DT_IOP_RETOUCH_BLUR) dt_control_log(_("default tool changed to %s"), _("blur")); } - - return TRUE; } -static gboolean rt_showmask_callback(GtkToggleButton *togglebutton, - GdkEventButton *event, - dt_iop_module_t *self) +static void rt_showmask_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(TRUE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; + + GtkWidget *widget = dt_gui_get_widget(gesture); dt_iop_retouch_gui_data_t *g = self->gui_data; @@ -2055,11 +2065,11 @@ static gboolean rt_showmask_callback(GtkToggleButton *togglebutton, { dt_control_log(_("cannot display masks when the blending mask is displayed")); - gtk_toggle_button_set_active(togglebutton, FALSE); - return TRUE; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), FALSE); + return; } - g->mask_display = !gtk_toggle_button_get_active(togglebutton); + g->mask_display = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); if(self->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), 1); @@ -2067,26 +2077,28 @@ static gboolean rt_showmask_callback(GtkToggleButton *togglebutton, dt_iop_refresh_center(self); - gtk_toggle_button_set_active(togglebutton, g->mask_display); - return TRUE; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), g->mask_display); } -static gboolean rt_suppress_callback(GtkToggleButton *togglebutton, - GdkEventButton *event, - dt_iop_module_t *self) +static void rt_suppress_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(TRUE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; + + GtkWidget *widget = dt_gui_get_widget(gesture); dt_iop_retouch_gui_data_t *g = self->gui_data; - g->suppress_mask = !gtk_toggle_button_get_active(togglebutton); + g->suppress_mask = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); if(self->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), 1); dt_iop_request_focus(self); dt_iop_refresh_center(self); - gtk_toggle_button_set_active(togglebutton, g->suppress_mask); - return TRUE; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), g->suppress_mask); } void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous) @@ -2532,20 +2544,11 @@ void gui_init(dt_iop_module_t *self) g_signal_connect(G_OBJECT(g->wd_bar), "draw", G_CALLBACK(rt_wdbar_draw), self); - g_signal_connect(G_OBJECT(g->wd_bar), "motion-notify-event", - G_CALLBACK(rt_wdbar_motion_notify), self); - g_signal_connect(G_OBJECT(g->wd_bar), "leave-notify-event", - G_CALLBACK(rt_wdbar_leave_notify), self); - g_signal_connect(G_OBJECT(g->wd_bar), "button-press-event", - G_CALLBACK(rt_wdbar_button_press), self); - g_signal_connect(G_OBJECT(g->wd_bar), "button-release-event", - G_CALLBACK(rt_wdbar_button_release), self); - g_signal_connect(G_OBJECT(g->wd_bar), "scroll-event", - G_CALLBACK(rt_wdbar_scrolled), self); - gtk_widget_add_events(GTK_WIDGET(g->wd_bar), - GDK_POINTER_MOTION_MASK - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_LEAVE_NOTIFY_MASK | darktable.gui->scroll_mask); + dt_gui_connect_click(g->wd_bar, rt_wdbar_button_press, rt_wdbar_button_release, self); + dt_gui_connect_motion(g->wd_bar, rt_wdbar_motion_notify, NULL, rt_wdbar_leave_notify, self); + dt_gui_connect_scroll(g->wd_bar, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + rt_wdbar_scrolled, self); gtk_widget_set_size_request(g->wd_bar, -1, DT_PIXEL_APPLY_DPI(40)); // toolbar display current scale / cut&paste / suppress&display masks diff --git a/src/iop/rgbcurve.c b/src/iop/rgbcurve.c index 149ae6c059db..148d5e854574 100644 --- a/src/iop/rgbcurve.c +++ b/src/iop/rgbcurve.c @@ -643,20 +643,18 @@ static gboolean _move_point_internal(dt_iop_module_t *self, #define RGBCURVE_DEFAULT_STEP (0.001f) -static gboolean _area_scrolled_callback(GtkWidget *widget, - GdkEventScroll *event, - dt_iop_module_t *self) +static void _area_scrolled_callback(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_rgbcurve_params_t *p = self->params; dt_iop_rgbcurve_gui_data_t *g = self->gui_data; - gdouble delta_y; - - if(dt_gui_ignore_scroll(event)) return FALSE; - if(darktable.develop->darkroom_skip_mouse_events) { - if(dt_gui_get_scroll_deltas(event, NULL, &delta_y)) + if(dy != 0.0) { GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); @@ -666,7 +664,7 @@ static gboolean _area_scrolled_callback(GtkWidget *widget, const float linx = _mouse_to_curve(mx, g->zoom_factor, g->offset_x), liny = _mouse_to_curve(my, g->zoom_factor, g->offset_y); - g->zoom_factor *= 1.0 - 0.1 * delta_y; + g->zoom_factor *= 1.0 - 0.1 * dy; if(g->zoom_factor < 1.f) g->zoom_factor = 1.f; g->offset_x = linx - (mx / g->zoom_factor); @@ -677,32 +675,30 @@ static gboolean _area_scrolled_callback(GtkWidget *widget, gtk_widget_queue_draw(GTK_WIDGET(g->area)); } - - return TRUE; } // if autoscale is on: do not modify g and b curves if((p->curve_autoscale != DT_S_SCALE_MANUAL_RGB) && g->channel != DT_IOP_RGBCURVE_R) - return TRUE; - if(g->selected < 0) return TRUE; + if(g->selected < 0) return; dt_iop_color_picker_reset(self, TRUE); - if(dt_gui_get_scroll_delta(event, &delta_y)) + if(dy != 0.0) { - delta_y *= -RGBCURVE_DEFAULT_STEP; - return _move_point_internal(self, widget, 0.0, delta_y, dt_gdk_event_get_state(event)); + dy *= -RGBCURVE_DEFAULT_STEP; + _move_point_internal(self, widget, 0.0, dy, dt_key_modifier_state()); } - - return TRUE; } -static gboolean _area_key_press_callback(GtkWidget *widget, - GdkEventKey *event, - dt_iop_module_t *self) +static gboolean _area_key_press_callback(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_rgbcurve_params_t *p = self->params; dt_iop_rgbcurve_gui_data_t *g = self->gui_data; @@ -717,22 +713,22 @@ static gboolean _area_key_press_callback(GtkWidget *widget, int handled = 0; float dx = 0.0f, dy = 0.0f; - if(dt_gdk_event_get_keyval(event) == GDK_KEY_Up || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Up) + if(keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up) { handled = 1; dy = RGBCURVE_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Down || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Down) + else if(keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down) { handled = 1; dy = -RGBCURVE_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Right || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Right) + else if(keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right) { handled = 1; dx = RGBCURVE_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Left || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Left) + else if(keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left) { handled = 1; dx = -RGBCURVE_DEFAULT_STEP; @@ -741,21 +737,19 @@ static gboolean _area_key_press_callback(GtkWidget *widget, if(!handled) return FALSE; dt_iop_color_picker_reset(self, TRUE); - return _move_point_internal(self, widget, dx, dy, dt_gdk_event_get_state(event)); + return _move_point_internal(self, widget, dx, dy, state); } #undef RGBCURVE_DEFAULT_STEP -static gboolean _area_leave_notify_callback(GtkWidget *widget, - GdkEventCrossing *event, - dt_iop_module_t *self) +static void _area_leave_notify_callback(GtkEventControllerMotion *controller, + dt_iop_module_t *self) { dt_iop_rgbcurve_gui_data_t *g = self->gui_data; - if(!(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK)) + if(!(dt_key_modifier_state() & GDK_BUTTON1_MASK)) g->selected = -1; - gtk_widget_queue_draw(widget); - return FALSE; + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } static gboolean _area_draw_callback(GtkWidget *widget, @@ -1166,10 +1160,12 @@ static gboolean _area_draw_callback(GtkWidget *widget, return TRUE; } -static gboolean _area_motion_notify_callback(GtkWidget *widget, - GdkEventMotion *event, - dt_iop_module_t *self) +static void _area_motion_notify_callback(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_rgbcurve_gui_data_t *g = self->gui_data; dt_iop_rgbcurve_params_t *p = self->params; @@ -1186,10 +1182,10 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, const float mx = g->mouse_x; const float my = g->mouse_y; - g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; - g->mouse_y = 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + g->mouse_x = CLAMP(x - inset, 0, width) / (float)width; + g->mouse_y = 1.0 - CLAMP(y - inset, 0, height) / (float)height; - if(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK) + if(dt_key_modifier_state() & GDK_BUTTON1_MASK) { g->offset_x += (mx - g->mouse_x) / g->zoom_factor; g->offset_y += (my - g->mouse_y) / g->zoom_factor; @@ -1199,7 +1195,6 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, gtk_widget_queue_draw(GTK_WIDGET(g->area)); } - return TRUE; } const int ch = g->channel; @@ -1218,15 +1213,15 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, const double old_m_x = g->mouse_x; const double old_m_y = g->mouse_y; - g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; - g->mouse_y = 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + g->mouse_x = CLAMP(x - inset, 0, width) / (float)width; + g->mouse_y = 1.0 - CLAMP(y - inset, 0, height) / (float)height; const float mx = g->mouse_x; const float my = g->mouse_y; const float linx = _mouse_to_curve(mx, g->zoom_factor, g->offset_x), liny = _mouse_to_curve(my, g->zoom_factor, g->offset_y); - if(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK) + if(dt_key_modifier_state() & GDK_BUTTON1_MASK) { // got a vertex selected: if(g->selected >= 0) @@ -1250,7 +1245,7 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, g->zoom_factor, g->offset_y); dt_iop_color_picker_reset(self, TRUE); - return _move_point_internal(self, widget, dx, dy, dt_gdk_event_get_state(event)); + _move_point_internal(self, widget, dx, dy, dt_key_modifier_state()); } else if(nodes < DT_IOP_RGBCURVE_MAXNODES && g->selected >= -1) { @@ -1286,29 +1281,30 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, finally: if(g->selected >= 0) gtk_widget_grab_focus(widget); gtk_widget_queue_draw(widget); - return TRUE; } -static gboolean _area_button_press_callback(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void _area_button_press_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(gesture); dt_iop_rgbcurve_params_t *p = self->params; const dt_iop_rgbcurve_params_t *const d = self->default_params; dt_iop_rgbcurve_gui_data_t *g = self->gui_data; if(darktable.develop->darkroom_skip_mouse_events) - return TRUE; + return; const int ch = g->channel; const int autoscale = p->curve_autoscale; const int nodes = p->curve_num_nodes[ch]; dt_iop_rgbcurve_node_t *curve_nodes = p->curve_nodes[ch]; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS - && dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK) + if(dt_modifier_is(dt_key_modifier_state(), GDK_CONTROL_MASK) && nodes < DT_IOP_RGBCURVE_MAXNODES && g->selected == -1) { // if we are not on a node -> add a new node at the current x of @@ -1319,8 +1315,8 @@ static gboolean _area_button_press_callback(GtkWidget *widget, const int width = allocation.width - 2 * inset; const int height = allocation.height - 2 * inset; - g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; - g->mouse_y = 1.0 - CLAMP(dt_gdk_event_get_y(event) - inset, 0, height) / (float)height; + g->mouse_x = CLAMP(x - inset, 0, width) / (float)width; + g->mouse_y = 1.0 - CLAMP(y - inset, 0, height) / (float)height; const float mx = g->mouse_x; const float linx = _mouse_to_curve(mx, g->zoom_factor, g->offset_x); @@ -1344,14 +1340,14 @@ static gboolean _area_button_press_callback(GtkWidget *widget, if(selected == -1) selected = nodes; // evaluate the curve at the current x position - const float y = dt_draw_curve_calc_value(g->minmax_curve[ch], linx); + const float curve_y = dt_draw_curve_calc_value(g->minmax_curve[ch], linx); - if(y >= 0.0f && y <= 1.0f) // never add something outside the + if(curve_y >= 0.0f && curve_y <= 1.0f) // never add something outside the // viewport, you couldn't change it // afterwards { // create a new node - selected = _add_node(curve_nodes, &p->curve_num_nodes[ch], linx, y); + selected = _add_node(curve_nodes, &p->curve_num_nodes[ch], linx, curve_y); // maybe set the new one as being selected const float min = .04f * .04f; // comparing against square @@ -1359,7 +1355,7 @@ static gboolean _area_button_press_callback(GtkWidget *widget, { const float other_y = _curve_to_mouse(curve_nodes[k].y, g->zoom_factor, g->offset_y); - const float dist = (y - other_y) * (y - other_y); + const float dist = (curve_y - other_y) * (curve_y - other_y); if(dist < min) g->selected = selected; } @@ -1367,10 +1363,8 @@ static gboolean _area_button_press_callback(GtkWidget *widget, dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + ch); gtk_widget_queue_draw(GTK_WIDGET(g->area)); } - - return TRUE; } - else if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + else if(n_press >= 2) { // reset current curve // if autoscale is on: allow only reset of L curve @@ -1401,10 +1395,9 @@ static gboolean _area_button_press_callback(GtkWidget *widget, gtk_widget_queue_draw(GTK_WIDGET(g->area)); } } - return TRUE; } } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY && g->selected >= 0) + else if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY && g->selected >= 0) { if(g->selected == 0 || g->selected == nodes - 1) { @@ -1413,7 +1406,6 @@ static gboolean _area_button_press_callback(GtkWidget *widget, dt_iop_color_picker_reset(self, TRUE); dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + ch); gtk_widget_queue_draw(GTK_WIDGET(g->area)); - return TRUE; } for(int k = g->selected; k < nodes - 1; k++) @@ -1427,9 +1419,7 @@ static gboolean _area_button_press_callback(GtkWidget *widget, dt_iop_color_picker_reset(self, TRUE); dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + ch); gtk_widget_queue_draw(GTK_WIDGET(g->area)); - return TRUE; } - return FALSE; } void gui_reset(dt_iop_module_t *self) @@ -1528,23 +1518,20 @@ void gui_init(dt_iop_module_t *self) // gtk_widget_set_tooltip_text(GTK_WIDGET(g->area), _("double click // to reset curve")); - gtk_widget_add_events(GTK_WIDGET(g->area), - GDK_POINTER_MOTION_MASK | darktable.gui->scroll_mask - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); gtk_widget_set_can_focus(GTK_WIDGET(g->area), TRUE); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(_area_draw_callback), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", - G_CALLBACK(_area_button_press_callback), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", - G_CALLBACK(_area_motion_notify_callback), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", - G_CALLBACK(_area_leave_notify_callback), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", - G_CALLBACK(_area_scrolled_callback), self); - g_signal_connect(G_OBJECT(g->area), "key-press-event", - G_CALLBACK(_area_key_press_callback), self); + gtk_widget_add_events(GTK_WIDGET(g->area), + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | darktable.gui->scroll_mask); + dt_gui_connect_click(g->area, _area_button_press_callback, NULL, self); + dt_gui_connect_motion(g->area, _area_motion_notify_callback, NULL, _area_leave_notify_callback, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _area_scrolled_callback, self); + dt_gui_connect_key(g->area, _area_key_press_callback, self); g->interpolator = dt_bauhaus_combobox_new_interpolation(self); gtk_widget_set_tooltip_text(g->interpolator, diff --git a/src/iop/rgblevels.c b/src/iop/rgblevels.c index 3dfa1c9b0e8b..8ea698fa777e 100644 --- a/src/iop/rgblevels.c +++ b/src/iop/rgblevels.c @@ -360,14 +360,12 @@ void gui_post_expose(dt_iop_module_t *self, cairo_stroke(cr); } -static gboolean _area_leave_notify_callback(GtkWidget *widget, - GdkEventCrossing *event, - dt_iop_module_t *self) +static void _area_leave_notify_callback(GtkEventControllerMotion *controller, + dt_iop_module_t *self) { dt_iop_rgblevels_gui_data_t *g = self->gui_data; g->mouse_x = g->mouse_y = -1.0; - gtk_widget_queue_draw(widget); - return TRUE; + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } static gboolean _area_draw_callback(GtkWidget *widget, @@ -549,28 +547,30 @@ static void _rgblevels_move_handle(dt_iop_module_t *self, gtk_widget_queue_draw(GTK_WIDGET(g->area)); } -static gboolean _area_motion_notify_callback(GtkWidget *widget, - GdkEventMotion *event, - dt_iop_module_t *self) +static void _area_motion_notify_callback(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_rgblevels_gui_data_t *g = self->gui_data; dt_iop_rgblevels_params_t *p = self->params; + GtkWidget *widget = dt_gui_get_widget(controller); const int inset = DT_GUI_CURVE_EDITOR_INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int height = allocation.height - 2 * inset - DT_RESIZE_HANDLE_SIZE, width = allocation.width - 2 * inset; if(!g->dragging) { - g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width); + g->mouse_x = CLAMP(x - inset, 0, width); g->drag_start_percentage = (p->levels[g->channel][1] - p->levels[g->channel][0]) / (p->levels[g->channel][2] - p->levels[g->channel][0]); } - g->mouse_y = CLAMP(dt_gdk_event_get_y(event) - inset, 0, height); + g->mouse_y = CLAMP(y - inset, 0, height); if(g->dragging) { if(g->handle_move >= 0 && g->handle_move < 3) { - const float mx = (CLAMP(dt_gdk_event_get_x(event) - inset, 0, width)) / (float)width; + const float mx = (CLAMP(x - inset, 0, width)) / (float)width; _rgblevels_move_handle(self, g->handle_move, mx, p->levels[g->channel], g->drag_start_percentage); } @@ -578,7 +578,7 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, else { g->handle_move = 0; - const float mx = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width) / (float)width; + const float mx = CLAMP(x - inset, 0, width) / (float)width; float dist = fabsf(p->levels[g->channel][0] - mx); for(int k = 1; k < 3; k++) { @@ -594,91 +594,81 @@ static gboolean _area_motion_notify_callback(GtkWidget *widget, gtk_widget_queue_draw(widget); } - - return TRUE; } -static gboolean _area_button_press_callback(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void _area_button_press_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - // set active point - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { - if(darktable.develop->gui_module != self) dt_iop_request_focus(self); + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) - { - _turn_selregion_picker_off(self); + if(darktable.develop->gui_module != self) dt_iop_request_focus(self); - // Reset - dt_iop_rgblevels_gui_data_t *g = self->gui_data; - dt_iop_rgblevels_params_t *p = self->params; - const dt_iop_rgblevels_params_t *const default_params = self->default_params; + if(n_press >= 2) + { + _turn_selregion_picker_off(self); - for(int i = 0; i < 3; i++) - p->levels[g->channel][i] = default_params->levels[g->channel][i]; + // Reset + dt_iop_rgblevels_gui_data_t *g = self->gui_data; + dt_iop_rgblevels_params_t *p = self->params; + const dt_iop_rgblevels_params_t *const default_params = self->default_params; - // Needed in case the user scrolls or drags immediately after a reset, - // as drag_start_percentage is only updated when the mouse is moved. - g->drag_start_percentage = 0.5; - dt_dev_add_history_item(darktable.develop, self, TRUE); - gtk_widget_queue_draw(GTK_WIDGET(g->area)); - } - else - { - _turn_selregion_picker_off(self); + for(int i = 0; i < 3; i++) + p->levels[g->channel][i] = default_params->levels[g->channel][i]; - dt_iop_rgblevels_gui_data_t *g = self->gui_data; - g->dragging = 1; - } - return TRUE; + // Needed in case the user scrolls or drags immediately after a reset, + // as drag_start_percentage is only updated when the mouse is moved. + g->drag_start_percentage = 0.5; + dt_dev_add_history_item(darktable.develop, self, TRUE); + gtk_widget_queue_draw(GTK_WIDGET(g->area)); } - return FALSE; -} - -static gboolean _area_button_release_callback(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) -{ - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + else { + _turn_selregion_picker_off(self); + dt_iop_rgblevels_gui_data_t *g = self->gui_data; - g->dragging = 0; - return TRUE; + g->dragging = 1; } - return FALSE; } -static gboolean _area_scroll_callback(GtkWidget *widget, - GdkEventScroll *event, - dt_iop_module_t *self) +static void _area_button_release_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; + dt_iop_rgblevels_gui_data_t *g = self->gui_data; - dt_iop_rgblevels_params_t *p = self->params; + g->dragging = 0; +} - if(dt_gui_ignore_scroll(event)) return FALSE; +static void _area_scroll_callback(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_iop_module_t *self) +{ + dt_iop_rgblevels_gui_data_t *g = self->gui_data; + dt_iop_rgblevels_params_t *p = self->params; + GtkWidget *widget = dt_gui_get_widget(controller); _turn_selregion_picker_off(self); if(g->dragging) - { - return FALSE; - } + return; if(darktable.develop->gui_module != self) dt_iop_request_focus(self); - const float interval = 0.002 * dt_accel_get_speed_multiplier(widget, dt_gdk_event_get_state(event)); - // Distance moved for each scroll event - int delta_y; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) + if(dy != 0.0) { - const float new_position = p->levels[g->channel][g->handle_move] - interval * delta_y; + const float interval = 0.002 * dt_accel_get_speed_multiplier(widget, dt_key_modifier_state()); + const float new_position = p->levels[g->channel][g->handle_move] - interval * dy; _rgblevels_move_handle(self, g->handle_move, new_position, p->levels[g->channel], g->drag_start_percentage); - return TRUE; } - - return TRUE; // Ensure that scrolling the widget cannot move side panel } static void _auto_levels_callback(GtkButton *button, @@ -1055,16 +1045,11 @@ void gui_init(dt_iop_module_t *self) "operates on L channel.")); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(_area_draw_callback), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", - G_CALLBACK(_area_button_press_callback), self); - g_signal_connect(G_OBJECT(g->area), "button-release-event", - G_CALLBACK(_area_button_release_callback), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", - G_CALLBACK(_area_motion_notify_callback), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", - G_CALLBACK(_area_leave_notify_callback), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", - G_CALLBACK(_area_scroll_callback), self); + dt_gui_connect_click(g->area, _area_button_press_callback, _area_button_release_callback, self); + dt_gui_connect_motion(g->area, _area_motion_notify_callback, NULL, _area_leave_notify_callback, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _area_scroll_callback, self); #define PICKER_SETUP(color, name, tooltip) \ g->color##pick = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL); \ diff --git a/src/iop/spots.c b/src/iop/spots.c index 03148fdd6afe..d498bde67569 100644 --- a/src/iop/spots.c +++ b/src/iop/spots.c @@ -18,6 +18,7 @@ #include "control/conf.h" #include "control/control.h" +#include "common/gdk_event_utils.h" #include "common/imagebuf.h" #include "develop/blend.h" #include "develop/imageop.h" @@ -328,15 +329,24 @@ static gboolean _add_shape(GtkWidget *widget, return FALSE; } -static gboolean _add_shape_callback(GtkWidget *widget, - GdkEventButton *e, - dt_iop_module_t *self) +static void _add_shape_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(FALSE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; + GtkWidget *widget = dt_gui_get_widget(gesture); const dt_iop_spots_gui_data_t *g = self->gui_data; - const gboolean creation_continuous = dt_modifier_is(e->state, GDK_CONTROL_MASK); + gboolean creation_continuous = FALSE; + GdkEvent *event = gtk_get_current_event(); + if(event) + { + creation_continuous = dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); + gdk_event_free(event); + } _add_shape(widget, creation_continuous, self); @@ -346,21 +356,21 @@ static gboolean _add_shape_callback(GtkWidget *widget, _shape_is_being_added(self, DT_MASKS_ELLIPSE)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_path), _shape_is_being_added(self, DT_MASKS_PATH)); - - return TRUE; } -static gboolean _edit_masks(GtkWidget *widget, - GdkEventButton *e, - dt_iop_module_t *self) +static void _edit_masks(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(FALSE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; // if we don't have the focus, request for it and quit, gui_focus() do the rest if(darktable.develop->gui_module != self) { dt_iop_request_focus(self); - return FALSE; + return; } dt_iop_gui_blend_data_t *bd = self->blend_data; @@ -408,8 +418,6 @@ static gboolean _edit_masks(GtkWidget *widget, DT_LEAVE_GUI_UPDATE(); dt_control_queue_redraw_center(); - - return TRUE; } static gboolean masks_form_is_in_roi(dt_iop_module_t *self, diff --git a/src/iop/temperature.c b/src/iop/temperature.c index 55f6e284fa8a..a0448e2df337 100644 --- a/src/iop/temperature.c +++ b/src/iop/temperature.c @@ -1730,12 +1730,15 @@ void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous) _update_preset(self, DT_IOP_TEMP_USER); } -static gboolean _btn_toggled(GtkWidget *togglebutton, - GdkEventButton *event, - dt_iop_module_t *self) +static void _btn_toggled(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(TRUE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; + GtkWidget *togglebutton = dt_gui_get_widget(gesture); dt_iop_temperature_gui_data_t *g = (dt_iop_temperature_gui_data_t*)self->gui_data; const int preset = togglebutton == g->btn_asshot ? DT_IOP_TEMP_AS_SHOT : @@ -1761,7 +1764,6 @@ static gboolean _btn_toggled(GtkWidget *togglebutton, "preset='%s': D65 %.3f %.3f %.3f, AS-SHOT %.3f %.3f %.3f", _preset_to_str(preset), chr->D65coeffs[0], chr->D65coeffs[1], chr->D65coeffs[2], chr->as_shot[0], chr->as_shot[1], chr->as_shot[2]); - return TRUE; } static void _preset_tune_callback(GtkWidget *widget, dt_iop_module_t *self) @@ -1995,9 +1997,11 @@ static void _gui_sliders_update(dt_iop_module_t *self) gtk_widget_set_visible(GTK_WIDGET(g->scale_y), (img->flags & DT_IMAGE_4BAYER)); } -static gboolean temp_label_click(GtkWidget *label, - GdkEventButton *event, - dt_iop_module_t *self) +static void temp_label_click(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { dt_iop_temperature_gui_data_t *g = self->gui_data; @@ -2027,7 +2031,6 @@ static gboolean temp_label_click(GtkWidget *label, _color_temptint_sliders(self); _color_rgb_sliders(self); _color_finetuning_slider(self); - return TRUE; } static void _preference_changed(gpointer instance, dt_iop_module_t *self) @@ -2130,8 +2133,7 @@ void gui_init(dt_iop_module_t *self) gtk_widget_set_tooltip_text(g->temp_label, _("click to cycle color mode on sliders")); gtk_container_add(GTK_CONTAINER(temp_label_box), g->temp_label); - g_signal_connect(G_OBJECT(temp_label_box), "button-release-event", - G_CALLBACK(temp_label_click), self); + dt_gui_connect_click(temp_label_box, NULL, temp_label_click, self); //Match UI order: temp first, then tint (like every other app ever) g->scale_k = dt_bauhaus_slider_new_with_range_and_feedback diff --git a/src/iop/tonecurve.c b/src/iop/tonecurve.c index 192a8b6fe1f3..c7b92043841a 100644 --- a/src/iop/tonecurve.c +++ b/src/iop/tonecurve.c @@ -51,18 +51,22 @@ DT_MODULE_INTROSPECTION(5, dt_iop_tonecurve_params_t) static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self); -static gboolean dt_iop_tonecurve_motion_notify(GtkWidget *widget, - GdkEventMotion *event, - dt_iop_module_t *self); -static gboolean dt_iop_tonecurve_button_press(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self); -static gboolean dt_iop_tonecurve_leave_notify(GtkWidget *widget, - GdkEventCrossing *event, - dt_iop_module_t *self); -static gboolean dt_iop_tonecurve_key_press(GtkWidget *widget, - GdkEventKey *event, +static void dt_iop_tonecurve_motion_notify(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self); +static void dt_iop_tonecurve_button_press(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, dt_iop_module_t *self); +static void dt_iop_tonecurve_leave_notify(GtkEventControllerMotion *controller, + dt_iop_module_t *self); +static gboolean dt_iop_tonecurve_key_press(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_iop_module_t *self); typedef enum tonecurve_channel_t @@ -1158,40 +1162,36 @@ static gboolean _move_point_internal(dt_iop_module_t *self, #define TONECURVE_DEFAULT_STEP (0.001f) -static gboolean _scrolled(GtkWidget *widget, - GdkEventScroll *event, +static void _scrolled(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_tonecurve_params_t *p = self->params; dt_iop_tonecurve_gui_data_t *g = self->gui_data; - if(dt_gui_ignore_scroll(event)) - return FALSE; + if(dy == 0.0) return; const int ch = g->channel; const int autoscale_ab = p->tonecurve_autoscale_ab; // if autoscale_ab is on: do not modify a and b curves - if((autoscale_ab != DT_S_SCALE_MANUAL) && ch != ch_L) - return TRUE; - - if(g->selected < 0) - return TRUE; + if((autoscale_ab != DT_S_SCALE_MANUAL) && ch != ch_L) return; - gdouble delta_y; - if(dt_gui_get_scroll_delta(event, &delta_y)) - { - delta_y *= -TONECURVE_DEFAULT_STEP; - return _move_point_internal(self, widget, 0.0, delta_y, dt_gdk_event_get_state(event)); - } + if(g->selected < 0) return; - return TRUE; + dy *= -TONECURVE_DEFAULT_STEP; + _move_point_internal(self, widget, 0.0, dy, dt_key_modifier_state()); } -static gboolean dt_iop_tonecurve_key_press(GtkWidget *widget, - GdkEventKey *event, - dt_iop_module_t *self) +static gboolean dt_iop_tonecurve_key_press(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_tonecurve_params_t *p = self->params; dt_iop_tonecurve_gui_data_t *g = self->gui_data; @@ -1207,22 +1207,22 @@ static gboolean dt_iop_tonecurve_key_press(GtkWidget *widget, int handled = 0; float dx = 0.0f, dy = 0.0f; - if(dt_gdk_event_get_keyval(event) == GDK_KEY_Up || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Up) + if(keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up) { handled = 1; dy = TONECURVE_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Down || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Down) + else if(keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down) { handled = 1; dy = -TONECURVE_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Right || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Right) + else if(keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right) { handled = 1; dx = TONECURVE_DEFAULT_STEP; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_Left || dt_gdk_event_get_keyval(event) == GDK_KEY_KP_Left) + else if(keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left) { handled = 1; dx = -TONECURVE_DEFAULT_STEP; @@ -1230,7 +1230,7 @@ static gboolean dt_iop_tonecurve_key_press(GtkWidget *widget, if(!handled) return FALSE; - return _move_point_internal(self, widget, dx, dy, dt_gdk_event_get_state(event)); + return _move_point_internal(self, widget, dx, dy, dt_key_modifier_state()); } #undef TONECURVE_DEFAULT_STEP @@ -1291,21 +1291,19 @@ void gui_init(dt_iop_module_t *self) // FIXME: that tooltip goes in the way of the numbers when you hover a node to get a reading //gtk_widget_set_tooltip_text(GTK_WIDGET(g->area), _("double click to reset curve")); - gtk_widget_add_events(GTK_WIDGET(g->area), GDK_POINTER_MOTION_MASK | darktable.gui->scroll_mask - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); gtk_widget_set_can_focus(GTK_WIDGET(g->area), TRUE); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(dt_iop_tonecurve_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", - G_CALLBACK(dt_iop_tonecurve_button_press), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", - G_CALLBACK(dt_iop_tonecurve_motion_notify), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", - G_CALLBACK(dt_iop_tonecurve_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", - G_CALLBACK(_scrolled), self); - g_signal_connect(G_OBJECT(g->area), "key-press-event", - G_CALLBACK(dt_iop_tonecurve_key_press), self); + gtk_widget_add_events(GTK_WIDGET(g->area), + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | darktable.gui->scroll_mask); + dt_gui_connect_click(g->area, dt_iop_tonecurve_button_press, NULL, self); + dt_gui_connect_motion(g->area, dt_iop_tonecurve_motion_notify, NULL, dt_iop_tonecurve_leave_notify, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _scrolled, self); + dt_gui_connect_key(g->area, dt_iop_tonecurve_key_press, self); g->interpolator = dt_bauhaus_combobox_new_interpolation(self); gtk_widget_set_tooltip_text @@ -1344,15 +1342,13 @@ void gui_cleanup(dt_iop_module_t *self) dt_draw_curve_destroy(g->minmax_curve[ch_b]); } -static gboolean dt_iop_tonecurve_leave_notify(GtkWidget *widget, - GdkEventCrossing *event, - dt_iop_module_t *self) +static void dt_iop_tonecurve_leave_notify(GtkEventControllerMotion *controller, + dt_iop_module_t *self) { dt_iop_tonecurve_gui_data_t *g = self->gui_data; - if(!(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK)) + if(!(dt_key_modifier_state() & GDK_BUTTON1_MASK)) g->selected = -1; - gtk_widget_queue_draw(widget); - return FALSE; + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } static void picker_scale(const float *in, float *out) @@ -1752,10 +1748,12 @@ static inline int _add_node(dt_iop_tonecurve_node_t *tonecurve, return selected; } -static gboolean dt_iop_tonecurve_motion_notify(GtkWidget *widget, - GdkEventMotion *event, - dt_iop_module_t *self) +static void dt_iop_tonecurve_motion_notify(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_iop_tonecurve_gui_data_t *g = self->gui_data; dt_iop_tonecurve_params_t *p = self->params; @@ -1765,7 +1763,12 @@ static gboolean dt_iop_tonecurve_motion_notify(GtkWidget *widget, int autoscale_ab = p->tonecurve_autoscale_ab; // if autoscale_ab is on: do not modify a and b curves - if((autoscale_ab != DT_S_SCALE_MANUAL) && ch != ch_L) goto finally; + if((autoscale_ab != DT_S_SCALE_MANUAL) && ch != ch_L) + { + if(g->selected >= 0) gtk_widget_grab_focus(widget); + gtk_widget_queue_draw(widget); + return; + } const int inset = DT_GUI_CURVE_EDITOR_INSET; GtkAllocation allocation; @@ -1773,15 +1776,15 @@ static gboolean dt_iop_tonecurve_motion_notify(GtkWidget *widget, int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset; double old_m_x = g->mouse_x; double old_m_y = g->mouse_y; - g->mouse_x = dt_gdk_event_get_x(event) - inset; - g->mouse_y = dt_gdk_event_get_y(event) - inset; + g->mouse_x = x - inset; + g->mouse_y = y - inset; const float mx = CLAMP(g->mouse_x, 0, width) / width; const float my = 1.0f - CLAMP(g->mouse_y, 0, height) / height; const float linx = to_lin(mx, g->loglogscale, ch, g->semilog, 0), liny = to_lin(my, g->loglogscale, ch, g->semilog, 1); - if(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK) + if(dt_key_modifier_state() & GDK_BUTTON1_MASK) { // got a vertex selected: if(g->selected >= 0) @@ -1795,7 +1798,7 @@ static gboolean dt_iop_tonecurve_motion_notify(GtkWidget *widget, - to_lin(old_m_x / width - translate_mouse_x, g->loglogscale, ch, g->semilog, 0); const float dy = to_lin(1 - g->mouse_y / height - translate_mouse_y, g->loglogscale, ch, g->semilog, 1) - to_lin(1 - old_m_y / height - translate_mouse_y, g->loglogscale, ch, g->semilog, 1); - return _move_point_internal(self, widget, dx, dy, dt_gdk_event_get_state(event)); + _move_point_internal(self, widget, dx, dy, dt_key_modifier_state()); } else if(nodes < DT_IOP_TONECURVE_MAXNODES && g->selected >= -1) { @@ -1823,16 +1826,17 @@ static gboolean dt_iop_tonecurve_motion_notify(GtkWidget *widget, } g->selected = nearest; } -finally: if(g->selected >= 0) gtk_widget_grab_focus(widget); gtk_widget_queue_draw(widget); - return TRUE; } -static gboolean dt_iop_tonecurve_button_press(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void dt_iop_tonecurve_button_press(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(gesture); dt_iop_tonecurve_params_t *p = self->params; const dt_iop_tonecurve_params_t *const d = self->default_params; dt_iop_tonecurve_gui_data_t *g = self->gui_data; @@ -1842,10 +1846,10 @@ static gboolean dt_iop_tonecurve_button_press(GtkWidget *widget, int nodes = p->tonecurve_nodes[ch]; dt_iop_tonecurve_node_t *tonecurve = p->tonecurve[ch]; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS - && dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK) + if(n_press >= 1 + && dt_modifier_is(dt_key_modifier_state(), GDK_CONTROL_MASK) && nodes < DT_IOP_TONECURVE_MAXNODES && g->selected == -1) { @@ -1855,8 +1859,8 @@ static gboolean dt_iop_tonecurve_button_press(GtkWidget *widget, GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int width = allocation.width - 2 * inset; - g->mouse_x = dt_gdk_event_get_x(event) - inset; - g->mouse_y = dt_gdk_event_get_y(event) - inset; + g->mouse_x = x - inset; + g->mouse_y = y - inset; const float mx = CLAMP(g->mouse_x, 0, width) / (float)width; const float linx = to_lin(mx, g->loglogscale, ch, g->semilog, 0); @@ -1883,12 +1887,12 @@ static gboolean dt_iop_tonecurve_button_press(GtkWidget *widget, (selected < nodes && tonecurve[selected].x - linx <= 0.025))) { // evaluate the curve at the current x position - const float y = dt_draw_curve_calc_value(g->minmax_curve[ch], linx); + const float curve_y = dt_draw_curve_calc_value(g->minmax_curve[ch], linx); - if(y >= 0.0 && y <= 1.0) // never add something outside the viewport, you couldn't change it afterwards + if(curve_y >= 0.0 && curve_y <= 1.0) // never add something outside the viewport, you couldn't change it afterwards { // create a new node - selected = _add_node(tonecurve, &p->tonecurve_nodes[ch], linx, y); + selected = _add_node(tonecurve, &p->tonecurve_nodes[ch], linx, curve_y); // maybe set the new one as being selected float min = .04f; @@ -1896,7 +1900,7 @@ static gboolean dt_iop_tonecurve_button_press(GtkWidget *widget, for(int k = 0; k < nodes; k++) { float other_y = to_log(tonecurve[k].y, g->loglogscale, ch, g->semilog, 1); - float dist = (y - other_y) * (y - other_y); + float dist = (curve_y - other_y) * (curve_y - other_y); if(dist < min) g->selected = selected; } @@ -1904,9 +1908,9 @@ static gboolean dt_iop_tonecurve_button_press(GtkWidget *widget, gtk_widget_queue_draw(GTK_WIDGET(g->area)); } } - return TRUE; + } - else if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + else if(n_press >= 2) { // reset current curve // if autoscale_ab is on: allow only reset of L curve @@ -1935,10 +1939,10 @@ static gboolean dt_iop_tonecurve_button_press(GtkWidget *widget, gtk_widget_queue_draw(GTK_WIDGET(g->area)); } } - return TRUE; + } } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY && g->selected >= 0) + else if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY && g->selected >= 0) { if(g->selected == 0 || g->selected == nodes - 1) { @@ -1946,7 +1950,6 @@ static gboolean dt_iop_tonecurve_button_press(GtkWidget *widget, tonecurve[g->selected].y = tonecurve[g->selected].x = reset_value; gtk_widget_queue_draw(GTK_WIDGET(g->area)); dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + ch); - return TRUE; } for(int k = g->selected; k < nodes - 1; k++) @@ -1959,9 +1962,7 @@ static gboolean dt_iop_tonecurve_button_press(GtkWidget *widget, p->tonecurve_nodes[ch]--; gtk_widget_queue_draw(GTK_WIDGET(g->area)); dt_dev_add_history_item_target(darktable.develop, self, TRUE, widget + ch); - return TRUE; } - return FALSE; } // clang-format off diff --git a/src/iop/toneequal.c b/src/iop/toneequal.c index 2ed7f896223f..a5d95cfe66be 100644 --- a/src/iop/toneequal.c +++ b/src/iop/toneequal.c @@ -1926,9 +1926,11 @@ static void auto_adjust_contrast_boost(GtkWidget *quad, dt_iop_module_t *self) } -static void show_luminance_mask_callback(GtkWidget *togglebutton, - GdkEventButton *event, - dt_iop_module_t *self) +static void show_luminance_mask_callback(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_iop_module_t *self) { DT_GUARD_GUI_UPDATE(); dt_iop_request_focus(self); @@ -2941,12 +2943,11 @@ static gboolean area_draw(GtkWidget *widget, } -static gboolean area_enter_leave_notify(GtkWidget *widget, - const GdkEventCrossing *event, - dt_iop_module_t *self) +static void area_leave_notify(GtkEventControllerMotion *controller, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(TRUE); - if(!self->enabled) return FALSE; + DT_GUARD_GUI_UPDATE(); + if(!self->enabled) return; dt_iop_toneequalizer_gui_data_t *g = self->gui_data; @@ -2960,33 +2961,37 @@ static gboolean area_enter_leave_notify(GtkWidget *widget, dt_dev_add_history_item(darktable.develop, self, FALSE); } dt_iop_gui_enter_critical_section(self); - g->area_x = (dt_gdk_event_get_x(event) - g->inset); - g->area_y = (dt_gdk_event_get_y(event) - g->inset); + g->area_x = g->inset; + g->area_y = g->inset; g->area_dragging = FALSE; g->area_active_node = -1; - g->area_cursor_valid = (g->area_x > 0.0f - && g->area_x < g->graph_width - && g->area_y > 0.0f - && g->area_y < g->graph_height); + g->area_cursor_valid = FALSE; dt_iop_gui_leave_critical_section(self); gtk_widget_queue_draw(GTK_WIDGET(g->area)); - return FALSE; } -static gboolean area_button_press(GtkWidget *widget, - const GdkEventButton *event, - dt_iop_module_t *self) +static void area_button_press(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - - DT_GUARD_GUI_UPDATE(TRUE); + DT_GUARD_GUI_UPDATE(); dt_iop_toneequalizer_gui_data_t *g = self->gui_data; dt_iop_request_focus(self); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + { + // Unlock the colour picker so we can display our own custom cursor + dt_iop_color_picker_reset(self, TRUE); + return; + } + + if(n_press >= 2) { dt_iop_toneequalizer_params_t *p = self->params; const dt_iop_toneequalizer_params_t *const d = self->default_params; @@ -3008,35 +3013,28 @@ static gboolean area_button_press(GtkWidget *widget, // Redraw graph gtk_widget_queue_draw(GTK_WIDGET(g->area)); dt_dev_add_history_item(darktable.develop, self, TRUE); - return TRUE; + return; } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + + if(self->enabled) { - if(self->enabled) - { - g->area_dragging = TRUE; - gtk_widget_queue_draw(GTK_WIDGET(g->area)); - } - else - { - dt_dev_add_history_item(darktable.develop, self, TRUE); - } - return TRUE; + g->area_dragging = TRUE; + gtk_widget_queue_draw(GTK_WIDGET(g->area)); + } + else + { + dt_dev_add_history_item(darktable.develop, self, TRUE); } - - // Unlock the colour picker so we can display our own custom cursor - dt_iop_color_picker_reset(self, TRUE); - - return FALSE; } -static gboolean area_motion_notify(GtkWidget *widget, - const GdkEventMotion *event, - dt_iop_module_t *self) +static void area_motion_notify(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(TRUE); - if(!self->enabled) return FALSE; + DT_GUARD_GUI_UPDATE(); + if(!self->enabled) return; dt_iop_toneequalizer_gui_data_t *g = self->gui_data; dt_iop_toneequalizer_params_t *p = self->params; @@ -3046,7 +3044,7 @@ static gboolean area_motion_notify(GtkWidget *widget, // vertical distance travelled since button_pressed event dt_iop_gui_enter_critical_section(self); // graph spans over 4 EV - const float offset = (-dt_gdk_event_get_y(event) + g->area_y) / g->graph_height * 4.0f; + const float offset = (-y + g->area_y) / g->graph_height * 4.0f; const float cursor_exposure = g->area_x / g->graph_width * 8.0f - 8.0f; // Get the desired correction on exposure channels @@ -3056,8 +3054,8 @@ static gboolean area_motion_notify(GtkWidget *widget, } dt_iop_gui_enter_critical_section(self); - g->area_x = dt_gdk_event_get_x(event) - g->inset; - g->area_y = dt_gdk_event_get_y(event); + g->area_x = x - g->inset; + g->area_y = y; g->area_cursor_valid = (g->area_x > 0.0f && g->area_x < g->graph_width && g->area_y > 0.0f @@ -3081,64 +3079,61 @@ static gboolean area_motion_notify(GtkWidget *widget, dt_iop_gui_leave_critical_section(self); gtk_widget_queue_draw(GTK_WIDGET(g->area)); - return TRUE; } -static gboolean area_button_release(GtkWidget *widget, - const GdkEventButton *event, - dt_iop_module_t *self) +static void area_button_release(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(TRUE); - if(!self->enabled) return FALSE; + DT_GUARD_GUI_UPDATE(); + if(!self->enabled) return; + + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_PRIMARY) + return; dt_iop_toneequalizer_gui_data_t *g = self->gui_data; // Give focus to module dt_iop_request_focus(self); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { - const dt_iop_toneequalizer_params_t *p = self->params; - - if(g->area_dragging) - { - // Update GUI with new params - update_exposure_sliders(g, p); + const dt_iop_toneequalizer_params_t *p = self->params; - dt_dev_add_history_item(darktable.develop, self, FALSE); + if(g->area_dragging) + { + // Update GUI with new params + update_exposure_sliders(g, p); - dt_iop_gui_enter_critical_section(self); - g->area_dragging = FALSE; - dt_iop_gui_leave_critical_section(self); + dt_dev_add_history_item(darktable.develop, self, FALSE); - return TRUE; - } + dt_iop_gui_enter_critical_section(self); + g->area_dragging = FALSE; + dt_iop_gui_leave_critical_section(self); } - return FALSE; } -static gboolean area_scroll(GtkWidget *widget, - GdkEventScroll *event, - gpointer user_data) +static void area_scroll(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_iop_module_t *self) { - // do not propagate to tab bar unless scrolling sidebar - return !dt_gui_ignore_scroll(event); + // scroll is already filtered by _scroll_sidebar in the proxy, + // so only valid scroll events on the area widget reach here. } -static gboolean notebook_button_press(GtkWidget *widget, - GdkEventButton *event, - dt_iop_module_t *self) +static void notebook_button_press(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *self) { - DT_GUARD_GUI_UPDATE(TRUE); - // Give focus to module dt_iop_request_focus(self); // Unlock the colour picker so we can display our own custom cursor dt_iop_color_picker_reset(self, TRUE); - - return FALSE; } GSList *mouse_actions(dt_iop_module_t *self) @@ -3283,25 +3278,19 @@ void gui_init(dt_iop_module_t *self) g_object_set_data(G_OBJECT(wrapper), "iop-instance", self); gtk_widget_set_name(GTK_WIDGET(wrapper), "toneeqgraph"); dt_action_define_iop(self, NULL, N_("graph"), GTK_WIDGET(wrapper), NULL); - gtk_widget_add_events(GTK_WIDGET(g->area), - GDK_POINTER_MOTION_MASK | darktable.gui->scroll_mask - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); gtk_widget_set_vexpand(GTK_WIDGET(g->area), TRUE); gtk_widget_set_can_focus(GTK_WIDGET(g->area), TRUE); g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(area_draw), self); - g_signal_connect(G_OBJECT(g->area), "button-press-event", - G_CALLBACK(area_button_press), self); - g_signal_connect(G_OBJECT(g->area), "button-release-event", - G_CALLBACK(area_button_release), self); - g_signal_connect(G_OBJECT(g->area), "leave-notify-event", - G_CALLBACK(area_enter_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "enter-notify-event", - G_CALLBACK(area_enter_leave_notify), self); - g_signal_connect(G_OBJECT(g->area), "motion-notify-event", - G_CALLBACK(area_motion_notify), self); - g_signal_connect(G_OBJECT(g->area), "scroll-event", - G_CALLBACK(area_scroll), self); + gtk_widget_add_events(GTK_WIDGET(g->area), + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK + | darktable.gui->scroll_mask); + dt_gui_connect_click(g->area, area_button_press, area_button_release, self); + dt_gui_connect_motion(g->area, area_motion_notify, NULL, area_leave_notify, self); + dt_gui_connect_scroll(g->area, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + area_scroll, self); gtk_widget_set_tooltip_text(GTK_WIDGET(g->area), _("double-click to reset the curve")); g->smoothing = dt_bauhaus_slider_new_with_range(self, -2.33f, +1.67f, 0, 0.0f, 2); @@ -3401,8 +3390,7 @@ void gui_init(dt_iop_module_t *self) gtk_widget_show(gtk_notebook_get_nth_page(g->notebook, active_page)); gtk_notebook_set_current_page(g->notebook, active_page); - g_signal_connect(G_OBJECT(g->notebook), "button-press-event", - G_CALLBACK(notebook_button_press), self); + dt_gui_connect_click(g->notebook, notebook_button_press, NULL, self); g->show_luminance_mask = dt_iop_togglebutton_new (self, NULL, diff --git a/src/iop/zonesystem.c b/src/iop/zonesystem.c index b455b0814120..d7859e9b89e7 100644 --- a/src/iop/zonesystem.c +++ b/src/iop/zonesystem.c @@ -386,16 +386,16 @@ static gboolean dt_iop_zonesystem_preview_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self); static gboolean dt_iop_zonesystem_bar_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self); -static gboolean dt_iop_zonesystem_bar_motion_notify(GtkWidget *widget, GdkEventMotion *event, - dt_iop_module_t *self); -static gboolean dt_iop_zonesystem_bar_leave_notify(GtkWidget *widget, GdkEventCrossing *event, - dt_iop_module_t *self); -static gboolean dt_iop_zonesystem_bar_button_press(GtkWidget *widget, GdkEventButton *event, +static void dt_iop_zonesystem_bar_motion_notify(GtkEventControllerMotion *controller, gdouble x, gdouble y, dt_iop_module_t *self); -static gboolean dt_iop_zonesystem_bar_button_release(GtkWidget *widget, GdkEventButton *event, - dt_iop_module_t *self); -static gboolean dt_iop_zonesystem_bar_scrolled(GtkWidget *widget, GdkEventScroll *event, - dt_iop_module_t *self); +static void dt_iop_zonesystem_bar_leave_notify(GtkEventControllerMotion *controller, + dt_iop_module_t *self); +static void dt_iop_zonesystem_bar_button_press(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, + dt_iop_module_t *self); +static void dt_iop_zonesystem_bar_button_release(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, + dt_iop_module_t *self); +static void dt_iop_zonesystem_bar_scrolled(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, + dt_iop_module_t *self); static void size_allocate_callback(GtkWidget *widget, GtkAllocation *allocation, dt_iop_module_t *self) @@ -443,18 +443,15 @@ void gui_init(dt_iop_module_t *self) "left-click on a border to create a marker\n" "right-click on a marker to delete it")); g_signal_connect(G_OBJECT(g->zones), "draw", G_CALLBACK(dt_iop_zonesystem_bar_draw), self); - g_signal_connect(G_OBJECT(g->zones), "motion-notify-event", G_CALLBACK(dt_iop_zonesystem_bar_motion_notify), - self); - g_signal_connect(G_OBJECT(g->zones), "leave-notify-event", G_CALLBACK(dt_iop_zonesystem_bar_leave_notify), - self); - g_signal_connect(G_OBJECT(g->zones), "button-press-event", G_CALLBACK(dt_iop_zonesystem_bar_button_press), - self); - g_signal_connect(G_OBJECT(g->zones), "button-release-event", - G_CALLBACK(dt_iop_zonesystem_bar_button_release), self); - g_signal_connect(G_OBJECT(g->zones), "scroll-event", G_CALLBACK(dt_iop_zonesystem_bar_scrolled), self); - gtk_widget_add_events(GTK_WIDGET(g->zones), GDK_POINTER_MOTION_MASK - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_LEAVE_NOTIFY_MASK | darktable.gui->scroll_mask); + gtk_widget_add_events(GTK_WIDGET(g->zones), + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK + | darktable.gui->scroll_mask); + dt_gui_connect_click_all(g->zones, dt_iop_zonesystem_bar_button_press, dt_iop_zonesystem_bar_button_release, self); + dt_gui_connect_motion(g->zones, dt_iop_zonesystem_bar_motion_notify, NULL, dt_iop_zonesystem_bar_leave_notify, self); + dt_gui_connect_scroll(g->zones, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + dt_iop_zonesystem_bar_scrolled, self); gtk_widget_set_size_request(g->zones, -1, DT_PIXEL_APPLY_DPI(40)); self->widget = dt_gui_vbox(g->preview, g->zones); @@ -572,15 +569,16 @@ static gboolean dt_iop_zonesystem_bar_draw(GtkWidget *widget, cairo_t *crf, dt_i return TRUE; } -static gboolean dt_iop_zonesystem_bar_button_press(GtkWidget *widget, GdkEventButton *event, - dt_iop_module_t *self) +static void dt_iop_zonesystem_bar_button_press(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, + dt_iop_module_t *self) { dt_iop_zonesystem_params_t *p = self->params; dt_iop_zonesystem_gui_data_t *g = self->gui_data; + GtkWidget *widget = dt_gui_get_widget(gesture); const int inset = DT_ZONESYSTEM_INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); - int width = allocation.width - 2 * inset; /*, height = allocation.height - 2*inset;*/ + int width = allocation.width - 2 * inset; /* calculate zonemap */ float zonemap[MAX_ZONE_SYSTEM_SIZE] = { -1 }; @@ -591,8 +589,9 @@ static gboolean dt_iop_zonesystem_bar_button_press(GtkWidget *widget, GdkEventBu float zw = zonemap[k + 1] - zonemap[k]; if((g->mouse_x / width) > zonemap[k] + (zw / 2)) k++; + const guint button = gtk_gesture_single_get_current_button(gesture); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(button == GDK_BUTTON_PRIMARY) { if(p->zone[k] == -1) { @@ -602,60 +601,51 @@ static gboolean dt_iop_zonesystem_bar_button_press(GtkWidget *widget, GdkEventBu g->is_dragging = TRUE; g->current_zone = k; } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) + else if(button == GDK_BUTTON_SECONDARY) { /* clear the controlpoint */ p->zone[k] = -1; dt_dev_add_history_item(darktable.develop, self, TRUE); } - - return TRUE; } -static gboolean dt_iop_zonesystem_bar_button_release(GtkWidget *widget, GdkEventButton *event, - dt_iop_module_t *self) +static void dt_iop_zonesystem_bar_button_release(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, + dt_iop_module_t *self) { dt_iop_zonesystem_gui_data_t *g = self->gui_data; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - { + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) g->is_dragging = FALSE; - } - return TRUE; } -static gboolean dt_iop_zonesystem_bar_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self) +static void dt_iop_zonesystem_bar_scrolled(GtkEventControllerScroll *controller, gdouble dx, gdouble dy, + dt_iop_module_t *self) { dt_iop_zonesystem_params_t *p = self->params; int cs = CLAMP(p->size, 4, MAX_ZONE_SYSTEM_SIZE); - if(dt_gui_ignore_scroll(event)) return FALSE; - - int delta_y; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) + if(dy != 0.0) { - p->size = CLAMP(p->size - delta_y, 4, MAX_ZONE_SYSTEM_SIZE); + p->size = CLAMP(p->size - dy, 4, MAX_ZONE_SYSTEM_SIZE); p->zone[cs] = -1; dt_dev_add_history_item(darktable.develop, self, TRUE); - gtk_widget_queue_draw(widget); + gtk_widget_queue_draw(dt_gui_get_widget(controller)); } - - return TRUE; } -static gboolean dt_iop_zonesystem_bar_leave_notify(GtkWidget *widget, GdkEventCrossing *event, - dt_iop_module_t *self) +static void dt_iop_zonesystem_bar_leave_notify(GtkEventControllerMotion *controller, + dt_iop_module_t *self) { dt_iop_zonesystem_gui_data_t *g = self->gui_data; g->hilite_zone = FALSE; gtk_widget_queue_draw(g->preview); - return TRUE; } -static gboolean dt_iop_zonesystem_bar_motion_notify(GtkWidget *widget, GdkEventMotion *event, - dt_iop_module_t *self) +static void dt_iop_zonesystem_bar_motion_notify(GtkEventControllerMotion *controller, gdouble x, gdouble y, + dt_iop_module_t *self) { dt_iop_zonesystem_params_t *p = self->params; dt_iop_zonesystem_gui_data_t *g = self->gui_data; + GtkWidget *widget = dt_gui_get_widget(controller); const int inset = DT_ZONESYSTEM_INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); @@ -666,8 +656,8 @@ static gboolean dt_iop_zonesystem_bar_motion_notify(GtkWidget *widget, GdkEventM _iop_zonesystem_calculate_zonemap(p, zonemap); /* record mouse position within control */ - g->mouse_x = CLAMP(dt_gdk_event_get_x(event) - inset, 0, width); - g->mouse_y = CLAMP(height - 1 - dt_gdk_event_get_y(event) + inset, 0, height); + g->mouse_x = CLAMP(x - inset, 0, width); + g->mouse_y = CLAMP(height - 1 - y + inset, 0, height); if(g->is_dragging) { @@ -704,7 +694,6 @@ static gboolean dt_iop_zonesystem_bar_motion_notify(GtkWidget *widget, GdkEventM gtk_widget_queue_draw(self->widget); gtk_widget_queue_draw(g->preview); - return TRUE; } diff --git a/src/libs/collect.c b/src/libs/collect.c index e0306005bd89..2c37fa11fb77 100644 --- a/src/libs/collect.c +++ b/src/libs/collect.c @@ -632,31 +632,45 @@ static void view_popup_menu(GtkWidget *treeview, gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event); } -static gboolean view_onButtonPressed(GtkWidget *treeview, - GdkEventButton *event, - dt_lib_collect_t *d) +static void view_onButtonPressed_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_collect_t *d) { + GtkWidget *treeview = dt_gui_get_widget(gesture); + static GdkModifierType last_mod_state = 0; + + GdkEvent *event = gtk_get_current_event(); + + /* gesture coordinates are relative to the widget allocation, while + * gtk_tree_view_get_path_at_pos() expects bin-window coordinates */ + gint bin_x, bin_y; + gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(treeview), + (gint)x, (gint)y, &bin_x, &bin_y); + /* Get tree path for row that was clicked */ GtkTreePath *path = NULL; const gboolean get_path = gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), - (gint)dt_gdk_event_get_x(event), (gint)dt_gdk_event_get_y(event), + bin_x, bin_y, &path, NULL, NULL, NULL); - if(dt_gdk_event_get_type(event) == GDK_DOUBLE_BUTTON_PRESS || d->singleclick) + GdkModifierType mod_state; + gtk_get_current_event_state(&mod_state); + + if((n_press >= 2 || d->singleclick) && path) { - if(dt_gdk_event_get_state(event) == last_state && path) + if(mod_state == last_mod_state) { if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(treeview), path)) gtk_tree_view_collapse_row(GTK_TREE_VIEW(treeview), path); else gtk_tree_view_expand_row(GTK_TREE_VIEW(treeview), path, FALSE); } - last_state = dt_gdk_event_get_state(event); + last_mod_state = mod_state; } // case of a range selection GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); - if(get_path && dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK) + if(get_path && dt_modifier_is(mod_state, GDK_SHIFT_MASK) && gtk_tree_selection_count_selected_rows(selection) > 0 && (d->view_rule == DT_COLLECTION_PROP_DAY || d->view_rule == DT_COLLECTION_PROP_MONTH @@ -678,10 +692,10 @@ static gboolean view_onButtonPressed(GtkWidget *treeview, gtk_tree_selection_select_range(selection, path2, path); g_list_free_full(sels, (GDestroyNotify)gtk_tree_path_free); - row_activated_with_event(GTK_TREE_VIEW(treeview), path, NULL, event, d); + row_activated_with_event(GTK_TREE_VIEW(treeview), path, NULL, (GdkEventButton *)event, d); gtk_tree_path_free(path); - return TRUE; + return; } if(path) @@ -690,37 +704,38 @@ static gboolean view_onButtonPressed(GtkWidget *treeview, gtk_tree_selection_select_path(selection, path); } + const guint button = gtk_gesture_single_get_current_button(gesture); + // case of a context-menu (folder/filmroll) if(((d->view_rule == DT_COLLECTION_PROP_FOLDERS) || (d->view_rule == DT_COLLECTION_PROP_FILMROLL)) - && (dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) - && !(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK) - || dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK))) + && (n_press == 1 && button == GDK_BUTTON_SECONDARY) + && !(dt_modifier_is(mod_state, GDK_SHIFT_MASK) + || dt_modifier_is(mod_state, GDK_CONTROL_MASK))) { - row_activated_with_event(GTK_TREE_VIEW(treeview), path, NULL, event, d); - view_popup_menu(treeview, event, d); + row_activated_with_event(GTK_TREE_VIEW(treeview), path, NULL, (GdkEventButton *)event, d); + view_popup_menu(treeview, (GdkEventButton *)event, d); if(path) gtk_tree_path_free(path); - return TRUE; + return; } // case of a activation - if((!d->singleclick && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - || (d->singleclick && dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - || (!d->singleclick && dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY - && (dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK) - || dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK))) + if((!d->singleclick && n_press >= 2 && button == GDK_BUTTON_PRIMARY) + || (d->singleclick && n_press == 1 && button == GDK_BUTTON_PRIMARY) + || (!d->singleclick && n_press == 1 && button == GDK_BUTTON_PRIMARY + && (dt_modifier_is(mod_state, GDK_SHIFT_MASK) + || dt_modifier_is(mod_state, GDK_CONTROL_MASK))) || (d->view_rule == DT_COLLECTION_PROP_MONTH - && dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY)) + && n_press == 1 && button == GDK_BUTTON_PRIMARY)) { - row_activated_with_event(GTK_TREE_VIEW(treeview), path, NULL, event, d); + row_activated_with_event(GTK_TREE_VIEW(treeview), path, NULL, (GdkEventButton *)event, d); if(path) gtk_tree_path_free(path); - return TRUE; + return; } if(path) gtk_tree_path_free(path); - return FALSE; } static gboolean view_onPopupMenu(GtkWidget *treeview, dt_lib_collect_t *d) @@ -3590,12 +3605,12 @@ static void menuitem_clear(GtkMenuItem *menuitem, DT_COLLECTION_PROP_UNDEF, NULL); } -static gboolean popup_button_callback(GtkWidget *widget, - GdkEventButton *event, - dt_lib_collect_rule_t *d) +static void popup_button_callback_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_collect_rule_t *d) { - if(dt_gdk_event_get_button(event) != 1) - return FALSE; + if(gtk_gesture_single_get_button(gesture) != 1) + return; GtkWidget *menu = gtk_menu_new(); GtkWidget *mi; @@ -3655,9 +3670,8 @@ static gboolean popup_button_callback(GtkWidget *widget, gtk_widget_show_all(GTK_WIDGET(menu)); - gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event); - - return TRUE; + gtk_menu_popup_at_pointer(GTK_MENU(menu), gtk_get_current_event()); + return; } static void view_set_click(gpointer instance, @@ -4045,9 +4059,7 @@ void gui_init(dt_lib_module_t *self) d->rule[i].button = w = dtgtk_button_new(dtgtk_cairo_paint_presets, 0, NULL); dt_gui_add_class(GTK_WIDGET(w), "dt_big_btn_canvas"); - gtk_widget_set_events(w, GDK_BUTTON_PRESS_MASK); - g_signal_connect(G_OBJECT(w), "button-press-event", - G_CALLBACK(popup_button_callback), d->rule + i); + dt_gui_connect_click(w, popup_button_callback_cb, NULL, d->rule + i); d->rule[i].hbox = dt_gui_hbox(d->rule[i].combo, dt_gui_expand(d->rule[i].text), d->rule[i].button); gtk_widget_set_name(d->rule[i].hbox, "lib-dtbutton"); @@ -4058,8 +4070,7 @@ void gui_init(dt_lib_module_t *self) d->view_rule = -1; d->view = view; gtk_tree_view_set_headers_visible(view, FALSE); - g_signal_connect(G_OBJECT(view), "button-press-event", - G_CALLBACK(view_onButtonPressed), d); + dt_gui_connect_click_all(view, view_onButtonPressed_cb, NULL, d); g_signal_connect(G_OBJECT(view), "popup-menu", G_CALLBACK(view_onPopupMenu), d); GtkTreeViewColumn *col = gtk_tree_view_column_new(); diff --git a/src/libs/colorpicker.c b/src/libs/colorpicker.c index dcb68aec1206..39dc733fe618 100644 --- a/src/libs/colorpicker.c +++ b/src/libs/colorpicker.c @@ -270,17 +270,17 @@ static void _update_picker_output(dt_lib_module_t *self) darktable.lib->proxy.colorpicker.picker_proxy != NULL); } -static gboolean _large_patch_toggle(GtkWidget *widget, - GdkEvent *event, - dt_lib_colorpicker_t *data) +static void _large_patch_toggle_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_colorpicker_t *data) { const gboolean show_large_patch = !dt_conf_get_bool("ui_last/colorpicker_large"); dt_conf_set_bool("ui_last/colorpicker_large", show_large_patch); gtk_widget_set_visible(gtk_widget_get_parent(data->large_color_patch), show_large_patch); - - return FALSE; } static void _picker_button_toggled(GtkToggleButton *button, @@ -474,9 +474,10 @@ static void _label_size_allocate_callback(GtkWidget *widget, } } -static gboolean _sample_enter_callback(GtkWidget *widget, - GdkEvent *event, - dt_colorpicker_sample_t *sample) +static void _sample_enter_callback(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_colorpicker_sample_t *sample) { darktable.lib->proxy.colorpicker.selected_sample = sample; @@ -484,16 +485,11 @@ static gboolean _sample_enter_callback(GtkWidget *widget, dt_dev_invalidate_all(darktable.develop); dt_control_queue_redraw_center(); - - return FALSE; } -static gboolean _sample_leave_callback(GtkWidget *widget, - GdkEvent *event, - gpointer data) +static void _sample_leave_callback(GtkEventControllerMotion *controller, + dt_colorpicker_sample_t *sample) { - if(event->crossing.detail == GDK_NOTIFY_INFERIOR) return FALSE; - if(darktable.lib->proxy.colorpicker.selected_sample) { darktable.lib->proxy.colorpicker.selected_sample = NULL; @@ -502,8 +498,6 @@ static gboolean _sample_leave_callback(GtkWidget *widget, dt_control_queue_redraw_center(); } - - return FALSE; } static void _remove_sample(dt_colorpicker_sample_t *sample) @@ -521,16 +515,20 @@ static void _remove_sample_cb(GtkButton *widget, dt_dev_invalidate_all(darktable.develop); } -static gboolean _live_sample_button(GtkWidget *widget, - GdkEventButton *event, - dt_colorpicker_sample_t *sample) +static void _live_sample_button_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_colorpicker_sample_t *sample) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + GtkWidget *widget = dt_gui_get_widget(gesture); + const guint button = gtk_gesture_single_get_current_button(gesture); + if(button == GDK_BUTTON_PRIMARY) { sample->locked = !sample->locked; gtk_widget_queue_draw(widget); } - else if(dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) + else if(button == GDK_BUTTON_SECONDARY) { // copy to active picker dt_lib_module_t *self = darktable.lib->proxy.colorpicker.module; @@ -592,7 +590,6 @@ static gboolean _live_sample_button(GtkWidget *widget, } dt_control_queue_redraw_center(); } - return FALSE; } static void _add_sample(GtkButton *widget, @@ -616,22 +613,16 @@ static void _add_sample(GtkButton *widget, sample->copied = FALSE; sample->container = gtk_event_box_new(); - gtk_widget_add_events(sample->container, - GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); - g_signal_connect(G_OBJECT(sample->container), "enter-notify-event", - G_CALLBACK(_sample_enter_callback), sample); - g_signal_connect(G_OBJECT(sample->container), "leave-notify-event", - G_CALLBACK(_sample_leave_callback), sample); + dt_gui_connect_motion(sample->container, NULL, _sample_enter_callback, _sample_leave_callback, sample); sample->color_patch = gtk_drawing_area_new(); - gtk_widget_add_events(sample->color_patch, GDK_BUTTON_PRESS_MASK); gtk_widget_set_tooltip_text (sample->color_patch, _("hover to highlight sample on canvas,\n" "click to lock sample,\n" "right-click to load sample area into active color picker")); - g_signal_connect(G_OBJECT(sample->color_patch), "button-press-event", - G_CALLBACK(_live_sample_button), sample); + gtk_widget_add_events(sample->color_patch, GDK_BUTTON_PRESS_MASK); + dt_gui_connect_click_all(sample->color_patch, _live_sample_button_cb, NULL, sample); g_signal_connect(G_OBJECT(sample->color_patch), "draw", G_CALLBACK(_sample_draw_callback), sample); @@ -734,18 +725,13 @@ void gui_init(dt_lib_module_t *self) GtkWidget *color_patch = gtk_drawing_area_new(); data->large_color_patch = color_patch; gtk_widget_set_tooltip_text(color_patch, _("click to (un)hide large color patch")); - gtk_widget_set_events(color_patch, - GDK_BUTTON_PRESS_MASK - | GDK_ENTER_NOTIFY_MASK - | GDK_LEAVE_NOTIFY_MASK); g_signal_connect(G_OBJECT(color_patch), "draw", G_CALLBACK(_sample_draw_callback), &data->primary_sample); - g_signal_connect(G_OBJECT(color_patch), "button-press-event", - G_CALLBACK(_large_patch_toggle), data); - g_signal_connect(G_OBJECT(color_patch), "enter-notify-event", - G_CALLBACK(_sample_enter_callback), &data->primary_sample); - g_signal_connect(G_OBJECT(color_patch), "leave-notify-event", - G_CALLBACK(_sample_leave_callback), &data->primary_sample); + gtk_widget_set_events(color_patch, + GDK_BUTTON_PRESS_MASK + | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); + dt_gui_connect_click(color_patch, _large_patch_toggle_cb, NULL, data); + dt_gui_connect_motion(color_patch, NULL, _sample_enter_callback, _sample_leave_callback, &data->primary_sample); GtkWidget *color_patch_wrapper = dt_gui_hbox(dt_gui_expand(color_patch)); gtk_widget_set_name(GTK_WIDGET(color_patch_wrapper), "color-picker-area"); gtk_widget_show(color_patch); @@ -786,17 +772,12 @@ void gui_init(dt_lib_module_t *self) // The small sample, label and add button GtkWidget *sample_row_events = gtk_event_box_new(); - gtk_widget_add_events(sample_row_events, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); - g_signal_connect(G_OBJECT(sample_row_events), "enter-notify-event", - G_CALLBACK(_sample_enter_callback), &data->primary_sample); - g_signal_connect(G_OBJECT(sample_row_events), "leave-notify-event", - G_CALLBACK(_sample_leave_callback), &data->primary_sample); + dt_gui_connect_motion(sample_row_events, NULL, _sample_enter_callback, _sample_leave_callback, &data->primary_sample); data->primary_sample.color_patch = color_patch = gtk_drawing_area_new(); gtk_widget_set_tooltip_text(color_patch, _("click to (un)hide large color patch")); gtk_widget_set_events(color_patch, GDK_BUTTON_PRESS_MASK); - g_signal_connect(G_OBJECT(color_patch), "button-press-event", - G_CALLBACK(_large_patch_toggle), data); + dt_gui_connect_click(color_patch, _large_patch_toggle_cb, NULL, data); g_signal_connect(G_OBJECT(color_patch), "draw", G_CALLBACK(_sample_draw_callback), &data->primary_sample); diff --git a/src/libs/duplicate.c b/src/libs/duplicate.c index a0b054e5d3e0..18ca22530f12 100644 --- a/src/libs/duplicate.c +++ b/src/libs/duplicate.c @@ -176,40 +176,42 @@ static void _lib_duplicate_delete(GtkButton *button, dt_lib_module_t *self) g_list_prepend(NULL, GINT_TO_POINTER(imgid))); } -static gboolean _lib_duplicate_thumb_press_callback(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _lib_duplicate_thumb_press_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { dt_lib_duplicate_t *d = self->data; + GtkWidget *widget = dt_gui_get_widget(gesture); dt_thumbnail_t *thumb = (dt_thumbnail_t *)g_object_get_data(G_OBJECT(widget), "thumb"); const dt_imgid_t imgid = thumb->imgid; - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) { - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS) + if(n_press == 1) { d->imgid = imgid; dt_control_queue_redraw_center(); } - else if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + else if(n_press >= 2) { // let's switch to the new image DT_CONTROL_SIGNAL_RAISE(DT_SIGNAL_VIEWMANAGER_THUMBTABLE_ACTIVATE, imgid); } } - return TRUE; } -static gboolean _lib_duplicate_thumb_release_callback(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _lib_duplicate_thumb_release_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { dt_lib_duplicate_t *d = self->data; d->imgid = NO_IMGID; dt_control_queue_redraw_center(); - - return TRUE; } void view_leave(struct dt_lib_module_t *self, @@ -336,10 +338,8 @@ static void _lib_duplicate_init_callback(gpointer instance, dt_lib_module_t *sel if(imgid != dev->image_storage.id) { - g_signal_connect(G_OBJECT(thumb->w_main), "button-press-event", - G_CALLBACK(_lib_duplicate_thumb_press_callback), self); - g_signal_connect(G_OBJECT(thumb->w_main), "button-release-event", - G_CALLBACK(_lib_duplicate_thumb_release_callback), self); + dt_gui_connect_click(thumb->w_main, _lib_duplicate_thumb_press_callback, + _lib_duplicate_thumb_release_callback, self); } gchar chl[256]; diff --git a/src/libs/export.c b/src/libs/export.c index 3e4558e52242..6b3772a92485 100644 --- a/src/libs/export.c +++ b/src/libs/export.c @@ -567,11 +567,12 @@ static void _scale_changed(GtkEntry *spin, static void _width_changed(GtkEditable *entry, gpointer user_data); static void _height_changed(GtkEditable *entry, gpointer user_data); -static gboolean _scale_mdlclick(GtkEntry *spin, - GdkEventButton *event, - dt_lib_export_t *d) +static void _scale_mdlclick_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_export_t *d) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_MIDDLE) + GtkWidget *spin = dt_gui_get_widget(gesture); + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_MIDDLE) { dt_conf_set_string(CONFIG_PREFIX "resizing_factor", "1"); g_signal_handlers_block_by_func(spin, _scale_changed, d); @@ -580,46 +581,44 @@ static gboolean _scale_mdlclick(GtkEntry *spin, } else { - _scale_changed(spin, d); + _scale_changed(GTK_ENTRY(spin), d); } - return FALSE; } -static gboolean _widht_mdlclick(GtkEntry *spin, - GdkEventButton *event, - gpointer user_data) +static void _widht_mdlclick_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_export_t *d) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_MIDDLE) + GtkWidget *spin = dt_gui_get_widget(gesture); + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_MIDDLE) { dt_conf_set_int(CONFIG_PREFIX "width", 0); - g_signal_handlers_block_by_func(spin, _width_changed, user_data); + g_signal_handlers_block_by_func(spin, _width_changed, d); gtk_entry_set_text(GTK_ENTRY(spin), "0"); - g_signal_handlers_unblock_by_func(spin, _width_changed, user_data); + g_signal_handlers_unblock_by_func(spin, _width_changed, d); } else { - _width_changed(GTK_EDITABLE(spin), user_data); + _width_changed(GTK_EDITABLE(spin), d); } - - return FALSE; } -static gboolean _height_mdlclick(GtkEntry *spin, - GdkEventButton *event, - gpointer user_data) +static void _height_mdlclick_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_export_t *d) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_MIDDLE) + GtkWidget *spin = dt_gui_get_widget(gesture); + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_MIDDLE) { dt_conf_set_int(CONFIG_PREFIX "height", 0); - g_signal_handlers_block_by_func(spin, _height_changed, user_data); + g_signal_handlers_block_by_func(spin, _height_changed, d); gtk_entry_set_text(GTK_ENTRY(spin), "0"); - g_signal_handlers_unblock_by_func(spin, _height_changed, user_data); + g_signal_handlers_unblock_by_func(spin, _height_changed, d); } else { - _height_changed(GTK_EDITABLE(spin), user_data); + _height_changed(GTK_EDITABLE(spin), d); } - return FALSE; } static void _size_in_px_update(dt_lib_export_t *d) @@ -1737,22 +1736,11 @@ void gui_init(dt_lib_module_t *self) dt_gui_box_add(d->cs.container, view, d->batch_export_button); - gtk_widget_add_events(d->width, GDK_BUTTON_PRESS_MASK); - gtk_widget_add_events(d->height, GDK_BUTTON_PRESS_MASK); - gtk_widget_add_events(d->print_width, GDK_BUTTON_PRESS_MASK); - gtk_widget_add_events(d->print_height, GDK_BUTTON_PRESS_MASK); - gtk_widget_add_events(d->scale, GDK_BUTTON_PRESS_MASK); - - g_signal_connect(G_OBJECT(d->width), "button-press-event", - G_CALLBACK(_widht_mdlclick), (gpointer)d); - g_signal_connect(G_OBJECT(d->height), "button-press-event", - G_CALLBACK(_height_mdlclick), (gpointer)d); - g_signal_connect(G_OBJECT(d->print_width), "button-press-event", - G_CALLBACK(_widht_mdlclick), (gpointer)d); - g_signal_connect(G_OBJECT(d->print_height), "button-press-event", - G_CALLBACK(_height_mdlclick), (gpointer)d); - g_signal_connect(G_OBJECT(d->scale), "button-press-event", - G_CALLBACK(_scale_mdlclick), (gpointer)d); + dt_gui_connect_click_all(d->width, _widht_mdlclick_cb, NULL, d); + dt_gui_connect_click_all(d->height, _height_mdlclick_cb, NULL, d); + dt_gui_connect_click_all(d->print_width, _widht_mdlclick_cb, NULL, d); + dt_gui_connect_click_all(d->print_height, _height_mdlclick_cb, NULL, d); + dt_gui_connect_click_all(d->scale, _scale_mdlclick_cb, NULL, d); // this takes care of keeping hidden widgets hidden gtk_widget_show_all(self->widget); diff --git a/src/libs/filtering.c b/src/libs/filtering.c index 978a4d869d08..d3b417ef279d 100644 --- a/src/libs/filtering.c +++ b/src/libs/filtering.c @@ -1033,27 +1033,24 @@ static void _topbar_reset(dt_lib_module_t *self) dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_UNDEF, NULL); } -static gboolean _topbar_reset_press(GtkWidget *w, - GdkEventButton *e, - dt_lib_module_t *self) +static void _topbar_reset_press_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { //reset the filters _topbar_reset(self); //close the popup dt_lib_filtering_t *d = self->data; gtk_widget_destroy(d->topbar_popup); - - return FALSE; } -static gboolean _topbar_label_press(GtkWidget *w, - GdkEventButton *e, - dt_lib_module_t *self) +static void _topbar_label_press_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { //reset on double-click - if(e->button == GDK_BUTTON_PRIMARY && e->type == GDK_2BUTTON_PRESS) + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY && n_press >= 2) _topbar_reset(self); - return FALSE; } static void _topbar_update(dt_lib_module_t *self) @@ -1088,7 +1085,7 @@ static void _topbar_update(dt_lib_module_t *self) GtkWidget *evtb = gtk_event_box_new(); GtkWidget *label = gtk_label_new(C_("quickfilter", "filter")); gtk_container_add(GTK_CONTAINER(evtb), label); - g_signal_connect(G_OBJECT(evtb), "button-press-event", G_CALLBACK(_topbar_label_press), self); + dt_gui_connect_click_all(evtb, _topbar_label_press_cb, NULL, self); gtk_box_pack_start(GTK_BOX(fbox), evtb, TRUE, TRUE, 0); gtk_widget_show_all(evtb); } @@ -1163,56 +1160,57 @@ static void _event_rule_disable(GtkWidget *widget, dt_lib_filtering_rule_t *rule _widget_header_update(rule); } -static gboolean _event_rule_close(GtkWidget *widget, GdkEventButton *event, dt_lib_module_t *self) +static void _rule_remove_config_entries(dt_lib_module_t *self, dt_lib_filtering_rule_t *rule) { - dt_lib_filtering_rule_t *rule = (dt_lib_filtering_rule_t *)g_object_get_data(G_OBJECT(widget), "rule"); - if(rule->manual_widget_set) return TRUE; + dt_lib_filtering_t *d = rule->lib; + if(d->nb_rules <= 0) return; + d->nb_rules--; + dt_conf_set_int("plugins/lighttable/filtering/num_rules", d->nb_rules); - if(!rule->topbar) + // move up all still active rules by one. + for(int i = rule->num; i < DT_COLLECTION_MAX_RULES - 1; i++) { - // decrease the nb of active rules - dt_lib_filtering_t *d = rule->lib; - if(d->nb_rules <= 0) return FALSE; - d->nb_rules--; - dt_conf_set_int("plugins/lighttable/filtering/num_rules", d->nb_rules); - - // move up all still active rules by one. - for(int i = rule->num; i < DT_COLLECTION_MAX_RULES - 1; i++) + char confname[200] = { 0 }; + snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/mode%1d", i + 1); + const int mode = dt_conf_get_int(confname); + snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/item%1d", i + 1); + const int item = dt_conf_get_int(confname); + snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/off%1d", i + 1); + const int off = dt_conf_get_int(confname); + snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/top%1d", i + 1); + const int top = dt_conf_get_int(confname); + snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/string%1d", i + 1); + gchar *string = dt_conf_get_string(confname); + if(string) { - char confname[200] = { 0 }; - snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/mode%1d", i + 1); - const int mode = dt_conf_get_int(confname); - snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/item%1d", i + 1); - const int item = dt_conf_get_int(confname); - snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/off%1d", i + 1); - const int off = dt_conf_get_int(confname); - snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/top%1d", i + 1); - const int top = dt_conf_get_int(confname); - snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/string%1d", i + 1); - gchar *string = dt_conf_get_string(confname); - if(string) - { - snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/mode%1d", i); - dt_conf_set_int(confname, mode); - snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/item%1d", i); - dt_conf_set_int(confname, item); - snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/off%1d", i); - dt_conf_set_int(confname, off); - snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/top%1d", i); - dt_conf_set_int(confname, top); - snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/string%1d", i); - dt_conf_set_string(confname, string); - g_free(string); - } + snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/mode%1d", i); + dt_conf_set_int(confname, mode); + snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/item%1d", i); + dt_conf_set_int(confname, item); + snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/off%1d", i); + dt_conf_set_int(confname, off); + snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/top%1d", i); + dt_conf_set_int(confname, top); + snprintf(confname, sizeof(confname), "plugins/lighttable/filtering/string%1d", i); + dt_conf_set_string(confname, string); + g_free(string); } - - _filters_gui_update(self); - dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, rule->prop, NULL); } - else - return FALSE; - return TRUE; + _filters_gui_update(self); + dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, rule->prop, NULL); +} + +static void _event_rule_close_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) +{ + GtkWidget *widget = dt_gui_get_widget(gesture); + dt_lib_filtering_rule_t *rule = (dt_lib_filtering_rule_t *)g_object_get_data(G_OBJECT(widget), "rule"); + if(rule->manual_widget_set) return; + + if(!rule->topbar) + _rule_remove_config_entries(self, rule); } static gboolean _rule_available_for_topbar(const dt_collection_properties_t prop) @@ -1308,7 +1306,7 @@ static gboolean _widget_init(dt_lib_filtering_rule_t *rule, const dt_collection_ // remove button rule->w_close = dtgtk_button_new(dtgtk_cairo_paint_remove, 0, NULL); g_object_set_data(G_OBJECT(rule->w_close), "rule", rule); - g_signal_connect(G_OBJECT(rule->w_close), "button-press-event", G_CALLBACK(_event_rule_close), self); + dt_gui_connect_click_all(rule->w_close, _event_rule_close_cb, NULL, self); gtk_box_pack_end(GTK_BOX(rule->w_btn_box), rule->w_close, FALSE, FALSE, 0); } @@ -1665,10 +1663,13 @@ static void _topbar_populate_rules_combo(GtkWidget *w, dt_lib_filtering_t *d) #undef ADD_COLLECT_ENTRY } -static gboolean _topbar_rule_remove(GtkWidget *widget, GdkEventButton *event, dt_lib_module_t *self) +static void _topbar_rule_remove_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(gesture); dt_lib_filtering_rule_t *rule = (dt_lib_filtering_rule_t *)g_object_get_data(G_OBJECT(widget), "rule"); - if(rule->manual_widget_set) return TRUE; + if(rule->manual_widget_set) return; dt_lib_filtering_t *d = self->data; // unpin the rule @@ -1676,19 +1677,19 @@ static gboolean _topbar_rule_remove(GtkWidget *widget, GdkEventButton *event, dt _topbar_update(self); // remove the rule - _event_rule_close(widget, NULL, self); + _rule_remove_config_entries(self, rule); // reconstruct the combobox GtkWidget *hb = gtk_widget_get_parent(widget); - GList *childs = gtk_container_get_children(GTK_CONTAINER(gtk_widget_get_parent(hb))); + GtkWidget *popover_vbox = gtk_widget_get_parent(hb); + GList *childs = gtk_container_get_children(GTK_CONTAINER(popover_vbox)); GtkWidget *combo = g_list_last(childs)->data; + g_list_free(childs); dt_bauhaus_combobox_clear(combo); _topbar_populate_rules_combo(combo, d); // remove the entry from the popover - gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(hb)), hb); - - return TRUE; + gtk_container_remove(GTK_CONTAINER(popover_vbox), hb); } static GtkWidget *_topbar_menu_new_rule(dt_lib_filtering_rule_t *rule, dt_lib_module_t *self) @@ -1698,7 +1699,7 @@ static GtkWidget *_topbar_menu_new_rule(dt_lib_filtering_rule_t *rule, dt_lib_mo gtk_box_pack_start(GTK_BOX(hb), lb, TRUE, TRUE, 0); GtkWidget *btr = dtgtk_button_new(dtgtk_cairo_paint_remove, 0, NULL); g_object_set_data(G_OBJECT(btr), "rule", rule); - g_signal_connect(G_OBJECT(btr), "button-press-event", G_CALLBACK(_topbar_rule_remove), self); + dt_gui_connect_click_all(btr, _topbar_rule_remove_cb, NULL, self); gtk_box_pack_start(GTK_BOX(hb), btr, FALSE, TRUE, 0); return hb; } @@ -1774,7 +1775,7 @@ static void _topbar_show_pref_menu(dt_lib_module_t *self, GtkWidget *bt) gtk_box_pack_start(GTK_BOX(vbox), lb, TRUE, TRUE, 0); GtkWidget *btr = gtk_button_new_with_label(_("reset quickfilters")); dt_gui_add_class(btr, "dt_transparent_background"); - g_signal_connect(G_OBJECT(btr), "button-press-event", G_CALLBACK(_topbar_reset_press), self); + dt_gui_connect_click_all(btr, _topbar_reset_press_cb, NULL, self); gtk_box_pack_start(GTK_BOX(vbox), btr, TRUE, TRUE, 0); // show the popover @@ -1891,14 +1892,17 @@ static void _proxy_reset_filter(dt_lib_module_t *self, gboolean smart_filter) } } -static gboolean _sort_close(GtkWidget *widget, GdkEventButton *event, dt_lib_module_t *self) +static void _sort_close_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(gesture); _widgets_sort_t *sort = (_widgets_sort_t *)g_object_get_data(G_OBJECT(widget), "sort"); - if(sort->lib->manual_sort_set) return TRUE; + if(sort->lib->manual_sort_set) return; // decrease the nb of active rules dt_lib_filtering_t *d = sort->lib; - if(d->nb_sort <= 1) return FALSE; + if(d->nb_sort <= 1) return; d->nb_sort--; dt_conf_set_int("plugins/lighttable/filtering/num_sort", d->nb_sort); @@ -1920,8 +1924,6 @@ static gboolean _sort_close(GtkWidget *widget, GdkEventButton *event, dt_lib_mod _history_save(d, TRUE); _sort_gui_update(self); dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_SORT, NULL); - - return TRUE; } static gboolean _sort_init(_widgets_sort_t *sort, const dt_collection_sort_t sortid, const int sortorder, @@ -1974,7 +1976,7 @@ static gboolean _sort_init(_widgets_sort_t *sort, const dt_collection_sort_t sor gtk_widget_set_no_show_all(sort->close, TRUE); g_object_set_data(G_OBJECT(sort->close), "sort", sort); gtk_widget_set_tooltip_text(sort->close, _("remove this sort order")); - g_signal_connect(G_OBJECT(sort->close), "button-press-event", G_CALLBACK(_sort_close), self); + dt_gui_connect_click_all(sort->close, _sort_close_cb, NULL, self); gtk_box_pack_start(GTK_BOX(sort->box), sort->close, FALSE, FALSE, 0); } diff --git a/src/libs/filters/colors.c b/src/libs/filters/colors.c index b497d068befe..c10398b360a0 100644 --- a/src/libs/filters/colors.c +++ b/src/libs/filters/colors.c @@ -1,6 +1,6 @@ /* This file is part of darktable, - Copyright (C) 2022 darktable developers. + Copyright (C) 2022-2026 darktable developers. darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -52,7 +52,8 @@ static void _set_mask(dt_lib_filtering_rule_t *rule , const int mask, const gboo g_free(txt); } -static gboolean _colors_clicked(GtkWidget *w, GdkEventButton *e, _widgets_colors_t *colors) +// kept for direct callers from accelerators +static gboolean _colors_clicked_old(GtkWidget *w, GdkEventButton *e, _widgets_colors_t *colors) { // double click reset the widget if(e->button == GDK_BUTTON_PRIMARY && e->type == GDK_2BUTTON_PRESS) @@ -102,6 +103,58 @@ static gboolean _colors_clicked(GtkWidget *w, GdkEventButton *e, _widgets_colors return FALSE; } +static void _colors_clicked_gesture(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, _widgets_colors_t *colors) +{ + GtkWidget *w = dt_gui_get_widget(gesture); + + // double click reset the widget + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY && n_press >= 2) + { + _set_mask(colors->rule, CL_AND_MASK, TRUE); + _colors_update(colors->rule); + return; + } + + dt_lib_filtering_rule_t *rule = colors->rule; + const int mask = _get_mask(rule->raw_text); + const int k = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w), "colors_index")); + const GdkModifierType state = dt_key_modifier_state(); + const int mask_k = (1 << k) | (1 << (k + 12)); + int new_mask = mask_k; + if(k == DT_COLORLABELS_LAST) + { + if(mask & mask_k) + new_mask = 0; + else if(state & GDK_CONTROL_MASK) + new_mask = CL_ALL_EXCLUDED | CL_GREY_EXCLUDED; + else + new_mask = CL_ALL_INCLUDED | CL_GREY_INCLUDED; + new_mask |= (mask & CL_AND_MASK); + } + else + { + if(mask & mask_k) + new_mask = 0; + else if(state & GDK_CONTROL_MASK) + new_mask = 1 << (k + 12); + else + new_mask = 1 << k; + new_mask |= (mask & ~mask_k); + } + + if((new_mask & CL_ALL_EXCLUDED) == CL_ALL_EXCLUDED) + new_mask |= CL_GREY_EXCLUDED; + else + new_mask &= ~CL_GREY_EXCLUDED; + if((new_mask & CL_ALL_INCLUDED) == CL_ALL_INCLUDED) + new_mask |= CL_GREY_INCLUDED; + else + new_mask &= ~CL_GREY_INCLUDED; + + _set_mask(colors->rule, new_mask, TRUE); + _colors_update(rule); +} + static void _colors_operator_clicked(GtkWidget *w, _widgets_colors_t *colors) { dt_lib_filtering_rule_t *rule = colors->rule; @@ -212,10 +265,11 @@ static gboolean _colors_update(dt_lib_filtering_rule_t *rule) return TRUE; } -static gboolean _colors_enter_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data) +static void _colors_enter_cb(GtkEventControllerMotion *controller, + double x, double y, + gpointer user_data) { darktable.control->element = GPOINTER_TO_INT(user_data) + 1; - return FALSE; } static float _action_process_colors(gpointer target, dt_action_element_t element, dt_action_effect_t effect, float move_size) @@ -236,7 +290,7 @@ static float _action_process_colors(gpointer target, dt_action_element_t element && (mask || effect != DT_ACTION_EFFECT_OFF)) { if(element) - _colors_clicked(w, &e, colors); + _colors_clicked_old(w, &e, colors); else _colors_operator_clicked(w, colors); } @@ -295,9 +349,8 @@ static void _colors_widget_init(dt_lib_filtering_rule_t *rule, const dt_collecti "\nclick to toggle the color label selection" "\nctrl+click to exclude the color label" "\nthe gray button affects all color labels")); - g_signal_connect(G_OBJECT(colors->colors[k]), "button-press-event", G_CALLBACK(_colors_clicked), colors); - g_signal_connect(G_OBJECT(colors->colors[k]), "enter-notify-event", G_CALLBACK(_colors_enter_notify), - GINT_TO_POINTER(k)); + dt_gui_connect_click(colors->colors[k], _colors_clicked_gesture, NULL, colors); + dt_gui_connect_motion(colors->colors[k], NULL, _colors_enter_cb, NULL, GINT_TO_POINTER(k)); dt_action_define(DT_ACTION(self), N_("rules"), N_("color label"), colors->colors[k], &dt_action_def_colors_rule); } colors->operator= dtgtk_button_new(dtgtk_cairo_paint_intersection, 0, NULL); @@ -307,8 +360,7 @@ static void _colors_widget_init(dt_lib_filtering_rule_t *rule, const dt_collecti "\nintersection: images having all selected color labels" "\nunion: images with at least one of the selected color labels")); g_signal_connect(G_OBJECT(colors->operator), "clicked", G_CALLBACK(_colors_operator_clicked), colors); - g_signal_connect(G_OBJECT(colors->operator), "enter-notify-event", G_CALLBACK(_colors_enter_notify), - GINT_TO_POINTER(-1)); + dt_gui_connect_motion(colors->operator, NULL, _colors_enter_cb, NULL, GINT_TO_POINTER(-1)); dt_action_t *ac = dt_action_define(DT_ACTION(self), N_("rules"), N_("color label"), colors->operator, &dt_action_def_colors_rule); dt_shortcut_register(ac, DT_COLORLABELS_RED + 1, DT_ACTION_EFFECT_TOGGLE, GDK_KEY_F1, GDK_SHIFT_MASK); diff --git a/src/libs/filters/filename.c b/src/libs/filters/filename.c index 95a91cbae1e0..bbbaa83a2cd6 100644 --- a/src/libs/filters/filename.c +++ b/src/libs/filters/filename.c @@ -1,6 +1,6 @@ /* This file is part of darktable, - Copyright (C) 2022 darktable developers. + Copyright (C) 2022-2026 darktable developers. darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -235,9 +235,11 @@ static void _filename_update_selection(_widgets_filename_t *filename) filename->internal_change--; } -static gboolean _filename_press(GtkWidget *w, GdkEventButton *e, _widgets_filename_t *filename) +static void _filename_press(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, _widgets_filename_t *filename) { - if(e->button == GDK_BUTTON_SECONDARY) + GtkWidget *w = dt_gui_get_widget(gesture); + + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY) { _filename_tree_update_visibility(w, filename); gtk_popover_set_default_widget(GTK_POPOVER(filename->pop), w); @@ -247,14 +249,13 @@ static gboolean _filename_press(GtkWidget *w, GdkEventButton *e, _widgets_filena _filename_update_selection(filename); gtk_widget_show_all(filename->pop); - return TRUE; } - else if(e->button == GDK_BUTTON_PRIMARY && e->type == GDK_2BUTTON_PRESS) + else if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY + && n_press >= 2) { gtk_entry_set_text(GTK_ENTRY(w), ""); _filename_changed(w, filename); } - return FALSE; } static gboolean _filename_update(dt_lib_filtering_rule_t *rule) @@ -371,7 +372,7 @@ static void _filename_widget_init(dt_lib_filtering_rule_t *rule, const dt_collec gtk_box_pack_start(GTK_BOX(hb), filename->name, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(filename->name), "activate", G_CALLBACK(_filename_changed), filename); g_signal_connect(G_OBJECT(filename->name), "focus-out-event", G_CALLBACK(_filename_focus_out), filename); - g_signal_connect(G_OBJECT(filename->name), "button-press-event", G_CALLBACK(_filename_press), filename); + dt_gui_connect_click(filename->name, _filename_press, NULL, filename); filename->ext = dt_ui_entry_new(top ? 5 : 0); gtk_widget_set_can_default(filename->ext, TRUE); @@ -383,7 +384,7 @@ static void _filename_widget_init(dt_lib_filtering_rule_t *rule, const dt_collec gtk_box_pack_start(GTK_BOX(hb), filename->ext, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(filename->ext), "activate", G_CALLBACK(_filename_changed), filename); g_signal_connect(G_OBJECT(filename->ext), "focus-out-event", G_CALLBACK(_filename_focus_out), filename); - g_signal_connect(G_OBJECT(filename->ext), "button-press-event", G_CALLBACK(_filename_press), filename); + dt_gui_connect_click(filename->ext, _filename_press, NULL, filename); if(top) { dt_gui_add_class(hb, "dt_quick_filter"); diff --git a/src/libs/filters/misc.c b/src/libs/filters/misc.c index 2b6172bc932d..db767ead1b62 100644 --- a/src/libs/filters/misc.c +++ b/src/libs/filters/misc.c @@ -1,6 +1,6 @@ /* This file is part of darktable, - Copyright (C) 2024 darktable developers. + Copyright (C) 2024-2026 darktable developers. darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -261,11 +261,15 @@ static void _misc_update_selection(_widgets_misc_t *misc) misc->internal_change--; } -static gboolean _misc_press(GtkWidget *w, - GdkEventButton *e, - _widgets_misc_t *misc) +static void _misc_press(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + _widgets_misc_t *misc) { - if(e->button == GDK_BUTTON_SECONDARY) + GtkWidget *w = dt_gui_get_widget(gesture); + + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY) { _misc_tree_update_visibility(w, misc); gtk_popover_set_default_widget(GTK_POPOVER(misc->pop), w); @@ -275,14 +279,13 @@ static gboolean _misc_press(GtkWidget *w, _misc_update_selection(misc); gtk_widget_show_all(misc->pop); - return TRUE; } - else if(e->button == GDK_BUTTON_PRIMARY && e->type == GDK_2BUTTON_PRESS) + else if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY + && n_press >= 2) { gtk_entry_set_text(GTK_ENTRY(misc->name), ""); _misc_changed(w, misc); } - return FALSE; } static gboolean _misc_update(dt_lib_filtering_rule_t *rule) @@ -459,7 +462,7 @@ static void _misc_widget_init(dt_lib_filtering_rule_t *rule, gtk_box_pack_start(GTK_BOX(hb), misc->name, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(misc->name), "activate", G_CALLBACK(_misc_changed), misc); g_signal_connect(G_OBJECT(misc->name), "focus-out-event", G_CALLBACK(_misc_focus_out), misc); - g_signal_connect(G_OBJECT(misc->name), "button-press-event", G_CALLBACK(_misc_press), misc); + dt_gui_connect_click(misc->name, _misc_press, NULL, misc); if(top) { diff --git a/src/libs/geotagging.c b/src/libs/geotagging.c index e148be82e1cd..4e50a22599b7 100644 --- a/src/libs/geotagging.c +++ b/src/libs/geotagging.c @@ -563,13 +563,16 @@ static void _refresh_display_all_tracks(GtkWidget *widget, dt_lib_module_t *self _refresh_displayed_images(self); } -static gboolean _click_for_entire_track(GtkEntry *spin, GdkEventButton *event, dt_lib_module_t *self) +static void _click_for_entire_track_pressed(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_lib_module_t *self) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY && dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(n_press == 2) { _refresh_display_all_tracks(NULL, self); } - return FALSE; } static void _track_seg_toggled(GtkCellRendererToggle *cell_renderer, gchar *path_str, dt_lib_module_t *self) @@ -1447,7 +1450,7 @@ static void _datetime_scroll_over(GtkEventControllerScroll *controller, dt_lib_geotagging_t *d = self->data; if(!d->editing) { - GtkWidget* w = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(controller)); + GtkWidget* w = dt_gui_get_widget(controller); int i = 0; for(i = 0; i < DT_GEOTAG_PARTS_NB; i++) if(w == d->dt.widget[i]) break; @@ -1565,10 +1568,14 @@ static GtkWidget *_gui_init_datetime(gchar *text, return flow; } -static gboolean _datetime_key_pressed(GtkWidget *entry, GdkEventKey *event, dt_lib_module_t *self) +static gboolean _datetime_key_pressed(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_lib_module_t *self) { dt_lib_geotagging_t *d = self->data; - switch(dt_gdk_event_get_keyval(event)) + switch(keyval) { case GDK_KEY_Escape: { @@ -1624,9 +1631,8 @@ static gboolean _datetime_key_pressed(GtkWidget *entry, GdkEventKey *event, dt_l g_signal_emit_by_name(d->dt.widget[0], "changed"); return FALSE; - default: // let shortcut system deal with everything else - g_signal_stop_emission_by_name(entry, "key-press-event"); - return FALSE; + default: + return TRUE; } } @@ -1655,9 +1661,13 @@ static void _timezone_save(dt_lib_module_t *self) #endif } -static gboolean _timezone_key_pressed(GtkWidget *entry, GdkEventKey *event, dt_lib_module_t *self) +static gboolean _timezone_key_pressed(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_lib_module_t *self) { - switch(dt_gdk_event_get_keyval(event)) + switch(keyval) { case GDK_KEY_Return: case GDK_KEY_KP_Enter: @@ -1861,7 +1871,7 @@ void gui_init(dt_lib_module_t *self) gtk_entry_completion_set_match_func(completion, _completion_match_func, NULL, NULL); gtk_entry_completion_set_minimum_key_length(completion, 0); gtk_entry_set_completion(GTK_ENTRY(d->timezone), completion); - g_signal_connect(G_OBJECT(d->timezone), "key-press-event", G_CALLBACK(_timezone_key_pressed), self); + dt_gui_connect_key(d->timezone, _timezone_key_pressed, self); g_signal_connect(G_OBJECT(d->timezone), "focus-out-event", G_CALLBACK(_timezone_focus_out), self); // gpx @@ -1928,7 +1938,7 @@ void gui_init(dt_lib_module_t *self) g_object_set(G_OBJECT(d->map.gpx_view), "has-tooltip", TRUE, NULL); g_signal_connect(G_OBJECT(d->map.gpx_view), "query-tooltip", G_CALLBACK(_row_tooltip_setup), self); - g_signal_connect(G_OBJECT(d->map.gpx_view), "button-press-event", G_CALLBACK(_click_for_entire_track), self); + dt_gui_connect_click(d->map.gpx_view, _click_for_entire_track_pressed, NULL, self); // avoid ugly console pixman messages due to headers gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->map.gpx_view), FALSE); @@ -1998,7 +2008,7 @@ void gui_init(dt_lib_module_t *self) for(int i = 0; i < DT_GEOTAG_PARTS_NB; i++) { g_signal_connect(d->dt.widget[i], "changed", G_CALLBACK(_datetime_entry_changed), self); - g_signal_connect(d->dt.widget[i], "key-press-event", G_CALLBACK(_datetime_key_pressed), self); + dt_gui_connect_key(d->dt.widget[i], _datetime_key_pressed, self); dt_gui_connect_scroll(d->dt.widget[i], GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, diff --git a/src/libs/histogram.c b/src/libs/histogram.c index 9884de9b0e0c..d9b01fbb2a68 100644 --- a/src/libs/histogram.c +++ b/src/libs/histogram.c @@ -484,13 +484,15 @@ static void _eventbox_scroll_callback(GtkEventControllerScroll* self, const gdouble dy, dt_scopes_t *s) { + // GTK4 note: gtk_get_current_event() returns NULL inside event controller + // callbacks in GTK4. For state checks, use + // gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(self), &state) + // instead. For forwarding the event via gtk_widget_event(), use + // gtk_gesture_get_last_event() and gdk_event_copy() if needed. GdkEvent *event = gtk_get_current_event(); if(!event) return; if(gdk_event_get_event_type(event) == GDK_SCROLL) { - // FIXME: so long as we have event, test its flags -- and for GTK 4 we can - // use gtk_get_current_event() and get flags -- make a helper function to do - // this. if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK | GDK_MOD1_MASK)) { @@ -561,7 +563,7 @@ static void _eventbox_motion_notify_callback(GtkEventControllerMotion *controlle // pass-through on otherwise -> exposure dragging reaches scope. // For the split option we test the inner button_box_split, not the full-width // outer_box_split, so dragging still works across the rest of the top. - GtkWidget *eb = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(controller)); + GtkWidget *eb = dt_gui_get_widget(controller); gtk_overlay_set_overlay_pass_through (GTK_OVERLAY(s->overlay), s->button_box_left, !_pointer_over_widget(eb, s->button_box_left, x, y)); diff --git a/src/libs/history.c b/src/libs/history.c index 4f181c05f015..68cc852afed1 100644 --- a/src/libs/history.c +++ b/src/libs/history.c @@ -71,13 +71,17 @@ typedef struct dt_lib_history_t static void _lib_history_compress_clicked_callback(GtkButton *widget, gpointer user_data); -static gboolean _lib_history_compress_pressed_callback(GtkWidget *widget, - GdkEventButton *e, - gpointer user_data); - -static gboolean _lib_history_button_clicked_callback(GtkWidget *widget, - GdkEventButton *e, - dt_lib_module_t *self); +static void _lib_history_compress_pressed_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self); + +static void _lib_history_button_clicked_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self); static void _lib_history_create_style_button_clicked_callback(GtkWidget *widget, gpointer user_data); @@ -140,8 +144,7 @@ void gui_init(dt_lib_module_t *self) (self, N_("compress history stack"), _lib_history_compress_clicked_callback, self, _("create a minimal history stack which produces the same image\n" "ctrl+click to truncate history to the selected item"), 0, 0); - g_signal_connect(G_OBJECT(d->compress_button), "button-press-event", - G_CALLBACK(_lib_history_compress_pressed_callback), self); + dt_gui_connect_click(d->compress_button, _lib_history_compress_pressed_callback, NULL, self); /* add toolbar button for creating style */ d->create_button = dtgtk_button_new(dtgtk_cairo_paint_styles, CPF_NONE, NULL); @@ -236,8 +239,7 @@ static GtkWidget *_lib_history_create_button(dt_lib_module_t *self, if(selected) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE); /* set callback when clicked */ - g_signal_connect(G_OBJECT(widget), "button-press-event", - G_CALLBACK(_lib_history_button_clicked_callback), self); + dt_gui_connect_click(widget, _lib_history_button_clicked_callback, NULL, self); /* associate the history number */ g_object_set_data(G_OBJECT(widget), "history-number", GINT_TO_POINTER(num + 1)); @@ -1241,31 +1243,34 @@ static void _lib_history_compress_clicked_callback(GtkButton *widget, gpointer u _lib_history_truncate(TRUE); } -static gboolean _lib_history_compress_pressed_callback(GtkWidget *widget, - GdkEventButton *e, - gpointer user_data) +static void _lib_history_compress_pressed_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { - const gboolean compress = !dt_modifier_is(e->state, GDK_CONTROL_MASK); + const gboolean compress = !(dt_key_modifier_state() & GDK_CONTROL_MASK); _lib_history_truncate(compress); - - return TRUE; } -static gboolean _lib_history_button_clicked_callback(GtkWidget *widget, - GdkEventButton *e, - dt_lib_module_t *self) +static void _lib_history_button_clicked_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(gesture); const dt_imgid_t imgid = darktable.develop->image_storage.id; - if(!dt_is_valid_imgid(imgid)) return FALSE; + if(!dt_is_valid_imgid(imgid)) return; static gboolean reset = FALSE; - if(reset) return FALSE; + if(reset) return; - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) return FALSE; + if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) return; // shift-click just show the corresponding module in modulegroups - if(dt_modifier_is(e->state, GDK_SHIFT_MASK)) + if(dt_key_modifier_state() & GDK_SHIFT_MASK) { const int num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "history-number")); dt_dev_history_item_t *hist = g_list_nth_data(darktable.develop->history, num - 1); @@ -1274,7 +1279,7 @@ static gboolean _lib_history_button_clicked_callback(GtkWidget *widget, dt_dev_modulegroups_switch(darktable.develop, hist->module); dt_iop_gui_set_expanded(hist->module, TRUE, TRUE); } - return TRUE; + return; } dt_lib_history_t *d = self->data; @@ -1292,7 +1297,7 @@ static gboolean _lib_history_button_clicked_callback(GtkWidget *widget, g_list_free(children); reset = FALSE; - DT_GUARD_GUI_UPDATE(FALSE); + if(dt_atomic_get_int(&darktable.gui->reset) != 0) return; dt_dev_undo_start_record(darktable.develop); @@ -1310,7 +1315,6 @@ static gboolean _lib_history_button_clicked_callback(GtkWidget *widget, dt_iop_connect_accels_all(); dt_dev_modulegroups_set(darktable.develop, dt_dev_modulegroups_get(darktable.develop)); - return FALSE; } static void _lib_history_create_style_button_clicked_callback(GtkWidget *widget, diff --git a/src/libs/import.c b/src/libs/import.c index 4979ff448411..6ee7d4a03ab1 100644 --- a/src/libs/import.c +++ b/src/libs/import.c @@ -15,8 +15,6 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#include "common/gdk_event_utils.h" - #include "common/collection.h" #include "common/darktable.h" #include "common/file_location.h" @@ -558,18 +556,24 @@ static void _thumb_set_in_listview(GtkTreeModel *model, g_free(fullname); } -static gboolean _files_button_press(GtkWidget *view, - GdkEventButton *event, - dt_lib_module_t *self) +static void _files_button_press_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { + GtkWidget *view = dt_gui_get_widget(gesture); dt_lib_import_t *d = self->data; - if((dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY)) + /* gesture coordinates are relative to the widget allocation, while the + * treeview coordinate APIs expect bin-window coordinates */ + gint bin_x, bin_y; + gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(view), + (gint)x, (gint)y, &bin_x, &bin_y); + if(n_press == 1) { GtkTreePath *path = NULL; GtkTreeViewColumn *column = NULL; // Get tree path for row that was clicked if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), - (gint)dt_gdk_event_get_x(event), (gint)dt_gdk_event_get_y(event), + bin_x, bin_y, &path, &column, NULL, NULL)) { if(column == d->from.pixcol) @@ -581,17 +585,17 @@ static gboolean _files_button_press(GtkWidget *view, gtk_tree_model_get(model, &iter, DT_IMPORT_SEL_THUMB, &thumb_sel, -1); _thumb_set_in_listview(model, &iter, !thumb_sel, self); gtk_tree_path_free(path); - return TRUE; + return; } } gtk_tree_path_free(path); } - else if((dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY)) + else if(n_press >= 2) { GtkTreePath *path = NULL; // Get tree path for row that was clicked if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), - (gint)dt_gdk_event_get_x(event), (gint)dt_gdk_event_get_y(event), + bin_x, bin_y, &path, NULL, NULL, NULL)) { GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); @@ -599,10 +603,9 @@ static gboolean _files_button_press(GtkWidget *view, gtk_tree_selection_select_path(selection, path); gtk_dialog_response(GTK_DIALOG(d->from.dialog), GTK_RESPONSE_ACCEPT); gtk_tree_path_free(path); - return TRUE; + return; } } - return FALSE; } static gboolean _thumb_set(dt_lib_module_t *self) @@ -1264,14 +1267,19 @@ static void _remove_selected_place(GtkWidget *widget, dt_lib_module_t *self) _update_files_list(self); } -static gboolean _places_button_press(GtkWidget *view, - GdkEventButton *event, - dt_lib_module_t *self) +static void _places_button_press_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { - gboolean res = FALSE; + GtkWidget *view = dt_gui_get_widget(gesture); GtkTreePath *path = NULL; + /* gesture coordinates are relative to the widget allocation, while the + * treeview coordinate APIs expect bin-window coordinates */ + gint bin_x, bin_y; + gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(view), + (gint)x, (gint)y, &bin_x, &bin_y); if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), - (gint)dt_gdk_event_get_x(event), (gint)dt_gdk_event_get_y(event), &path, NULL, NULL, NULL)) + bin_x, bin_y, &path, NULL, NULL, NULL)) { GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); GtkTreeIter iter; @@ -1280,10 +1288,8 @@ static gboolean _places_button_press(GtkWidget *view, char *folder_name, *folder_path; gtk_tree_model_get(model, &iter, 0, &folder_name, 1, &folder_path, -1); - const int button_pressed = (dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS) ? dt_gdk_event_get_button(event) : 0; - // left-click: set as new root - if(button_pressed == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY) { GtkTreeSelection *place_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); gtk_tree_selection_select_path(place_selection, path); @@ -1296,35 +1302,42 @@ static gboolean _places_button_press(GtkWidget *view, g_free(folder_name); g_free(folder_path); - - res = TRUE; } gtk_tree_path_free(path); - return res; } -static gboolean _folders_button_press(GtkWidget *view, - GdkEventButton *event, - dt_lib_module_t *self) +static void _folders_button_press_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { + GtkWidget *view = dt_gui_get_widget(gesture); dt_lib_import_t *d = self->data; - gboolean res = FALSE; - const int button_pressed = (dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS) ? dt_gdk_event_get_button(event) : 0; - const gboolean modifier = dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK | GDK_CONTROL_MASK); - if((button_pressed == GDK_BUTTON_PRIMARY) && !modifier) + + /* gesture coordinates are relative to the widget allocation, while the + * treeview coordinate APIs expect bin-window coordinates */ + gint bin_x, bin_y; + gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(view), + (gint)x, (gint)y, &bin_x, &bin_y); + + GdkModifierType state; + gtk_get_current_event_state(&state); + + if(n_press == 1 + && gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY + && !dt_modifier_is(state, GDK_SHIFT_MASK | GDK_CONTROL_MASK)) { GtkTreePath *path = NULL; if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), - dt_gdk_event_get_x(event), dt_gdk_event_get_y(event), &path, NULL, NULL, NULL)) + bin_x, bin_y, &path, NULL, NULL, NULL)) { GdkRectangle rect; gtk_tree_view_get_cell_area(GTK_TREE_VIEW(view), path, d->from.foldercol, &rect); const gboolean blank = gtk_tree_view_is_blank_at_pos(GTK_TREE_VIEW(view), - dt_gdk_event_get_x(event), dt_gdk_event_get_y(event), + bin_x, bin_y, NULL, NULL, NULL, NULL); // select and save new folder only if not click on expander - if(blank || (dt_gdk_event_get_x(event) > rect.x)) + if(blank || (bin_x > rect.x)) { GtkTreeSelection *selection = gtk_tree_view_get_selection(d->from.folderview); gtk_tree_selection_select_path(selection, path); @@ -1337,26 +1350,26 @@ static gboolean _folders_button_press(GtkWidget *view, g_free(folder); _update_files_list(self); _show_all_thumbs(self); - res = TRUE; } } gtk_tree_path_free(path); } - - if(dt_gdk_event_get_type(event) == GDK_DOUBLE_BUTTON_PRESS) + else if(n_press >= 2 && gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY) { GtkTreePath *path = NULL; gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), - dt_gdk_event_get_x(event), dt_gdk_event_get_y(event), &path, NULL, NULL, NULL); - if(gtk_tree_view_row_expanded(d->from.folderview, path)) - gtk_tree_view_collapse_row (d->from.folderview, path); - else - gtk_tree_view_expand_row (d->from.folderview, path, FALSE); - gtk_tree_path_free(path); + bin_x, bin_y, &path, NULL, NULL, NULL); + if(path) + { + if(gtk_tree_view_row_expanded(d->from.folderview, path)) + gtk_tree_view_collapse_row(d->from.folderview, path); + else + gtk_tree_view_expand_row(d->from.folderview, path, FALSE); + gtk_tree_path_free(path); + } } g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 100, (GSourceFunc)_clear_parasitic_selection, self, NULL); - return res; } static void _folder_order_clicked(GtkTreeViewColumn *column, @@ -1465,8 +1478,7 @@ static void _set_places_list(GtkWidget *places_paned, gtk_paned_pack1(GTK_PANED(places_paned), places_top_box, TRUE, TRUE); - g_signal_connect(G_OBJECT(d->placesView), "button-press-event", - G_CALLBACK(_places_button_press), self); + dt_gui_connect_click(d->placesView, _places_button_press_cb, NULL, self); } static void _set_folders_list(GtkWidget *places_paned, dt_lib_module_t* self) @@ -1490,8 +1502,7 @@ static void _set_folders_list(GtkWidget *places_paned, dt_lib_module_t* self) gtk_tree_view_column_set_resizable(column, TRUE); gtk_tree_view_set_expander_column(d->from.folderview, column); g_signal_connect(d->from.folderview, "row-expanded", G_CALLBACK(_row_expanded), self); - g_signal_connect(G_OBJECT(d->from.folderview), "button-press-event", - G_CALLBACK(_folders_button_press), self); + dt_gui_connect_click(d->from.folderview, _folders_button_press_cb, NULL, self); gtk_tree_view_column_set_sort_column_id(column, DT_FOLDER_PATH); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), DT_FOLDER_PATH, dt_conf_get_bool("ui_last/import_last_folder_descending") @@ -1901,8 +1912,7 @@ static void _set_files_list(GtkWidget *rbox, dt_lib_module_t* self) gtk_tree_view_column_set_clickable(column, TRUE); gtk_tree_view_column_set_min_width(column, DT_PIXEL_APPLY_DPI(128)); d->from.pixcol = column; - g_signal_connect(G_OBJECT(d->from.treeview), "button-press-event", - G_CALLBACK(_files_button_press), self); + dt_gui_connect_click(d->from.treeview, _files_button_press_cb, NULL, self); GtkTreeSelection *selection = gtk_tree_view_get_selection(d->from.treeview); gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); diff --git a/src/libs/lib.c b/src/libs/lib.c index ac184700f848..a3f15255648a 100644 --- a/src/libs/lib.c +++ b/src/libs/lib.c @@ -964,30 +964,29 @@ void _lib_unload_module(dt_lib_module_t *module) g_module_close(module->module); } -static gboolean _lib_gui_reset_callback(GtkButton *button, - GdkEventButton *e, - gpointer user_data) +static void _lib_gui_reset_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *module) { - dt_lib_module_t *module = (dt_lib_module_t *)user_data; module->gui_reset(module); if(module->has_preset_label(module)) gtk_label_set_text(GTK_LABEL(module->preset_label), ""); - return TRUE; + dt_gui_claim(gesture); } -static gboolean _presets_popup_callback(GtkButton *button, - GdkEventButton *e, - dt_lib_module_t *module) +static void _presets_popup_callback(GtkWidget *button, + dt_lib_module_t *module) { dt_lib_module_info_t *mi = _get_module_info_for_module(module); - _dt_lib_presets_popup_menu_show(mi, GTK_WIDGET(button)); + _dt_lib_presets_popup_menu_show(mi, button); if(button) dtgtk_button_set_active(DTGTK_BUTTON(button), FALSE); - - return TRUE; } + void dt_lib_gui_set_expanded(dt_lib_module_t *module, const gboolean expanded) { if(!module->expander || !module->arrow) return; @@ -1029,24 +1028,27 @@ gboolean dt_lib_gui_get_expanded(dt_lib_module_t *module) return dtgtk_expander_get_expanded(DTGTK_EXPANDER(module->expander)); } -static gboolean _lib_plugin_arrow_button_press(GtkWidget *w, - GdkEventButton *e, - gpointer user_data) +static void _lib_plugin_arrow_button_press_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *module) { - if(e->type == GDK_2BUTTON_PRESS || e->type == GDK_3BUTTON_PRESS) return TRUE; - - dt_lib_module_t *module = (dt_lib_module_t *)user_data; + if(n_press > 1) return; + const guint button = gtk_gesture_single_get_current_button(gesture); + GdkModifierType state; + gtk_get_current_event_state(&state); - if(e->button == GDK_BUTTON_PRIMARY) + if(button == GDK_BUTTON_PRIMARY) { /* bail out if module is static */ - if(!module->expandable(module)) return FALSE; + if(!module->expandable(module)) return; - if(dt_modifier_is(e->state, GDK_SHIFT_MASK | GDK_CONTROL_MASK)) + if(dt_modifier_is(state, GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ; // do nothing (for easier dragging) /* handle shiftclick on expander, hide all except this */ else if(!dt_conf_get_bool("lighttable/ui/single_module") != - !dt_modifier_is(e->state, GDK_SHIFT_MASK)) + !dt_modifier_is(state, GDK_SHIFT_MASK)) { const dt_view_t *v = dt_view_manager_get_current_view(darktable.view_manager); const uint32_t side = dt_lib_get_container(module); @@ -1082,24 +1084,36 @@ static gboolean _lib_plugin_arrow_button_press(GtkWidget *w, //ensure that any gtkentry fields lose focus gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui)); - return TRUE; + return; } - else if(e->button == GDK_BUTTON_SECONDARY) + else if(button == GDK_BUTTON_SECONDARY) { if(gtk_widget_get_sensitive(module->presets_button)) - _presets_popup_callback(NULL, NULL, module); + _presets_popup_callback(NULL, module); - return TRUE; + return; } - return FALSE; } -static gboolean _lib_plugin_header_button_release(GtkWidget *w, - GdkEventButton *e, - gpointer user_data) +static void _lib_plugin_header_button_release_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *module) { - if(GTK_IS_BUTTON(gtk_get_event_widget((GdkEvent*)e))) return FALSE; - return _lib_plugin_arrow_button_press(w, e, user_data); + // ignore clicks on buttons inside the header (presets, reset, enable) + // GTK4: use gtk_gesture_get_last_event(gesture) instead of gtk_get_current_event() + GdkEvent *event = gtk_get_current_event(); + if(event) + { + if(GTK_IS_BUTTON(gtk_get_event_widget(event))) + { + gdk_event_free(event); + return; + } + gdk_event_free(event); + } + _lib_plugin_arrow_button_press_cb(gesture, n_press, x, y, module); } static void show_module_callback(dt_lib_module_t *module) @@ -1140,23 +1154,44 @@ static void show_module_callback(dt_lib_module_t *module) } } -static gboolean _header_enter_notify_callback(GtkWidget *eventbox, - GdkEventCrossing *event, - gpointer user_data) +static void _header_enter_notify_callback(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + gpointer user_data) { darktable.control->element = GPOINTER_TO_INT(user_data); - return FALSE; } -static gboolean _body_enter_leave_callback(GtkWidget *widget, - GdkEventCrossing *e, - gpointer user_data) +static void _body_enter_callback(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_lib_module_t *module) { - // set or clear focused module when entering or leaving (not when opening popup) - if(e->detail != GDK_NOTIFY_INFERIOR && e->mode == GDK_CROSSING_NORMAL) - darktable.lib->gui_module = e->type == GDK_ENTER_NOTIFY ? user_data : NULL; + // set focused module when entering (not when opening popup) + // only act on non-inferior (not from child) normal crossings + GdkEvent *event = gtk_get_current_event(); + if(event) + { + if(event->crossing.detail != GDK_NOTIFY_INFERIOR + && event->crossing.mode == GDK_CROSSING_NORMAL) + darktable.lib->gui_module = module; + gdk_event_free(event); + } +} - return FALSE; +static void _body_leave_callback(GtkEventControllerMotion *controller, + dt_lib_module_t *module) +{ + // clear focused module when leaving + // only act on non-inferior (not to child) normal crossings + GdkEvent *event = gtk_get_current_event(); + if(event) + { + if(event->crossing.detail != GDK_NOTIFY_INFERIOR + && event->crossing.mode == GDK_CROSSING_NORMAL) + darktable.lib->gui_module = NULL; + gdk_event_free(event); + } } static gboolean _on_drag_motion(GtkWidget *widget, @@ -1278,8 +1313,7 @@ GtkWidget *dt_lib_gui_get_expander(dt_lib_module_t *module) // FIXME separately define as darkroom widget shortcut/action, // because not automatically registered via lib if presets btn // has been loaded to be shown outside expander - g_signal_connect(G_OBJECT(module->presets_button), "button-press-event", - G_CALLBACK(_presets_popup_callback), module); + g_signal_connect(G_OBJECT(module->presets_button), "clicked", G_CALLBACK(_presets_popup_callback), module); } module->expander = NULL; return NULL; @@ -1305,18 +1339,11 @@ GtkWidget *dt_lib_gui_get_expander(dt_lib_module_t *module) } /* setup the header box */ - g_signal_connect(G_OBJECT(header_evb), "button-release-event", - G_CALLBACK(_lib_plugin_header_button_release), - module); - g_signal_connect(G_OBJECT(header_evb), "enter-notify-event", - G_CALLBACK(_header_enter_notify_callback), - GINT_TO_POINTER(DT_ACTION_ELEMENT_SHOW)); + dt_gui_connect_click(header_evb, NULL, _lib_plugin_header_button_release_cb, module); + dt_gui_connect_motion(header_evb, NULL, _header_enter_notify_callback, NULL, GINT_TO_POINTER(DT_ACTION_ELEMENT_SHOW)); /* (un)focus module when entering/leaving body */ - g_signal_connect(G_OBJECT(body_evb), "enter-notify-event", - G_CALLBACK(_body_enter_leave_callback), module); - g_signal_connect(G_OBJECT(body_evb), "leave-notify-event", - G_CALLBACK(_body_enter_leave_callback), module); + dt_gui_connect_motion(body_evb, NULL, _body_enter_callback, _body_leave_callback, module); /* * initialize the header widgets @@ -1325,8 +1352,7 @@ GtkWidget *dt_lib_gui_get_expander(dt_lib_module_t *module) module->arrow = dtgtk_button_new(dtgtk_cairo_paint_solid_arrow, 0, NULL); gtk_widget_set_tooltip_text(module->arrow, _("show module")); - g_signal_connect(G_OBJECT(module->arrow), "button-press-event", - G_CALLBACK(_lib_plugin_arrow_button_press), module); + dt_gui_connect_click_all(module->arrow, _lib_plugin_arrow_button_press_cb, NULL, module); dt_action_define(&module->actions, NULL, NULL, module->arrow, NULL); gtk_box_pack_start(GTK_BOX(header), module->arrow, FALSE, FALSE, 0); @@ -1358,11 +1384,8 @@ GtkWidget *dt_lib_gui_get_expander(dt_lib_module_t *module) /* add preset button if module has implementation */ module->presets_button = dtgtk_button_new(dtgtk_cairo_paint_presets, 0, NULL); gtk_widget_set_tooltip_text(module->presets_button, _("presets and preferences")); - g_signal_connect(G_OBJECT(module->presets_button), "button-press-event", - G_CALLBACK(_presets_popup_callback), module); - g_signal_connect(G_OBJECT(module->presets_button), "enter-notify-event", - G_CALLBACK(_header_enter_notify_callback), - GINT_TO_POINTER(DT_ACTION_ELEMENT_PRESETS)); + g_signal_connect(G_OBJECT(module->presets_button), "clicked", G_CALLBACK(_presets_popup_callback), module); + dt_gui_connect_motion(module->presets_button, NULL, _header_enter_notify_callback, NULL, GINT_TO_POINTER(DT_ACTION_ELEMENT_PRESETS)); if(!module->get_params && !module->set_preferences) gtk_widget_set_sensitive(GTK_WIDGET(module->presets_button), FALSE); @@ -1372,11 +1395,8 @@ GtkWidget *dt_lib_gui_get_expander(dt_lib_module_t *module) /* add reset button if module has implementation */ module->reset_button = dtgtk_button_new(dtgtk_cairo_paint_reset, 0, NULL); - g_signal_connect(G_OBJECT(module->reset_button), "button-press-event", - G_CALLBACK(_lib_gui_reset_callback), module); - g_signal_connect(G_OBJECT(module->reset_button), "enter-notify-event", - G_CALLBACK(_header_enter_notify_callback), - GINT_TO_POINTER(DT_ACTION_ELEMENT_RESET)); + dt_gui_connect_click(module->reset_button, _lib_gui_reset_callback, NULL, module); + dt_gui_connect_motion(module->reset_button, NULL, _header_enter_notify_callback, NULL, GINT_TO_POINTER(DT_ACTION_ELEMENT_RESET)); if(!module->gui_reset) gtk_widget_set_sensitive(module->reset_button, FALSE); dt_action_define(&module->actions, NULL, NULL, module->reset_button, NULL); gtk_box_pack_end(GTK_BOX(header), module->reset_button, FALSE, FALSE, 0); @@ -1712,11 +1732,11 @@ static float _action_process(gpointer target, break; case DT_ACTION_ELEMENT_RESET: if(module->gui_reset) - _lib_gui_reset_callback(NULL, NULL, module); + _lib_gui_reset_callback(NULL, 1, 0.0, 0.0, module); break; case DT_ACTION_ELEMENT_PRESETS: if(module->get_params || module->set_preferences) - _presets_popup_callback(NULL, NULL, module); + _presets_popup_callback(module->presets_button, module); break; } } diff --git a/src/libs/location.c b/src/libs/location.c index 225353e6af08..c6c743aaa52c 100644 --- a/src/libs/location.c +++ b/src/libs/location.c @@ -15,8 +15,6 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#include "common/gdk_event_utils.h" - #include "common/darktable.h" #include "common/geo.h" #include "common/curl_tools.h" @@ -89,9 +87,9 @@ typedef struct _callback_param_t static void _lib_location_entry_activated(GtkButton *button, dt_lib_module_t *self); -static gboolean _lib_location_result_item_activated(GtkButton *button, - GdkEventButton *ev, - gpointer user_data); +static void _lib_location_result_item_activated(GtkGestureSingle *gesture, int n_press, + double x, double y, + gpointer user_data); static void _lib_location_parser_start_element(GMarkupParseContext *cxt, const char *element_name, @@ -156,16 +154,16 @@ void gui_cleanup(dt_lib_module_t *self) self->data = NULL; } -static gboolean _event_box_enter_leave(GtkWidget *widget, - GdkEventCrossing *event, - gpointer user_data) +static void _event_box_enter(GtkEventControllerMotion *controller, gdouble x, gdouble y, gpointer user_data) { - if(dt_gdk_event_get_type(event) == GDK_ENTER_NOTIFY) - gtk_widget_set_state_flags(widget, GTK_STATE_FLAG_PRELIGHT, FALSE); - else - gtk_widget_unset_state_flags(widget, GTK_STATE_FLAG_PRELIGHT); + GtkWidget *widget = dt_gui_get_widget(controller); + gtk_widget_set_state_flags(widget, GTK_STATE_FLAG_PRELIGHT, FALSE); +} - return FALSE; +static void _event_box_leave(GtkEventControllerMotion *controller, gpointer user_data) +{ + GtkWidget *widget = dt_gui_get_widget(controller); + gtk_widget_unset_state_flags(widget, GTK_STATE_FLAG_PRELIGHT); } static GtkWidget *_lib_location_place_widget_new(dt_lib_location_t *lib, @@ -174,10 +172,7 @@ static GtkWidget *_lib_location_place_widget_new(dt_lib_location_t *lib, GtkWidget *eb, *vb, *w; eb = gtk_event_box_new(); gtk_widget_set_name(eb, "dt-map-location"); - g_signal_connect(G_OBJECT(eb), "enter-notify-event", - G_CALLBACK(_event_box_enter_leave), NULL); - g_signal_connect(G_OBJECT(eb), "leave-notify-event", - G_CALLBACK(_event_box_enter_leave), NULL); + dt_gui_connect_motion(eb, NULL, _event_box_enter, _event_box_leave, NULL); vb = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); @@ -212,9 +207,7 @@ static GtkWidget *_lib_location_place_widget_new(dt_lib_location_t *lib, lib->callback_params = g_list_append(lib->callback_params, param); param->lib = lib; param->result = place; - g_signal_connect(G_OBJECT(eb), "button-press-event", - G_CALLBACK(_lib_location_result_item_activated), - (gpointer)param); + dt_gui_connect_click(eb, _lib_location_result_item_activated, NULL, (gpointer)param); } return eb; } @@ -443,15 +436,14 @@ static gboolean _lib_location_search(dt_lib_module_t *self) return FALSE; } -gboolean _lib_location_result_item_activated(GtkButton *button, - GdkEventButton *ev, - gpointer user_data) +static void _lib_location_result_item_activated(GtkGestureSingle *gesture, int n_press, + double x, double y, + gpointer user_data) { _callback_param_t *param = (_callback_param_t *)user_data; dt_lib_location_t *lib = param->lib; _lib_location_result_t *result = param->result; _show_location(lib, result); - return TRUE; } void _lib_location_entry_activated(GtkButton *button, diff --git a/src/libs/map_locations.c b/src/libs/map_locations.c index d0ebe63e79b8..a035ca1f39e2 100644 --- a/src/libs/map_locations.c +++ b/src/libs/map_locations.c @@ -831,42 +831,47 @@ static void _selection_changed(GtkTreeSelection *selection, dt_lib_module_t *sel _display_buttons(self); } -static gboolean _click_on_view(GtkWidget *view, GdkEventButton *event, dt_lib_module_t *self) +static void _click_on_view(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { dt_lib_map_locations_t *d = self->data; + GtkWidget *view = dt_gui_get_widget(gesture); gboolean editing; g_object_get(G_OBJECT(d->renderer), "editing", &editing, NULL); if(editing) { dt_control_log(_("terminate edit (press enter or escape) before selecting another location")); - return TRUE; + return; } - const int button_pressed = (dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS) ? dt_gdk_event_get_button(event) : 0; - const gboolean ctrl_pressed = dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); + const guint button_pressed = gtk_gesture_single_get_current_button(gesture); + const gboolean ctrl_pressed = dt_key_modifier_state() & GDK_CONTROL_MASK; if(button_pressed == GDK_BUTTON_SECONDARY || button_pressed == GDK_BUTTON_PRIMARY) { GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); GtkTreePath *path = NULL; // Get tree path for row that was clicked - if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), (gint)dt_gdk_event_get_x(event), - (gint)dt_gdk_event_get_y(event), &path, NULL, NULL, NULL)) + if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), (gint)x, + (gint)y, &path, NULL, NULL, NULL)) { if(button_pressed == GDK_BUTTON_SECONDARY) { gtk_tree_selection_select_path(selection, path); - _pop_menu_view(view, event, self); + _pop_menu_view(view, NULL, self); gtk_tree_path_free(path); _display_buttons(self); - return TRUE; + return; } else if(button_pressed == GDK_BUTTON_PRIMARY && !ctrl_pressed) { if(gtk_tree_selection_path_is_selected(selection, path)) g_timeout_add(100, (GSourceFunc)_force_selection_changed, self); gtk_tree_path_free(path); - return FALSE; + return; } else if(button_pressed == GDK_BUTTON_PRIMARY && ctrl_pressed) { @@ -875,16 +880,15 @@ static gboolean _click_on_view(GtkWidget *view, GdkEventButton *event, dt_lib_mo gtk_tree_view_set_cursor(GTK_TREE_VIEW(d->view), path, d->name_col, TRUE); gtk_tree_path_free(path); _display_buttons(self); - return TRUE; + return; } } else { g_timeout_add(10, (GSourceFunc)_force_selection_changed, self); - return FALSE; + return; } } - return FALSE; } void gui_init(dt_lib_module_t *self) @@ -921,7 +925,7 @@ void gui_init(dt_lib_module_t *self) gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); gtk_tree_view_set_model(view, GTK_TREE_MODEL(treestore)); g_object_unref(treestore); - g_signal_connect(G_OBJECT(view), "button-press-event", G_CALLBACK(_click_on_view), self); + dt_gui_connect_click(view, _click_on_view, NULL, self); gtk_widget_set_tooltip_text(GTK_WIDGET(view), _("list of user locations," "\nclick to show or hide a location on the map:" diff --git a/src/libs/masks.c b/src/libs/masks.c index f1031e86cece..85e1e8db1d7e 100644 --- a/src/libs/masks.c +++ b/src/libs/masks.c @@ -664,22 +664,23 @@ static void _tree_add_shape(GtkButton *button, gpointer shape) dt_control_queue_redraw_center(); } -static gboolean _bt_add_shape(GtkWidget *widget, GdkEventButton *event, gpointer shape) +static void _bt_add_shape_cb(GtkGestureSingle *gesture, int n_press, double x, double y, gpointer shape) { - DT_GUARD_GUI_UPDATE(FALSE); - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY) { #ifdef HAVE_AI if(GPOINTER_TO_INT(shape) == DT_MASKS_OBJECT && !dt_masks_object_available()) { dt_control_log(_("AI model is not available. Check preferences > AI")); - return TRUE; + return; } #endif _tree_add_shape(NULL, shape); - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) + GdkModifierType state; + gtk_get_current_event_state(&state); + if(dt_modifier_is(state, GDK_CONTROL_MASK)) { darktable.develop->form_gui->creation_continuous = TRUE; darktable.develop->form_gui->creation_continuous_module = @@ -688,7 +689,6 @@ static gboolean _bt_add_shape(GtkWidget *widget, GdkEventButton *event, gpointer _lib_masks_inactivate_icons(darktable.develop->proxy.masks.module); } - return TRUE; } static void _tree_add_exist(GtkButton *button, dt_masks_form_t *grp) @@ -1222,10 +1222,9 @@ static void _tree_selection_change(GtkTreeSelection *selection, dt_lib_masks_t * _update_all_properties(self); } -static int _tree_button_pressed(GtkWidget *treeview, - GdkEventButton *event, - dt_lib_module_t *self) +static void _tree_button_pressed_cb(GtkGestureSingle *gesture, int n_press, double x, double y, dt_lib_module_t *self) { + GtkWidget *treeview = dt_gui_get_widget(gesture); // we first need to adjust selection GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)); @@ -1235,7 +1234,7 @@ static int _tree_button_pressed(GtkWidget *treeview, dt_iop_module_t *module = NULL; gboolean on_row = FALSE; if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), - (gint)dt_gdk_event_get_x(event), (gint)dt_gdk_event_get_y(event), &mouse_path, NULL, + (gint)x, (gint)y, &mouse_path, NULL, NULL, NULL)) { on_row = TRUE; @@ -1246,7 +1245,8 @@ static int _tree_button_pressed(GtkWidget *treeview, } } /* single click with the right mouse button? */ - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + const guint button = gtk_gesture_single_get_current_button(gesture); + if(button == GDK_BUTTON_PRIMARY) { // if click on a blank space, then deselect all if(!on_row) @@ -1254,13 +1254,15 @@ static int _tree_button_pressed(GtkWidget *treeview, gtk_tree_selection_unselect_all(selection); } } - else if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) + else if(button == GDK_BUTTON_SECONDARY) { + GdkModifierType state; + gtk_get_current_event_state(&state); // if we are already inside the selection, no change if(on_row && !gtk_tree_selection_path_is_selected(selection, mouse_path)) { - if(!dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) + if(!dt_modifier_is(state, GDK_CONTROL_MASK)) gtk_tree_selection_unselect_all(selection); gtk_tree_selection_select_path(selection, mouse_path); @@ -1516,12 +1518,8 @@ static int _tree_button_pressed(GtkWidget *treeview, gtk_widget_show_all(GTK_WIDGET(menu)); - gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event); - - return 1; + gtk_menu_popup_at_pointer(GTK_MENU(menu), gtk_get_current_event()); } - - return 0; } static gboolean _tree_restrict_select(GtkTreeSelection *selection, @@ -2276,40 +2274,35 @@ void gui_init(dt_lib_module_t *self) d->bt_gradient = dtgtk_togglebutton_new(dtgtk_cairo_paint_masks_gradient, 0, NULL); dt_action_define(DT_ACTION(self), N_("shapes"), N_("add gradient"), d->bt_gradient, &dt_action_def_toggle); - g_signal_connect(G_OBJECT(d->bt_gradient), "button-press-event", - G_CALLBACK(_bt_add_shape), GINT_TO_POINTER(DT_MASKS_GRADIENT)); + dt_gui_connect_click(d->bt_gradient, _bt_add_shape_cb, NULL, GINT_TO_POINTER(DT_MASKS_GRADIENT)); gtk_widget_set_tooltip_text(d->bt_gradient, _("add gradient")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->bt_gradient), FALSE); d->bt_path = dtgtk_togglebutton_new(dtgtk_cairo_paint_masks_path, 0, NULL); dt_action_define(DT_ACTION(self), N_("shapes"), N_("add path"), d->bt_path, &dt_action_def_toggle); - g_signal_connect(G_OBJECT(d->bt_path), "button-press-event", - G_CALLBACK(_bt_add_shape), GINT_TO_POINTER(DT_MASKS_PATH)); + dt_gui_connect_click(d->bt_path, _bt_add_shape_cb, NULL, GINT_TO_POINTER(DT_MASKS_PATH)); gtk_widget_set_tooltip_text(d->bt_path, _("add path")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->bt_path), FALSE); d->bt_ellipse = dtgtk_togglebutton_new(dtgtk_cairo_paint_masks_ellipse, 0, NULL); dt_action_define(DT_ACTION(self), N_("shapes"), N_("add ellipse"), d->bt_ellipse, &dt_action_def_toggle); - g_signal_connect(G_OBJECT(d->bt_ellipse), "button-press-event", - G_CALLBACK(_bt_add_shape), GINT_TO_POINTER(DT_MASKS_ELLIPSE)); + dt_gui_connect_click(d->bt_ellipse, _bt_add_shape_cb, NULL, GINT_TO_POINTER(DT_MASKS_ELLIPSE)); gtk_widget_set_tooltip_text(d->bt_ellipse, _("add ellipse")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->bt_ellipse), FALSE); d->bt_circle = dtgtk_togglebutton_new(dtgtk_cairo_paint_masks_circle, 0, NULL); dt_action_define(DT_ACTION(self), N_("shapes"), N_("add circle"), d->bt_circle, &dt_action_def_toggle); - g_signal_connect(G_OBJECT(d->bt_circle), "button-press-event", - G_CALLBACK(_bt_add_shape), GINT_TO_POINTER(DT_MASKS_CIRCLE)); + dt_gui_connect_click(d->bt_circle, _bt_add_shape_cb, NULL, GINT_TO_POINTER(DT_MASKS_CIRCLE)); gtk_widget_set_tooltip_text(d->bt_circle, _("add circle")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->bt_circle), FALSE); d->bt_brush = dtgtk_togglebutton_new(dtgtk_cairo_paint_masks_brush, 0, NULL); dt_action_define(DT_ACTION(self), N_("shapes"), N_("add brush"), d->bt_brush, &dt_action_def_toggle); - g_signal_connect(G_OBJECT(d->bt_brush), "button-press-event", - G_CALLBACK(_bt_add_shape), GINT_TO_POINTER(DT_MASKS_BRUSH)); + dt_gui_connect_click(d->bt_brush, _bt_add_shape_cb, NULL, GINT_TO_POINTER(DT_MASKS_BRUSH)); gtk_widget_set_tooltip_text(d->bt_brush, _("add brush")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->bt_brush), FALSE); @@ -2317,8 +2310,7 @@ void gui_init(dt_lib_module_t *self) d->bt_object = dtgtk_togglebutton_new(dtgtk_cairo_paint_masks_object, 0, NULL); dt_action_define(DT_ACTION(self), N_("shapes"), N_("add object"), d->bt_object, &dt_action_def_toggle); - g_signal_connect(G_OBJECT(d->bt_object), "button-press-event", - G_CALLBACK(_bt_add_shape), GINT_TO_POINTER(DT_MASKS_OBJECT)); + dt_gui_connect_click(d->bt_object, _bt_add_shape_cb, NULL, GINT_TO_POINTER(DT_MASKS_OBJECT)); gtk_widget_set_tooltip_text(d->bt_object, _("add AI object")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->bt_object), FALSE); #endif @@ -2355,8 +2347,7 @@ void gui_init(dt_lib_module_t *self) gtk_widget_set_has_tooltip(d->treeview, TRUE); g_signal_connect(d->treeview, "query-tooltip", G_CALLBACK(_tree_query_tooltip), NULL); g_signal_connect(selection, "changed", G_CALLBACK(_tree_selection_change), d); - g_signal_connect(d->treeview, "button-press-event", - G_CALLBACK(_tree_button_pressed), self); + dt_gui_connect_click_all(d->treeview, _tree_button_pressed_cb, NULL, self); GtkWidget *shape_buttons = dt_gui_hbox (dt_gui_expand(dt_ui_label_new(_("created shapes"))), diff --git a/src/libs/metadata.c b/src/libs/metadata.c index 5c22763252f8..d976a41fcfbf 100644 --- a/src/libs/metadata.c +++ b/src/libs/metadata.c @@ -15,8 +15,6 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#include "common/gdk_event_utils.h" - #include "common/metadata.h" #include "common/collection.h" #include "common/darktable.h" @@ -420,24 +418,27 @@ static void _cancel_button_clicked(GtkButton *button, gtk_window_set_focus(GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)), NULL); } -static gboolean _key_pressed(GtkWidget *textview, - GdkEventKey *event, - dt_lib_module_t *self) +static gboolean _key_pressed_cb(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_lib_module_t *self) { + GtkWidget *textview = dt_gui_get_widget(controller); dt_lib_metadata_t *d = self->data; - switch(dt_gdk_event_get_keyval(event)) + switch(keyval) { case GDK_KEY_Return: case GDK_KEY_KP_Enter: - if(!dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) + if(!dt_modifier_is(state, GDK_CONTROL_MASK)) { gtk_button_clicked(GTK_BUTTON(d->apply_button)); return TRUE; } break; case GDK_KEY_Escape: - if(dt_modifier_is(dt_gdk_event_get_state(event), 0)) + if(dt_modifier_is(state, 0)) { gtk_button_clicked(GTK_BUTTON(d->cancel_button)); return TRUE; @@ -447,7 +448,7 @@ static gboolean _key_pressed(GtkWidget *textview, break; } - return gtk_text_view_im_context_filter_keypress(GTK_TEXT_VIEW(textview), event); + return gtk_text_view_im_context_filter_keypress(GTK_TEXT_VIEW(textview), (GdkEventKey *)gtk_get_current_event()); } static gboolean _textview_focus(GtkWidget *widget, @@ -569,11 +570,11 @@ static void _populate_popup_multi(GtkTextView *textview, gtk_widget_show_all(popup); } -static gboolean _metadata_reset(GtkWidget *label, - GdkEventButton *event, - GtkWidget *widget) +static void _metadata_reset_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + GtkWidget *widget) { - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS) + if(n_press >= 2) { g_object_set_data(G_OBJECT(widget), "tv_multiple", GINT_TO_POINTER(FALSE)); GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); @@ -582,7 +583,6 @@ static gboolean _metadata_reset(GtkWidget *label, else g_signal_emit_by_name(G_OBJECT(buffer), "changed"); // even if unchanged } - return TRUE; } static void _add_grid_row(dt_metadata_t *metadata, int row, dt_lib_module_t *self) @@ -640,10 +640,10 @@ static void _add_grid_row(dt_metadata_t *metadata, int row, dt_lib_module_t *sel gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR); gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(textview), FALSE); gtk_widget_add_events(textview, GDK_FOCUS_CHANGE_MASK | GDK_ENTER_NOTIFY_MASK); - g_signal_connect(textview, "key-press-event", G_CALLBACK(_key_pressed), self); + dt_gui_connect_key(textview, _key_pressed_cb, self); g_signal_connect(textview, "focus", G_CALLBACK(_textview_focus), self); g_signal_connect(textview, "populate-popup", G_CALLBACK(_populate_popup_multi), self); - g_signal_connect(labelev, "button-press-event", G_CALLBACK(_metadata_reset), textview); + dt_gui_connect_click_all(labelev, _metadata_reset_cb, NULL, textview); g_signal_connect(buffer, "changed", G_CALLBACK(_textbuffer_changed), self); gtk_widget_set_hexpand(textview, TRUE); gtk_widget_set_vexpand(textview, TRUE); diff --git a/src/libs/metadata_view.c b/src/libs/metadata_view.c index ed0c4ecb7cd7..4354ab110a93 100644 --- a/src/libs/metadata_view.c +++ b/src/libs/metadata_view.c @@ -1154,11 +1154,10 @@ static void _jump_to() } } -static gboolean _filmroll_clicked(GtkWidget *widget, GdkEventButton *event, gpointer null) +static void _filmroll_clicked(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, gpointer null) { - if(dt_gdk_event_get_type(event) != GDK_2BUTTON_PRESS) return FALSE; + if(n_press < 2) return; _jump_to(); - return TRUE; } static void _jump_to_accel(dt_action_t *data) @@ -1219,8 +1218,7 @@ static void _lib_metadata_refill_grid(dt_lib_module_t *self) if(d->filmroll_event && GTK_IS_WIDGET(d->filmroll_event)) g_signal_handlers_disconnect_by_func(d->filmroll_event, G_CALLBACK(_filmroll_clicked), NULL); - g_signal_connect(G_OBJECT(w_value), "button-press-event", - G_CALLBACK(_filmroll_clicked), NULL); + dt_gui_connect_click(w_value, _filmroll_clicked, NULL, NULL); d->filmroll_event = G_OBJECT(w_value); } diff --git a/src/libs/modulegroups.c b/src/libs/modulegroups.c index ca199e82d4f5..b5b495ba01f5 100644 --- a/src/libs/modulegroups.c +++ b/src/libs/modulegroups.c @@ -402,9 +402,12 @@ static void _basics_hide(dt_lib_module_t *self) d->vbox_basic = NULL; } -static gboolean _basics_goto_module(GtkWidget *w, GdkEventButton *e, gpointer user_data) +static void _basics_goto_module(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_iop_module_t *module) { - dt_iop_module_t *module = (dt_iop_module_t *)(user_data); dt_dev_modulegroups_switch(darktable.develop, module); const gboolean single = dt_conf_get_bool("darkroom/ui/single_module"); dt_iop_gui_set_expanded(module, TRUE, single); @@ -413,13 +416,15 @@ static gboolean _basics_goto_module(GtkWidget *w, GdkEventButton *e, gpointer us // this second call ensures the module ends up expanded. if(single) dt_iop_gui_set_expanded(module, TRUE, FALSE); - return TRUE; } -static gboolean _basics_on_off_label_callback(GtkWidget *widget, GdkEventButton *e, GtkToggleButton *btn) +static void _basics_on_off_label_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + GtkWidget *btn) { - gtk_toggle_button_set_active(btn, !gtk_toggle_button_get_active(btn)); - return TRUE; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(btn), !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn))); } static void _sync_visibility(GtkWidget *widget, @@ -434,7 +439,11 @@ static void _sync_visibility(GtkWidget *widget, gtk_widget_set_visible(item->box, !dt_action_widget_invisible(item->temp_widget)); } -static gboolean _manage_direct_module_popup(GtkWidget *widget, GdkEventButton *event, dt_lib_module_t *self); +static void _manage_direct_module_popup(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self); static void _basics_add_widget(dt_lib_module_t *self, dt_lib_modulegroups_basic_item_t *item, GtkWidget *w, dt_lib_modulegroups_basic_item_position_t item_pos) @@ -476,7 +485,7 @@ static void _basics_add_widget(dt_lib_module_t *self, dt_lib_modulegroups_basic_ gtk_label_set_xalign(GTK_LABEL(lb), 0.0); gtk_widget_set_name(lb, "basics-iop_name"); gtk_container_add(GTK_CONTAINER(evb), lb); - g_signal_connect(G_OBJECT(evb), "button-press-event", G_CALLBACK(_basics_on_off_label_callback), btn); + dt_gui_connect_click(evb, _basics_on_off_label_callback, NULL, btn); gtk_box_pack_start(GTK_BOX(item->box), evb, FALSE, TRUE, 0); // disable widget if needed (multiinstance) @@ -599,7 +608,8 @@ static void _basics_add_widget(dt_lib_module_t *self, dt_lib_modulegroups_basic_ gtk_container_add(GTK_CONTAINER(evb), header_box); gtk_widget_show_all(evb); g_object_set_data(G_OBJECT(evb), "module", item->module->so); - g_signal_connect(evb, "button-press-event", G_CALLBACK(_manage_direct_module_popup), self); + GtkGestureSingle *g_evb = dt_gui_connect_click(evb, _manage_direct_module_popup, NULL, self); + gtk_gesture_single_set_button(g_evb, GDK_BUTTON_SECONDARY); gtk_widget_set_name(header_box, "basics-header-box"); dt_gui_add_class(header_box, "dt_big_btn_canvas"); gtk_box_pack_start(GTK_BOX(d->vbox_basic), evb, FALSE, FALSE, 0); @@ -620,7 +630,7 @@ static void _basics_add_widget(dt_lib_module_t *self, dt_lib_modulegroups_basic_ gtk_widget_set_name(wbt, "basics-link"); gtk_widget_set_valign(wbt, GTK_ALIGN_CENTER); g_free(tt); - g_signal_connect(G_OBJECT(wbt), "button-press-event", G_CALLBACK(_basics_goto_module), item->module); + dt_gui_connect_click(wbt, _basics_goto_module, NULL, item->module); gtk_box_pack_end(GTK_BOX(compact_ui ? hbox_basic : header_box), wbt, FALSE, FALSE, 0); // we create a button to open the presets menu @@ -2658,65 +2668,69 @@ static void _manage_editor_module_add_popup(GtkWidget *widget, G_CALLBACK(_manage_editor_module_add), self, FALSE); } -static gboolean _presets_pressed(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _presets_pressed_cb(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_lib_module_t *self) { - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) + GdkModifierType state; + gtk_get_current_event_state(&state); + if(state & GDK_CONTROL_MASK) { manage_presets(self); - return TRUE; + dt_gui_claim(gesture); } - return FALSE; } -static gboolean _manage_direct_popup(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _manage_direct_popup(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) - { - dt_lib_modulegroups_group_t *gr = g_object_get_data(G_OBJECT(widget), "group"); - if(!g_strcmp0(gr->name, C_("modulegroup", "deprecated"))) return FALSE; - _manage_module_add_popup(widget, gr, - G_CALLBACK(_manage_direct_module_toggle), self, TRUE); - return TRUE; - } - return FALSE; + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_SECONDARY) + return; + + GtkWidget *widget = dt_gui_get_widget(gesture); + dt_lib_modulegroups_group_t *gr = g_object_get_data(G_OBJECT(widget), "group"); + if(!g_strcmp0(gr->name, C_("modulegroup", "deprecated"))) return; + _manage_module_add_popup(widget, gr, + G_CALLBACK(_manage_direct_module_toggle), self, TRUE); } -static gboolean _manage_direct_basic_popup(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _manage_direct_basic_popup(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) - { - _manage_basics_add_popup(widget, self, TRUE); - return TRUE; - } - return FALSE; + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_SECONDARY) + return; + + _manage_basics_add_popup(dt_gui_get_widget(gesture), self, TRUE); } -static gboolean _manage_direct_module_popup(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _manage_direct_module_popup(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { - dt_action_t *module = g_object_get_data(G_OBJECT(widget), "module"); + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_SECONDARY) + return; - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) - { - int nba = 0; // nb of already present items - GtkWidget *pop = gtk_menu_new(); - gtk_widget_set_name(pop, "modulegroups-popup"); + GtkWidget *widget = dt_gui_get_widget(gesture); + dt_action_t *module = g_object_get_data(G_OBJECT(widget), "module"); - GtkWidget *this_module = _build_menu_from_actions(module->target, self, - NULL, pop, TRUE, &nba); + int nba = 0; // nb of already present items + GtkWidget *pop = gtk_menu_new(); + gtk_widget_set_name(pop, "modulegroups-popup"); - dt_gui_menu_popup(GTK_MENU(this_module), NULL, GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH); + GtkWidget *this_module = _build_menu_from_actions(module->target, self, + NULL, pop, TRUE, &nba); - return TRUE; - } - return FALSE; + dt_gui_menu_popup(GTK_MENU(this_module), NULL, GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH); } static void _manage_direct_full_active_toggled(GtkWidget *widget, @@ -2730,31 +2744,32 @@ static void _manage_direct_full_active_toggled(GtkWidget *widget, _lib_modulegroups_update_iop_visibility(self); } -static gboolean _manage_direct_active_popup(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _manage_direct_active_popup(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) - { - dt_lib_modulegroups_t *d = self->data; - GtkWidget *pop = gtk_menu_new(); - gtk_widget_set_name(pop, "modulegroups-popup"); - - GtkWidget *smt = gtk_check_menu_item_new_with_label(_("show all history modules")); - gtk_widget_set_tooltip_text( - smt, - _("show modules that are present in the history stack," - " regardless of whether or not they are currently enabled")); - gtk_widget_set_name(smt, "modulegroups-popup-item"); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(smt), d->full_active); - g_signal_connect(G_OBJECT(smt), "toggled", - G_CALLBACK(_manage_direct_full_active_toggled), self); - gtk_menu_shell_append(GTK_MENU_SHELL(pop), smt); + if(gtk_gesture_single_get_current_button(gesture) != GDK_BUTTON_SECONDARY) + return; - dt_gui_menu_popup(GTK_MENU(pop), widget, GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH); - return TRUE; - } - return FALSE; + dt_lib_modulegroups_t *d = self->data; + GtkWidget *widget = dt_gui_get_widget(gesture); + GtkWidget *pop = gtk_menu_new(); + gtk_widget_set_name(pop, "modulegroups-popup"); + + GtkWidget *smt = gtk_check_menu_item_new_with_label(_("show all history modules")); + gtk_widget_set_tooltip_text( + smt, + _("show modules that are present in the history stack," + " regardless of whether or not they are currently enabled")); + gtk_widget_set_name(smt, "modulegroups-popup-item"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(smt), d->full_active); + g_signal_connect(G_OBJECT(smt), "toggled", + G_CALLBACK(_manage_direct_full_active_toggled), self); + gtk_menu_shell_append(GTK_MENU_SHELL(pop), smt); + + dt_gui_menu_popup(GTK_MENU(pop), widget, GDK_GRAVITY_SOUTH, GDK_GRAVITY_NORTH); } static void _dt_dev_image_changed_callback(gpointer instance, @@ -2834,13 +2849,16 @@ static void _dt_dev_image_changed_callback(gpointer instance, } -static gboolean _scroll_group_buttons(GtkWidget *widget, - GdkEventScroll *event, - dt_lib_module_t *self) +static void _scroll_group_buttons(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_lib_module_t *self) { dt_lib_modulegroups_t *d = self->data; - int delta; - if(dt_gui_get_scroll_unit_delta(event, &delta)) + int delta = (dy > 0) ? 1 : (dy < 0) ? -1 : 0; + if(delta == 0) + delta = (dx > 0) ? -1 : (dx < 0) ? 1 : 0; + if(delta != 0) { GtkWidget *adjacent = d->current == DT_MODULEGROUP_BASICS && delta < 0 ? d->active_btn @@ -2849,8 +2867,6 @@ static gboolean _scroll_group_buttons(GtkWidget *widget, : _buttons_get_from_pos(self, d->current - delta); if(adjacent) gtk_button_clicked(GTK_BUTTON(adjacent)); } - - return TRUE; } // cycle module groups action @@ -2963,15 +2979,18 @@ void gui_init(dt_lib_module_t *self) d->hbox_groups = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); GtkWidget *scrollbox = gtk_event_box_new(); gtk_container_add(GTK_CONTAINER(scrollbox), d->hbox_groups); - g_signal_connect(scrollbox, "scroll-event", - G_CALLBACK(_scroll_group_buttons), self); - gtk_widget_add_events(scrollbox, darktable.gui->scroll_mask); + dt_gui_connect_scroll(scrollbox, + GTK_EVENT_CONTROLLER_SCROLL_VERTICAL + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _scroll_group_buttons, self); gtk_box_pack_start(GTK_BOX(d->hbox_buttons), scrollbox, TRUE, TRUE, 0); // basic group button d->basic_btn = dtgtk_togglebutton_new(dtgtk_cairo_paint_modulegroup_basics, 0, NULL); - g_signal_connect(d->basic_btn, "button-press-event", - G_CALLBACK(_manage_direct_basic_popup), self); + { + GtkGestureSingle *g = dt_gui_connect_click(d->basic_btn, _manage_direct_basic_popup, NULL, self); + gtk_gesture_single_set_button(g, GDK_BUTTON_SECONDARY); + } g_signal_connect(d->basic_btn, "toggled", G_CALLBACK(_lib_modulegroups_toggle), self); gtk_widget_set_tooltip_text(d->basic_btn, _("quick access panel\n" "right-click tab icon to add/remove widgets")); @@ -2984,8 +3003,10 @@ void gui_init(dt_lib_module_t *self) // active group button d->active_btn = dtgtk_togglebutton_new(dtgtk_cairo_paint_modulegroup_active, 0, NULL); - g_signal_connect(d->active_btn, "button-press-event", - G_CALLBACK(_manage_direct_active_popup), self); + { + GtkGestureSingle *g = dt_gui_connect_click(d->active_btn, _manage_direct_active_popup, NULL, self); + gtk_gesture_single_set_button(g, GDK_BUTTON_SECONDARY); + } g_signal_connect(d->active_btn, "toggled", G_CALLBACK(_lib_modulegroups_toggle), self); gtk_widget_set_tooltip_text(d->active_btn, _("show only active modules")); @@ -3001,8 +3022,7 @@ void gui_init(dt_lib_module_t *self) self->presets_button = dtgtk_button_new(dtgtk_cairo_paint_presets, 0, NULL); gtk_widget_set_tooltip_text(self->presets_button, _("presets\nctrl+click to manage")); gtk_box_pack_start(GTK_BOX(d->hbox_buttons), self->presets_button, FALSE, FALSE, 0); - g_signal_connect(self->presets_button, "button-press-event", - G_CALLBACK(_presets_pressed), self); + dt_gui_connect_click(self->presets_button, _presets_pressed_cb, NULL, self); /* search box */ d->text_entry = gtk_search_entry_new(); @@ -3138,7 +3158,10 @@ static void _buttons_update(dt_lib_module_t *self) dt_lib_modulegroups_group_t *gr = l->data; GtkWidget *bt = dtgtk_togglebutton_new(_buttons_get_icon_fct(gr->icon), 0, NULL); g_object_set_data(G_OBJECT(bt), "group", gr); - g_signal_connect(bt, "button-press-event", G_CALLBACK(_manage_direct_popup), self); + { + GtkGestureSingle *g_bt = dt_gui_connect_click(bt, _manage_direct_popup, NULL, self); + gtk_gesture_single_set_button(g_bt, GDK_BUTTON_SECONDARY); + } g_signal_connect(bt, "toggled", G_CALLBACK(_lib_modulegroups_toggle), self); char *tooltip = g_strdup_printf(_("%s\nright-click tab icon to add/remove modules"), gr->name); gtk_widget_set_tooltip_text(bt, tooltip); @@ -3256,10 +3279,13 @@ static void _manage_editor_group_name_changed(GtkWidget *tb, gr->name = g_strdup(txt); } -static gboolean _manage_editor_group_icon_changed(GtkWidget *widget, - GdkEventButton *event, - dt_lib_modulegroups_group_t *gr) +static void _manage_editor_group_icon_changed(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_modulegroups_group_t *gr) { + GtkWidget *widget = dt_gui_get_widget(gesture); const char *ic = (char *)g_object_get_data(G_OBJECT(widget), "ic_name"); g_free(gr->icon); gr->icon = g_strdup(ic); @@ -3267,7 +3293,6 @@ static gboolean _manage_editor_group_icon_changed(GtkWidget *widget, GtkWidget *btn = gtk_popover_get_relative_to(GTK_POPOVER(pop)); dtgtk_button_set_paint(DTGTK_BUTTON(btn), _buttons_get_icon_fct(ic), 0, NULL); gtk_popover_popdown(GTK_POPOVER(pop)); - return TRUE; } static void _manage_editor_group_icon_popup(GtkWidget *btn, @@ -3286,8 +3311,7 @@ static void _manage_editor_group_icon_popup(GtkWidget *btn, gtk_box_pack_start(GTK_BOX(hb), ic, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hb), gtk_label_new(_("basic icon")), TRUE, TRUE, 0); g_object_set_data(G_OBJECT(eb), "ic_name", "basic"); - g_signal_connect(G_OBJECT(eb), "button-press-event", - G_CALLBACK(_manage_editor_group_icon_changed), gr); + dt_gui_connect_click_all(eb, _manage_editor_group_icon_changed, NULL, gr); gtk_container_add(GTK_CONTAINER(eb), hb); gtk_box_pack_start(GTK_BOX(vb), eb, FALSE, TRUE, 0); @@ -3297,8 +3321,7 @@ static void _manage_editor_group_icon_popup(GtkWidget *btn, gtk_box_pack_start(GTK_BOX(hb), ic, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hb), gtk_label_new(_("active icon")), TRUE, TRUE, 0); g_object_set_data(G_OBJECT(eb), "ic_name", "active"); - g_signal_connect(G_OBJECT(eb), "button-press-event", - G_CALLBACK(_manage_editor_group_icon_changed), gr); + dt_gui_connect_click_all(eb, _manage_editor_group_icon_changed, NULL, gr); gtk_container_add(GTK_CONTAINER(eb), hb); gtk_box_pack_start(GTK_BOX(vb), eb, FALSE, TRUE, 0); @@ -3308,8 +3331,7 @@ static void _manage_editor_group_icon_popup(GtkWidget *btn, gtk_box_pack_start(GTK_BOX(hb), ic, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hb), gtk_label_new(_("color icon")), TRUE, TRUE, 0); g_object_set_data(G_OBJECT(eb), "ic_name", "color"); - g_signal_connect(G_OBJECT(eb), "button-press-event", - G_CALLBACK(_manage_editor_group_icon_changed), gr); + dt_gui_connect_click_all(eb, _manage_editor_group_icon_changed, NULL, gr); gtk_container_add(GTK_CONTAINER(eb), hb); gtk_box_pack_start(GTK_BOX(vb), eb, FALSE, TRUE, 0); @@ -3319,8 +3341,7 @@ static void _manage_editor_group_icon_popup(GtkWidget *btn, gtk_box_pack_start(GTK_BOX(hb), ic, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hb), gtk_label_new(_("correct icon")), TRUE, TRUE, 0); g_object_set_data(G_OBJECT(eb), "ic_name", "correct"); - g_signal_connect(G_OBJECT(eb), "button-press-event", - G_CALLBACK(_manage_editor_group_icon_changed), gr); + dt_gui_connect_click_all(eb, _manage_editor_group_icon_changed, NULL, gr); gtk_container_add(GTK_CONTAINER(eb), hb); gtk_box_pack_start(GTK_BOX(vb), eb, FALSE, TRUE, 0); @@ -3330,8 +3351,7 @@ static void _manage_editor_group_icon_popup(GtkWidget *btn, gtk_box_pack_start(GTK_BOX(hb), ic, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hb), gtk_label_new(_("effect icon")), TRUE, TRUE, 0); g_object_set_data(G_OBJECT(eb), "ic_name", "effect"); - g_signal_connect(G_OBJECT(eb), "button-press-event", - G_CALLBACK(_manage_editor_group_icon_changed), gr); + dt_gui_connect_click_all(eb, _manage_editor_group_icon_changed, NULL, gr); gtk_container_add(GTK_CONTAINER(eb), hb); gtk_box_pack_start(GTK_BOX(vb), eb, FALSE, TRUE, 0); @@ -3341,8 +3361,7 @@ static void _manage_editor_group_icon_popup(GtkWidget *btn, gtk_box_pack_start(GTK_BOX(hb), ic, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hb), gtk_label_new(_("favorites icon")), TRUE, TRUE, 0); g_object_set_data(G_OBJECT(eb), "ic_name", "favorites"); - g_signal_connect(G_OBJECT(eb), "button-press-event", - G_CALLBACK(_manage_editor_group_icon_changed), gr); + dt_gui_connect_click_all(eb, _manage_editor_group_icon_changed, NULL, gr); gtk_container_add(GTK_CONTAINER(eb), hb); gtk_box_pack_start(GTK_BOX(vb), eb, FALSE, TRUE, 0); @@ -3352,8 +3371,7 @@ static void _manage_editor_group_icon_popup(GtkWidget *btn, gtk_box_pack_start(GTK_BOX(hb), ic, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hb), gtk_label_new(_("tone icon")), TRUE, TRUE, 0); g_object_set_data(G_OBJECT(eb), "ic_name", "tone"); - g_signal_connect(G_OBJECT(eb), "button-press-event", - G_CALLBACK(_manage_editor_group_icon_changed), gr); + dt_gui_connect_click_all(eb, _manage_editor_group_icon_changed, NULL, gr); gtk_container_add(GTK_CONTAINER(eb), hb); gtk_box_pack_start(GTK_BOX(vb), eb, FALSE, TRUE, 0); @@ -3363,8 +3381,7 @@ static void _manage_editor_group_icon_popup(GtkWidget *btn, gtk_box_pack_start(GTK_BOX(hb), ic, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hb), gtk_label_new(_("grading icon")), TRUE, TRUE, 0); g_object_set_data(G_OBJECT(eb), "ic_name", "grading"); - g_signal_connect(G_OBJECT(eb), "button-press-event", - G_CALLBACK(_manage_editor_group_icon_changed), gr); + dt_gui_connect_click_all(eb, _manage_editor_group_icon_changed, NULL, gr); gtk_container_add(GTK_CONTAINER(eb), hb); gtk_box_pack_start(GTK_BOX(vb), eb, FALSE, TRUE, 0); @@ -3374,8 +3391,7 @@ static void _manage_editor_group_icon_popup(GtkWidget *btn, gtk_box_pack_start(GTK_BOX(hb), ic, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hb), gtk_label_new(_("technical icon")), TRUE, TRUE, 0); g_object_set_data(G_OBJECT(eb), "ic_name", "technical"); - g_signal_connect(G_OBJECT(eb), "button-press-event", - G_CALLBACK(_manage_editor_group_icon_changed), gr); + dt_gui_connect_click_all(eb, _manage_editor_group_icon_changed, NULL, gr); gtk_container_add(GTK_CONTAINER(eb), hb); gtk_box_pack_start(GTK_BOX(vb), eb, FALSE, TRUE, 0); diff --git a/src/libs/navigation.c b/src/libs/navigation.c index 1925d1bd3f78..b26f50f4c3aa 100644 --- a/src/libs/navigation.c +++ b/src/libs/navigation.c @@ -49,9 +49,9 @@ static gboolean _lib_navigation_draw_callback(GtkWidget *widget, cairo_t *crf, gpointer user_data); /* motion notify callback handler*/ -static gboolean _lib_navigation_motion_notify_callback(GtkWidget *widget, - GdkEventMotion *event, - dt_lib_module_t *self); +static void _lib_navigation_motion_notify_callback(GtkEventControllerMotion *controller, + double x, double y, + dt_lib_module_t *self); /* scroll callback */ static void _lib_navigation_scroll_callback(GtkEventControllerScroll *controller, double dx, double dy, @@ -65,17 +65,16 @@ static void _lib_navigation_pinch_scale_callback(GtkGesture *gesture, gdouble scale, dt_lib_module_t *self); /* button press callback */ -static gboolean _lib_navigation_button_press_callback(GtkWidget *widget, - GdkEvent *event, - dt_lib_module_t *self); +static void _lib_navigation_button_press_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self); /* button release callback */ -static gboolean _lib_navigation_button_release_callback(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self); +static void _lib_navigation_button_release_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self); /* leave notify callback */ -static gboolean _lib_navigation_leave_notify_callback(GtkWidget *widget, - GdkEventCrossing *event, - dt_lib_module_t *self); +static void _lib_navigation_leave_notify_callback(GtkEventControllerMotion *controller, + dt_lib_module_t *self); /* helper function for position set */ static void _lib_navigation_set_position(struct dt_lib_module_t *self, @@ -229,10 +228,7 @@ void gui_init(dt_lib_module_t *self) dt_gui_add_class(thumbnail, "dt_transparent_background"); g_signal_connect(G_OBJECT(thumbnail), "draw", G_CALLBACK(_lib_navigation_draw_callback), self); - g_signal_connect(G_OBJECT(thumbnail), "button-press-event", - G_CALLBACK(_lib_navigation_button_press_callback), self); - g_signal_connect(G_OBJECT(thumbnail), "button-release-event", - G_CALLBACK(_lib_navigation_button_release_callback), self); + dt_gui_connect_click_all(thumbnail, _lib_navigation_button_press_callback, _lib_navigation_button_release_callback, self); dt_gui_connect_scroll(thumbnail, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES, _lib_navigation_scroll_callback, self); // FIXME: Make helper function for zoom gesture. If discrete zooming @@ -242,10 +238,7 @@ void gui_init(dt_lib_module_t *self) g_object_weak_ref(G_OBJECT(thumbnail), (GWeakNotify) g_object_unref, zoom_gesture); g_signal_connect(zoom_gesture, "begin", G_CALLBACK(_lib_navigation_pinch_begin_callback), self); g_signal_connect(zoom_gesture, "scale-changed", G_CALLBACK(_lib_navigation_pinch_scale_callback), self); - g_signal_connect(G_OBJECT(thumbnail), "motion-notify-event", - G_CALLBACK(_lib_navigation_motion_notify_callback), self); - g_signal_connect(G_OBJECT(thumbnail), "leave-notify-event", - G_CALLBACK(_lib_navigation_leave_notify_callback), self); + dt_gui_connect_motion(thumbnail, _lib_navigation_motion_notify_callback, NULL, _lib_navigation_leave_notify_callback, self); /* set size of navigation draw area */ // gtk_widget_set_size_request(thumbnail, -1, DT_PIXEL_APPLY_DPI(175)); @@ -422,15 +415,15 @@ void _lib_navigation_set_position(dt_lib_module_t *self, } } -static gboolean _lib_navigation_motion_notify_callback(GtkWidget *widget, - GdkEventMotion *event, - dt_lib_module_t *self) +static void _lib_navigation_motion_notify_callback(GtkEventControllerMotion *controller, + double x, double y, + dt_lib_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); - _lib_navigation_set_position(self, dt_gdk_event_get_x(event), dt_gdk_event_get_y(event), + _lib_navigation_set_position(self, x, y, allocation.width, allocation.height); - return TRUE; } static void _zoom_changed(GtkWidget *widget, gpointer user_data) @@ -592,48 +585,53 @@ static void _lib_navigation_pinch_scale_callback(GtkGesture *gesture, } } -static gboolean _lib_navigation_button_press_callback(GtkWidget *widget, - GdkEvent *event, - dt_lib_module_t *self) +static void _lib_navigation_button_press_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { dt_lib_navigation_t *d = self->data; + GtkWidget *widget = dt_gui_get_widget(gesture); GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) != 2) + const guint button = gtk_gesture_single_get_current_button(gesture); + + if(button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_SECONDARY) { d->dragging = 1; - _lib_navigation_set_position(self, dt_gdk_event_get_x(event), dt_gdk_event_get_y(event), + _lib_navigation_set_position(self, x, y, allocation.width, allocation.height); - - return TRUE; } - else + else if(button == GDK_BUTTON_MIDDLE) { GtkWidget *center = dt_ui_center(darktable.gui->ui); GtkAllocation center_alloc; gtk_widget_get_allocation(center, ¢er_alloc); - event->scroll.x *= (gdouble)center_alloc.width / allocation.width; - event->scroll.y *= (gdouble)center_alloc.height / allocation.height; - return gtk_widget_event(center, event); + GdkEvent *ev = gdk_event_new(GDK_BUTTON_PRESS); + gdk_event_set_screen(ev, gtk_widget_get_screen(center)); + ev->button.window = gtk_widget_get_window(center); + ev->button.button = GDK_BUTTON_MIDDLE; + ev->button.x = x * (gdouble)center_alloc.width / allocation.width; + ev->button.y = y * (gdouble)center_alloc.height / allocation.height; + ev->button.type = GDK_BUTTON_PRESS; + ev->button.time = GDK_CURRENT_TIME; + if(ev->button.window) g_object_ref(ev->button.window); + gtk_widget_event(center, ev); + gdk_event_free(ev); } } -static gboolean _lib_navigation_button_release_callback(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _lib_navigation_button_release_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { dt_lib_navigation_t *d = self->data; d->dragging = 0; - - return TRUE; } -static gboolean _lib_navigation_leave_notify_callback(GtkWidget *widget, - GdkEventCrossing *event, - dt_lib_module_t *self) +static void _lib_navigation_leave_notify_callback(GtkEventControllerMotion *controller, + dt_lib_module_t *self) { - return TRUE; } // clang-format off diff --git a/src/libs/neural_restore.c b/src/libs/neural_restore.c index 915dd398f196..d1767455d3e0 100644 --- a/src/libs/neural_restore.c +++ b/src/libs/neural_restore.c @@ -15,8 +15,6 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#include "common/gdk_event_utils.h" - // neural restore — lighttable module for AI-based image restoration // // overview @@ -3501,11 +3499,11 @@ static void _pick_toggled(GtkToggleButton *btn, gtk_widget_queue_draw(d->preview_area); } -static gboolean _pick_double_click(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _pick_double_click_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { - if(dt_gdk_event_get_type(event) != GDK_2BUTTON_PRESS) return FALSE; + if(n_press < 2) return; dt_lib_neural_restore_t *d = self->data; d->patch_center[0] = 0.5f; d->patch_center[1] = 0.5f; @@ -3517,7 +3515,6 @@ static gboolean _pick_double_click(GtkWidget *widget, gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->pick_button), FALSE); d->preview_requested = TRUE; _trigger_preview(self); - return TRUE; } // upper bound on tooltip pixel dimensions so we never overflow the @@ -3927,18 +3924,14 @@ static gboolean _preview_draw(GtkWidget *widget, // clicks within this distance grip the divider without snapping it #define PREVIEW_DIVIDER_NEAR_PX 3.0 -static gboolean _preview_button_press(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _preview_button_press_cb(GtkGestureSingle *gesture, int n_press, + double ex, double ey, + dt_lib_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(gesture); dt_lib_neural_restore_t *d = (dt_lib_neural_restore_t *)self->data; - guint button = 0; - gdk_event_get_button((GdkEvent *)event, &button); - if(button != 1) return FALSE; - - double ex = 0.0, ey = 0.0; - gdk_event_get_coords((GdkEvent *)event, &ex, &ey); + if(gtk_gesture_single_get_button(gesture) != 1) return; // thumbnail picking mode: click to select area and trigger preview if(d->picking_thumbnail && d->export_cairo) @@ -3949,17 +3942,10 @@ static gboolean _preview_button_press(GtkWidget *widget, _picking_geometry(d, w, h, &img_w, &img_h, &ox, &oy); // convert click to normalized image coords. - // * RGB denoise / upscale: clamp so the crop rectangle stays - // within the image (master behaviour — the export-based preview - // needs this because the worker and draw share a single - // export_w-based scale). - // * raw denoise: no inner-margin clamp — user can pick corners. - // the raw worker CLAMPs crop_x/y, and _preview_draw pushes the - // rectangle inward to match. const float nx = (float)((ex - ox) / img_w); const float ny = (float)((ey - oy) / img_h); if(nx < 0.0f || nx > 1.0f || ny < 0.0f || ny > 1.0f) - return TRUE; + return; if(d->task == NEURAL_TASK_RAW_DENOISE) { @@ -3987,7 +3973,7 @@ static gboolean _preview_button_press(GtkWidget *widget, d->preview_requested = TRUE; dt_control_log(_("generating preview...")); _trigger_preview(self); - return TRUE; + return; } // click to start preview generation @@ -3995,56 +3981,51 @@ static gboolean _preview_button_press(GtkWidget *widget, { d->preview_requested = TRUE; _trigger_preview(self); - return TRUE; + return; } - if(!d->preview_ready) return FALSE; + if(!d->preview_ready) return; const int w = gtk_widget_get_allocated_width(widget); const int h = gtk_widget_get_allocated_height(widget); const int pw = d->preview_w; const int ph = d->preview_h; - if(pw <= 0 || ph <= 0) return FALSE; + if(pw <= 0 || ph <= 0) return; const double scale = fmax(1.0, fmin((double)w / pw, (double)h / ph)); - const double ox = (w - pw * scale) / 2.0; - const double div_x = ox + d->split_pos * pw * scale; + const double div_ox = (w - pw * scale) / 2.0; + const double div_x = div_ox + d->split_pos * pw * scale; if(fabs(ex - div_x) < PREVIEW_DIVIDER_NEAR_PX) { // precision grip on the divider — drag without snapping d->dragging_split = TRUE; - return TRUE; + return; } // click anywhere else snaps the divider then enters drag - d->split_pos = CLAMP((ex - ox) / (pw * scale), 0.0, 1.0); + d->split_pos = CLAMP((ex - div_ox) / (pw * scale), 0.0, 1.0); d->dragging_split = TRUE; gtk_widget_queue_draw(widget); - return TRUE; } -static gboolean _preview_button_release(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _preview_button_release_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { dt_lib_neural_restore_t *d = (dt_lib_neural_restore_t *)self->data; if(d->dragging_split) { d->dragging_split = FALSE; - return TRUE; } - return FALSE; } -static gboolean _preview_motion(GtkWidget *widget, - GdkEventMotion *event, - dt_lib_module_t *self) +static void _preview_motion_cb(GtkEventControllerMotion *controller, + double ex, double ey, + dt_lib_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); dt_lib_neural_restore_t *d = (dt_lib_neural_restore_t *)self->data; - double ex = 0.0, ey = 0.0; - gdk_event_get_coords((GdkEvent *)event, &ex, &ey); - // move crop rectangle while hovering in picking mode if(d->picking_thumbnail && d->export_cairo) { @@ -4072,7 +4053,7 @@ static gboolean _preview_motion(GtkWidget *widget, d->patch_center[1] = CLAMP(ry, half_h, 1.0f - half_h); } gtk_widget_queue_draw(widget); - return TRUE; + return; } if(d->dragging_split) @@ -4080,7 +4061,7 @@ static gboolean _preview_motion(GtkWidget *widget, const int w = gtk_widget_get_allocated_width(widget); const int pw = d->preview_w; const int ph = d->preview_h; - if(pw <= 0 || ph <= 0) return FALSE; + if(pw <= 0 || ph <= 0) return; const int ah = gtk_widget_get_allocated_height(widget); const double scale = fmax(1.0, fmin((double)w / pw, (double)ah / ph)); @@ -4089,7 +4070,7 @@ static gboolean _preview_motion(GtkWidget *widget, d->split_pos = CLAMP((ex - ox) / img_w, 0.0, 1.0); gtk_widget_queue_draw(widget); - return TRUE; + return; } // change cursor near divider @@ -4123,7 +4104,7 @@ static gboolean _preview_motion(GtkWidget *widget, } } - return FALSE; + return; } static void _selection_changed_callback(gpointer instance, @@ -4342,23 +4323,14 @@ void gui_init(dt_lib_module_t *self) "double-click to reset to center")); g_signal_connect(d->pick_button, "toggled", G_CALLBACK(_pick_toggled), self); - g_signal_connect(d->pick_button, "button-press-event", - G_CALLBACK(_pick_double_click), self); + dt_gui_connect_click_all(d->pick_button, _pick_double_click_cb, NULL, self); // preview area (resizable via dt_ui_resize_wrap) d->preview_area = GTK_WIDGET(dt_ui_resize_wrap(NULL, 200, CONF_PREVIEW_HEIGHT)); - gtk_widget_add_events(d->preview_area, - GDK_BUTTON_PRESS_MASK - | GDK_BUTTON_RELEASE_MASK - | GDK_POINTER_MOTION_MASK); g_signal_connect(d->preview_area, "draw", G_CALLBACK(_preview_draw), self); - g_signal_connect(d->preview_area, "button-press-event", - G_CALLBACK(_preview_button_press), self); - g_signal_connect(d->preview_area, "button-release-event", - G_CALLBACK(_preview_button_release), self); - g_signal_connect(d->preview_area, "motion-notify-event", - G_CALLBACK(_preview_motion), self); + dt_gui_connect_click(d->preview_area, _preview_button_press_cb, _preview_button_release_cb, self); + dt_gui_connect_motion(d->preview_area, _preview_motion_cb, NULL, NULL, self); // hover-zoom tooltip: shows the same preview at 2x so the user can // see individual pixels. only fires when there's a valid preview; // suppressed during inference / picking / dragging diff --git a/src/libs/snapshots.c b/src/libs/snapshots.c index 18cec1fa3920..a0493448ea95 100644 --- a/src/libs/snapshots.c +++ b/src/libs/snapshots.c @@ -562,15 +562,18 @@ static void _entry_activated_callback(GtkEntry *entry, dt_lib_module_t *self) gtk_widget_grab_focus(d->snapshot[index].button); } -static gboolean _lib_button_button_pressed_callback(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _lib_button_button_pressed_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { dt_lib_snapshots_t *d = self->data; + GtkWidget *widget = dt_gui_get_widget(gesture); const int index = _look_for_widget(self, widget, FALSE); - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) + if(dt_key_modifier_state() & GDK_CONTROL_MASK) { gtk_widget_hide(d->snapshot[index].name); gtk_widget_show(d->snapshot[index].entry); @@ -578,7 +581,6 @@ static gboolean _lib_button_button_pressed_callback(GtkWidget *widget, } gtk_widget_set_focus_on_click(widget, FALSE); - return gtk_widget_has_focus(d->snapshot[index].entry); } static void _init_snapshot_entry(dt_lib_module_t *self, @@ -589,8 +591,7 @@ static void _init_snapshot_entry(dt_lib_module_t *self, gtk_widget_set_name(s->button, "snapshot-button"); g_signal_connect(G_OBJECT(s->button), "toggled", G_CALLBACK(_lib_snapshots_toggled_callback), self); - g_signal_connect(G_OBJECT(s->button), "button-press-event", - G_CALLBACK(_lib_button_button_pressed_callback), self); + dt_gui_connect_click(s->button, _lib_button_button_pressed_callback, NULL, self); s->num = gtk_label_new(""); gtk_widget_set_name(s->num, "history-number"); diff --git a/src/libs/tagging.c b/src/libs/tagging.c index 55b56393ef2b..95aba8e33de8 100644 --- a/src/libs/tagging.c +++ b/src/libs/tagging.c @@ -1469,22 +1469,26 @@ static void _pop_menu_attached(GtkWidget *treeview, gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event); } -static gboolean _click_on_view_attached(GtkWidget *view, - GdkEventButton *event, - dt_lib_module_t *self) +static void _click_on_view_attached(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_lib_module_t *self) { dt_lib_tagging_t *d = self->data; + GtkWidget *view = dt_gui_get_widget(gesture); _unselect_all_in_view(d->dictionary_view); - if((dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) - || (dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) - || (dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY)) + const guint button = gtk_gesture_single_get_current_button(gesture); + if((n_press == 1 && button == GDK_BUTTON_SECONDARY) + || (n_press == 2 && button == GDK_BUTTON_PRIMARY) + || (n_press == 1 && button == GDK_BUTTON_PRIMARY)) { GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); GtkTreePath *path = NULL; // Get tree path for row that was clicked if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), - (gint)dt_gdk_event_get_x(event), (gint)dt_gdk_event_get_y(event), + (gint)x, (gint)y, &path, NULL, NULL, NULL)) { gboolean valid_tag = FALSE; @@ -1497,46 +1501,51 @@ static gboolean _click_on_view_attached(GtkWidget *view, { gtk_tree_selection_select_path(selection, path); dt_lib_gui_queue_update(self); - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS - && dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) + if(n_press == 1 && button == GDK_BUTTON_SECONDARY) { - _pop_menu_attached(view, event, self); + dt_gui_claim(gesture); + GdkEvent *event = gtk_get_current_event(); + _pop_menu_attached(view, (GdkEventButton *)event, self); + if(event) gdk_event_free(event); gtk_tree_path_free(path); - return TRUE; + return; } - else if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS - && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + else if(n_press == 2 && button == GDK_BUTTON_PRIMARY) { + dt_gui_claim(gesture); _detach_selected_tag(d->attached_view, self); gtk_tree_path_free(path); - return TRUE; + return; } } else { // prevent to select a darktable tag + dt_gui_claim(gesture); gtk_tree_path_free(path); - return TRUE; + return; } } gtk_tree_path_free(path); } - return FALSE; } -static gboolean _attached_key_pressed(GtkWidget *view, - GdkEventKey *event, - dt_lib_module_t *self) +static gboolean _attached_key_pressed(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_lib_module_t *self) { dt_lib_tagging_t *d = self->data; _unselect_all_in_view(d->dictionary_view); + GtkWidget *view = dt_gui_get_widget(controller); GtkTreeIter iter; GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->attached_view)); GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); if(gtk_tree_selection_get_selected(selection, &model, &iter)) { GtkTreePath *path = gtk_tree_model_get_path(model, &iter); - switch(dt_gdk_event_get_keyval(event)) + switch(keyval) { case GDK_KEY_Delete: case GDK_KEY_KP_Delete: @@ -1548,13 +1557,13 @@ static gboolean _attached_key_pressed(GtkWidget *view, } gtk_tree_path_free(path); } - if(dt_gdk_event_get_keyval(event) == GDK_KEY_Tab) + if(keyval == GDK_KEY_Tab) { gtk_tree_selection_unselect_all(selection); gtk_widget_grab_focus(GTK_WIDGET(d->entry)); return TRUE; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_ISO_Left_Tab) + else if(keyval == GDK_KEY_ISO_Left_Tab) { gtk_tree_selection_unselect_all(selection); return TRUE; @@ -1603,12 +1612,15 @@ static void _new_button_clicked(GtkButton *button, dt_lib_module_t *self) tagname ? tagname + 1 : d->last_tag, FALSE, TRUE); } -static gboolean _enter_key_pressed(GtkWidget *entry, - GdkEventKey *event, - dt_lib_module_t *self) +static gboolean _enter_key_pressed(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_lib_module_t *self) { dt_lib_tagging_t *d = self->data; - switch(dt_gdk_event_get_keyval(event)) + GtkWidget *entry = dt_gui_get_widget(controller); + switch(keyval) { case GDK_KEY_Return: case GDK_KEY_KP_Enter: @@ -2635,28 +2647,33 @@ static void _pop_menu_dictionary(GtkWidget *treeview, } } -static gboolean _click_on_view_dictionary(GtkWidget *view, - GdkEventButton *event, - dt_lib_module_t *self) +static void _click_on_view_dictionary(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_lib_module_t *self) { dt_lib_tagging_t *d = self->data; + GtkWidget *view = dt_gui_get_widget(gesture); _unselect_all_in_view(d->attached_view); - const int button_pressed = (dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS) ? dt_gdk_event_get_button(event) : 0; - const gboolean shift_pressed = dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK); - if((button_pressed == GDK_BUTTON_SECONDARY) // contextual menu - || (d->tree_flag && button_pressed == GDK_BUTTON_PRIMARY) // expand & drag - || (dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY)) // attach + GdkEvent *event = gtk_get_current_event(); + const GdkModifierType state = event ? dt_gdk_event_get_state(event) : 0; + const guint button = gtk_gesture_single_get_current_button(gesture); + const gboolean shift_pressed = dt_modifier_is(state, GDK_SHIFT_MASK); + if((button == GDK_BUTTON_SECONDARY && n_press == 1) // contextual menu + || (d->tree_flag && button == GDK_BUTTON_PRIMARY && n_press == 1) // expand & drag + || (n_press == 2 && button == GDK_BUTTON_PRIMARY)) // attach { GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); GtkTreePath *path = NULL; // Get tree path for row that was clicked if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), - (gint)dt_gdk_event_get_x(event), (gint)dt_gdk_event_get_y(event), + (gint)x, (gint)y, &path, NULL, NULL, NULL)) { if(d->tree_flag - && button_pressed == GDK_BUTTON_PRIMARY + && button == GDK_BUTTON_PRIMARY && n_press == 1 && !shift_pressed) { GtkTreeModel *model = gtk_tree_view_get_model(d->dictionary_view); @@ -2671,60 +2688,69 @@ static gboolean _click_on_view_dictionary(GtkWidget *view, d->drag.path = path; if(d->drag.lastpath) gtk_tree_path_free(d->drag.lastpath); d->drag.lastpath = NULL; - return FALSE; + if(event) gdk_event_free(event); + return; } else { gtk_tree_selection_select_path(selection, path); dt_lib_gui_queue_update(self); - if(button_pressed == GDK_BUTTON_SECONDARY) + if(button == GDK_BUTTON_SECONDARY && n_press == 1) { - _pop_menu_dictionary(view, event, self); + dt_gui_claim(gesture); + _pop_menu_dictionary(view, (GdkEventButton *)event, self); gtk_tree_path_free(path); - return TRUE; + if(event) gdk_event_free(event); + return; } else if(d->tree_flag - && button_pressed == GDK_BUTTON_PRIMARY + && button == GDK_BUTTON_PRIMARY && n_press == 1 && shift_pressed) { + dt_gui_claim(gesture); gtk_tree_view_expand_row(GTK_TREE_VIEW(view), path, TRUE); gtk_tree_path_free(path); - return TRUE; + if(event) gdk_event_free(event); + return; } - else if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS - && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + else if(n_press == 2 && button == GDK_BUTTON_PRIMARY) { + dt_gui_claim(gesture); _attach_selected_tag(self, d); gtk_tree_path_free(path); - return TRUE; + if(event) gdk_event_free(event); + return; } } } gtk_tree_path_free(path); } - return FALSE; + if(event) gdk_event_free(event); } -static gboolean _dictionary_key_pressed(GtkWidget *view, - GdkEventKey *event, - dt_lib_module_t *self) +static gboolean _dictionary_key_pressed(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_lib_module_t *self) { dt_lib_tagging_t *d = self->data; _unselect_all_in_view(d->attached_view); GtkTreeIter iter; GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->dictionary_view)); + GtkWidget *view = dt_gui_get_widget(controller); GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); gboolean res = FALSE; if(gtk_tree_selection_get_selected(selection, &model, &iter)) { GtkTreePath *path = gtk_tree_model_get_path(model, &iter); - switch(dt_gdk_event_get_keyval(event)) + switch(keyval) { case GDK_KEY_Return: case GDK_KEY_KP_Enter: { _attach_selected_tag(self, d); - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK)) + if(dt_modifier_is(state, GDK_SHIFT_MASK)) { gtk_tree_selection_unselect_all(selection); gtk_entry_set_text(GTK_ENTRY(d->entry), ""); @@ -2736,7 +2762,7 @@ static gboolean _dictionary_key_pressed(GtkWidget *view, case GDK_KEY_Left: if(path) { - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK)) + if(dt_modifier_is(state, GDK_SHIFT_MASK)) gtk_tree_view_collapse_all(GTK_TREE_VIEW(view)); else gtk_tree_view_collapse_row(GTK_TREE_VIEW(view), path); @@ -2747,7 +2773,7 @@ static gboolean _dictionary_key_pressed(GtkWidget *view, if(path) { gtk_tree_view_expand_row(GTK_TREE_VIEW(view), path, - dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK)); + dt_modifier_is(state, GDK_SHIFT_MASK)); res = TRUE; } break; @@ -2756,12 +2782,12 @@ static gboolean _dictionary_key_pressed(GtkWidget *view, } gtk_tree_path_free(path); } - if(dt_gdk_event_get_keyval(event) == GDK_KEY_Tab) + if(keyval == GDK_KEY_Tab) { gtk_tree_selection_unselect_all(selection); res = TRUE; } - else if(dt_gdk_event_get_keyval(event) == GDK_KEY_ISO_Left_Tab) + else if(keyval == GDK_KEY_ISO_Left_Tab) { gtk_tree_selection_unselect_all(selection); gtk_widget_grab_focus(GTK_WIDGET(d->entry)); @@ -3529,10 +3555,8 @@ void gui_init(dt_lib_module_t *self) "\npress Delete or double-click to detach" "\nright-click for other actions on attached tag," "\nTab to give the focus to entry")); - g_signal_connect(G_OBJECT(view), "button-press-event", - G_CALLBACK(_click_on_view_attached), (gpointer)self); - g_signal_connect(G_OBJECT(view), "key-press-event", - G_CALLBACK(_attached_key_pressed), (gpointer)self); + dt_gui_connect_click_all(view, _click_on_view_attached, NULL, self); + dt_gui_connect_key(view, _attached_key_pressed, self); g_signal_connect(gtk_tree_view_get_selection(view), "changed", G_CALLBACK(_tree_selection_changed), self); @@ -3591,8 +3615,7 @@ void gui_init(dt_lib_module_t *self) gtk_widget_add_events(GTK_WIDGET(w), GDK_KEY_RELEASE_MASK); g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(_tag_name_changed), (gpointer)self); - g_signal_connect(G_OBJECT(w), "key-press-event", - G_CALLBACK(_enter_key_pressed), (gpointer)self); + dt_gui_connect_key(w, _enter_key_pressed, self); d->entry = GTK_ENTRY(w); button = dtgtk_button_new(dtgtk_cairo_paint_multiply_small, 0, NULL); @@ -3669,10 +3692,8 @@ void gui_init(dt_lib_module_t *self) "\nright-click for other actions on selected tag" "\nshift+Tab to give the focus to entry")); - g_signal_connect(G_OBJECT(view), "button-press-event", - G_CALLBACK(_click_on_view_dictionary), (gpointer)self); - g_signal_connect(G_OBJECT(view), "key-press-event", - G_CALLBACK(_dictionary_key_pressed), (gpointer)self); + dt_gui_connect_click_all(view, _click_on_view_dictionary, NULL, self); + dt_gui_connect_key(view, _dictionary_key_pressed, self); gtk_tree_view_set_model(view, GTK_TREE_MODEL(d->dictionary_listfilter)); g_object_unref(d->dictionary_listfilter); g_object_set(G_OBJECT(view), "has-tooltip", TRUE, NULL); @@ -3767,12 +3788,14 @@ void gui_cleanup(dt_lib_module_t *self) } // http://stackoverflow.com/questions/4631388/transparent-floating-gtkentry -static gboolean _lib_tagging_tag_key_press(GtkWidget *entry, - GdkEventKey *event, - dt_lib_module_t *self) +static gboolean _lib_tagging_tag_key_press(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_lib_module_t *self) { dt_lib_tagging_t *d = self->data; - switch(dt_gdk_event_get_keyval(event)) + switch(keyval) { case GDK_KEY_Escape: g_list_free(d->floating_tag_imgs); @@ -3784,6 +3807,7 @@ static gboolean _lib_tagging_tag_key_press(GtkWidget *entry, case GDK_KEY_Return: case GDK_KEY_KP_Enter: { + GtkWidget *entry = dt_gui_get_widget(controller); dt_gui_cursor_set_busy(); const gchar *tag = gtk_entry_get_text(GTK_ENTRY(entry)); const gboolean res = dt_tag_attach_string_list(tag, d->floating_tag_imgs, TRUE); @@ -3895,8 +3919,7 @@ static void _lib_tagging_tag_show(dt_action_t *action) gtk_container_add(GTK_CONTAINER(d->floating_tag_window), entry); g_signal_connect(entry, "focus-out-event", G_CALLBACK(_lib_tagging_tag_destroy), d->floating_tag_window); - g_signal_connect(entry, "key-press-event", - G_CALLBACK(_lib_tagging_tag_key_press), self); + dt_gui_connect_key(entry, _lib_tagging_tag_key_press, self); gtk_widget_show_all(d->floating_tag_window); gtk_widget_grab_focus(entry); diff --git a/src/libs/tools/colorlabels.c b/src/libs/tools/colorlabels.c index 8e125a560912..a6e66f686c26 100644 --- a/src/libs/tools/colorlabels.c +++ b/src/libs/tools/colorlabels.c @@ -15,8 +15,6 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#include "common/gdk_event_utils.h" - #include "bauhaus/bauhaus.h" #include "common/colorlabels.h" #include "common/collection.h" @@ -45,9 +43,12 @@ typedef struct dt_lib_colorlabels_t } dt_lib_colorlabels_t; /* callback when a colorlabel button is clicked */ -static gboolean _lib_colorlabels_button_clicked_callback(GtkWidget *w, - GdkEventButton *event, - dt_lib_module_t *self); +static void _lib_colorlabels_button_press_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self); +static void _lib_colorlabels_enter_notify_callback(GtkEventControllerMotion *controller, + double x, double y, + dt_lib_module_t *self); gint _get_colorlabel(dt_lib_module_t *self, GtkWidget *w) { @@ -85,14 +86,14 @@ int position(const dt_lib_module_t *self) return 1001; } -static gboolean _lib_colorlabels_enter_notify_callback(GtkWidget *widget, - GdkEventCrossing *event, +static void _lib_colorlabels_enter_notify_callback(GtkEventControllerMotion *controller, + double x, double y, dt_lib_module_t *self) { + GtkWidget *widget = dt_gui_get_widget(controller); const gint colorlabel = _get_colorlabel(self, widget); darktable.control->element = (colorlabel + 1) % 6; - return FALSE; } static char *_get_tooltip_for(const int coloridx) @@ -137,12 +138,8 @@ void gui_init(dt_lib_module_t *self) gtk_widget_set_tooltip_markup(button, tooltip); g_free(tooltip); gtk_box_pack_start(GTK_BOX(self->widget), button, TRUE, TRUE, 0); - g_signal_connect(G_OBJECT(button), "button-press-event", - G_CALLBACK(_lib_colorlabels_button_clicked_callback), - self); - g_signal_connect(G_OBJECT(button), "enter-notify-event", - G_CALLBACK(_lib_colorlabels_enter_notify_callback), - self); + dt_gui_connect_click_all(button, _lib_colorlabels_button_press_callback, NULL, self); + dt_gui_connect_motion(button, NULL, _lib_colorlabels_enter_notify_callback, NULL, self); ac = dt_action_define(&darktable.control->actions_thumb, NULL, N_("color label"), button, &dt_action_def_color_label); } @@ -164,13 +161,15 @@ void gui_cleanup(dt_lib_module_t *self) #define FLOATING_ENTRY_WIDTH DT_PIXEL_APPLY_DPI(150) -static gboolean _lib_colorlabels_key_press(GtkWidget *entry, - GdkEventKey *event, - dt_lib_module_t *self) +static gboolean _lib_colorlabels_key_press_cb(GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + dt_lib_module_t *self) { dt_lib_colorlabels_t *d = self->data; - switch(dt_gdk_event_get_keyval(event)) + switch(keyval) { case GDK_KEY_Escape: gtk_widget_destroy(d->floating_window); @@ -181,6 +180,7 @@ static gboolean _lib_colorlabels_key_press(GtkWidget *entry, case GDK_KEY_Return: case GDK_KEY_KP_Enter: { + GtkWidget *entry = dt_gui_get_widget(controller); char confname[128]; const gchar *label = gtk_entry_get_text(GTK_ENTRY(entry)); snprintf(confname, sizeof(confname), "colorlabel/%s", @@ -210,7 +210,8 @@ static gboolean _lib_colorlabels_destroy(GtkWidget *widget, } static void _lib_colorlabels_edit(dt_lib_module_t *self, - GdkEventButton *event) + const gdouble root_x, + const gdouble root_y) { dt_lib_colorlabels_t *d = self->data; @@ -225,7 +226,7 @@ static void _lib_colorlabels_edit(dt_lib_module_t *self, gtk_widget_add_events(entry, GDK_FOCUS_CHANGE_MASK); gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1); g_signal_connect(entry, "focus-out-event", G_CALLBACK(_lib_colorlabels_destroy), self); - g_signal_connect(entry, "key-press-event", G_CALLBACK(_lib_colorlabels_key_press), self); + dt_gui_connect_key(entry, _lib_colorlabels_key_press_cb, self); gtk_widget_set_tooltip_text(entry, _("enter a description of how you use this color label")); if(use_popover) @@ -248,8 +249,8 @@ static void _lib_colorlabels_edit(dt_lib_module_t *self, else { // Legacy/X11 path: keep undecorated popup GtkWindow - const gint x = dt_gdk_event_get_root_x(event); - const gint y = dt_gdk_event_get_root_y(event) - DT_PIXEL_APPLY_DPI(50); + const gint x = root_x; + const gint y = root_y - DT_PIXEL_APPLY_DPI(50); d->floating_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); #ifdef GDK_WINDOWING_QUARTZ @@ -268,20 +269,26 @@ static void _lib_colorlabels_edit(dt_lib_module_t *self, } } -static gboolean _lib_colorlabels_button_clicked_callback(GtkWidget *w, - GdkEventButton *event, - dt_lib_module_t *self) +static void _lib_colorlabels_button_press_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { + GtkWidget *w = dt_gui_get_widget(gesture); dt_lib_colorlabels_t *d = self->data; const gint colorlabel = _get_colorlabel(self, w); - if(dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS - && dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY && colorlabel != 5) // The button to reset colorlabels needs no description { d->colorlabel = colorlabel; - _lib_colorlabels_edit(self, event); + GdkEvent *ev = gtk_get_current_event(); + if(ev) + { + gdouble root_x, root_y; + gdk_event_get_root_coords(ev, &root_x, &root_y); + _lib_colorlabels_edit(self, root_x, root_y); + } } else { @@ -291,7 +298,6 @@ static gboolean _lib_colorlabels_button_clicked_callback(GtkWidget *w, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_COLORLABEL, imgs); } - return TRUE; } // clang-format off diff --git a/src/libs/tools/darktable.c b/src/libs/tools/darktable.c index b954ba967952..ef19ebd0b3b3 100644 --- a/src/libs/tools/darktable.c +++ b/src/libs/tools/darktable.c @@ -55,9 +55,11 @@ static gboolean _lib_darktable_draw_callback(GtkWidget *widget, cairo_t *cr, dt_lib_module_t *self); /* button press callback */ -static gboolean _lib_darktable_button_press_callback(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self); +static void _lib_darktable_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self); const char *name(dt_lib_module_t *self) { @@ -96,8 +98,7 @@ void gui_init(dt_lib_module_t *self) /* connect callbacks */ g_signal_connect(G_OBJECT(self->widget), "draw", G_CALLBACK(_lib_darktable_draw_callback), self); - g_signal_connect(G_OBJECT(self->widget), "button-press-event", - G_CALLBACK(_lib_darktable_button_press_callback), self); + dt_gui_connect_click(self->widget, _lib_darktable_clicked, NULL, self); /* create a cairo surface of dt icon */ @@ -275,13 +276,14 @@ static gboolean _lib_darktable_draw_callback(GtkWidget *widget, return TRUE; } -static gboolean _lib_darktable_button_press_callback(GtkWidget *widget, - GdkEventButton *event, - dt_lib_module_t *self) +static void _lib_darktable_clicked(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { /* show about box */ darktable_show_about_dialog(); - return TRUE; } // clang-format off diff --git a/src/libs/tools/filter.c b/src/libs/tools/filter.c index 1274619849ac..705925e9455c 100644 --- a/src/libs/tools/filter.c +++ b/src/libs/tools/filter.c @@ -1,6 +1,6 @@ /* This file is part of darktable, - Copyright (C) 2011-2022 darktable developers. + Copyright (C) 2011-2026 darktable developers. darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -85,10 +85,9 @@ static GtkWidget *_lib_filter_get_count(dt_lib_module_t *self) return d->count; } -static gboolean _pref_show(GtkWidget *widget, GdkEventButton *event, dt_lib_module_t *self) +static void _pref_show(GtkButton *button, dt_lib_module_t *self) { - dt_view_filtering_show_pref_menu(darktable.view_manager, widget); - return TRUE; + dt_view_filtering_show_pref_menu(darktable.view_manager, GTK_WIDGET(button)); } void gui_init(dt_lib_module_t *self) @@ -102,7 +101,7 @@ void gui_init(dt_lib_module_t *self) GtkWidget *bt = dtgtk_button_new(dtgtk_cairo_paint_filtering_menu, 0, NULL); gtk_widget_set_tooltip_text(bt, _("filter preferences")); - g_signal_connect(G_OBJECT(bt), "button-press-event", G_CALLBACK(_pref_show), self); + g_signal_connect(G_OBJECT(bt), "clicked", G_CALLBACK(_pref_show), self); gtk_box_pack_start(GTK_BOX(self->widget), bt, FALSE, TRUE, 0); d->filter_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); diff --git a/src/libs/tools/global_toolbox.c b/src/libs/tools/global_toolbox.c index ca92f0a4fb5e..d1328fd2a50b 100644 --- a/src/libs/tools/global_toolbox.c +++ b/src/libs/tools/global_toolbox.c @@ -54,7 +54,8 @@ static void _lib_preferences_button_clicked(GtkWidget *widget, gpointer user_dat static void _lib_help_button_clicked(GtkWidget *widget, gpointer user_data); /* callbacks for key mapping button */ static void _lib_keymap_button_clicked(GtkWidget *widget, gpointer user_data); -static gboolean _lib_keymap_button_press_release(GtkWidget *button, GdkEventButton *event, gpointer user_data); +static void _lib_keymap_button_pressed(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data); +static void _lib_keymap_button_released(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data); const char *name(dt_lib_module_t *self) { @@ -502,8 +503,7 @@ void gui_init(dt_lib_module_t *self) "\n" "right-click to exit mapping mode")); g_signal_connect(G_OBJECT(d->keymap_button), "clicked", G_CALLBACK(_lib_keymap_button_clicked), d); - g_signal_connect(G_OBJECT(d->keymap_button), "button-press-event", G_CALLBACK(_lib_keymap_button_press_release), d); - g_signal_connect(G_OBJECT(d->keymap_button), "button-release-event", G_CALLBACK(_lib_keymap_button_press_release), d); + dt_gui_connect_click_all(d->keymap_button, _lib_keymap_button_pressed, _lib_keymap_button_released, (gpointer)(d)); // the rest of these is added in reverse order as they are always put at the end of the container. // that's done so that buttons added via Lua will come first. @@ -658,8 +658,17 @@ static void _set_mapping_mode_cursor(GtkWidget *widget) g_object_unref(cursor); } +static gboolean _shortcuts_dialog_open = FALSE; + static void _show_shortcuts_prefs(GtkWidget *w) { + /* guard against re-entrant calls: a long-press or right-click on the + * keymap button can trigger both the pressed and the released gesture + * callback while the (modeless) dialog is already running */ + if(_shortcuts_dialog_open) + return; + _shortcuts_dialog_open = TRUE; + GtkWidget *shortcuts_dialog = gtk_dialog_new_with_buttons(_("shortcuts"), GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)), GTK_DIALOG_DESTROY_WITH_PARENT, NULL, NULL); if(!_shortcuts_dialog_posize.w) @@ -679,6 +688,8 @@ static void _show_shortcuts_prefs(GtkWidget *w) gtk_dialog_run(GTK_DIALOG(shortcuts_dialog)); gtk_widget_destroy(shortcuts_dialog); + + _shortcuts_dialog_open = FALSE; } static void _main_do_event_keymap(GdkEvent *event, gpointer data) @@ -792,25 +803,28 @@ static void _lib_keymap_button_clicked(GtkWidget *widget, gpointer user_data) } } -static gboolean _lib_keymap_button_press_release(GtkWidget *button, GdkEventButton *event, gpointer user_data) +static guint _keymap_button_start_time = 0; + +static void _lib_keymap_button_pressed(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data) { - static guint start_time = 0; + darktable.control->confirm_mapping = !(dt_key_modifier_state() & GDK_CONTROL_MASK); + + _keymap_button_start_time = gtk_get_current_event_time(); - darktable.control->confirm_mapping = !dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY) + { + _show_shortcuts_prefs(NULL); + } +} +static void _lib_keymap_button_released(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data) +{ int delay = 0; g_object_get(gtk_settings_get_default(), "gtk-long-press-time", &delay, NULL); - if((dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) || - (dt_gdk_event_get_type(event) == GDK_BUTTON_RELEASE && dt_gdk_event_get_time(event) - start_time > delay)) + if(gtk_get_current_event_time() - _keymap_button_start_time > (guint)delay) { _show_shortcuts_prefs(NULL); - return TRUE; - } - else - { - start_time = dt_gdk_event_get_time(event); - return FALSE; } } diff --git a/src/libs/tools/lighttable.c b/src/libs/tools/lighttable.c index 39c77e5d4142..0c1a9852f68d 100644 --- a/src/libs/tools/lighttable.c +++ b/src/libs/tools/lighttable.c @@ -15,8 +15,6 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#include "common/gdk_event_utils.h" - #include #include "common/collection.h" @@ -211,24 +209,31 @@ static void _lib_lighttable_set_layout(dt_lib_module_t *self, _lib_lighttable_update_btn(self); } -static gboolean _lib_lighttable_layout_btn_release(GtkWidget *w, GdkEventButton *event, dt_lib_module_t *self) +static void _lib_lighttable_layout_btn_release_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { + GtkWidget *w = dt_gui_get_widget(gesture); dt_lib_tool_lighttable_t *d = self->data; const gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); // note : this is the state before the change dt_lighttable_layout_t new_layout = DT_LIGHTTABLE_LAYOUT_FILEMANAGER; + + GdkModifierType state; + gtk_get_current_event_state(&state); + if(!active) { // that means we want to activate the button if(w == d->layout_preview) { - d->fullpreview_focus = dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); + d->fullpreview_focus = dt_modifier_is(state, GDK_CONTROL_MASK); new_layout = DT_LIGHTTABLE_LAYOUT_PREVIEW; } else if(w == d->layout_culling_fix) { - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) + if(dt_modifier_is(state, GDK_CONTROL_MASK)) d->culling_init_restriction = DT_LIGHTTABLE_CULLING_RESTRICTION_COLLECTION; else d->culling_init_restriction = DT_LIGHTTABLE_CULLING_RESTRICTION_AUTO; @@ -249,23 +254,29 @@ static gboolean _lib_lighttable_layout_btn_release(GtkWidget *w, GdkEventButton else { // we can't exit from filemanager or zoomable - return TRUE; + return; } } _lib_lighttable_set_layout(self, new_layout); - return TRUE; } -static gboolean _lib_lighttable_restricted_btn_release(GtkWidget *w, GdkEventButton *event, dt_lib_module_t *self) +static void _lib_lighttable_do_restricted_btn(dt_lib_module_t *self, GtkWidget *w) { dt_lighttable_culling_restriction_t restriction = DT_LIGHTTABLE_CULLING_RESTRICTION_SELECTION; if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) - restriction = DT_LIGHTTABLE_CULLING_RESTRICTION_COLLECTION; // note : this is the state before the change + restriction = DT_LIGHTTABLE_CULLING_RESTRICTION_COLLECTION; dt_view_lighttable_set_culling_restricted_state(darktable.view_manager, restriction); _lib_lighttable_update_btn(self); - return TRUE; +} + +static void _lib_lighttable_restricted_btn_release_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) +{ + _lib_lighttable_do_restricted_btn(self, + dt_gui_get_widget(gesture)); } static void _lib_lighttable_key_accel_toggle_filemanager(dt_action_t *action) @@ -318,7 +329,7 @@ static void _lib_lighttable_key_accel_toggle_restricted_mode(dt_action_t *action if(d->layout == DT_LIGHTTABLE_LAYOUT_CULLING || dt_view_lighttable_preview_state(darktable.view_manager)) { // if we are already in culling layout or fullpreview, we switch between restricted and unrestricted - _lib_lighttable_restricted_btn_release(d->layout_culling_restricted, NULL, self); + _lib_lighttable_do_restricted_btn(self, d->layout_culling_restricted); } } @@ -462,31 +473,27 @@ void gui_init(dt_lib_module_t *self) dt_action_register(ac, NULL, _lib_lighttable_key_accel_toggle_filemanager, 0, 0); dt_gui_add_help_link(d->layout_filemanager, "layout_filemanager"); gtk_widget_set_tooltip_text(d->layout_filemanager, _("click to enter filemanager layout.")); - g_signal_connect(G_OBJECT(d->layout_filemanager), "button-release-event", - G_CALLBACK(_lib_lighttable_layout_btn_release), self); + dt_gui_connect_click(d->layout_filemanager, NULL, _lib_lighttable_layout_btn_release_cb, self); d->layout_zoomable = dtgtk_togglebutton_new(dtgtk_cairo_paint_lt_mode_zoom, 0, NULL); ac = dt_action_define(ltv, NULL, N_("toggle zoomable lighttable layout"), d->layout_zoomable, NULL); dt_action_register(ac, NULL, _lib_lighttable_key_accel_toggle_zoomable, 0, 0); dt_gui_add_help_link(d->layout_zoomable, "layout_zoomable"); gtk_widget_set_tooltip_text(d->layout_zoomable, _("click to enter zoomable lighttable layout.")); - g_signal_connect(G_OBJECT(d->layout_zoomable), "button-release-event", - G_CALLBACK(_lib_lighttable_layout_btn_release), self); + dt_gui_connect_click(d->layout_zoomable, NULL, _lib_lighttable_layout_btn_release_cb, self); d->layout_culling_fix = dtgtk_togglebutton_new(dtgtk_cairo_paint_lt_mode_culling_fixed, 0, NULL); ac = dt_action_define(ltv, NULL, N_("toggle culling mode"), d->layout_culling_fix, &_action_def_culling); dt_shortcut_register(ac, DT_ACTION_ELEMENT_DEFAULT, DT_ACTION_EFFECT_HOLD_TOGGLE, GDK_KEY_x, 0); dt_shortcut_register(ac, DT_ACTION_ELEMENT_CULLING_NO_RESTRICTION, DT_ACTION_EFFECT_HOLD_TOGGLE, GDK_KEY_x, GDK_SHIFT_MASK); dt_gui_add_help_link(d->layout_culling_fix, "layout_culling"); - g_signal_connect(G_OBJECT(d->layout_culling_fix), "button-release-event", - G_CALLBACK(_lib_lighttable_layout_btn_release), self); + dt_gui_connect_click(d->layout_culling_fix, NULL, _lib_lighttable_layout_btn_release_cb, self); d->layout_culling_dynamic = dtgtk_togglebutton_new(dtgtk_cairo_paint_lt_mode_culling_dynamic, 0, NULL); ac = dt_action_define(ltv, NULL, N_("toggle culling dynamic mode"), d->layout_culling_dynamic, NULL); dt_action_register(ac, NULL, _lib_lighttable_key_accel_toggle_culling_dynamic_mode, GDK_KEY_x, GDK_CONTROL_MASK); dt_gui_add_help_link(d->layout_culling_dynamic, "layout_culling"); - g_signal_connect(G_OBJECT(d->layout_culling_dynamic), "button-release-event", - G_CALLBACK(_lib_lighttable_layout_btn_release), self); + dt_gui_connect_click(d->layout_culling_dynamic, NULL, _lib_lighttable_layout_btn_release_cb, self); d->layout_preview = dtgtk_togglebutton_new(dtgtk_cairo_paint_lt_mode_fullpreview, 0, NULL); ac = dt_action_define(ltv, NULL, N_("preview"), d->layout_preview, &_action_def_preview); @@ -495,8 +502,7 @@ void gui_init(dt_lib_module_t *self) dt_shortcut_register(ac, DT_ACTION_ELEMENT_DEFAULT, DT_ACTION_EFFECT_HOLD, GDK_KEY_w, 0); dt_shortcut_register(ac, DT_ACTION_ELEMENT_PREVIEW_FOCUS_DETECT, DT_ACTION_EFFECT_HOLD, GDK_KEY_w, GDK_CONTROL_MASK); dt_gui_add_help_link(d->layout_preview, "layout_preview"); - g_signal_connect(G_OBJECT(d->layout_preview), "button-release-event", - G_CALLBACK(_lib_lighttable_layout_btn_release), self); + dt_gui_connect_click(d->layout_preview, NULL, _lib_lighttable_layout_btn_release_cb, self); d->layout_box = dt_gui_hbox(d->layout_filemanager, d->layout_zoomable, d->layout_culling_fix, d->layout_culling_dynamic, @@ -517,8 +523,7 @@ void gui_init(dt_lib_module_t *self) dt_action_register(ac, NULL, _lib_lighttable_key_accel_toggle_restricted_mode, GDK_KEY_r, GDK_CONTROL_MASK); dt_gui_add_help_link(d->layout_culling_restricted, "layout_culling"); gtk_widget_set_no_show_all(d->layout_culling_restricted, TRUE); - g_signal_connect(G_OBJECT(d->layout_culling_restricted), "button-release-event", - G_CALLBACK(_lib_lighttable_restricted_btn_release), self); + dt_gui_connect_click(d->layout_culling_restricted, NULL, _lib_lighttable_restricted_btn_release_cb, self); self->widget = dt_gui_hbox(d->layout_box, d->zoom, d->layout_culling_restricted); diff --git a/src/libs/tools/log_history.c b/src/libs/tools/log_history.c index b4ba8dfc472b..e9e1435df933 100644 --- a/src/libs/tools/log_history.c +++ b/src/libs/tools/log_history.c @@ -15,8 +15,6 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#include "common/gdk_event_utils.h" - #include "control/signal.h" #include "control/control.h" #include "dtgtk/button.h" @@ -37,14 +35,14 @@ typedef struct dt_lib_log_history_t static void _populate_list_box(dt_lib_module_t *self); static void _log_redraw_callback(gpointer instance, dt_lib_module_t *self); -static gboolean _button_press_release(GtkWidget *button, - GdkEventButton *event, - dt_lib_module_t *self); +static void _button_press_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self); static gboolean _suppress_popup(GtkWidget *widget, gpointer user_data); -static gboolean _label_button_press(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data); +static void _label_button_press_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + gpointer user_data); const char *name(dt_lib_module_t *self) { @@ -97,8 +95,7 @@ static void _populate_list_box(dt_lib_module_t *self) gtk_label_set_xalign(GTK_LABEL(label), 0.0f); gtk_label_set_selectable(GTK_LABEL(label), TRUE); dt_gui_add_class(label, "dt_monospace"); - g_signal_connect(G_OBJECT(label), "button-press-event", - G_CALLBACK(_label_button_press), NULL); + dt_gui_connect_click_all(label, _label_button_press_cb, NULL, NULL); g_signal_connect(G_OBJECT(label), "popup-menu", G_CALLBACK(_suppress_popup), NULL); gtk_list_box_insert(GTK_LIST_BOX(d->list_box), label, -1); @@ -137,29 +134,20 @@ static gboolean _suppress_popup(GtkWidget *widget, return TRUE; } -static gboolean _label_button_press(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static void _label_button_press_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + gpointer user_data) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) - return TRUE; - return FALSE; + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY) + dt_gui_claim(gesture); } -static gboolean _button_press_release(GtkWidget *button, - GdkEventButton *event, - dt_lib_module_t *self) +static void _button_press_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { - static guint start_time = 0; - - int delay = 0; - g_object_get(gtk_settings_get_default(), "gtk-long-press-time", &delay, NULL); - - if((dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS - && (dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY - || dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY)) - || (dt_gdk_event_get_type(event) == GDK_BUTTON_RELEASE - && dt_gdk_event_get_time(event) - start_time > delay)) + const guint button = gtk_gesture_single_get_current_button(gesture); + if(button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_SECONDARY) { dt_lib_log_history_t *d = self->data; if(gtk_widget_is_visible(d->popover)) @@ -171,12 +159,6 @@ static gboolean _button_press_release(GtkWidget *button, // hide unread indicator now that the user sees the log gtk_widget_hide(d->badge); } - return TRUE; - } - else - { - start_time = dt_gdk_event_get_time(event); - return FALSE; } } @@ -223,10 +205,7 @@ void gui_init(dt_lib_module_t *self) gtk_container_add(GTK_CONTAINER(d->scrolled), d->list_box); gtk_container_add(GTK_CONTAINER(d->popover), d->scrolled); - g_signal_connect(G_OBJECT(d->button), "button-press-event", - G_CALLBACK(_button_press_release), self); - g_signal_connect(G_OBJECT(d->button), "button-release-event", - G_CALLBACK(_button_press_release), self); + dt_gui_connect_click_all(d->button, _button_press_cb, NULL, self); self->widget = dt_gui_hbox(overlay); diff --git a/src/libs/tools/ratings.c b/src/libs/tools/ratings.c index 148c83ca362c..d7a01e36abae 100644 --- a/src/libs/tools/ratings.c +++ b/src/libs/tools/ratings.c @@ -15,8 +15,6 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#include "common/gdk_event_utils.h" - #include "common/ratings.h" #include "common/collection.h" #include "common/debug.h" @@ -40,17 +38,20 @@ typedef struct dt_lib_ratings_t /* redraw the ratings */ static gboolean _lib_ratings_draw_callback(GtkWidget *widget, cairo_t *cr, dt_lib_module_t *self); /* motion notify handler*/ -static gboolean _lib_ratings_motion_notify_callback(GtkWidget *widget, GdkEventMotion *event, - dt_lib_module_t *self); +static void _lib_ratings_motion_notify_callback(GtkEventControllerMotion *controller, + double x, double y, + dt_lib_module_t *self); /* motion leavel handler */ -static gboolean _lib_ratings_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event, - dt_lib_module_t *self); +static void _lib_ratings_leave_notify_callback(GtkEventControllerMotion *controller, + dt_lib_module_t *self); /* button press handler */ -static gboolean _lib_ratings_button_press_callback(GtkWidget *widget, GdkEventButton *event, - dt_lib_module_t *self); +static void _lib_ratings_button_press_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self); /* button release handler */ -static gboolean _lib_ratings_button_release_callback(GtkWidget *widget, GdkEventButton *event, - dt_lib_module_t *self); +static void _lib_ratings_button_release_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self); const char *name(dt_lib_module_t *self) { @@ -89,20 +90,17 @@ void gui_init(dt_lib_module_t *self) GtkWidget *drawing = gtk_drawing_area_new(); - gtk_widget_set_events(drawing, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK + gtk_widget_set_events(drawing, GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK + | GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_STRUCTURE_MASK); + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); /* connect callbacks */ gtk_widget_set_tooltip_text(drawing, _("set star rating for selected images")); dt_gui_add_class(drawing, "dt_transparent_background"); g_signal_connect(G_OBJECT(drawing), "draw", G_CALLBACK(_lib_ratings_draw_callback), self); - g_signal_connect(G_OBJECT(drawing), "button-press-event", G_CALLBACK(_lib_ratings_button_press_callback), self); - g_signal_connect(G_OBJECT(drawing), "button-release-event", G_CALLBACK(_lib_ratings_button_release_callback), - self); - g_signal_connect(G_OBJECT(drawing), "motion-notify-event", G_CALLBACK(_lib_ratings_motion_notify_callback), self); - g_signal_connect(G_OBJECT(drawing), "leave-notify-event", G_CALLBACK(_lib_ratings_leave_notify_callback), self); + dt_gui_connect_click(drawing, _lib_ratings_button_press_callback, _lib_ratings_button_release_callback, self); + dt_gui_connect_motion(drawing, _lib_ratings_motion_notify_callback, NULL, _lib_ratings_leave_notify_callback, self); gtk_box_pack_start(GTK_BOX(self->widget), drawing, TRUE, TRUE, 0); @@ -180,19 +178,20 @@ static gboolean _lib_ratings_draw_callback(GtkWidget *widget, cairo_t *crf, dt_l return TRUE; } -static gboolean _lib_ratings_motion_notify_callback(GtkWidget *widget, GdkEventMotion *event, - dt_lib_module_t *self) +static void _lib_ratings_motion_notify_callback(GtkEventControllerMotion *controller, + double x, double y, + dt_lib_module_t *self) { dt_lib_ratings_t *d = self->data; - d->pointerx = dt_gdk_event_get_x(event); - d->pointery = dt_gdk_event_get_y(event); + d->pointerx = x; + d->pointery = y; gtk_widget_queue_draw(self->widget); - return TRUE; } -static gboolean _lib_ratings_button_press_callback(GtkWidget *widget, GdkEventButton *event, - dt_lib_module_t *self) +static void _lib_ratings_button_press_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { dt_lib_ratings_t *d = self->data; if(d->current > 0) @@ -203,22 +202,20 @@ static gboolean _lib_ratings_button_press_callback(GtkWidget *widget, GdkEventBu dt_control_queue_redraw_center(); } - return TRUE; } -static gboolean _lib_ratings_button_release_callback(GtkWidget *widget, GdkEventButton *event, - dt_lib_module_t *self) +static void _lib_ratings_button_release_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_lib_module_t *self) { - return TRUE; } -static gboolean _lib_ratings_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event, - dt_lib_module_t *self) +static void _lib_ratings_leave_notify_callback(GtkEventControllerMotion *controller, + dt_lib_module_t *self) { dt_lib_ratings_t *d = self->data; d->pointery = d->pointerx = 0; gtk_widget_queue_draw(self->widget); - return TRUE; } diff --git a/src/libs/tools/timeline.c b/src/libs/tools/timeline.c index aa2ec475ad2d..f3d5119a9cd6 100644 --- a/src/libs/tools/timeline.c +++ b/src/libs/tools/timeline.c @@ -1,6 +1,6 @@ /* This file is part of darktable, - Copyright (C) 2019-2021 darktable developers. + Copyright (C) 2019-2026 darktable developers. darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1072,44 +1072,47 @@ static gboolean _lib_timeline_draw_callback(GtkWidget *widget, cairo_t *wcr, dt_ return TRUE; } -static gboolean _lib_timeline_button_press_callback(GtkWidget *w, GdkEventButton *e, dt_lib_module_t *self) +static void _lib_timeline_button_press_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { dt_lib_timeline_t *strip = self->data; - if(e->button == GDK_BUTTON_PRIMARY) + const guint button = gtk_gesture_single_get_current_button(gesture); + + if(button == GDK_BUTTON_PRIMARY && n_press == 1) { - if(e->type == GDK_BUTTON_PRESS) + if(x - strip->start_x < 2 && x - strip->start_x > -2) { - if(e->x - strip->start_x < 2 && e->x - strip->start_x > -2) - { - strip->start_x = strip->stop_x; - strip->start_t = strip->stop_t; - strip->stop_x = e->x; - strip->stop_t = _time_get_from_pos(e->x, strip); - strip->move_edge = TRUE; - } - else if(e->x - strip->stop_x < 2 && e->x - strip->stop_x > -2) - { - strip->stop_x = e->x; - strip->stop_t = _time_get_from_pos(e->x, strip); - strip->move_edge = TRUE; - } + strip->start_x = strip->stop_x; + strip->start_t = strip->stop_t; + strip->stop_x = x; + strip->stop_t = _time_get_from_pos(x, strip); + strip->move_edge = TRUE; + } + else if(x - strip->stop_x < 2 && x - strip->stop_x > -2) + { + strip->stop_x = x; + strip->stop_t = _time_get_from_pos(x, strip); + strip->move_edge = TRUE; + } + else + { + strip->start_x = strip->stop_x = x; + dt_datetime_t tt = _time_get_from_pos(x, strip); + if(_time_compare(tt, _time_init()) == 0) + strip->start_t = strip->stop_t = strip->time_maxi; //we are past the end so selection extends until the end else - { - strip->start_x = strip->stop_x = e->x; - dt_datetime_t tt = _time_get_from_pos(e->x, strip); - if(_time_compare(tt, _time_init()) == 0) - strip->start_t = strip->stop_t = strip->time_maxi; //we are past the end so selection extends until the end - else - strip->start_t = strip->stop_t = tt; - strip->move_edge = FALSE; - } - strip->selecting = TRUE; - strip->has_selection = TRUE; - gtk_widget_queue_draw(strip->timeline); + strip->start_t = strip->stop_t = tt; + strip->move_edge = FALSE; } + strip->selecting = TRUE; + strip->has_selection = TRUE; + gtk_widget_queue_draw(strip->timeline); } - else if(e->button == GDK_BUTTON_SECONDARY) + else if(button == GDK_BUTTON_SECONDARY) { // we remove the last rule if it's a datetime one const int nb_rules = dt_conf_get_int("plugins/lighttable/collect/num_rules"); @@ -1127,18 +1130,20 @@ static gboolean _lib_timeline_button_press_callback(GtkWidget *w, GdkEventButton } } } - - return FALSE; } -static gboolean _lib_timeline_button_release_callback(GtkWidget *w, GdkEventButton *e, dt_lib_module_t *self) +static void _lib_timeline_button_release_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_lib_module_t *self) { dt_lib_timeline_t *strip = self->data; if(strip->selecting) { - strip->stop_x = e->x; - dt_datetime_t tt = _time_get_from_pos(e->x, strip); + strip->stop_x = x; + dt_datetime_t tt = _time_get_from_pos(x, strip); if(_time_compare(tt, _time_init()) == 0) strip->stop_t = strip->time_maxi; //we are past the end so selection extends until the end else @@ -1160,14 +1165,14 @@ static gboolean _lib_timeline_button_release_callback(GtkWidget *w, GdkEventButt } strip->selecting = FALSE; - if(!strip->move_edge && dt_modifier_is(e->state, GDK_SHIFT_MASK)) + GdkModifierType state; + gtk_get_current_event_state(&state); + if(!strip->move_edge && dt_modifier_is(state, GDK_SHIFT_MASK)) _selection_collect(strip, DT_LIB_TIMELINE_MODE_RESET); else _selection_collect(strip, DT_LIB_TIMELINE_MODE_AND); gtk_widget_queue_draw(strip->timeline); } - - return TRUE; } static void _selection_start(dt_action_t *action) @@ -1258,14 +1263,17 @@ static gboolean _block_autoscroll(dt_lib_module_t *self) return TRUE; } -static gboolean _lib_timeline_motion_notify_callback(GtkWidget *w, GdkEventMotion *e, dt_lib_module_t *self) +static void _lib_timeline_motion_notify_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_lib_module_t *self) { dt_lib_timeline_t *strip = self->data; strip->in = TRUE; // auto-scroll if cursor is at one end of the panel - if((e->x < 10 || e->x > strip->panel_width - 10) && !strip->autoscroll) + if((x < 10 || x > strip->panel_width - 10) && !strip->autoscroll) { // first scroll immediately and then every 400ms until cursor quit the "auto-zone" if(_block_autoscroll(self)) @@ -1275,22 +1283,22 @@ static gboolean _lib_timeline_motion_notify_callback(GtkWidget *w, GdkEventMotio } } - strip->current_x = e->x; + strip->current_x = x; if(strip->selecting) { - strip->stop_x = e->x; - strip->stop_t = _time_get_from_pos(e->x, strip); + strip->stop_x = x; + strip->stop_t = _time_get_from_pos(x, strip); dt_control_change_cursor("default"); } else { // we change the cursor if we are close enough of a selection limit - if(e->x - strip->start_x < 2 && e->x - strip->start_x > -2) + if(x - strip->start_x < 2 && x - strip->start_x > -2) { dt_control_change_cursor("w-resize"); } - else if(e->x - strip->stop_x < 2 && e->x - strip->stop_x > -2) + else if(x - strip->stop_x < 2 && x - strip->stop_x > -2) { dt_control_change_cursor("e-resize"); } @@ -1300,25 +1308,29 @@ static gboolean _lib_timeline_motion_notify_callback(GtkWidget *w, GdkEventMotio } } gtk_widget_queue_draw(strip->timeline); - return TRUE; } -static gboolean _lib_timeline_scroll_callback(GtkWidget *w, GdkEventScroll *e, dt_lib_module_t *self) +static void _lib_timeline_scroll_cb(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_lib_module_t *self) { dt_lib_timeline_t *strip = self->data; + GdkModifierType state; + gtk_get_current_event_state(&state); + // zoom change (with Ctrl key) - if(dt_modifier_is(e->state, GDK_CONTROL_MASK)) + if(dt_modifier_is(state, GDK_CONTROL_MASK)) { int z = strip->zoom; - int delta_y = 0; - if(dt_gui_get_scroll_unit_delta(e, &delta_y)) + if(dy != 0.0) { - if(delta_y < 0) + if(dy < 0) { if(z != DT_LIB_TIMELINE_ZOOM_HOUR) z++; } - else if(delta_y > 0) + else if(dy > 0) { if(z != DT_LIB_TIMELINE_ZOOM_YEAR) z--; } @@ -1338,37 +1350,32 @@ static gboolean _lib_timeline_scroll_callback(GtkWidget *w, GdkEventScroll *e, d strip->surface = NULL; gtk_widget_queue_draw(strip->timeline); } - return TRUE; + return; } else { // timeline panning: right==down==forward - int delta_x = 0, delta_y = 0; - if(dt_gui_get_scroll_unit_deltas(e, &delta_x, &delta_y)) - { - int move = abs(delta_x) > abs(delta_y) ? delta_x : delta_y; - if(dt_modifier_is(e->state, GDK_SHIFT_MASK)) move *= 2; + int move = fabs(dx) > fabs(dy) ? (int)dx : (int)dy; + if(dt_modifier_is(state, GDK_SHIFT_MASK)) move *= 2; - _time_add(&(strip->time_pos), move, strip->zoom); - // we ensure that the fimlstrip stay in the bounds - strip->time_pos = _selection_scroll_to(strip->time_pos, strip); + _time_add(&(strip->time_pos), move, strip->zoom); + // we ensure that the fimlstrip stay in the bounds + strip->time_pos = _selection_scroll_to(strip->time_pos, strip); - cairo_surface_destroy(strip->surface); - strip->surface = NULL; - gtk_widget_queue_draw(strip->timeline); - } + cairo_surface_destroy(strip->surface); + strip->surface = NULL; + gtk_widget_queue_draw(strip->timeline); } - return FALSE; } -static gboolean _lib_timeline_mouse_leave_callback(GtkWidget *w, GdkEventCrossing *e, dt_lib_module_t *self) +static void _lib_timeline_mouse_leave_cb(GtkEventControllerMotion *controller, + dt_lib_module_t *self) { dt_lib_timeline_t *strip = self->data; strip->in = FALSE; gtk_widget_queue_draw(strip->timeline); - return TRUE; } void gui_init(dt_lib_module_t *self) @@ -1396,20 +1403,14 @@ void gui_init(dt_lib_module_t *self) /* creating timeline box*/ d->timeline = gtk_event_box_new(); - gtk_widget_add_events(d->timeline, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK - | GDK_BUTTON_RELEASE_MASK | darktable.gui->scroll_mask - | GDK_LEAVE_NOTIFY_MASK); + gtk_widget_add_events(d->timeline, 0); g_signal_connect(G_OBJECT(d->timeline), "draw", G_CALLBACK(_lib_timeline_draw_callback), self); - g_signal_connect(G_OBJECT(d->timeline), "button-press-event", G_CALLBACK(_lib_timeline_button_press_callback), - self); - g_signal_connect(G_OBJECT(d->timeline), "button-release-event", - G_CALLBACK(_lib_timeline_button_release_callback), self); - g_signal_connect(G_OBJECT(d->timeline), "scroll-event", G_CALLBACK(_lib_timeline_scroll_callback), self); - g_signal_connect(G_OBJECT(d->timeline), "motion-notify-event", G_CALLBACK(_lib_timeline_motion_notify_callback), - self); - g_signal_connect(G_OBJECT(d->timeline), "leave-notify-event", G_CALLBACK(_lib_timeline_mouse_leave_callback), - self); + dt_gui_connect_click_all(d->timeline, _lib_timeline_button_press_callback, _lib_timeline_button_release_callback, self); + dt_gui_connect_motion(d->timeline, _lib_timeline_motion_notify_cb, NULL, _lib_timeline_mouse_leave_cb, self); + dt_gui_connect_scroll(d->timeline, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _lib_timeline_scroll_cb, self); gtk_box_pack_start(GTK_BOX(self->widget), d->timeline, TRUE, TRUE, 0); diff --git a/src/libs/tools/viewswitcher.c b/src/libs/tools/viewswitcher.c index 766911f3acd1..19b178eaf70a 100644 --- a/src/libs/tools/viewswitcher.c +++ b/src/libs/tools/viewswitcher.c @@ -1,6 +1,6 @@ /* This file is part of darktable, - Copyright (C) 2011-2024 darktable developers. + Copyright (C) 2011-2026 darktable developers. darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -45,12 +45,19 @@ typedef struct dt_lib_viewswitcher_t } dt_lib_viewswitcher_t; /* callback when a view label is pressed */ -static gboolean _lib_viewswitcher_button_press_callback(GtkWidget *w, GdkEventButton *ev, const dt_view_t *view); +static void _lib_viewswitcher_button_press_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_view_t *view); /* helper function to create a label */ static GtkWidget *_lib_viewswitcher_create_label(dt_view_t *view); /* callback when view changed signal happens */ static void _lib_viewswitcher_view_changed_callback(gpointer instance, dt_view_t *old_view, dt_view_t *new_view, dt_lib_module_t *self); +static void _lib_viewswitcher_enter_callback(GtkEventControllerMotion *controller, + double x, double y, + GtkWidget *label); +static void _lib_viewswitcher_leave_callback(GtkEventControllerMotion *controller, + GtkWidget *label); static void _lib_viewswitcher_view_cannot_change_callback(gpointer instance, dt_view_t *old_view, dt_view_t *new_view, dt_lib_module_t *self); static void _switch_view(const dt_view_t *view); @@ -171,15 +178,22 @@ void gui_cleanup(dt_lib_module_t *self) self->data = NULL; } -static gboolean _lib_viewswitcher_enter_leave_notify_callback(GtkWidget *w, GdkEventCrossing *e, GtkLabel *l) +static void _lib_viewswitcher_enter_callback(GtkEventControllerMotion *controller, + double x, double y, + GtkWidget *label) { + GtkWidget *w = dt_gui_get_widget(controller); /* if not active view lets highlight */ - if(e->type == GDK_ENTER_NOTIFY && - strcmp(g_object_get_data(G_OBJECT(w), "view-label"), dt_view_manager_name(darktable.view_manager))) - gtk_widget_set_state_flags(GTK_WIDGET(l), GTK_STATE_FLAG_PRELIGHT, FALSE); + if(strcmp(g_object_get_data(G_OBJECT(w), "view-label"), dt_view_manager_name(darktable.view_manager))) + gtk_widget_set_state_flags(label, GTK_STATE_FLAG_PRELIGHT, FALSE); else - gtk_widget_unset_state_flags(GTK_WIDGET(l), GTK_STATE_FLAG_PRELIGHT); - return FALSE; + gtk_widget_unset_state_flags(label, GTK_STATE_FLAG_PRELIGHT); +} + +static void _lib_viewswitcher_leave_callback(GtkEventControllerMotion *controller, + GtkWidget *label) +{ + gtk_widget_unset_state_flags(label, GTK_STATE_FLAG_PRELIGHT); } static void _lib_viewswitcher_view_cannot_change_callback(gpointer instance, dt_view_t *old_view, @@ -259,13 +273,10 @@ static GtkWidget *_lib_viewswitcher_create_label(dt_view_t *view) gtk_widget_set_state_flags(b, GTK_STATE_FLAG_NORMAL, TRUE); /* connect button press handler */ - g_signal_connect(G_OBJECT(eb), "button-press-event", G_CALLBACK(_lib_viewswitcher_button_press_callback), view); + dt_gui_connect_click(eb, _lib_viewswitcher_button_press_callback, NULL, view); /* set enter/leave notify events and connect signals */ - gtk_widget_add_events(GTK_WIDGET(eb), GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); - - g_signal_connect(G_OBJECT(eb), "enter-notify-event", G_CALLBACK(_lib_viewswitcher_enter_leave_notify_callback), b); - g_signal_connect(G_OBJECT(eb), "leave-notify-event", G_CALLBACK(_lib_viewswitcher_enter_leave_notify_callback), b); + dt_gui_connect_motion(eb, NULL, _lib_viewswitcher_enter_callback, _lib_viewswitcher_leave_callback, b); return eb; } @@ -275,14 +286,14 @@ static void _switch_view(const dt_view_t *view) dt_ctl_switch_mode_to_by_view(view); } -static gboolean _lib_viewswitcher_button_press_callback(GtkWidget *w, GdkEventButton *ev, const dt_view_t *view) +static void _lib_viewswitcher_button_press_callback(GtkGestureSingle *gesture, int n_press, + double x, double y, + dt_view_t *view) { - if(ev->button == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY) { _switch_view(view); - return TRUE; } - return FALSE; } diff --git a/src/lua/preferences.c b/src/lua/preferences.c index fde7439b76f5..9311440f7085 100644 --- a/src/lua/preferences.c +++ b/src/lua/preferences.c @@ -1,6 +1,6 @@ /* This file is part of darktable, - Copyright (C) 2013-2023 darktable developers. + Copyright (C) 2013-2026 darktable developers. darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -410,94 +410,79 @@ static void response_callback_lua(GtkDialog *dialog, gint response_id, pref_elem } -static gboolean reset_widget_enum(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt) +static void reset_widget_enum_cb(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, pref_element *cur_elt) { - if(event->type == GDK_2BUTTON_PRESS) + if(n_press == 2) { gtk_combo_box_set_active(GTK_COMBO_BOX(cur_elt->widget), 0); - return TRUE; } - return FALSE; } -static gboolean reset_widget_dir(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt) +static void reset_widget_dir_cb(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, pref_element *cur_elt) { - if(event->type == GDK_2BUTTON_PRESS) + if(n_press == 2) { gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(cur_elt->widget), cur_elt->type_data.dir_data.default_value); - return TRUE; } - return FALSE; } -static gboolean reset_widget_file(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt) +static void reset_widget_file_cb(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, pref_element *cur_elt) { - if(event->type == GDK_2BUTTON_PRESS) + if(n_press == 2) { gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(cur_elt->widget), cur_elt->type_data.file_data.default_value); - return TRUE; } - return FALSE; } -static gboolean reset_widget_string(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt) +static void reset_widget_string_cb(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, pref_element *cur_elt) { - if(event->type == GDK_2BUTTON_PRESS) + if(n_press == 2) { gtk_entry_set_text(GTK_ENTRY(cur_elt->widget), cur_elt->type_data.string_data.default_value); - return TRUE; } - return FALSE; } -static gboolean click_widget_bool(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt) +static void click_widget_bool_cb(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, pref_element *cur_elt) { - if(event->type == GDK_BUTTON_PRESS) + if(n_press == 1) { gtk_button_clicked(GTK_BUTTON(cur_elt->widget)); - return TRUE; } - if(event->type == GDK_2BUTTON_PRESS) + else if(n_press == 2) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cur_elt->widget), cur_elt->type_data.bool_data.default_value); - return TRUE; } - return FALSE; } -static gboolean reset_widget_int(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt) +static void reset_widget_int_cb(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, pref_element *cur_elt) { - if(event->type == GDK_2BUTTON_PRESS) + if(n_press == 2) { gtk_spin_button_set_value(GTK_SPIN_BUTTON(cur_elt->widget), cur_elt->type_data.int_data.default_value); - return TRUE; } - return FALSE; } -static gboolean reset_widget_float(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt) +static void reset_widget_float_cb(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, pref_element *cur_elt) { - if(event->type == GDK_2BUTTON_PRESS) + if(n_press == 2) { gtk_spin_button_set_value(GTK_SPIN_BUTTON(cur_elt->widget), cur_elt->type_data.float_data.default_value); - return TRUE; } - return FALSE; } -static gboolean reset_widget_lua(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt) +static void reset_widget_lua_cb(GtkGestureSingle *gesture, gint n_press, gdouble x, gdouble y, pref_element *cur_elt) { - if(event->type == GDK_2BUTTON_PRESS) + if(n_press == 2) { char pref_name[1024]; get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name); @@ -513,9 +498,7 @@ static gboolean reset_widget_lua(GtkWidget *label, GdkEventButton *event, pref_e dt_lua_unlock(); dt_conf_set_string(pref_name, old_str); g_free(old_str); - return TRUE; } - return FALSE; } @@ -523,7 +506,7 @@ static void update_widget_enum(pref_element* cur_elt, GtkWidget* dialog, GtkWidg { char pref_name[1024]; get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name); - g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_enum), cur_elt); + dt_gui_connect_click(labelev, reset_widget_enum_cb, NULL, cur_elt); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_enum), cur_elt); gtk_combo_box_set_active(GTK_COMBO_BOX(cur_elt->widget), 0); const char *value = dt_conf_get_string_const(pref_name); @@ -555,7 +538,7 @@ static void update_widget_dir(pref_element* cur_elt, GtkWidget* dialog, GtkWidge get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name); const char *str = dt_conf_get_string_const(pref_name); gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(cur_elt->widget), str); - g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_dir), cur_elt); + dt_gui_connect_click(labelev, reset_widget_dir_cb, NULL, cur_elt); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_filechooser), cur_elt); } @@ -566,7 +549,7 @@ static void update_widget_file(pref_element* cur_elt, GtkWidget* dialog, GtkWidg get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name); const char *str = dt_conf_get_string_const(pref_name); gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(cur_elt->widget), str); - g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_file), cur_elt); + dt_gui_connect_click(labelev, reset_widget_file_cb, NULL, cur_elt); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_filechooser), cur_elt); } @@ -575,7 +558,7 @@ static void update_widget_string(pref_element* cur_elt, GtkWidget* dialog, GtkWi { char pref_name[1024]; get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name); - g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_string), cur_elt); + dt_gui_connect_click(labelev, reset_widget_string_cb, NULL, cur_elt); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_string), cur_elt); const char *str = dt_conf_get_string_const(pref_name); gtk_entry_set_text(GTK_ENTRY(cur_elt->widget), str); @@ -586,7 +569,7 @@ static void update_widget_bool(pref_element* cur_elt, GtkWidget* dialog, GtkWidg { char pref_name[1024]; get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name); - g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(click_widget_bool), cur_elt); + dt_gui_connect_click(labelev, click_widget_bool_cb, NULL, cur_elt); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_bool), cur_elt); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cur_elt->widget), dt_conf_get_bool(pref_name)); } @@ -597,7 +580,7 @@ static void update_widget_int(pref_element* cur_elt, GtkWidget* dialog, GtkWidge char pref_name[1024]; get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name); gtk_spin_button_set_value(GTK_SPIN_BUTTON(cur_elt->widget), dt_conf_get_int(pref_name)); - g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_int), cur_elt); + dt_gui_connect_click(labelev, reset_widget_int_cb, NULL, cur_elt); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_int), cur_elt); } @@ -607,7 +590,7 @@ static void update_widget_float(pref_element* cur_elt, GtkWidget* dialog, GtkWid char pref_name[1024]; get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name); gtk_spin_button_set_value(GTK_SPIN_BUTTON(cur_elt->widget), dt_conf_get_float(pref_name)); - g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_float), cur_elt); + dt_gui_connect_click(labelev, reset_widget_float_cb, NULL, cur_elt); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_float), cur_elt); } @@ -621,7 +604,7 @@ static void update_widget_lua(pref_element* cur_elt, GtkWidget* dialog, GtkWidge lua_pushstring(L, "reset"); lua_call(L, 2, 0); dt_lua_unlock(); - g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_lua), cur_elt); + dt_gui_connect_click(labelev, reset_widget_lua_cb, NULL, cur_elt); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_lua), cur_elt); } diff --git a/src/views/darkroom.c b/src/views/darkroom.c index dfb5caa78459..5e3b8819fc28 100644 --- a/src/views/darkroom.c +++ b/src/views/darkroom.c @@ -2756,38 +2756,48 @@ const dt_action_def_t _action_def_move _action_elements_move, NULL, TRUE }; -static gboolean _quickbutton_press_release(GtkWidget *button, - GdkEventButton *event, - GtkWidget *popover) -{ - static guint start_time = 0; +static guint _quickbutton_start_time = 0; - int delay = 0; - g_object_get(gtk_settings_get_default(), "gtk-long-press-time", &delay, NULL); +static void _quickbutton_pressed_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + GtkWidget *popover) +{ + GtkWidget *button = dt_gui_get_widget(gesture); - if((dt_gdk_event_get_type(event) == GDK_BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_SECONDARY) || - (dt_gdk_event_get_type(event) == GDK_BUTTON_RELEASE && dt_gdk_event_get_time(event) - start_time > delay)) + /* secondary click: show popup immediately */ + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_SECONDARY) { gtk_popover_set_relative_to(GTK_POPOVER(popover), button); - g_object_set(G_OBJECT(popover), "transitions-enabled", FALSE, NULL); - _toolbar_show_popup(popover); - return TRUE; } else { - start_time = dt_gdk_event_get_time(event); - return FALSE; + _quickbutton_start_time = gtk_get_current_event_time(); + } +} + +static void _quickbutton_released_cb(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + GtkWidget *popover) +{ + /* primary button long-press: show popup */ + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) + { + int delay = 0; + g_object_get(gtk_settings_get_default(), "gtk-long-press-time", &delay, NULL); + if(gtk_get_current_event_time() - _quickbutton_start_time > (guint)delay) + _toolbar_show_popup(popover); } } void connect_button_press_release(GtkWidget *w, GtkWidget *p) { - g_signal_connect(w, "button-press-event", - G_CALLBACK(_quickbutton_press_release), p); - g_signal_connect(w, "button-release-event", - G_CALLBACK(_quickbutton_press_release), p); + dt_gui_connect_click_all(w, _quickbutton_pressed_cb, _quickbutton_released_cb, p); } // cycle modules begins @@ -4564,7 +4574,16 @@ int button_pressed(dt_view_t *self, if(handled) return handled; } - if(which == GDK_BUTTON_PRIMARY && type == GDK_2BUTTON_PRESS) return 0; + if(which == GDK_BUTTON_PRIMARY && type == GDK_2BUTTON_PRESS) + { + // When masks are being edited, consume the double-click to avoid + // accidentally switching to lighttable. The mask-specific handlers + // (brush, path, gradient, object, and our ellipse/circle guards) + // already return 1 for 2BUTTON_PRESS when form_visible is set; + // this is a belt-and-suspenders check for edge cases. + if(dev->form_visible) return 1; + return 0; + } if(which == GDK_BUTTON_PRIMARY) { dt_control_change_cursor("pointer"); @@ -4985,82 +5004,90 @@ static gboolean _second_window_draw_callback(GtkWidget *widget, return TRUE; } -static gboolean _second_window_scrolled_callback(GtkWidget *widget, - GdkEventScroll *event, - dt_develop_t *dev) +static void _second_window_scrolled_callback(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_develop_t *dev) { - if(dev->gui_leaving) return TRUE; - int delta_y; - if(dt_gui_get_scroll_unit_delta(event, &delta_y)) - { - // Use pinned viewport if pinned, otherwise main dev's preview2 - dt_develop_t *pinned_dev = dev->preview2_pinned ? dev->preview2_pinned_dev : NULL; - if(pinned_dev && pinned_dev->gui_leaving) pinned_dev = NULL; + if(dev->gui_leaving) return; - dt_dev_viewport_t *port = pinned_dev ? &pinned_dev->preview2 : &dev->preview2; + // Use pinned viewport if pinned, otherwise main dev's preview2 + dt_develop_t *pinned_dev = dev->preview2_pinned ? dev->preview2_pinned_dev : NULL; + if(pinned_dev && pinned_dev->gui_leaving) pinned_dev = NULL; - const gboolean constrained = - dev->constrain_zoom && !dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK); - dt_dev_zoom_move(port, DT_ZOOM_SCROLL, 0.0f, delta_y < 0, - dt_gdk_event_get_x(event), dt_gdk_event_get_y(event), constrained); - } + dt_dev_viewport_t *port = pinned_dev ? &pinned_dev->preview2 : &dev->preview2; - return TRUE; + GdkModifierType state; + gtk_get_current_event_state(&state); + const gboolean constrained = + dev->constrain_zoom && !dt_modifier_is(state, GDK_CONTROL_MASK); + gdouble x = 0.0, y = 0.0; + const GdkEvent *current = gtk_get_current_event(); + if(current) gdk_event_get_coords(current, &x, &y); + dt_dev_zoom_move(port, DT_ZOOM_SCROLL, 0.0f, dy < 0, + x, y, constrained); } -static gboolean _second_window_button_pressed_callback(GtkWidget *w, - GdkEventButton *event, - dt_develop_t *dev) +static void _second_window_button_pressed_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_develop_t *dev) { - if(dev->gui_leaving) return FALSE; + if(dev->gui_leaving) return; // Use pinned viewport if pinned, otherwise main dev's preview2 dt_develop_t *pinned_dev = dev->preview2_pinned ? dev->preview2_pinned_dev : NULL; if(pinned_dev && pinned_dev->gui_leaving) pinned_dev = NULL; dt_dev_viewport_t *port = pinned_dev ? &pinned_dev->preview2 : &dev->preview2; + const guint button = gtk_gesture_single_get_current_button(gesture); // Handle double-click to reset zoom and center - if(dt_gdk_event_get_type(event) == GDK_2BUTTON_PRESS && dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(n_press == 2 && button == GDK_BUTTON_PRIMARY) { - dt_dev_zoom_move(port, DT_ZOOM_FIT, 0.0f, 0, - dt_gdk_event_get_x(event), dt_gdk_event_get_y(event), TRUE); - return TRUE; + dt_dev_zoom_move(port, DT_ZOOM_FIT, 0.0f, 0, x, y, TRUE); + return; } - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) + if(button == GDK_BUTTON_PRIMARY) { // store coordinates in logical pixels (as delivered by event) - darktable.control->button_x = dt_gdk_event_get_x(event); - darktable.control->button_y = dt_gdk_event_get_y(event); + darktable.control->button_x = x; + darktable.control->button_y = y; _dt_second_window_change_cursor(dev, "grabbing"); - return TRUE; + return; } - if(dt_gdk_event_get_button(event) == GDK_BUTTON_MIDDLE) + if(button == GDK_BUTTON_MIDDLE) { - dt_dev_zoom_move(port, DT_ZOOM_1, 0.0f, -2, - dt_gdk_event_get_x(event), dt_gdk_event_get_y(event), !dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)); - return TRUE; + GdkModifierType state; + gtk_get_current_event_state(&state); + dt_dev_zoom_move(port, DT_ZOOM_1, 0.0f, -2, x, y, !dt_modifier_is(state, GDK_CONTROL_MASK)); + return; } - return FALSE; } -static gboolean _second_window_button_released_callback(GtkWidget *w, - GdkEventButton *event, - dt_develop_t *dev) +static void _second_window_button_released_callback(GtkGestureSingle *gesture, + gint n_press, + gdouble x, + gdouble y, + dt_develop_t *dev) { - if(dt_gdk_event_get_button(event) == GDK_BUTTON_PRIMARY) _dt_second_window_change_cursor(dev, "default"); + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) _dt_second_window_change_cursor(dev, "default"); + GtkWidget *w = dt_gui_get_widget(gesture); gtk_widget_queue_draw(w); - return TRUE; } -static gboolean _second_window_mouse_moved_callback(GtkWidget *w, - GdkEventMotion *event, - dt_develop_t *dev) +static void _second_window_mouse_moved_callback(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_develop_t *dev) { - if(dev->gui_leaving) return FALSE; + if(dev->gui_leaving) return; - if(dt_gdk_event_get_state(event) & GDK_BUTTON1_MASK) + GdkModifierType state; + gtk_get_current_event_state(&state); + if(state & GDK_BUTTON1_MASK) { dt_control_t *ctl = darktable.control; @@ -5071,20 +5098,16 @@ static gboolean _second_window_mouse_moved_callback(GtkWidget *w, dt_dev_viewport_t *port = pinned_dev ? &pinned_dev->preview2 : &dev->preview2; dt_dev_zoom_move(port, DT_ZOOM_MOVE, -1.f, 0, - dt_gdk_event_get_x(event) - ctl->button_x, dt_gdk_event_get_y(event) - ctl->button_y, TRUE); - ctl->button_x = dt_gdk_event_get_x(event); - ctl->button_y = dt_gdk_event_get_y(event); - return TRUE; + x - ctl->button_x, y - ctl->button_y, TRUE); + ctl->button_x = x; + ctl->button_y = y; } - return FALSE; } -static gboolean _second_window_leave_callback(GtkWidget *widget, - GdkEventCrossing *event, - dt_develop_t *dev) +static void _second_window_leave_callback(GtkEventControllerMotion *controller, + dt_develop_t *dev) { _second_window_leave(dev); - return TRUE; } static gboolean _second_window_configure_callback(GtkWidget *da, @@ -5145,9 +5168,10 @@ static gboolean _second_window_configure_callback(GtkWidget *da, return TRUE; } -static gboolean _second_window_buttons_enter_notify_callback(GtkWidget *widget, - GdkEventCrossing *event, - GtkWidget *button_box) +static void _second_window_buttons_enter_notify_callback(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + GtkWidget *button_box) { // Make buttons visible and interactive. Using opacity instead of hide/show // keeps the GdkWindow (and its NSView tracking areas on macOS) always alive, @@ -5155,22 +5179,24 @@ static gboolean _second_window_buttons_enter_notify_callback(GtkWidget *widget, gtk_widget_set_opacity(button_box, 1.0); gtk_overlay_set_overlay_pass_through(GTK_OVERLAY(gtk_widget_get_parent(button_box)), button_box, FALSE); - return FALSE; } -static gboolean _second_window_buttons_leave_notify_callback(GtkWidget *widget, - GdkEventCrossing *event, - GtkWidget *button_box) +static void _second_window_buttons_leave_notify_callback(GtkEventControllerMotion *controller, + GtkWidget *button_box) { // GDK_NOTIFY_INFERIOR means the pointer moved into a child window (still // within the second window); keep the buttons visible in that case. - if(event->detail != GDK_NOTIFY_INFERIOR) + GdkEvent *event = gtk_get_current_event(); + if(event && event->crossing.detail == GDK_NOTIFY_INFERIOR) { - gtk_widget_set_opacity(button_box, 0.0); - gtk_overlay_set_overlay_pass_through(GTK_OVERLAY(gtk_widget_get_parent(button_box)), - button_box, TRUE); + gdk_event_free(event); + return; } - return FALSE; + if(event) gdk_event_free(event); + + gtk_widget_set_opacity(button_box, 0.0); + gtk_overlay_set_overlay_pass_through(GTK_OVERLAY(gtk_widget_get_parent(button_box)), + button_box, TRUE); } // Callback for the pin button in the overlay @@ -5232,13 +5258,8 @@ static void _darkroom_ui_second_window_init(GtkWidget *overlay, // Needed to display/hide the widgets. // Must be done before the window is realized. - gtk_widget_add_events(window, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); - // Show / hide controls on enter/leave events. - g_signal_connect(G_OBJECT(window), "enter-notify-event", - G_CALLBACK(_second_window_buttons_enter_notify_callback), event_box); - g_signal_connect(G_OBJECT(window), "leave-notify-event", - G_CALLBACK(_second_window_buttons_leave_notify_callback), event_box); + dt_gui_connect_motion(window, NULL, _second_window_buttons_enter_notify_callback, _second_window_buttons_leave_notify_callback, event_box); dev->preview2.pin_button = pin_button; @@ -5430,16 +5451,11 @@ static void _darkroom_display_second_window(dt_develop_t *dev) /* connect callbacks */ g_signal_connect(G_OBJECT(dev->preview2.widget), "draw", G_CALLBACK(_second_window_draw_callback), dev); - g_signal_connect(G_OBJECT(dev->preview2.widget), "scroll-event", - G_CALLBACK(_second_window_scrolled_callback), dev); - g_signal_connect(G_OBJECT(dev->preview2.widget), "button-press-event", - G_CALLBACK(_second_window_button_pressed_callback), dev); - g_signal_connect(G_OBJECT(dev->preview2.widget), "button-release-event", - G_CALLBACK(_second_window_button_released_callback), dev); - g_signal_connect(G_OBJECT(dev->preview2.widget), "motion-notify-event", - G_CALLBACK(_second_window_mouse_moved_callback), dev); - g_signal_connect(G_OBJECT(dev->preview2.widget), "leave-notify-event", - G_CALLBACK(_second_window_leave_callback), dev); + dt_gui_connect_scroll(dev->preview2.widget, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _second_window_scrolled_callback, dev); + dt_gui_connect_click_all(dev->preview2.widget, _second_window_button_pressed_callback, _second_window_button_released_callback, dev); + dt_gui_connect_motion(dev->preview2.widget, _second_window_mouse_moved_callback, NULL, _second_window_leave_callback, dev); g_signal_connect(G_OBJECT(dev->preview2.widget), "configure-event", G_CALLBACK(_second_window_configure_callback), dev); diff --git a/src/views/lighttable.c b/src/views/lighttable.c index 662d3ca46539..6d4826315ca5 100644 --- a/src/views/lighttable.c +++ b/src/views/lighttable.c @@ -635,6 +635,13 @@ static void _preview_set_state(dt_view_t *self, _preview_quit(self); } +static void _profile_popup_show_cb(GtkGestureSingle *gesture, int n_press, + double x, double y, + GtkWidget *popover) +{ + gtk_widget_show_all(popover); +} + void init(dt_view_t *self) { self->data = calloc(1, sizeof(dt_library_t)); @@ -1475,8 +1482,7 @@ void gui_init(dt_view_t *self) lib->profile_floating_window = gtk_popover_new(profile_button); g_object_set(G_OBJECT(lib->profile_floating_window), "transitions-enabled", FALSE, NULL); - g_signal_connect_swapped(G_OBJECT(profile_button), "button-press-event", - G_CALLBACK(gtk_widget_show_all), lib->profile_floating_window); + dt_gui_connect_click_all(profile_button, _profile_popup_show_cb, NULL, lib->profile_floating_window); GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); diff --git a/src/views/map.c b/src/views/map.c index 047f6bd1f46a..2676ee17a2a5 100644 --- a/src/views/map.c +++ b/src/views/map.c @@ -213,20 +213,26 @@ static void _view_map_dnd_get_callback(GtkWidget *widget, static void _view_map_changed_callback(OsmGpsMap *map, dt_view_t *self); /* callback that handles mouse scroll */ -static gboolean _view_map_scroll_event(GtkWidget *w, - GdkEventScroll *event, - dt_view_t *self); +static void _view_map_scroll_cb(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_view_t *self); /* callback that handles clicks on the map */ -static gboolean _view_map_button_press_callback(GtkWidget *w, - GdkEventButton *e, - dt_view_t *self); -static gboolean _view_map_button_release_callback(GtkWidget *w, - GdkEventButton *e, - dt_view_t *self); +static void _view_map_click_pressed_cb(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_view_t *self); +static void _view_map_click_released_cb(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_view_t *self); /* callback when the mouse is moved */ -static gboolean _view_map_motion_notify_callback(GtkWidget *w, - GdkEventMotion *e, - dt_view_t *self); +static void _view_map_motion_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_view_t *self); static gboolean _view_map_drag_motion_callback(GtkWidget *widget, GdkDragContext *dc, const gint x, @@ -767,8 +773,10 @@ void init(dt_view_t *self) gtk_drag_dest_set(GTK_WIDGET(lib->map), GTK_DEST_DEFAULT_ALL, target_list_internal, n_targets_internal, GDK_ACTION_MOVE); - g_signal_connect(GTK_WIDGET(lib->map), "scroll-event", - G_CALLBACK(_view_map_scroll_event), self); + dt_gui_connect_scroll(GTK_WIDGET(lib->map), + GTK_EVENT_CONTROLLER_SCROLL_VERTICAL + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + _view_map_scroll_cb, self); g_signal_connect(GTK_WIDGET(lib->map), "drag-data-received", G_CALLBACK(_drag_and_drop_received), self); g_signal_connect(GTK_WIDGET(lib->map), "drag-data-get", @@ -778,13 +786,28 @@ void init(dt_view_t *self) g_signal_connect(GTK_WIDGET(lib->map), "changed", G_CALLBACK(_view_map_changed_callback), self); - g_signal_connect_after(G_OBJECT(lib->map), "button-press-event", - G_CALLBACK(_view_map_button_press_callback), self); - g_signal_connect_after(G_OBJECT(lib->map), "button-release-event", - G_CALLBACK(_view_map_button_release_callback), self); - g_signal_connect(G_OBJECT(lib->map), "motion-notify-event", - G_CALLBACK(_view_map_motion_notify_callback), - self); + /* OsmGpsMap uses widget class vfuncs (button_press_event, + * motion_notify_event) for its event handling — not GtkGesture. + * In GTK3, the vfunc fires AFTER CAPTURE controllers and BEFORE + * BUBBLE controllers on the same widget. If the vfunc returns + * TRUE (event consumed), BUBBLE controllers never fire, which + * is why BUBBLE phase doesn't work here. + * + * Use CAPTURE phase so our controllers run first. We don't + * claim the sequence, so the event continues to the widget class + * vfunc and the map handles it normally. */ + { + GtkGestureSingle *g + = dt_gui_connect_click(lib->map, _view_map_click_pressed_cb, + _view_map_click_released_cb, self); + gtk_event_controller_set_propagation_phase( + GTK_EVENT_CONTROLLER(g), GTK_PHASE_CAPTURE); + } + { + GtkEventController *m + = dt_gui_connect_motion(lib->map, _view_map_motion_cb, NULL, NULL, self); + gtk_event_controller_set_propagation_phase(m, GTK_PHASE_CAPTURE); + } g_signal_connect(G_OBJECT(lib->map), "drag-motion", G_CALLBACK(_view_map_drag_motion_callback), self); @@ -1850,91 +1873,110 @@ static gboolean _view_map_drag_motion_callback(GtkWidget *widget, return FALSE; } -static gboolean _view_map_motion_notify_callback(GtkWidget *widget, - GdkEventMotion *e, - dt_view_t *self) +static void _view_map_motion_cb(GtkEventControllerMotion *controller, + gdouble x, + gdouble y, + dt_view_t *self) { dt_map_t *lib = self->data; - OsmGpsMapPoint *p = osm_gps_map_get_event_location(lib->map, (GdkEventButton *)e); - float lat, lon; - osm_gps_map_point_get_degrees(p, &lat, &lon); - _toast_log_lat_lon(lat, lon); + GdkEvent *event = gtk_get_current_event(); - if(lib->loc.drag - && lib->loc.main.id > 0 - && (abs(lib->start_drag_x - (int)ceil(e->x_root)) + - abs(lib->start_drag_y - (int)ceil(e->y_root))) > DT_PIXEL_APPLY_DPI(8)) - { - lib->loc.drag = FALSE; - osm_gps_map_image_remove(lib->map, lib->loc.main.location); - GtkTargetList *targets = gtk_target_list_new(target_list_internal, n_targets_internal); - - GdkDragContext *context = - gtk_drag_begin_with_coordinates(GTK_WIDGET(lib->map), targets, - GDK_ACTION_MOVE, 1, - (GdkEvent *)e, -1, -1); - - int width; - int height; - GdkPixbuf *location = _draw_location(lib, &width, &height, &lib->loc.main.data, TRUE); - if(location) - { - GtkWidget *image = gtk_image_new_from_pixbuf(location); - dt_gui_add_class(image, "dt_transparent_background"); - gtk_widget_set_name(image, "map-drag-icon"); - gtk_widget_show(image); - gtk_drag_set_icon_widget(context, image, - DT_PIXEL_APPLY_DPI(width), - DT_PIXEL_APPLY_DPI(height)); - g_object_unref(location); - } - gtk_target_list_unref(targets); - return TRUE; + if(event) + { + OsmGpsMapPoint *p = osm_gps_map_point_new_degrees(0.0, 0.0); + osm_gps_map_convert_screen_to_geographic(lib->map, x, y, p); + float lat, lon; + osm_gps_map_point_get_degrees(p, &lat, &lon); + osm_gps_map_point_free(p); + _toast_log_lat_lon(lat, lon); } - if(lib->start_drag - && lib->selected_images - && (abs(lib->start_drag_x - (int)ceil(e->x_root)) + - abs(lib->start_drag_y - (int)ceil(e->y_root))) > DT_PIXEL_APPLY_DPI(8)) + if(event && lib->loc.drag + && lib->loc.main.id > 0) { - const int nb = g_list_length(lib->selected_images); - for(const GSList *iter = lib->images; - iter; - iter = g_slist_next(iter)) + gdouble root_x, root_y; + gdk_event_get_root_coords(event, &root_x, &root_y); + if((abs(lib->start_drag_x - (int)ceil(root_x)) + + abs(lib->start_drag_y - (int)ceil(root_y))) > DT_PIXEL_APPLY_DPI(8)) { - dt_map_image_t *entry = iter->data; - if(entry->image) + lib->loc.drag = FALSE; + osm_gps_map_image_remove(lib->map, lib->loc.main.location); + GtkTargetList *targets = gtk_target_list_new(target_list_internal, n_targets_internal); + + GdkDragContext *context = + gtk_drag_begin_with_coordinates(GTK_WIDGET(lib->map), targets, + GDK_ACTION_MOVE, 1, + event, -1, -1); + + int width; + int height; + GdkPixbuf *location = _draw_location(lib, &width, &height, &lib->loc.main.data, TRUE); + if(location) { - const GList *sel_img = lib->selected_images; - if(entry->imgid == GPOINTER_TO_INT(sel_img->data)) + GtkWidget *image = gtk_image_new_from_pixbuf(location); + dt_gui_add_class(image, "dt_transparent_background"); + gtk_widget_set_name(image, "map-drag-icon"); + gtk_widget_show(image); + gtk_drag_set_icon_widget(context, image, + DT_PIXEL_APPLY_DPI(width), + DT_PIXEL_APPLY_DPI(height)); + g_object_unref(location); + } + gtk_target_list_unref(targets); + gdk_event_free(event); + return; + } + } + + if(event && lib->start_drag + && lib->selected_images) + { + gdouble root_x, root_y; + gdk_event_get_root_coords(event, &root_x, &root_y); + if((abs(lib->start_drag_x - (int)ceil(root_x)) + + abs(lib->start_drag_y - (int)ceil(root_y))) > DT_PIXEL_APPLY_DPI(8)) + { + const int nb = g_list_length(lib->selected_images); + for(const GSList *iter = lib->images; + iter; + iter = g_slist_next(iter)) + { + dt_map_image_t *entry = iter->data; + if(entry->image) { - if(entry->group_count == nb) + const GList *sel_img = lib->selected_images; + if(entry->imgid == GPOINTER_TO_INT(sel_img->data)) { - osm_gps_map_image_remove(lib->map, entry->image); - entry->image = NULL; + if(entry->group_count == nb) + { + osm_gps_map_image_remove(lib->map, entry->image); + entry->image = NULL; + } + else + _display_next_image(self, entry, TRUE); + break; } - else - _display_next_image(self, entry, TRUE); - break; } } - } - const int group_count = g_list_length(lib->selected_images); + const int group_count = g_list_length(lib->selected_images); - lib->start_drag = FALSE; - GtkTargetList *targets = gtk_target_list_new(target_list_all, n_targets_all); - GdkDragContext *context = gtk_drag_begin_with_coordinates(GTK_WIDGET(lib->map), targets, - GDK_ACTION_MOVE, 1, - (GdkEvent *)e, -1, -1); - _view_map_drag_set_icon(self, context, GPOINTER_TO_INT(lib->selected_images->data), - group_count); - gtk_target_list_unref(targets); - return TRUE; + lib->start_drag = FALSE; + GtkTargetList *targets = gtk_target_list_new(target_list_all, n_targets_all); + GdkDragContext *context = gtk_drag_begin_with_coordinates(GTK_WIDGET(lib->map), targets, + GDK_ACTION_MOVE, 1, + event, -1, -1); + _view_map_drag_set_icon(self, context, GPOINTER_TO_INT(lib->selected_images->data), + group_count); + gtk_target_list_unref(targets); + gdk_event_free(event); + return; + } } + if(event) gdk_event_free(event); - dt_map_image_t *entry = _view_map_get_entry_at_pos(self, e->x, e->y); + dt_map_image_t *entry = _view_map_get_entry_at_pos(self, x, y); if(entry) { // show image information if image is hovered @@ -1944,7 +1986,7 @@ static gboolean _view_map_motion_notify_callback(GtkWidget *widget, { _view_map_draw_image(entry, TRUE, DT_MAP_THUMB_THUMB, self); lib->last_hovered_entry = entry; - return TRUE; + return; } } else @@ -1957,7 +1999,6 @@ static gboolean _view_map_motion_notify_callback(GtkWidget *widget, lib->last_hovered_entry = NULL; } } - return FALSE; } static gboolean _zoom_and_center(const gint x, @@ -2003,38 +2044,54 @@ static gboolean _zoom_and_center(const gint x, return TRUE; } -static gboolean _view_map_scroll_event(GtkWidget *w, - GdkEventScroll *event, - dt_view_t *self) +static void _view_map_scroll_cb(GtkEventControllerScroll *controller, + gdouble dx, + gdouble dy, + dt_view_t *self) { dt_map_t *lib = self->data; - // check if the click was on image(s) or just some random position - dt_map_image_t *entry = _view_map_get_entry_at_pos(self, dt_gdk_event_get_x(event), dt_gdk_event_get_y(event)); + + // get the current event for position and modifier state + GdkEvent *event = gtk_get_current_event(); + if(!event) return; + gdouble x, y; + GdkModifierType state; + gdk_event_get_coords(event, &x, &y); + gdk_event_get_state(event, &state); + gdk_event_free(event); + + // determine scroll direction from dy + const gboolean scroll_down = dy > 0; + const gboolean scroll_up = dy < 0; + + // check if the scroll was on image(s) or just some random position + dt_map_image_t *entry = _view_map_get_entry_at_pos(self, x, y); if(entry) { - if(_display_next_image(self, entry, dt_gdk_event_get_scroll_direction(event) == GDK_SCROLL_DOWN)) - return TRUE; + if(_display_next_image(self, entry, scroll_down)) + return; } - OsmGpsMapPoint *p = osm_gps_map_get_event_location(lib->map, (GdkEventButton *)event); + OsmGpsMapPoint *p = osm_gps_map_point_new_degrees(0.0, 0.0); + osm_gps_map_convert_screen_to_geographic(lib->map, x, y, p); float lat, lon; osm_gps_map_point_get_degrees(p, &lat, &lon); + osm_gps_map_point_free(p); + if(lib->loc.main.id > 0) { - const gboolean increase = dt_gdk_event_get_scroll_direction(event) == GDK_SCROLL_UP - || dt_gdk_event_get_scroll_direction(event) == GDK_SCROLL_RIGHT; - const gboolean decrease = dt_gdk_event_get_scroll_direction(event) == GDK_SCROLL_DOWN - || dt_gdk_event_get_scroll_direction(event) == GDK_SCROLL_LEFT; + const gboolean increase = scroll_up; + const gboolean decrease = scroll_down; if(dt_map_location_included(lon, lat, &lib->loc.main.data)) { - if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_SHIFT_MASK)) + if(dt_modifier_is(state, GDK_SHIFT_MASK)) { if(increase) lib->loc.main.data.delta1 *= 1.1; else if(decrease) lib->loc.main.data.delta1 /= 1.1; } - else if(dt_modifier_is(dt_gdk_event_get_state(event), GDK_CONTROL_MASK)) + else if(dt_modifier_is(state, GDK_CONTROL_MASK)) { if(increase) lib->loc.main.data.delta2 *= 1.1; @@ -2048,7 +2105,7 @@ static gboolean _view_map_scroll_event(GtkWidget *w, lib->loc.main.data.delta1 *= 1.1; lib->loc.main.data.delta2 *= 1.1; } - else if (decrease) + else if(decrease) { lib->loc.main.data.delta1 /= 1.1; lib->loc.main.data.delta2 /= 1.1; @@ -2057,27 +2114,32 @@ static gboolean _view_map_scroll_event(GtkWidget *w, _view_map_draw_main_location(lib, &lib->loc.main); _view_map_update_location_geotag(self); _view_map_signal_change_wait(self, 3); // wait 3/10 sec after last scroll - return TRUE; + return; } else // scroll on the map. try to keep the map where it is { _toast_log_lat_lon(lat, lon); - return _zoom_and_center(dt_gdk_event_get_x(event), dt_gdk_event_get_y(event), dt_gdk_event_get_scroll_direction(event), self); + _zoom_and_center(x, y, scroll_up ? GDK_SCROLL_UP : GDK_SCROLL_DOWN, self); + return; } } else { _toast_log_lat_lon(lat, lon); - return _zoom_and_center(dt_gdk_event_get_x(event), dt_gdk_event_get_y(event), dt_gdk_event_get_scroll_direction(event), self); + _zoom_and_center(x, y, scroll_up ? GDK_SCROLL_UP : GDK_SCROLL_DOWN, self); } - return FALSE; } -static gboolean _view_map_button_press_callback(GtkWidget *w, - GdkEventButton *e, - dt_view_t *self) +static void _view_map_click_pressed_cb(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_view_t *self) { dt_map_t *lib = self->data; + GdkModifierType state; + gtk_get_current_event_state(&state); + if(lib->selected_images) { g_list_free(lib->selected_images); @@ -2087,9 +2149,7 @@ static gboolean _view_map_button_press_callback(GtkWidget *w, if(lib->osd) { // check if the OSD circle was clicked - GValue value = { - 0, - }; + GValue value = { 0 }; g_object_get_property((GObject*) lib->osd, "osd-x", &value); const gint osd_x = g_value_get_int(&value); @@ -2098,46 +2158,52 @@ static gboolean _view_map_button_press_callback(GtkWidget *w, g_object_get_property((GObject*) lib->osd, "dpad-radius", &value); const gint dpad_radius = g_value_get_int(&value); g_object_get_property((GObject*) lib->osd, "show-zoom", &value); - // g_value_get_boolean returns FALSE although the value is 1! Bug? const gint show_zoom = g_value_get_int(&value); - const gint zoom_height = show_zoom? 40:0; + const gint zoom_height = show_zoom ? 40 : 0; - if(e->x >= osd_x && e->x <= osd_x+2*dpad_radius && - e->y >= osd_y && e->y <= osd_y+2*dpad_radius+zoom_height) + if(x >= osd_x && x <= osd_x + 2 * dpad_radius + && y >= osd_y && y <= osd_y + 2 * dpad_radius + zoom_height) { - return FALSE; + return; } } - if(e->button == GDK_BUTTON_PRIMARY) + if(gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY) { - // check if the click was in a location form - crtl gives priority to images + // check if the click was in a location form - ctrl gives priority to images if(lib->loc.main.id > 0 && (lib->loc.main.data.shape != MAP_LOCATION_SHAPE_POLYGONS) - && !dt_modifier_is(e->state, GDK_CONTROL_MASK)) + && !dt_modifier_is(state, GDK_CONTROL_MASK)) { - - OsmGpsMapPoint *p = osm_gps_map_get_event_location(lib->map, e); + OsmGpsMapPoint *p = osm_gps_map_point_new_degrees(0.0, 0.0); + osm_gps_map_convert_screen_to_geographic(lib->map, x, y, p); float lat, lon; osm_gps_map_point_get_degrees(p, &lat, &lon); + osm_gps_map_point_free(p); if(dt_map_location_included(lon, lat, &lib->loc.main.data)) { - if(!dt_modifier_is(e->state, GDK_SHIFT_MASK)) + if(!dt_modifier_is(state, GDK_SHIFT_MASK)) { - lib->start_drag_x = ceil(e->x_root); - lib->start_drag_y = ceil(e->y_root); + GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(lib->map)); + GdkDevice *pointer = gdk_seat_get_pointer(gdk_display_get_default_seat(display)); + gint root_x, root_y; + gdk_device_get_position(pointer, NULL, &root_x, &root_y); + lib->start_drag_x = root_x; + lib->start_drag_y = root_y; lib->loc.drag = TRUE; - return TRUE; + return; } } } // check if another location is clicked - ctrl gives priority to images - if(!dt_modifier_is(e->state, GDK_CONTROL_MASK) && - dt_conf_get_bool("plugins/map/showalllocations")) + if(!dt_modifier_is(state, GDK_CONTROL_MASK) + && dt_conf_get_bool("plugins/map/showalllocations")) { - OsmGpsMapPoint *p = osm_gps_map_get_event_location(lib->map, e); + OsmGpsMapPoint *p = osm_gps_map_point_new_degrees(0.0, 0.0); + osm_gps_map_convert_screen_to_geographic(lib->map, x, y, p); float lat, lon; osm_gps_map_point_get_degrees(p, &lat, &lon); + osm_gps_map_point_free(p); for(GList *other = lib->loc.others; other; other = g_list_next(other)) @@ -2154,44 +2220,42 @@ static gboolean _view_map_button_press_callback(GtkWidget *w, G_CALLBACK(_view_map_collection_changed), self); dt_control_signal_unblock_by_func(darktable.signals, G_CALLBACK(_view_map_geotag_changed), self); - return TRUE; + return; } } } // check if the click was on image(s) or just some random position lib->selected_images = - _view_map_get_imgs_at_pos(self, e->x, e->y, + _view_map_get_imgs_at_pos(self, x, y, &lib->start_drag_offset_x, &lib->start_drag_offset_y, - !dt_modifier_is(e->state, GDK_SHIFT_MASK)); - if(e->type == GDK_BUTTON_PRESS) + !dt_modifier_is(state, GDK_SHIFT_MASK)); + if(n_press == 1) { if(lib->selected_images) { - lib->start_drag_x = ceil(e->x_root); - lib->start_drag_y = ceil(e->y_root); + GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(lib->map)); + GdkDevice *pointer = gdk_seat_get_pointer(gdk_display_get_default_seat(display)); + gint root_x, root_y; + gdk_device_get_position(pointer, NULL, &root_x, &root_y); + lib->start_drag_x = root_x; + lib->start_drag_y = root_y; lib->start_drag = TRUE; - return TRUE; - } - else - { - return FALSE; } } - if(e->type == GDK_2BUTTON_PRESS) + else if(n_press == 2) { if(lib->selected_images) { // open the image in darkroom dt_control_set_mouse_over_id(GPOINTER_TO_INT(lib->selected_images->data)); dt_ctl_switch_mode_to("darkroom"); - return TRUE; } else { // zoom into that position float longitude, latitude; OsmGpsMapPoint *pt = osm_gps_map_point_new_degrees(0.0, 0.0); - osm_gps_map_convert_screen_to_geographic(lib->map, e->x, e->y, pt); + osm_gps_map_convert_screen_to_geographic(lib->map, x, y, pt); osm_gps_map_point_get_degrees(pt, &latitude, &longitude); osm_gps_map_point_free(pt); int zoom, max_zoom; @@ -2199,22 +2263,21 @@ static gboolean _view_map_button_press_callback(GtkWidget *w, zoom = MIN(zoom + 1, max_zoom); _view_map_center_on_location(self, longitude, latitude, zoom); } - return TRUE; } } - return FALSE; } -static gboolean _view_map_button_release_callback(GtkWidget *w, - GdkEventButton *e, - dt_view_t *self) +static void _view_map_click_released_cb(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_view_t *self) { dt_map_t *lib = self->data; lib->start_drag = FALSE; lib->start_drag_offset_x = 0; lib->start_drag_offset_y = 0; lib->loc.drag = FALSE; - return FALSE; } static gboolean _view_map_display_selected(const gpointer user_data)