Add support for the pinch gesture
All checks were successful
Alpine 3.22 Success
Arch Linux Success
Arch Linux AUR Success
Debian Bookworm Success
Fedora 39 Success
OpenBSD 7.8 Success
openSUSE 15.5 Success

Given the way our viewport works, it behaves a bit oddly.
This commit is contained in:
2025-12-15 19:10:10 +01:00
parent fd60959d41
commit f2f69c7c46

View File

@@ -77,6 +77,9 @@ 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_initial_scale; ///< Pinch gesture initial scale
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
@@ -1128,28 +1131,14 @@ widget_to_surface(FivView *self, double *x, double *y)
}
static gboolean
set_scale(FivView *self, double scale, const GdkEvent *event)
set_scale_with_focus(FivView *self, double scale,
double focus_x, double focus_y, double surface_x, double surface_y)
{
// FIXME: Zooming to exactly 1:1 breaks rendering with some images
// when using a native X11 Window. This is a silly workaround.
GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(self));
if (window && gdk_window_has_native(window) && scale == 1)
scale = 0.999999999999999;
if (self->scale == scale)
goto out;
GtkAllocation allocation;
gtk_widget_get_allocation(GTK_WIDGET(self), &allocation);
double focus_x = 0, focus_y = 0;
if (!event || !gdk_event_get_coords(event, &focus_x, &focus_y)) {
focus_x = 0.5 * allocation.width;
focus_y = 0.5 * allocation.height;
}
double surface_x = focus_x;
double surface_y = focus_y;
widget_to_surface(self, &surface_x, &surface_y);
self->scale = scale;
g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_SCALE]);
@@ -1174,6 +1163,31 @@ out:
return set_scale_to_fit(self, false);
}
static gboolean
set_scale(FivView *self, double scale, const GdkEvent *event)
{
// FIXME: Zooming to exactly 1:1 breaks rendering with some images
// when using a native X11 Window. This is a silly workaround.
GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(self));
if (window && gdk_window_has_native(window) && scale == 1)
scale = 0.999999999999999;
GtkAllocation allocation;
gtk_widget_get_allocation(GTK_WIDGET(self), &allocation);
double focus_x = 0, focus_y = 0;
if (!event || !gdk_event_get_coords(event, &focus_x, &focus_y)) {
focus_x = 0.5 * allocation.width;
focus_y = 0.5 * allocation.height;
}
double surface_x = focus_x;
double surface_y = focus_y;
widget_to_surface(self, &surface_x, &surface_y);
return set_scale_with_focus(
self, scale, focus_x, focus_y, surface_x, surface_y);
}
static void
set_scale_to_fit_width(FivView *self)
{
@@ -1418,6 +1432,33 @@ on_drag_end(GtkGestureDrag *drag, G_GNUC_UNUSED gdouble start_x,
gdk_window_set_cursor(window, NULL);
}
static void
on_zoom_begin(GtkGestureZoom *gesture,
G_GNUC_UNUSED GdkEventSequence *sequence, gpointer user_data)
{
FivView *self = FIV_VIEW(user_data);
self->zoom_gesture_initial_scale = self->scale;
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(
G_GNUC_UNUSED GtkGestureZoom *gesture, gdouble scale, gpointer user_data)
{
FivView *self = FIV_VIEW(user_data);
set_scale_with_focus(self, self->zoom_gesture_initial_scale * scale,
self->zoom_gesture_center[0], self->zoom_gesture_center[1],
self->zoom_gesture_surface[0], self->zoom_gesture_surface[1]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
@@ -1903,6 +1944,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 --------------------------------------------------------