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
18 changes: 8 additions & 10 deletions src/bauhaus/bauhaus.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
This file is part of darktable,
Copyright (C) 2012-2025 darktable developers.
Copyright (C) 2012-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
Expand Down Expand Up @@ -3077,12 +3077,11 @@ static void _widget_scroll(GtkEventControllerScroll *controller,
{
GdkEvent *event = gtk_get_current_event();
if(!event || gdk_event_get_event_type(event) != GDK_SCROLL)
dt_print(DT_DEBUG_ALWAYS, "[_widget_scroll] called on non-scroll event");
else if(dt_gui_ignore_scroll(&event->scroll))
{
// NOP, should not happen
}
if(dt_gui_ignore_scroll(&event->scroll))
{
// hack: GTK3 event controller handlers can't return GDK_EVENT_PROPAGATE
// event controller handlers can't propagate events, so synthesize
// an event directly to the destination widget
GtkWidget *sw = gtk_widget_get_ancestor(widget, GTK_TYPE_SCROLLED_WINDOW);
if(sw)
gtk_widget_event(sw, event);
Expand Down Expand Up @@ -3118,7 +3117,7 @@ static void _widget_scroll(GtkEventControllerScroll *controller,
_combobox_next_sensitive(w, delta, 0, FALSE);
}
}
gdk_event_free(event);
if(event) gdk_event_free(event);
}

static gboolean _widget_key_press(GtkWidget *widget, GdkEventKey *event)
Expand Down Expand Up @@ -3733,9 +3732,8 @@ static void dt_bh_init(DtBauhausWidget *w)
dt_gui_connect_motion(w, _widget_motion, _widget_enter, _widget_leave, widget);

GtkEventController *scroll_controller =
dt_gui_connect_scroll(w, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES
| GTK_EVENT_CONTROLLER_SCROLL_DISCRETE,
_widget_scroll, widget);
dt_gui_connect_scroll_discrete(w, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES,
_widget_scroll, widget);
// allows for capturing propagated events from other widgets
gtk_event_controller_set_propagation_phase(scroll_controller, GTK_PHASE_BUBBLE);

Expand Down
76 changes: 76 additions & 0 deletions src/gui/gtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -4773,6 +4773,82 @@ GtkEventController *(dt_gui_connect_scroll)(GtkWidget *widget,
return controller;
}

typedef void (*scroll_handler_t)(GtkEventControllerScroll*, gdouble, gdouble, gpointer);
static gdouble _scroll_discrete_dx = 0.0;
static gdouble _scroll_discrete_dy = 0.0;
static const char *_scroll_discrete_real_handler_key = "real-scroll-discrete-handler";

static void _scroll_discrete_proxy(GtkEventControllerScroll* controller,
gdouble dx,
gdouble dy,
gpointer user_data)
{
GdkEvent *event = gtk_get_current_event();
if(!event) return;
// avoid double counting real and emulated events
if(!gdk_event_get_pointer_emulated(event))
{
if(gdk_event_get_event_type(event) == GDK_SCROLL
&& event->scroll.direction == GDK_SCROLL_SMOOTH)
{
// MacOS scrolling is apparently distance-based, so can produce
// a range of large/small deltas. Most current code accumulates
// & attenuates via dt_gui_get_scroll_unit_deltas(). For
// GTK4-ready "scroll" events, use discrete scrolling based on
// gtk_event_controller_scroll_handle_event(), with attenuation.
#ifdef GDK_WINDOWING_QUARTZ
const double scale = 0.06;
const double compression = 0.7;
#else
const double scale = 0.95;
const double compression = 0.9;
#endif
dx = scale * copysign(pow(fabs(dx), compression), dx);
dy = scale * copysign(pow(fabs(dy), compression), dy);
_scroll_discrete_dx += dx;
_scroll_discrete_dy += dy;
dx = dy = 0.0;
if(fabs(_scroll_discrete_dx) >= 1.0)
{
int steps = trunc(_scroll_discrete_dx);
_scroll_discrete_dx -= steps;
dx = steps;
}
if(fabs(_scroll_discrete_dy) >= 1.0)
{
int steps = trunc(_scroll_discrete_dy);
_scroll_discrete_dy -= steps;
dy = steps;
}
// FIXME: modern mouse wheels can produce smooth scroll events
// with |delta| > 1, for now the caller must clamp these
}
if(dx != 0.0 || dy != 0.0)
{
scroll_handler_t real_handler =
g_object_get_data(G_OBJECT(controller), _scroll_discrete_real_handler_key);
real_handler(controller, dx, dy, user_data);
}
}
gdk_event_free(event);
}

GtkEventController *(dt_gui_connect_scroll_discrete)(GtkWidget *widget,
GtkEventControllerScrollFlags flags,
GCallback scroll_callback,
gpointer user_data)
{
// Use proxy, not GTK discrete scrolling, to attenuate. We could use
// GTK discrete scrolling for non-MacOS, but it makes it hard to
// test if a chunk of code is particular to one OS.
GtkEventController *controller =
dt_gui_connect_scroll(widget, flags & ~GTK_EVENT_CONTROLLER_SCROLL_DISCRETE,
_scroll_discrete_proxy, user_data);
g_object_set_data(G_OBJECT(controller),
_scroll_discrete_real_handler_key, scroll_callback);
return controller;
}


static int busy_nest_count = 0;

Expand Down
18 changes: 13 additions & 5 deletions src/gui/gtk.h
Original file line number Diff line number Diff line change
Expand Up @@ -564,11 +564,6 @@ GtkGesture *(dt_gui_connect_drag)(GtkWidget *widget,
ASSERT_FUNC_TYPE(drag_update, void(*)(GtkGestureDrag *, double, double, __typeof__(data))), \
dt_gui_connect_drag(GTK_WIDGET(widget), G_CALLBACK(drag_begin), G_CALLBACK(drag_end), G_CALLBACK(drag_update), (data)))

#define dt_gui_claim(gesture) \
gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_CLAIMED)
#define dt_gui_deny(gesture) \
gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_DENIED)

