Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/develop/develop.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ typedef struct dt_develop_t
uint32_t timestamp;
uint32_t preview_average_delay;
struct dt_iop_module_t *gui_module; // this module claims gui expose/event callbacks.
struct dt_iop_module_t *header_buttons_module; // module whose header buttons are currently shown (if any)

// image processing pipeline with caching
struct dt_dev_pixelpipe_t *preview_pipe;
Expand Down
126 changes: 101 additions & 25 deletions src/develop/imageop.c
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,42 @@ void dt_iop_init_pipe(dt_iop_module_t *module,
piece->blendop_data = calloc(1, sizeof(dt_develop_blend_params_t));
}

#if !GTK_CHECK_VERSION(4, 0, 0)
static gboolean _pointer_in_module(const dt_iop_module_t *module)
{
GdkDevice *const device =
gdk_seat_get_pointer(gdk_display_get_default_seat(gdk_display_get_default()));
gint x, y;

/* gdk_window_get_device_position() returns the window under the device, not
* a within-bounds test (over the label's event box it returns that window,
* over the plain header background it returns NULL). Compare the returned
* coordinates against the window size instead.
* GTK3-only: on the GTK4 switch this becomes gdk_surface_get_device_position(). */
GtkWidget *const header_evb =
dtgtk_expander_get_header_event_box(DTGTK_EXPANDER(module->expander));
if(gtk_widget_get_realized(header_evb))
{
GdkWindow *const win = gtk_widget_get_window(header_evb);
gdk_window_get_device_position(win, device, &x, &y, NULL);
if(x >= 0 && y >= 0
&& x < gdk_window_get_width(win) && y < gdk_window_get_height(win))
return TRUE;
}
GtkWidget *const body_evb =
dtgtk_expander_get_body_event_box(DTGTK_EXPANDER(module->expander));
if(gtk_widget_get_realized(body_evb))
{
GdkWindow *const win = gtk_widget_get_window(body_evb);
gdk_window_get_device_position(win, device, &x, &y, NULL);
if(x >= 0 && y >= 0
&& x < gdk_window_get_width(win) && y < gdk_window_get_height(win))
return TRUE;
}
return FALSE;
}
#endif

