diff --git a/src/dtgtk/culling.c b/src/dtgtk/culling.c index 44114711288..489f277f02d 100644 --- a/src/dtgtk/culling.c +++ b/src/dtgtk/culling.c @@ -700,12 +700,14 @@ static void _event_scroll(GtkEventControllerScroll *controller, if(direction == GDK_SCROLL_SMOOTH && !is_stop && dt_modifiers_include(state, GDK_CONTROL_MASK)) { - if(dx != 0.0 || dy != 0.0) + // raw platform deltas, not the attenuated controller deltas, so that + // one full unit of scroll (delta_y == 1.0) still matches the 0.5 + // zoom_delta of a discrete mouse-wheel click. right==up==zoom-in + gdouble ddx = 0.0, ddy = 0.0; + if(dt_gui_get_scroll_deltas((const GdkEventScroll *)event, &ddx, &ddy) + && (ddx != 0.0 || ddy != 0.0)) { - // 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; + const gdouble delta = fabs(ddx) > fabs(ddy) ? -ddx : ddy; const float zoom_delta = (float)(-delta * 0.5); // convert screen to culling coordinates int ox = 0, oy = 0; @@ -742,18 +744,20 @@ static void _event_scroll(GtkEventControllerScroll *controller, fz, fz > 1.0f ? "pan path" : "navigate path"); if(fz > 1.0f) { - if(dx != 0.0 || dy != 0.0) + gdouble ddx = 0.0, ddy = 0.0; + if(dt_gui_get_scroll_deltas((const GdkEventScroll *)event, &ddx, &ddy) + && (ddx != 0.0 || ddy != 0.0)) { - // controller dx/dy is platform-normalised fractional units; - // scale to pixel-scale (matches the factor used by the center-widget pan path). + // raw platform deltas; 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), state); + ddx, ddy, ddx * 50.0, ddy * 50.0); + dt_culling_pan_move(table, (float)(-ddx * 50.0), (float)(-ddy * 50.0), state); } else { - dt_print(DT_DEBUG_INPUT, "[culling scroll] smooth pan: no delta from controller"); + dt_print(DT_DEBUG_INPUT, "[culling scroll] smooth pan: no delta"); } gdk_event_free(event); return; @@ -1297,8 +1301,7 @@ 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); - dt_gui_connect_scroll(table->widget, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES - | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + dt_gui_connect_scroll(table->widget, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES, _event_scroll, table); g_signal_connect(G_OBJECT(table->widget), "draw", G_CALLBACK(_event_draw), table); diff --git a/src/dtgtk/thumbtable.c b/src/dtgtk/thumbtable.c index e01e9808619..1a0eef719e4 100644 --- a/src/dtgtk/thumbtable.c +++ b/src/dtgtk/thumbtable.c @@ -1136,14 +1136,20 @@ static void _event_scroll(GtkEventControllerScroll *controller, { gdouble deltaf = 0.f; gboolean did_scroll; - if(dt_conf_get_bool("thumbtable_fractional_scrolling")) - { - // use controller dx/dy directly for fractional scrolling - did_scroll = (dx != 0.0 || dy != 0.0); + if(dt_conf_get_bool("thumbtable_fractional_scrolling") + && dt_gdk_event_get_scroll_direction(e) == GDK_SCROLL_SMOOTH) + { + // pixel-precise scrolling for precision touch pads: use the raw + // platform deltas (scaled back up in _event_scroll_compressed), not + // the attenuated controller deltas, so movement tracks the finger + // 1:1 like the native scrollbars. clicky wheels keep the + // row-by-row path below. + 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 (dx > 0) or scroll down (dy > 0) -> down - deltaf = fabs(dx) > fabs(dy) ? dx : dy; + // file manager scroll: tilt right (delta_x > 0) or scroll down (delta_y > 0) -> down + deltaf = fabs(deltaf_x) > fabs(deltaf_y) ? deltaf_x : deltaf_y; } } else @@ -2663,8 +2669,7 @@ dt_thumbtable_t *dt_thumbtable_new() g_signal_connect(table->widget, "drag-data-received", G_CALLBACK(dt_thumbtable_event_dnd_received), table); - dt_gui_connect_scroll(table->widget, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES - | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, + dt_gui_connect_scroll(table->widget, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES, _event_scroll, table); g_signal_connect(G_OBJECT(table->widget), "draw", G_CALLBACK(_event_draw), table); diff --git a/src/gui/gtk.c b/src/gui/gtk.c index 303e28fc7ff..b9cf3017310 100644 --- a/src/gui/gtk.c +++ b/src/gui/gtk.c @@ -5083,7 +5083,12 @@ GtkEventController *(dt_gui_connect_scroll)(GtkWidget *widget, flags &= ~GTK_EVENT_CONTROLLER_SCROLL_DISCRETE; GtkEventController *const controller = gtk_event_controller_scroll_new(widget, flags); - gtk_event_controller_set_propagation_phase(controller, GTK_PHASE_TARGET); + /* BUBBLE phase matches the bubbling behavior of the replaced + * "scroll-event" signal: the controller fires whenever the event target + * is the widget or any of its descendants (e.g. child widgets such as + * thumbnails or star icons placed on a GtkLayout). GTK_PHASE_TARGET + * would only fire when the widget is the target itself. */ + gtk_event_controller_set_propagation_phase(controller, GTK_PHASE_BUBBLE); dt_gui_add_controller(widget, controller); // GTK4 gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(controller)); g_signal_connect(controller, "scroll", G_CALLBACK(proxy), data);