GtkEventController *(dt_gui_connect_motion)(GtkWidget *widget,
GCallback motion,
GCallback enter,
Expand All @@ -588,6 +583,19 @@ 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_scroll_discrete)(GtkWidget *widget,
GtkEventControllerScrollFlags flags,
GCallback scroll,
gpointer data);
#define dt_gui_connect_scroll_discrete(widget, flags, scroll, data) ( \
ASSERT_FUNC_TYPE(scroll, void(*)(GtkEventControllerScroll *, double, double, __typeof__(data))), \
dt_gui_connect_scroll_discrete(GTK_WIDGET(widget), (flags), G_CALLBACK(scroll), (data)))

#define dt_gui_claim(gesture) \
gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_CLAIMED)
#define dt_gui_deny(gesture) \
gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_DENIED)

// GTK4 gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(controller));
#define dt_modifier_eq(controller, mask)\
dt_modifier_is(dt_key_modifier_state(), mask)
Expand Down
9 changes: 3 additions & 6 deletions src/libs/histogram.c
Original file line number Diff line number Diff line change
Expand Up @@ -486,17 +486,15 @@ static void _eventbox_scroll_callback(GtkEventControllerScroll* self,
GDK_SHIFT_MASK | GDK_MOD1_MASK))
{
// bubble to adjusting the overall widget size
// FIXME: use gtk_event_controller_handle_event()
gtk_widget_event(s->scope_draw, event);
}
else if(s->highlight != DT_SCOPES_HIGHLIGHT_NONE)
{
// FIXME: should scroll for exposure change be handled by each scope, rather than here?
// FIXME: should handle horizontal scrolling as well?
// FIXME: should handle smooth scrolling rather than discrete?
// FIXME: should scrolling of scope be handled in the drawable rather than
// the eventbox.
dt_dev_exposure_handle_event(0, dy, event->scroll.state,
dt_dev_exposure_handle_event(0, dy - dx, event->scroll.state,
s->highlight == DT_SCOPES_HIGHLIGHT_BLACK_POINT);
}
else
Expand Down Expand Up @@ -863,9 +861,8 @@ void gui_init(dt_lib_module_t *self)

// FIXME: add (optional) propagation phase argument to dt_gui_connect_*()
GtkEventController *scroll_controller =
dt_gui_connect_scroll(eventbox, GTK_EVENT_CONTROLLER_SCROLL_VERTICAL
| GTK_EVENT_CONTROLLER_SCROLL_DISCRETE,
_eventbox_scroll_callback, s);
dt_gui_connect_scroll_discrete(eventbox, GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES,
_eventbox_scroll_callback, s);
gtk_event_controller_set_propagation_phase(scroll_controller, GTK_PHASE_CAPTURE);
// use GTK_PHASE_TARGET to capture enter/leave events, as
// enter/leave events apparently not bubbled in GTK < 3.24.43.
Expand Down
Loading