diff options
author | Přemysl Eric Janouch <p@janouch.name> | 2021-11-22 16:36:38 +0100 |
---|---|---|
committer | Přemysl Eric Janouch <p@janouch.name> | 2022-07-15 14:00:31 +0200 |
commit | c55500f51ad560273b9d5cd71468baf8b0d3df5a (patch) | |
tree | 74360533de00f29921794759827cf08303d1486e | |
parent | 1fee920902265cfa8943065d0228cbcc7296f304 (diff) | |
download | fiv-c55500f51ad560273b9d5cd71468baf8b0d3df5a.tar.gz fiv-c55500f51ad560273b9d5cd71468baf8b0d3df5a.tar.xz fiv-c55500f51ad560273b9d5cd71468baf8b0d3df5a.zip |
Support dragging the view
It would also be possible to handle this through press/motion/release
event handlers, though GtkGestureDrag is more convenient for hacking in
support for dragging to widgets not supporting GtkScrollable (yet).
There may be some undesired interactions lurking, besides the jarring
movements when dragging native GdkWindows (these are a pain).
-rw-r--r-- | fiv-view.c | 8 | ||||
-rw-r--r-- | fiv.c | 85 |
2 files changed, 91 insertions, 2 deletions
@@ -384,8 +384,12 @@ fiv_view_realize(GtkWidget *widget) // Assuming here that we can't ask for a higher-precision Visual // than what we get automatically. .visual = gtk_widget_get_visual(widget), - .event_mask = gtk_widget_get_events(widget) | GDK_SCROLL_MASK | - GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK, + + // Pointer motion/release enables attaching GtkGestureDrag to + // the parent GtkScrolledWindow, having it work with the mouse. + .event_mask = gtk_widget_get_events(widget) | GDK_KEY_PRESS_MASK | + GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK, }; // We need this window to receive input events at all. @@ -1321,6 +1321,73 @@ on_button_press_browser_paned( } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static void +on_view_scroller_drag_begin( + GtkGestureDrag *self, gdouble start_x, gdouble start_y, gpointer user_data) +{ + GtkGesture *gesture = GTK_GESTURE(self); + GdkEventSequence *sequence = gtk_gesture_get_last_updated_sequence(gesture); + GdkModifierType state = 0; + gdk_event_get_state(gtk_gesture_get_last_event(gesture, sequence), &state); + if (state & gtk_accelerator_get_default_mod_mask()) { + gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_DENIED); + return; + } + + // Since we set this up as a pointer-only gesture, there is only the NULL + // sequence, so gtk_gesture_set_sequence_state() is completely unneeded. + gtk_gesture_set_state(gesture, GTK_EVENT_SEQUENCE_CLAIMED); + + GdkWindow *window = gtk_widget_get_window( + gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self))); + GdkCursor *cursor = + gdk_cursor_new_from_name(gdk_window_get_display(window), "grabbing"); + gdk_window_set_cursor(window, cursor); + g_object_unref(cursor); + + double *last = user_data; + last[0] = start_x; + last[1] = start_y; +} + +static void +on_view_scroller_drag(GtkGestureDrag *self, gdouble offset_x, gdouble offset_y, + gpointer user_data) +{ + double start_x = 0, start_y = 0; + gtk_gesture_drag_get_start_point(self, &start_x, &start_y); + + double *last = user_data, + diff_x = (start_x + offset_x) - last[0], + diff_y = (start_y + offset_y) - last[1]; + + last[0] = start_x + offset_x; + last[1] = start_y + offset_y; + + GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW( + gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self))); + GtkAdjustment *h = gtk_scrolled_window_get_hadjustment(sw); + GtkAdjustment *v = gtk_scrolled_window_get_vadjustment(sw); + + if (diff_x) + gtk_adjustment_set_value(h, gtk_adjustment_get_value(h) - diff_x); + if (diff_y) + gtk_adjustment_set_value(v, gtk_adjustment_get_value(v) - diff_y); +} + +static void +on_view_scroller_drag_end(GtkGestureDrag *self, G_GNUC_UNUSED gdouble start_x, + G_GNUC_UNUSED gdouble start_y, G_GNUC_UNUSED gpointer user_data) +{ + GdkWindow *window = gtk_widget_get_window( + gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self))); + gdk_window_set_cursor(window, NULL); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + static GtkWidget * make_toolbar_button(const char *symbolic, const char *tooltip) { @@ -1891,6 +1958,24 @@ main(int argc, char *argv[]) G_CALLBACK(on_view_drag_data_received), NULL); gtk_container_add(GTK_CONTAINER(view_scroller), g.view); + GtkGesture *drag = gtk_gesture_drag_new(view_scroller); + gtk_event_controller_set_propagation_phase( + GTK_EVENT_CONTROLLER(drag), GTK_PHASE_CAPTURE); + + // GtkScrolledWindow's internal GtkGestureDrag is set to only look for + // touch events (and its "event_controllers" are perfectly private, + // so we can't change this), hopefully this is mutually exclusive with that. + // Though note that the GdkWindow doesn't register for touch events now. + gtk_gesture_single_set_exclusive(GTK_GESTURE_SINGLE(drag), TRUE); + + double last_drag_point[2] = {}; + g_signal_connect(drag, "drag-begin", + G_CALLBACK(on_view_scroller_drag_begin), last_drag_point); + g_signal_connect(drag, "drag-update", + G_CALLBACK(on_view_scroller_drag), last_drag_point); + g_signal_connect(drag, "drag-end", + G_CALLBACK(on_view_scroller_drag_end), last_drag_point); + // We need to hide it together with the separator. g.view_toolbar = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_box_pack_start(GTK_BOX(g.view_toolbar), |