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).
This commit is contained in:
Přemysl Eric Janouch 2021-11-22 16:36:38 +01:00
parent 1fee920902
commit c55500f51a
Signed by: p
GPG Key ID: A0420B94F92B9493
2 changed files with 91 additions and 2 deletions

View File

@ -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.

85
fiv.c
View File

@ -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),