aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2021-11-22 16:36:38 +0100
committerPřemysl Eric Janouch <p@janouch.name>2022-07-15 14:00:31 +0200
commitc55500f51ad560273b9d5cd71468baf8b0d3df5a (patch)
tree74360533de00f29921794759827cf08303d1486e
parent1fee920902265cfa8943065d0228cbcc7296f304 (diff)
downloadfiv-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.c8
-rw-r--r--fiv.c85
2 files changed, 91 insertions, 2 deletions
diff --git a/fiv-view.c b/fiv-view.c
index 83173f7..d4a95c9 100644
--- a/fiv-view.c
+++ b/fiv-view.c
@@ -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.
diff --git a/fiv.c b/fiv.c
index f3ac933..3644455 100644
--- a/fiv.c
+++ b/fiv.c
@@ -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),