From afe22da73126600d4c20841d62dceda2ac637534 Mon Sep 17 00:00:00 2001 From: Christian-Kr Date: Sun, 2 Jun 2024 11:35:15 +0200 Subject: [PATCH 1/4] Migration of button-pressed and button-released to new GtkGesture API. --- src/dtgtk/button.c | 23 +++++++++++++ src/dtgtk/button.h | 16 +++++++++ src/dtgtk/thumbtable.c | 73 ++++++++++++++++++++++-------------------- src/dtgtk/thumbtable.h | 4 +++ 4 files changed, 82 insertions(+), 34 deletions(-) diff --git a/src/dtgtk/button.c b/src/dtgtk/button.c index 500479f586bc..a8569bddd6f8 100644 --- a/src/dtgtk/button.c +++ b/src/dtgtk/button.c @@ -147,6 +147,29 @@ void dtgtk_button_set_active(GtkDarktableButton *button, gboolean active) button->icon_flags &= ~CPF_ACTIVE; } +GtkGesture *dtgtk_button_default_handler_new( + GtkWidget *widget, + guint button, + GCallback func_press, + GCallback func_release, + gpointer data) +{ + GtkGesture *gesture = gtk_gesture_multi_press_new(widget); + gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), button); + + if (func_press != NULL) + { + g_signal_connect(G_OBJECT(gesture), "pressed", func_press, data); + } + + if (func_release != NULL) + { + g_signal_connect(G_OBJECT(gesture), "released", func_release, data); + } + + return gesture; +} + // clang-format off // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py // vim: shiftwidth=2 expandtab tabstop=2 cindent diff --git a/src/dtgtk/button.h b/src/dtgtk/button.h index b0994cb1eb3a..6393d2f16d45 100644 --- a/src/dtgtk/button.h +++ b/src/dtgtk/button.h @@ -48,6 +48,22 @@ void dtgtk_button_set_paint(GtkDarktableButton *button, DTGTKCairoPaintIconFunc /** set the active state of the button icon */ void dtgtk_button_set_active(GtkDarktableButton *button, gboolean active); +/** + * Create default handler for primary button. + * @param widget widget to create the gesture object for + * @param button type of button + * @param func_press gets called when button will be pressed + * @param func_release gets called when button will be released + * @param data user data send to function + * @return new gesture object; receiver is responsible for deletion + */ +GtkGesture *dtgtk_button_default_handler_new( + GtkWidget *widget, + guint button, + GCallback func_press, + GCallback func_release, + gpointer data); + G_END_DECLS // clang-format off diff --git a/src/dtgtk/thumbtable.c b/src/dtgtk/thumbtable.c index 2189028293cd..b7d1d2120db1 100644 --- a/src/dtgtk/thumbtable.c +++ b/src/dtgtk/thumbtable.c @@ -1296,9 +1296,12 @@ static gboolean _event_enter_notify(GtkWidget *widget, return TRUE; } -static gboolean _event_button_press(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static gboolean _event_button_press_primary( + GtkGestureMultiPress *gesture, + int n_press, + double x, + double y, + gpointer user_data) { dt_set_backthumb_time(0.0); @@ -1306,15 +1309,14 @@ static gboolean _event_button_press(GtkWidget *widget, const dt_imgid_t id = dt_control_get_mouse_over_id(); if(dt_is_valid_imgid(id) - && event->button == 1 - && (table->mode == DT_THUMBTABLE_MODE_FILEMANAGER - || table->mode == DT_THUMBTABLE_MODE_ZOOM) - && event->type == GDK_2BUTTON_PRESS) + && n_press == 2 + && (table->mode == DT_THUMBTABLE_MODE_FILEMANAGER || table->mode == DT_THUMBTABLE_MODE_ZOOM)) { + // switch to darkroom on double click dt_view_manager_switch(darktable.view_manager, "darkroom"); } - if(event->button == 1 && event->type == GDK_BUTTON_PRESS) + if (n_press == 1) { // make sure any edition field loses the focus gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui)); @@ -1322,8 +1324,7 @@ static gboolean _event_button_press(GtkWidget *widget, if(table->mode != DT_THUMBTABLE_MODE_ZOOM && id < 1 - && event->button == 1 - && event->type == GDK_BUTTON_PRESS) + && n_press == 1) { // we click in an empty area, let's deselect all images dt_selection_clear(darktable.selection); @@ -1333,7 +1334,7 @@ static gboolean _event_button_press(GtkWidget *widget, if(table->mode != DT_THUMBTABLE_MODE_ZOOM) return FALSE; - if(event->button == 1 && event->type == GDK_BUTTON_PRESS) + if(n_press == 1) { table->dragging = TRUE; table->drag_dx = table->drag_dy = 0; @@ -1377,9 +1378,12 @@ static gboolean _event_motion_notify(GtkWidget *widget, return ret; } -static gboolean _event_button_release(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static gboolean _event_button_release_primary( + GtkGestureMultiPress *gesture, + int n_press, + double x, + double y, + gpointer user_data) { dt_set_backthumb_time(0.0); @@ -1391,27 +1395,25 @@ static gboolean _event_button_release(GtkWidget *widget, dt_view_manager_t *vm = darktable.view_manager; dt_view_t *view = vm->current_view; const dt_imgid_t id = dt_control_get_mouse_over_id(); + + // get current state from the event to get the modifier keys + GdkModifierType state; + gtk_get_current_event_state(&state); + if(dt_is_valid_imgid(id) - && event->button == 1 && table->mode == DT_THUMBTABLE_MODE_FILMSTRIP - && event->type == GDK_BUTTON_RELEASE - && !strcmp(view->module_name, "map") - && dt_modifier_is(event->state, 0)) + && dt_modifier_is(state, 0)) { - DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, - DT_SIGNAL_VIEWMANAGER_THUMBTABLE_ACTIVATE, id); - return TRUE; - } - else if(dt_is_valid_imgid(id) - && event->button == 1 - && table->mode == DT_THUMBTABLE_MODE_FILMSTRIP - && event->type == GDK_BUTTON_RELEASE - && strcmp(view->module_name, "map") - && dt_modifier_is(event->state, 0)) + DT_DEBUG_CONTROL_SIGNAL_RAISE( + darktable.signals, + DT_SIGNAL_VIEWMANAGER_THUMBTABLE_ACTIVATE, + id); + + if (!strcmp(view->module_name, "map")) { - DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, - DT_SIGNAL_VIEWMANAGER_THUMBTABLE_ACTIVATE, id); + return TRUE; } + } } if(table->mode != DT_THUMBTABLE_MODE_ZOOM) @@ -2286,12 +2288,15 @@ dt_thumbtable_t *dt_thumbtable_new() 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); + + table->gesture_button_primary = dtgtk_button_default_handler_new( + GTK_WIDGET(table->widget), + GDK_BUTTON_PRIMARY, + G_CALLBACK(_event_button_press_primary), + G_CALLBACK(_event_button_release_primary), + table); // we register globals signals DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED, diff --git a/src/dtgtk/thumbtable.h b/src/dtgtk/thumbtable.h index e6c104174cfd..955e12d53930 100644 --- a/src/dtgtk/thumbtable.h +++ b/src/dtgtk/thumbtable.h @@ -104,6 +104,10 @@ typedef struct dt_thumbtable_t // scroll timeout values guint scroll_timeout_id; float scroll_value; + + // different controller for event processing + GtkGesture *gesture_button_primary; + } dt_thumbtable_t; dt_thumbtable_t *dt_thumbtable_new(); From abaaf84f8e743d5949a3525c7195460087fbdcf7 Mon Sep 17 00:00:00 2001 From: Christian-Kr Date: Sun, 2 Jun 2024 15:21:20 +0200 Subject: [PATCH 2/4] Migrate GtkDarktableResetLabel to GtkGesture. --- src/dtgtk/resetlabel.c | 22 +++++++++++++++++++--- src/dtgtk/resetlabel.h | 5 +++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/dtgtk/resetlabel.c b/src/dtgtk/resetlabel.c index 2a4a1d255550..f65db06ede4e 100644 --- a/src/dtgtk/resetlabel.c +++ b/src/dtgtk/resetlabel.c @@ -28,16 +28,26 @@ static void dtgtk_reset_label_init(GtkDarktableResetLabel *label) { } -static gboolean _reset_label_callback(GtkDarktableResetLabel *label, GdkEventButton *event, gpointer user_data) +static gboolean _reset_label_callback( + GtkGestureMultiPress *gesture, + int n_press, + double x, + double y, + gpointer user_data) { - if(event->type == GDK_2BUTTON_PRESS) + if(n_press == 2) { + // on double click, reset all settings + GtkDarktableResetLabel *label = (GtkDarktableResetLabel *)user_data; + memcpy(((char *)label->module->params) + label->offset, ((char *)label->module->default_params) + label->offset, label->size); if(label->module->gui_update) label->module->gui_update(label->module); dt_dev_add_history_item(darktable.develop, label->module, FALSE); + return TRUE; } + return FALSE; } @@ -57,7 +67,13 @@ GtkWidget *dtgtk_reset_label_new(const gchar *text, dt_iop_module_t *module, voi 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); + + label->gesture_button_primary = dtgtk_button_default_handler_new( + GTK_WIDGET(label), + GDK_BUTTON_PRIMARY, + G_CALLBACK(_reset_label_callback), + NULL, + label); return (GtkWidget *)label; } diff --git a/src/dtgtk/resetlabel.h b/src/dtgtk/resetlabel.h index 19d8199c9e5b..4ca27f88a87a 100644 --- a/src/dtgtk/resetlabel.h +++ b/src/dtgtk/resetlabel.h @@ -19,6 +19,8 @@ #pragma once #include "develop/imageop.h" +#include "dtgtk/button.h" + #include G_BEGIN_DECLS @@ -33,6 +35,9 @@ struct _GtkDarktableResetLabel dt_iop_module_t *module; int offset; // offset in params to reset int size; // size of param to reset + + // different controller for event processing + GtkGesture *gesture_button_primary; }; /** instantiate a new darktable reset label for the given module and param. */ From ba470727cdb4902fe011e2eb0ae429c9f2bd9ee1 Mon Sep 17 00:00:00 2001 From: Christian-Kr Date: Sun, 2 Jun 2024 16:25:22 +0200 Subject: [PATCH 3/4] Migrate culling table to GtkGesture for buttons. --- src/dtgtk/culling.c | 36 ++++++++++++++++++++++++------------ src/dtgtk/culling.h | 4 ++++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/dtgtk/culling.c b/src/dtgtk/culling.c index 8d0ef0bf5ffe..27cdfbd1625e 100644 --- a/src/dtgtk/culling.c +++ b/src/dtgtk/culling.c @@ -618,13 +618,18 @@ static gboolean _event_enter_notify(GtkWidget *widget, return TRUE; } -static gboolean _event_button_press(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static gboolean _event_button_press( + GtkGestureMultiPress *gesture, + int n_press, + double x, + double y, + gpointer user_data) { + GdkEventButton *event = (GdkEventButton *)gtk_gesture_get_last_event(GTK_GESTURE(gesture), NULL); + dt_culling_t *table = (dt_culling_t *)user_data; - if(event->button == 1 && event->type == GDK_BUTTON_PRESS) + if(event->button == 1) { // make sure any edition field loses the focus gtk_widget_grab_focus(dt_ui_center(darktable.gui->ui)); @@ -642,7 +647,7 @@ static gboolean _event_button_press(GtkWidget *widget, const dt_imgid_t id = dt_control_get_mouse_over_id(); - if(dt_is_valid_imgid(id) && event->button == 1 && event->type == GDK_2BUTTON_PRESS) + if(dt_is_valid_imgid(id) && event->button == 1 && n_press == 2) { dt_view_manager_switch(darktable.view_manager, "darkroom"); return TRUE; @@ -739,9 +744,12 @@ static gboolean _event_motion_notify(GtkWidget *widget, return TRUE; } -static gboolean _event_button_release(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static gboolean _event_button_release( + GtkGestureMultiPress *gesture, + int n_press, + double x, + double y, + gpointer user_data) { dt_culling_t *table = (dt_culling_t *)user_data; table->panning = FALSE; @@ -928,12 +936,16 @@ dt_culling_t *dt_culling_new(dt_culling_mode_t mode) 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); + + // the button handler will react on all mouse buttons (0 as argument) + table->gesture_button_all = dtgtk_button_default_handler_new( + GTK_WIDGET(table->widget), + 0, + G_CALLBACK(_event_button_press), + G_CALLBACK(_event_button_release), + table); // we register globals signals DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, diff --git a/src/dtgtk/culling.h b/src/dtgtk/culling.h index 80d1d0780262..71345145bc4d 100644 --- a/src/dtgtk/culling.h +++ b/src/dtgtk/culling.h @@ -74,6 +74,10 @@ typedef struct dt_culling_t dt_thumbnail_overlay_t overlays; // overlays type int overlays_block_timeout; // overlay block visibility duration gboolean show_tooltips; // are tooltips visible ? + + // different controller for event processing + GtkGesture *gesture_button_all; + } dt_culling_t; dt_culling_t *dt_culling_new(dt_culling_mode_t mode); From b51b9c392ecdfa95ef40e0f5b550c91cece3d091 Mon Sep 17 00:00:00 2001 From: Christian-Kr Date: Sun, 2 Jun 2024 19:57:39 +0200 Subject: [PATCH 4/4] Migrate thumbnail to GtkGesture for buttons. --- src/dtgtk/thumbnail.c | 81 ++++++++++++++++++++++++++++--------------- src/dtgtk/thumbnail.h | 6 ++++ 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/dtgtk/thumbnail.c b/src/dtgtk/thumbnail.c index c5824f425e76..6466a39593a7 100644 --- a/src/dtgtk/thumbnail.c +++ b/src/dtgtk/thumbnail.c @@ -940,14 +940,19 @@ static gboolean _event_main_motion(GtkWidget *widget, return FALSE; } -static gboolean _event_main_press(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static gboolean _event_main_press( + GtkGestureMultiPress *gesture, + int n_press, + double x, + double y, + gpointer user_data) { + GdkEventButton *event = (GdkEventButton *)gtk_gesture_get_last_event(GTK_GESTURE(gesture), NULL); + dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; if(event->button == 1 - && ((event->type == GDK_2BUTTON_PRESS && !thumb->single_click) - || (event->type == GDK_BUTTON_PRESS + && ((n_press == 2 && !thumb->single_click) + || (n_press == 1 && dt_modifier_is(event->state, 0) && thumb->single_click))) { dt_control_set_mouse_over_id(thumb->imgid); @@ -955,10 +960,14 @@ static gboolean _event_main_press(GtkWidget *widget, } return FALSE; } -static gboolean _event_main_release(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static gboolean _event_main_release( + GtkGestureMultiPress *gesture, + int n_press, + double x, + double y, + gpointer user_data) { + GdkEventButton *event = (GdkEventButton *)gtk_gesture_get_last_event(GTK_GESTURE(gesture), NULL); dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; if(event->button == 1 @@ -979,17 +988,26 @@ static gboolean _event_main_release(GtkWidget *widget, return FALSE; } -static gboolean _event_rating_press(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static gboolean _event_rating_press( + GtkGestureMultiPress *gesture, + int n_press, + double x, + double y, + gpointer user_data) { return TRUE; } -static gboolean _event_rating_release(GtkWidget *widget, - GdkEventButton *event, - gpointer user_data) +static gboolean _event_rating_release( + GtkGestureMultiPress *gesture, + int n_press, + double x, + double y, + gpointer user_data) { + GdkEventButton *event = (GdkEventButton *)gtk_gesture_get_last_event(GTK_GESTURE(gesture), NULL); dt_thumbnail_t *thumb = (dt_thumbnail_t *)user_data; + GtkWidget *widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture)); + if(thumb->disable_actions) return FALSE; if(dtgtk_thumbnail_btn_is_hidden(widget)) @@ -1401,10 +1419,12 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, g_signal_connect(G_OBJECT(thumb->w_main), "drag-motion", G_CALLBACK(_event_main_drag_motion), thumb); - g_signal_connect(G_OBJECT(thumb->w_main), "button-press-event", - G_CALLBACK(_event_main_press), thumb); - g_signal_connect(G_OBJECT(thumb->w_main), "button-release-event", - G_CALLBACK(_event_main_release), thumb); + thumb->gesture_button_all_main = dtgtk_button_default_handler_new( + GTK_WIDGET(thumb->w_main), + 0, + G_CALLBACK(_event_main_press), + G_CALLBACK(_event_main_release), + thumb); g_object_set_data(G_OBJECT(thumb->w_main), "thumb", thumb); DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, DT_SIGNAL_ACTIVE_IMAGES_CHANGE, @@ -1549,10 +1569,14 @@ 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); + + thumb->gesture_button_all_reject = dtgtk_button_default_handler_new( + GTK_WIDGET(thumb->w_reject), + 0, + G_CALLBACK(_event_rating_press), + 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", @@ -1567,11 +1591,14 @@ GtkWidget *dt_thumbnail_create_widget(dt_thumbnail_t *thumb, 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); + + thumb->gesture_button_all_stars[i] = dtgtk_button_default_handler_new( + GTK_WIDGET(thumb->w_stars[i]), + 0, + G_CALLBACK(_event_rating_press), + G_CALLBACK(_event_rating_release), + 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); diff --git a/src/dtgtk/thumbnail.h b/src/dtgtk/thumbnail.h index fed1d998c5fb..77e439efa43d 100644 --- a/src/dtgtk/thumbnail.h +++ b/src/dtgtk/thumbnail.h @@ -151,6 +151,12 @@ typedef struct gboolean display_focus; // do we display rectangles to show focused part of the image gboolean busy; // should we show the busy message ? + + // different controller for event processing + GtkGesture *gesture_button_all_main; + GtkGesture *gesture_button_all_reject; + GtkGesture *gesture_button_all_stars[MAX_STARS]; + } dt_thumbnail_t; dt_thumbnail_t *dt_thumbnail_new(const int width,