static void _header_enter_notify_callback(GtkEventControllerMotion *controller,
gdouble x,
gdouble y,
Expand All @@ -463,18 +499,31 @@ static void _header_motion_notify_show_callback(GtkEventControllerMotion *contro
dt_iop_module_t *module)
{
darktable.control->element = DT_ACTION_ELEMENT_SHOW;

/* entering another module's header generates no usable leave for the
* previous one: the leave crossing's position is still within the old
* module's bounds, so the position check in the hide callback cannot hide
* it. Hide the previously shown buttons here instead. */
if(darktable.develop->header_buttons_module && darktable.develop->header_buttons_module != module
&& g_list_find(darktable.develop->iop, darktable.develop->header_buttons_module))
dt_iop_show_hide_header_buttons(darktable.develop->header_buttons_module, NULL, FALSE, FALSE);
darktable.develop->header_buttons_module = module;

dt_iop_show_hide_header_buttons(module, NULL, TRUE, FALSE);
}

static void _header_motion_notify_hide_callback(GtkEventControllerMotion *controller,
dt_iop_module_t *module)
{
dt_iop_show_hide_header_buttons(module, NULL, FALSE, FALSE);
}

static void _header_menu_deactivate_callback(GtkMenuShell *menushell,
dt_iop_module_t *module)
{
#if !GTK_CHECK_VERSION(4, 0, 0)
/* GTK3-only: the motion controller fires leave for every crossing,
* including inferior ones (pointer moving to a child button) and the
* synthetic ones a menu popup/close generates. Only hide when the pointer
* has really left the module.
* GTK4: compiled out -- GtkEventControllerMotion only emits leave for real
* widget crossings there. */
if(_pointer_in_module(module)) return;
#endif
dt_iop_show_hide_header_buttons(module, NULL, FALSE, FALSE);
}

Expand Down Expand Up @@ -1047,9 +1096,6 @@ static gboolean _gui_multiinstance_callback(GtkButton *button,
G_CALLBACK(_gui_rename_callback), module);
gtk_menu_shell_append(menu, item);

g_signal_connect(G_OBJECT(menu), "deactivate",
G_CALLBACK(_header_menu_deactivate_callback), module);

dt_gui_menu_popup(GTK_MENU(menu), GTK_WIDGET(button),
GDK_GRAVITY_SOUTH_EAST, GDK_GRAVITY_NORTH_EAST);

Expand Down Expand Up @@ -1083,7 +1129,21 @@ static void _gui_multiinstance_clicked(GtkGestureSingle *gesture,
gdouble y,
dt_iop_module_t *module)
{
_gui_multiinstance_callback(NULL, NULL, module);
// restore the per-button behaviour of the old button-press-event handler:
// secondary-click creates a new instance, middle-click does nothing,
// anything else opens the multi-instance popup.
// gtk_gesture_single_get_current_button() is unchanged in GTK4.
const guint button = gtk_gesture_single_get_current_button(gesture);
if(button == GDK_BUTTON_SECONDARY)
{
if(!(module->flags() & IOP_FLAGS_ONE_INSTANCE))
_gui_copy_callback(NULL, module);
return;
}
if(button == GDK_BUTTON_MIDDLE)
return;

_gui_multiinstance_callback(GTK_BUTTON(dt_gui_get_widget(gesture)), NULL, module);
}

static gboolean _rename_module_key_pressed(GtkEventControllerKey *controller,
Expand Down Expand Up @@ -2358,6 +2418,8 @@ void dt_iop_commit_params(dt_iop_module_t *module,

void dt_iop_gui_cleanup_module(dt_iop_module_t *module)
{
if(darktable.develop->header_buttons_module == module)
darktable.develop->header_buttons_module = NULL;
g_slist_free_full(module->widget_list, g_free);
module->widget_list = NULL;
DT_CONTROL_SIGNAL_DISCONNECT_ALL(module, module->so->op);
Expand Down Expand Up @@ -2507,9 +2569,6 @@ static gboolean _presets_popup_callback(GtkButton *button,

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,
GTK_WIDGET(button), GDK_GRAVITY_SOUTH_EAST, GDK_GRAVITY_NORTH_EAST);

Expand All @@ -2528,9 +2587,6 @@ static void _presets_popup_clicked(GtkGestureSingle *gesture,
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);
}
Expand Down Expand Up @@ -3236,14 +3292,28 @@ GtkWidget *dt_iop_gui_header_button(dt_iop_module_t *module,
}
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);
if(element == DT_ACTION_ELEMENT_PRESETS || element == DT_ACTION_ELEMENT_INSTANCE)
{
// open the menu on release so that the GtkButton finishes its own
// press/release cycle before the menu grabs the pointer: a grab on press
// would swallow the release and leave the button stuck in its pressed
// state. connect "released" directly, as dt_gui_connect_click() would
// also bridge "cancel" to a synthetic release and open the menu twice
GtkGestureSingle *const gesture = dt_gui_connect_click(button, NULL, NULL, module);
g_signal_connect(gesture, "released",
G_CALLBACK(element == DT_ACTION_ELEMENT_PRESETS
? _presets_popup_clicked
: _gui_multiinstance_clicked),
module);
}
else
{
dt_gui_connect_click(button,
element == DT_ACTION_ELEMENT_ENABLE
? _gui_off_button_clicked
: _gui_reset_clicked,
NULL, module);
}
dt_action_define(&module->so->actions, NULL, NULL, button, NULL);
gtk_widget_show(button);

Expand Down Expand Up @@ -3360,7 +3430,13 @@ void dt_iop_gui_set_expander(dt_iop_module_t *module)
module->header = header;

/* setup the header box */
dt_gui_connect_click(header_evb, NULL, _iop_plugin_header_released, module);
// connect the header's released handler directly: dt_gui_connect_click()
// would also bridge "cancel" to a synthetic release, which -- after the
// presets menu (opened by the real release) grabs the pointer and cancels
// the gesture -- would open the menu a second time
GtkGestureSingle *const header_gesture = dt_gui_connect_click(header_evb, NULL, NULL, module);
g_signal_connect(header_gesture, "released",
G_CALLBACK(_iop_plugin_header_released), module);
dt_gui_connect_motion(header_evb, NULL,
_header_motion_notify_show_callback,
_header_motion_notify_hide_callback, module);
Expand Down
2 changes: 1 addition & 1 deletion src/dtgtk/culling.c
Original file line number Diff line number Diff line change
Expand Up @@ -1262,7 +1262,7 @@ dt_culling_t *dt_culling_new(const dt_culling_mode_t mode)
* 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()
* natively and the callback can use gtk_gesture_single_get_current_button()
* safely on the real gesture pointer. */
dt_gui_connect_double_click(table->widget, _event_button_press_cb, table);

Expand Down
2 changes: 1 addition & 1 deletion src/dtgtk/thumbtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -2634,7 +2634,7 @@ dt_thumbtable_t *dt_thumbtable_new()
* 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()
* natively and the callback can use gtk_gesture_single_get_current_button()
* safely on the real gesture pointer. */
dt_gui_connect_double_click(table->widget, _event_button_press_cb, table);

Expand Down
6 changes: 6 additions & 0 deletions src/gui/gtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -4802,6 +4802,12 @@ GtkGestureSingle *(dt_gui_connect_click)(GtkWidget *widget,
// GTK4 GtkGesture *gesture = gtk_gesture_click_new();
// gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(gesture));

// GtkGestureSingle:button defaults to GDK_BUTTON_PRIMARY on GTK3, which
// would silently drop right/middle clicks. The old button-press-event
// handlers reacted to all buttons, so listen to any button; callers
// distinguish them via gtk_gesture_single_get_current_button().
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 0);

if(pressed) g_signal_connect(gesture, "pressed", G_CALLBACK(pressed), data);
if(released)
{
Expand Down
2 changes: 1 addition & 1 deletion src/gui/preferences_ai.c
Original file line number Diff line number Diff line change
Expand Up @@ -1281,7 +1281,7 @@ static void _on_info_button_press_cb(GtkGestureSingle *gesture, int n_press,
gpointer user_data)
{
GtkWidget *widget = dt_gui_get_widget(gesture);
if(gtk_gesture_single_get_button(gesture) != 1) return;
if(gtk_gesture_single_get_current_button(gesture) != 1) return;

dt_prefs_ai_data_t *data = (dt_prefs_ai_data_t *)user_data;
GtkTreeView *tv = GTK_TREE_VIEW(widget);
Expand Down
2 changes: 1 addition & 1 deletion src/libs/collect.c
Original file line number Diff line number Diff line change
Expand Up @@ -3609,7 +3609,7 @@ static void popup_button_callback_cb(GtkGestureSingle *gesture, int n_press,
double x, double y,
dt_lib_collect_rule_t *d)
{
if(gtk_gesture_single_get_button(gesture) != 1)
if(gtk_gesture_single_get_current_button(gesture) != 1)
return;

GtkWidget *menu = gtk_menu_new();
Expand Down
6 changes: 3 additions & 3 deletions src/libs/import.c
Original file line number Diff line number Diff line change
Expand Up @@ -1289,7 +1289,7 @@ static void _places_button_press_cb(GtkGestureSingle *gesture, int n_press,
gtk_tree_model_get(model, &iter, 0, &folder_name, 1, &folder_path, -1);

// left-click: set as new root
if(gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY)
if(gtk_gesture_single_get_current_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);
Expand Down Expand Up @@ -1324,7 +1324,7 @@ static void _folders_button_press_cb(GtkGestureSingle *gesture, int n_press,
gtk_get_current_event_state(&state);

if(n_press == 1
&& gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY
&& gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY
&& !dt_modifier_is(state, GDK_SHIFT_MASK | GDK_CONTROL_MASK))
{
GtkTreePath *path = NULL;
Expand Down Expand Up @@ -1354,7 +1354,7 @@ static void _folders_button_press_cb(GtkGestureSingle *gesture, int n_press,
}
gtk_tree_path_free(path);
}
else if(n_press >= 2 && gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY)
else if(n_press >= 2 && gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY)
{
GtkTreePath *path = NULL;
gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view),
Expand Down
2 changes: 1 addition & 1 deletion src/libs/masks.c
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ static void _tree_add_shape(GtkButton *button, gpointer shape)
static void _bt_add_shape_cb(GtkGestureSingle *gesture, int n_press, double x, double y, gpointer shape)
{

if(gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY)
if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY)
{
#ifdef HAVE_AI
if(GPOINTER_TO_INT(shape) == DT_MASKS_OBJECT && !dt_masks_object_available())
Expand Down
2 changes: 1 addition & 1 deletion src/libs/neural_restore.c
Original file line number Diff line number Diff line change
Expand Up @@ -3931,7 +3931,7 @@ static void _preview_button_press_cb(GtkGestureSingle *gesture, int n_press,
GtkWidget *widget = dt_gui_get_widget(gesture);
dt_lib_neural_restore_t *d = (dt_lib_neural_restore_t *)self->data;

if(gtk_gesture_single_get_button(gesture) != 1) return;
if(gtk_gesture_single_get_current_button(gesture) != 1) return;

// thumbnail picking mode: click to select area and trigger preview
if(d->picking_thumbnail && d->export_cairo)
Expand Down
2 changes: 1 addition & 1 deletion src/libs/tools/viewswitcher.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ static void _lib_viewswitcher_button_press_callback(GtkGestureSingle *gesture, i
double x, double y,
dt_view_t *view)
{
if(gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY)
if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY)
{
_switch_view(view);
}
Expand Down
2 changes: 1 addition & 1 deletion src/views/map.c
Original file line number Diff line number Diff line change
Expand Up @@ -2168,7 +2168,7 @@ static void _view_map_click_pressed_cb(GtkGestureSingle *gesture,
}
}

if(gtk_gesture_single_get_button(gesture) == GDK_BUTTON_PRIMARY)
if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY)
{
// check if the click was in a location form - ctrl gives priority to images
if(lib->loc.main.id > 0
Expand Down
Loading