aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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),