aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fiv-view.c70
1 files changed, 70 insertions, 0 deletions
diff --git a/fiv-view.c b/fiv-view.c
index 98c8eb6..76fcdcb 100644
--- a/fiv-view.c
+++ b/fiv-view.c
@@ -77,6 +77,8 @@ struct _FivView {
bool fixate : 1; ///< Keep zoom and position
double scale; ///< Scaling factor
double drag_start[2]; ///< Adjustment values for drag origin
+ double zoom_gesture_center[2]; ///< Pinch gesture widget coordinates
+ double zoom_gesture_surface[2]; ///< Pinch gesture surface coordinates
FivIoImage *enhance_swap; ///< Quick swap in/out
FivIoProfile *screen_cms_profile; ///< Target colour profile for widget
@@ -1354,6 +1356,63 @@ fiv_view_unmap(GtkWidget *widget)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
+on_zoom_begin(GtkGestureZoom *gesture,
+ G_GNUC_UNUSED GdkEventSequence *sequence, gpointer user_data)
+{
+ FivView *self = FIV_VIEW(user_data);
+ self->drag_start[0] = self->scale;
+
+ // Store widget coordinates and convert to surface coordinates ONCE.
+ double x = 0, y = 0;
+ gtk_gesture_get_bounding_box_center(GTK_GESTURE(gesture), &x, &y);
+ self->zoom_gesture_center[0] = x;
+ self->zoom_gesture_center[1] = y;
+
+ widget_to_surface(self, &x, &y);
+ self->zoom_gesture_surface[0] = x;
+ self->zoom_gesture_surface[1] = y;
+}
+
+static void
+on_zoom_scale_changed(
+ GtkGestureZoom *gesture, gdouble scale, gpointer user_data)
+{
+ FivView *self = FIV_VIEW(user_data);
+ double new_scale = self->drag_start[0] * scale;
+
+ if (self->scale == new_scale)
+ return;
+
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(GTK_WIDGET(self), &allocation);
+
+ // Use the stored widget and surface coordinates from gesture start.
+ double focus_x = self->zoom_gesture_center[0];
+ double focus_y = self->zoom_gesture_center[1];
+ double surface_x = self->zoom_gesture_surface[0];
+ double surface_y = self->zoom_gesture_surface[1];
+
+ self->scale = new_scale;
+ g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_SCALE]);
+ prescale_page(self);
+
+ if (self->hadjustment && self->vadjustment) {
+ Dimensions surface_dimensions = get_surface_dimensions(self);
+ update_adjustments(self);
+
+ if (surface_dimensions.width * self->scale > allocation.width)
+ gtk_adjustment_set_value(
+ self->hadjustment, surface_x * self->scale - focus_x);
+ if (surface_dimensions.height * self->scale > allocation.height)
+ gtk_adjustment_set_value(
+ self->vadjustment, surface_y * self->scale - focus_y);
+ }
+
+ gtk_widget_queue_resize(GTK_WIDGET(self));
+ set_scale_to_fit(self, false);
+}
+
+static void
on_drag_begin(GtkGestureDrag *drag, G_GNUC_UNUSED gdouble start_x,
G_GNUC_UNUSED gdouble start_y, gpointer user_data)
{
@@ -1903,6 +1962,17 @@ fiv_view_init(FivView *self)
G_CALLBACK(on_drag_update), self);
g_signal_connect(drag, "drag-end",
G_CALLBACK(on_drag_end), self);
+
+ GtkGesture *zoom = gtk_gesture_zoom_new(GTK_WIDGET(self));
+ gtk_event_controller_set_propagation_phase(
+ GTK_EVENT_CONTROLLER(zoom), GTK_PHASE_BUBBLE);
+ g_object_set_data_full(
+ G_OBJECT(self), "fiv-view-zoom-gesture", zoom, g_object_unref);
+
+ g_signal_connect(zoom, "begin",
+ G_CALLBACK(on_zoom_begin), self);
+ g_signal_connect(zoom, "scale-changed",
+ G_CALLBACK(on_zoom_scale_changed), self);
}
// --- Public interface --------------------------------------------------------