diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-07-01 21:29:36 +0200 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-07-01 21:29:36 +0200 |
commit | 9e3746a4e42f98cad7dfe5b41779ffa81bd4366a (patch) | |
tree | 8e0bdb9af1c275d33c92b0f649821cfe4cbcef91 /src | |
parent | 39249ed57170f09e7dd2fc76c62b51d90a2a4d76 (diff) | |
download | sciteco-9e3746a4e42f98cad7dfe5b41779ffa81bd4366a.tar.gz |
GTK: handle (smooth) scrolling with a scroll controller
* On interfaces, which only support smooth scrolling, we had to emulate
discrete events. Moreover, once we enabled GDK_SMOOTH_SCROLL_MASK,
systems that would previously report discrete UP/DOWN events, would
suddenly report GDK_SCROLL_SMOOTH.
* Converting from smooth scroll events to discrete scroll events
turned out to be trickier than anticipated.
Scrolling was therefore more sluggish than it used to be before
2f448c976889fe60aba8557b5aa4aa0a0d939281.
* Scrolling is therefore now delegated to a GtkEventControllerScroll,
which is used to synthesize a discrete GDK_SCROLL event that's
fed into the usual event pipeline via teco_interface_input_cb().
Diffstat (limited to 'src')
-rw-r--r-- | src/interface-gtk/interface.c | 57 |
1 files changed, 42 insertions, 15 deletions
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index 3a2a971..3d33972 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -69,6 +69,8 @@ static void teco_interface_cmdline_commit_cb(GtkIMContext *context, gchar *str, gpointer user_data); static gboolean teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data); +static void teco_interface_scroll_cb(GtkEventControllerScroll *controller, + double dx, double dy, gpointer data); static void teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong len, gpointer user_data); static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, @@ -124,6 +126,7 @@ static struct { GtkWidget *info_name_widget; GtkWidget *event_box_widget; + GtkEventController *scroll_controller; GtkWidget *message_bar_widget; GtkWidget *message_widget; @@ -260,8 +263,17 @@ teco_interface_init(void) G_CALLBACK(teco_interface_input_cb), NULL); g_signal_connect(teco_interface.event_box_widget, "button-release-event", G_CALLBACK(teco_interface_input_cb), NULL); - g_signal_connect(teco_interface.event_box_widget, "scroll-event", - G_CALLBACK(teco_interface_input_cb), NULL); + + /* + * On some platforms only GDK_SCROLL_SMOOTH events are reported, which are hard + * to translate to discrete scroll events, as required by the `4EJ` API. + * This work is therefore delegated to a scroll controller. + */ + teco_interface.scroll_controller = gtk_event_controller_scroll_new(teco_interface.event_box_widget, + GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | + GTK_EVENT_CONTROLLER_SCROLL_DISCRETE); + g_signal_connect(teco_interface.scroll_controller, "scroll", + G_CALLBACK(teco_interface_scroll_cb), NULL); teco_interface.message_bar_widget = gtk_info_bar_new(); gtk_widget_set_name(teco_interface.message_bar_widget, "sciteco-message-bar"); @@ -1114,8 +1126,6 @@ teco_interface_handle_mouse_button(GdkEventButton *event, GError **error) static gboolean teco_interface_handle_scroll(GdkEventScroll *event, GError **error) { - static gdouble delta_y = 0; - g_assert(event->type == GDK_SCROLL); switch (event->direction) { @@ -1125,15 +1135,6 @@ teco_interface_handle_scroll(GdkEventScroll *event, GError **error) case GDK_SCROLL_DOWN: teco_mouse.type = TECO_MOUSE_SCROLLDOWN; break; - case GDK_SCROLL_SMOOTH: - /* emulate discrete scrolling */ - delta_y += event->delta_y; - if (ABS(delta_y) < 12) - return TRUE; - teco_mouse.type = delta_y < 0 ? TECO_MOUSE_SCROLLUP - : TECO_MOUSE_SCROLLDOWN; - delta_y = 0; - break; default: return TRUE; } @@ -1282,6 +1283,8 @@ teco_interface_cleanup(void) if (teco_interface.window) gtk_widget_destroy(teco_interface.window); + if (teco_interface.scroll_controller) + g_object_unref(teco_interface.scroll_controller); scintilla_release_resources(); @@ -1468,6 +1471,28 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) } static void +teco_interface_scroll_cb(GtkEventControllerScroll *controller, double dx, double dy, gpointer data) +{ + /* + * FIXME: Using teco_interface.event_box_widget will cause crashes in + * teco_interface_input_cb() + */ + GtkWidget *widget = teco_interface.window; + + /* + * Emulate a GDK_SCROLL event to make use of the existing + * event queuing in teco_interface_input_cb(). + */ + g_autoptr(GdkEvent) scroll_event = gdk_event_new(GDK_SCROLL); + scroll_event->scroll.window = gtk_widget_get_parent_window(widget); + scroll_event->scroll.direction = dy > 0 ? GDK_SCROLL_DOWN : GDK_SCROLL_UP; + scroll_event->scroll.delta_x = dx; + scroll_event->scroll.delta_y = dy; + + teco_interface_input_cb(widget, scroll_event, NULL); +} + +static void teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong len, gpointer user_data) { g_assert(len >= teco_interface.popup_prefix_len); @@ -1494,7 +1519,6 @@ teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer { /* * Emulate that the "close" key was pressed - * which may then be handled by the execution thread * which invokes the appropriate "function key macro" * if it exists. Its default action will ensure that * the execution thread shuts down and the main loop @@ -1513,8 +1537,11 @@ teco_interface_sigterm_handler(gpointer user_data) /* * Similar to window deletion - emulate "close" key press. */ + GtkWidget *widget = teco_interface.window; + g_autoptr(GdkEvent) close_event = gdk_event_new(GDK_KEY_PRESS); + close_event->key.window = gtk_widget_get_parent_window(widget); close_event->key.keyval = GDK_KEY_Close; - return teco_interface_input_cb(teco_interface.window, close_event, NULL); + return teco_interface_input_cb(widget, close_event, NULL); } |